mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-10 15:36:53 +02:00
* feat: Display blossom servers in user menu with caching Implements caching and display of user's blossom server lists (kind 10063) in the user menu dropdown. **Key Changes:** 1. **Database Schema (db.ts)**: - Added `CachedBlossomServerList` interface - Added `blossomServers` table to Dexie v15 - Indexed by pubkey and updatedAt for efficient querying 2. **Caching Service (blossom-server-cache.ts)**: - Dual-layer caching: LRU memory cache (100 entries) + Dexie persistent cache - 24-hour TTL to reduce network requests - Auto-caches kind:10063 events from EventStore - Mirrors relay-list-cache pattern for consistency 3. **Sync Hooks**: - `useBlossomServerCacheSync`: Subscribes to EventStore for auto-caching kind:10063 events - `useAccountSync`: Extended to fetch and watch active account's blossom servers 4. **State Management**: - Added `blossomServers` field to `activeAccount` in GrimoireState - Created `setActiveAccountBlossomServers` pure function and callback - Reactive updates when user's server list changes 5. **User Menu UI (user-menu.tsx)**: - Added blossom servers section with HardDrive icon - Shows server count badge (e.g., "3 servers") - Clickable links open servers in new tabs - Displays only when user has configured servers **Architecture:** - Follows existing relay list caching pattern for consistency - Reactive: UI auto-updates when kind:10063 events arrive - Incremental sync: Fetches on login, caches for 24h - Performance: Memory cache for <1ms lookups, Dexie for persistence **Testing:** - Build: ✓ No TypeScript errors - Tests: ✓ All 838 tests passing * feat: Open blossom server file lists directly from menus **User Menu & Profile Viewer Improvements:** 1. **Enhanced Click Behavior**: - Clicking a blossom server now opens the file list for that server - Shows blobs uploaded by the user (user menu) or profile owner (profile viewer) - Pre-selects the clicked server in the dropdown 2. **UX Improvements**: - Removed server count from user menu label (cleaner UI) - Added `cursor-crosshair` to blossom server items (consistent with other clickable items) - Removed external link icon (not opening external URL anymore) 3. **Technical Changes**: - Updated `ListBlobsView` to accept optional `serverUrl` prop for pre-selection - User menu: Opens `blossom list` with `serverUrl` for active user - Profile viewer: Opens `blossom list` with both `pubkey` and `serverUrl` **Flow:** - User menu → Click server → Opens files for active user on that server - Profile viewer → Click server → Opens files for viewed user on that server * fix: Properly fetch blossom servers for any profile view **Problem:** Blossom servers were only visible for the logged-in user's profile, not for other users' profiles being viewed. **Solution:** Enhanced ProfileViewer blossom server fetching with multi-layer approach: 1. **Cache-first loading**: Check blossomServerCache for instant display 2. **EventStore check**: Use existing cached event if available 3. **Reactive subscription**: Subscribe to EventStore for real-time updates 4. **Network fetch**: Use addressLoader to fetch latest from relays 5. **Auto-caching**: Update cache when new events arrive **Benefits:** - Blossom servers now display for ANY user's profile - Instant display from cache (< 1ms) - Reactive updates when data changes - Proper cache hydration for future visits - Consistent with relay list fetching pattern **Technical:** - Imported and integrated blossomServerCache service - Added cache check before network fetch - Separated EventStore subscription from network fetch - Added cache updates on event arrival --------- Co-authored-by: Claude <noreply@anthropic.com>
113 lines
3.0 KiB
TypeScript
113 lines
3.0 KiB
TypeScript
import type { MosaicNode } from "react-mosaic-component";
|
|
import type { GlobalRelayState } from "./relay-state";
|
|
|
|
export type AppId =
|
|
| "nip"
|
|
| "nips"
|
|
| "kind"
|
|
| "kinds"
|
|
| "man"
|
|
| "req"
|
|
//| "event"
|
|
| "open"
|
|
| "profile"
|
|
| "encode"
|
|
| "decode"
|
|
| "relay"
|
|
| "debug"
|
|
| "conn"
|
|
| "chat"
|
|
| "spells"
|
|
| "spellbooks"
|
|
| "blossom"
|
|
| "win";
|
|
|
|
export interface WindowInstance {
|
|
id: string;
|
|
appId: AppId;
|
|
title?: string; // Legacy field - rarely used now that DynamicWindowTitle handles all titles
|
|
customTitle?: string; // User-provided custom title via --title flag (overrides dynamic title)
|
|
props: any;
|
|
commandString?: string; // Original command that created this window (e.g., "profile alice@domain.com")
|
|
spellId?: string; // ID of the spell that created this window (if any)
|
|
}
|
|
|
|
/**
|
|
* Configuration for how new windows are inserted into the workspace layout tree
|
|
*/
|
|
export interface LayoutConfig {
|
|
/**
|
|
* How to determine split direction for new windows
|
|
* - 'smart': Auto-balance horizontal/vertical splits (recommended)
|
|
* - 'row': Always horizontal splits (side-by-side)
|
|
* - 'column': Always vertical splits (stacked)
|
|
*/
|
|
insertionMode: "smart" | "row" | "column";
|
|
|
|
/**
|
|
* Split percentage for new windows (10-90)
|
|
* Example: 70 means existing content gets 70%, new window gets 30%
|
|
*/
|
|
splitPercentage: number;
|
|
|
|
/**
|
|
* Where to place the new window
|
|
* - 'first': Left (for row) or Top (for column)
|
|
* - 'second': Right (for row) or Bottom (for column)
|
|
*/
|
|
insertionPosition: "first" | "second";
|
|
|
|
/**
|
|
* Optional: Auto-maintain a preset layout structure
|
|
* When set, system tries to preserve this preset when adding windows
|
|
*/
|
|
autoPreset?: string;
|
|
}
|
|
|
|
export interface Workspace {
|
|
id: string;
|
|
number: number; // Numeric identifier for shortcuts (e.g., Cmd+1, Cmd+2)
|
|
label?: string; // Optional user-editable label
|
|
layout: MosaicNode<string> | null;
|
|
windowIds: string[];
|
|
}
|
|
|
|
export interface RelayInfo {
|
|
url: string;
|
|
read: boolean;
|
|
write: boolean;
|
|
}
|
|
|
|
export interface GrimoireState {
|
|
__version: number; // Schema version for migrations
|
|
windows: Record<string, WindowInstance>;
|
|
workspaces: Record<string, Workspace>;
|
|
activeWorkspaceId: string;
|
|
layoutConfig: LayoutConfig; // Global configuration for window insertion behavior
|
|
activeAccount?: {
|
|
pubkey: string;
|
|
relays?: RelayInfo[];
|
|
blossomServers?: string[];
|
|
};
|
|
compactModeKinds?: number[];
|
|
locale?: {
|
|
locale: string;
|
|
language: string;
|
|
region?: string;
|
|
timezone: string;
|
|
timeFormat: "12h" | "24h";
|
|
};
|
|
relayState?: GlobalRelayState;
|
|
activeSpellbook?: {
|
|
id: string; // event id or local uuid
|
|
slug: string; // d-tag
|
|
title: string;
|
|
description?: string;
|
|
pubkey?: string; // owner's pubkey (undefined = local-only, never published)
|
|
// Enhanced fields for better UX:
|
|
source: "local" | "network"; // Where the spellbook was loaded from
|
|
localId?: string; // Local DB ID if saved to library
|
|
isPublished?: boolean; // Whether it has been published to Nostr
|
|
};
|
|
}
|