Browse Source

Fix web UI reactivity and center login prompts

- Fix BlossomView blob list not refreshing after upload by replacing
  getDisplayBlobs() function call with reactive $: displayBlobs declaration
- Fix EventsView not reflecting login state by moving permission check
  from EventsView template to App.svelte where reactive variables live
- Await fetchUserRole() in loadRelayData() to prevent race condition
  during auto-login from localStorage
- Trigger explicit event loading after handleLogin() completes
- Center login prompts and permission messages in main content panel
  using margin:auto and align-items:center on main-content flex container
- Add warning log for blossom upload auth validation failures
- Remove temporary debug logging from blossom handlers
- Bump version to v0.58.7

Files modified:
- app/web/src/App.svelte: Permission gating, event loading, centered prompts
- app/web/src/BlossomView.svelte: Reactive displayBlobs declaration
- app/web/src/EventsView.svelte: Remove internal permission checks
- pkg/blossom/handlers.go: Clean up debug logging, add auth failure warning
- pkg/version/version: v0.58.7

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
imwald-v0.58.10
woikos 4 months ago
parent
commit
7d7f16dee7
No known key found for this signature in database
  1. 4
      app/web/dist/bundle.css
  2. 28
      app/web/dist/bundle.js
  3. 2
      app/web/dist/bundle.js.map
  4. 50
      app/web/src/App.svelte
  5. 14
      app/web/src/BlossomView.svelte
  6. 19
      app/web/src/EventsView.svelte
  7. 4
      pkg/blossom/handlers.go
  8. 2
      pkg/version/version

4
app/web/dist/bundle.css vendored

File diff suppressed because one or more lines are too long

28
app/web/dist/bundle.js vendored

File diff suppressed because one or more lines are too long

2
app/web/dist/bundle.js.map vendored

File diff suppressed because one or more lines are too long

50
app/web/src/App.svelte

@ -911,9 +911,9 @@
async function loadRelayData() { async function loadRelayData() {
// Fetch user role for already logged in users // Fetch user role for already logged in users
if (isLoggedIn) { if (isLoggedIn) {
fetchUserRole(); await fetchUserRole();
} }
fetchACLMode(); await fetchACLMode();
// Load sprocket configuration // Load sprocket configuration
loadSprocketConfig(); loadSprocketConfig();
@ -1966,6 +1966,14 @@
// Fetch user role/permissions // Fetch user role/permissions
await fetchUserRole(); await fetchUserRole();
await fetchACLMode(); await fetchACLMode();
// Trigger event loading if currently on events tab
if (selectedTab === "events") {
hasAttemptedEventLoad = false;
const authors =
showOnlyMyEvents && userPubkey ? [userPubkey] : null;
loadAllEvents(true, authors);
}
} }
function handleLogout() { function handleLogout() {
@ -3040,6 +3048,7 @@
on:openLoginModal={openLoginModal} on:openLoginModal={openLoginModal}
/> />
{:else if selectedTab === "events"} {:else if selectedTab === "events"}
{#if isLoggedIn && (userRole === "read" || userRole === "write" || userRole === "admin" || userRole === "owner")}
<EventsView <EventsView
{isLoggedIn} {isLoggedIn}
{userRole} {userRole}
@ -3061,6 +3070,16 @@
on:filterApply={handleFilterApply} on:filterApply={handleFilterApply}
on:filterClear={handleFilterClear} on:filterClear={handleFilterClear}
/> />
{:else if isLoggedIn && !userRole}
<div class="events-loading-permissions">
<div class="spinner"></div>
<p>Checking permissions...</p>
</div>
{:else}
<div class="permission-denied">
<p>Read, write, admin, or owner permission required to view events.</p>
</div>
{/if}
{:else if selectedTab === "blossom"} {:else if selectedTab === "blossom"}
{#key $relayUrl} {#key $relayUrl}
<BlossomView <BlossomView
@ -3851,10 +3870,9 @@
background-color: var(--bg-color); background-color: var(--bg-color);
color: var(--text-color); color: var(--text-color);
display: flex; display: flex;
align-items: flex-start; align-items: center;
justify-content: flex-start; justify-content: flex-start;
flex-direction: column; flex-direction: column;
display: flex;
} }
.welcome-message { .welcome-message {
@ -4797,4 +4815,28 @@
.change-relay-btn:hover { .change-relay-btn:hover {
background: #00acc1; background: #00acc1;
} }
/* Centered permission/login prompts within main-content */
.events-loading-permissions,
.permission-denied,
.access-denied {
margin: auto;
text-align: center;
color: var(--text-color);
}
.main-content :global(.login-prompt),
.main-content :global(.permission-denied) {
margin: auto;
}
.events-loading-permissions .spinner {
width: 24px;
height: 24px;
border: 2px solid var(--border-color);
border-top-color: var(--primary);
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 1em;
}
</style> </style>

14
app/web/src/BlossomView.svelte

@ -38,6 +38,7 @@
$: canAccess = isLoggedIn && userPubkey; $: canAccess = isLoggedIn && userPubkey;
$: isAdmin = currentEffectiveRole === "admin" || currentEffectiveRole === "owner"; $: isAdmin = currentEffectiveRole === "admin" || currentEffectiveRole === "owner";
$: displayBlobs = selectedAdminUser ? selectedUserBlobs : blobs;
// Track if we've loaded once to prevent repeated loads // Track if we've loaded once to prevent repeated loads
let hasLoadedOnce = false; let hasLoadedOnce = false;
@ -429,12 +430,7 @@
} }
} }
function getDisplayBlobs() {
if (selectedAdminUser) {
return selectedUserBlobs;
}
return blobs;
}
</script> </script>
<svelte:window on:keydown={handleKeydown} /> <svelte:window on:keydown={handleKeydown} />
@ -549,15 +545,15 @@
{/if} {/if}
{:else} {:else}
<!-- Normal blob list view (own files or selected user's files) --> <!-- Normal blob list view (own files or selected user's files) -->
{#if isLoading && getDisplayBlobs().length === 0} {#if isLoading && displayBlobs.length === 0}
<div class="loading">Loading blobs...</div> <div class="loading">Loading blobs...</div>
{:else if getDisplayBlobs().length === 0} {:else if displayBlobs.length === 0}
<div class="empty-state"> <div class="empty-state">
<p>{selectedAdminUser ? "No files found for this user." : "No files found in your Blossom storage."}</p> <p>{selectedAdminUser ? "No files found for this user." : "No files found in your Blossom storage."}</p>
</div> </div>
{:else} {:else}
<div class="blob-list"> <div class="blob-list">
{#each getDisplayBlobs() as blob} {#each displayBlobs as blob}
<div <div
class="blob-item" class="blob-item"
on:click={() => openModal(blob)} on:click={() => openModal(blob)}

19
app/web/src/EventsView.svelte

@ -121,7 +121,6 @@
</script> </script>
<div class="events-view-container"> <div class="events-view-container">
{#if isLoggedIn && (userRole === "read" || userRole === "write" || userRole === "admin" || userRole === "owner")}
<div class="events-view-content" on:scroll={handleScroll}> <div class="events-view-content" on:scroll={handleScroll}>
{#if filteredEvents.length > 0} {#if filteredEvents.length > 0}
{#each filteredEvents as event} {#each filteredEvents as event}
@ -230,15 +229,6 @@
</div> </div>
{/if} {/if}
</div> </div>
{:else}
<div class="permission-denied">
<p>
❌ Read, write, admin, or owner permission required to view all
events.
</p>
</div>
{/if}
{#if isLoggedIn && (userRole === "read" || userRole === "write" || userRole === "admin" || userRole === "owner")}
<div class="events-view-footer"> <div class="events-view-footer">
<!-- Filter Builder Slide-up Panel --> <!-- Filter Builder Slide-up Panel -->
<div class="filter-panel" class:open={showFilterBuilder}> <div class="filter-panel" class:open={showFilterBuilder}>
@ -307,7 +297,6 @@
</div> </div>
</div> </div>
</div> </div>
{/if}
</div> </div>
<style> <style>
@ -554,14 +543,6 @@
} }
} }
.permission-denied {
text-align: center;
padding: 2em;
background-color: var(--card-bg);
border-radius: 8px;
border: 1px solid var(--border-color);
color: var(--text-color);
}
.events-view-footer { .events-view-footer {
position: relative; position: relative;

4
pkg/blossom/handlers.go

@ -183,9 +183,11 @@ func (s *Server) handleUpload(w http.ResponseWriter, r *http.Request) {
// Optional authorization validation (do this BEFORE ACL check) // Optional authorization validation (do this BEFORE ACL check)
// For upload, we don't pass sha256Hash because upload auth events don't have 'x' tags // For upload, we don't pass sha256Hash because upload auth events don't have 'x' tags
// (the hash isn't known at auth event creation time) // (the hash isn't known at auth event creation time)
if r.Header.Get(AuthorizationHeader) != "" { authHeader := r.Header.Get(AuthorizationHeader)
if authHeader != "" {
authEv, err := ValidateAuthEvent(r, "upload", nil) authEv, err := ValidateAuthEvent(r, "upload", nil)
if err != nil { if err != nil {
log.W.F("blossom upload: auth validation failed: %v", err)
s.setErrorResponse(w, http.StatusUnauthorized, err.Error()) s.setErrorResponse(w, http.StatusUnauthorized, err.Error())
return return
} }

2
pkg/version/version

@ -1 +1 @@
v0.58.6 v0.58.7

Loading…
Cancel
Save