+
+
{#if loading}
-
Loading bookmarks and highlights...
+
Loading bookmarks...
{:else if error}
- {:else if allItems.length === 0}
+ {:else if events.length === 0}
-
No bookmarks or highlights found.
+
No bookmarks yet. Bookmark events to see them here.
{:else}
-
-
-
-
-
-
- {#if currentUserPubkey}
-
-
-
- {/if}
-
-
-
-
-
- {#if totalPages > 1 && !searchResults.events.length && !searchResults.profiles.length}
-
- {/if}
-
-
- {#if searchResults.events.length > 0 || searchResults.profiles.length > 0}
-
-
Search Results
-
- {#if searchResults.profiles.length > 0}
-
-
Profiles
-
- {#each searchResults.profiles as pubkey}
-
-
-
- {/each}
-
-
- {/if}
-
- {#if searchResults.events.length > 0}
-
-
Events ({searchResults.events.length})
-
- {#each paginatedSearchEvents as event}
- {#if event.kind === KIND.HIGHLIGHTED_ARTICLE}
-
- goto(`/event/${e.id}`)} />
-
- {:else}
-
-
-
- {/if}
- {/each}
-
-
- {/if}
-
- {:else}
-
-
- Showing {paginatedItems.length} of {filteredItems.length} items
- {#if allItems.length >= maxTotalItems}
- (limited to {maxTotalItems})
- {/if}
- {#if filterResult.value}
- (filtered)
- {/if}
-
-
-
-
- {#each paginatedItems as item (item.event.id)}
-
- {#if item.type === 'highlight'}
-
goto(`/event/${event.id}`)} />
- {:else}
-
-
-
-
- {/if}
+
+ {#each paginatedEvents as event}
+
+
{/each}
-
- {#if totalPages > 1}
-
- {/if}
+
+ {#if events.length > ITEMS_PER_PAGE}
+
{/if}
{/if}
diff --git a/src/routes/cache/+page.svelte b/src/routes/cache/+page.svelte
index 6485b29..2e4aaf2 100644
--- a/src/routes/cache/+page.svelte
+++ b/src/routes/cache/+page.svelte
@@ -14,7 +14,7 @@
import { getCacheStats, getAllCachedEvents, clearAllCache, clearCacheByKind, clearCacheByKinds, clearCacheByDate, deleteEventById, type CacheStats } from '../../lib/services/cache/cache-manager.js';
import { cacheEvent } from '../../lib/services/cache/event-cache.js';
import type { CachedEvent } from '../../lib/services/cache/event-cache.js';
- import { getArchiveStats, clearOldArchivedEvents } from '../../lib/services/cache/event-archive.js';
+ import { getArchiveStats, clearOldArchivedEvents, recoverAllArchivedEvents, recoverArchivedEventById, isEventArchived } from '../../lib/services/cache/event-archive.js';
import { triggerArchive } from '../../lib/services/cache/archive-scheduler.js';
import { KIND, getKindInfo } from '../../lib/types/kind-lookup.js';
import { nip19 } from 'nostr-tools';
@@ -42,6 +42,9 @@
let offset = $state(0);
const PAGE_SIZE = 50;
let archiving = $state(false);
+ let recovering = $state(false);
+ let eventIdToRecover = $state
('');
+ let recoveringEvent = $state(false);
// Filters
let selectedKind = $state(null);
@@ -107,6 +110,64 @@
}
}
+ async function handleRecoverAllArchived() {
+ if (!confirm('Are you sure you want to recover all archived events? This will restore them to the main cache.')) {
+ return;
+ }
+
+ recovering = true;
+ try {
+ const recovered = await recoverAllArchivedEvents();
+ await loadStats();
+ await loadArchiveStats();
+ await loadEvents(true);
+ alert(`Recovered ${recovered} archived events. They are now back in the main cache.`);
+ } catch (error) {
+ alert('Failed to recover archived events');
+ } finally {
+ recovering = false;
+ }
+ }
+
+ async function handleRecoverEventById() {
+ if (!eventIdToRecover.trim()) {
+ alert('Please enter an event ID');
+ return;
+ }
+
+ // Decode bech32 if needed
+ const hexId = decodeEventIdToHex(eventIdToRecover.trim());
+ if (!hexId) {
+ alert('Invalid event ID format');
+ return;
+ }
+
+ // Check if event is archived
+ const isArchived = await isEventArchived(hexId);
+ if (!isArchived) {
+ alert('Event not found in archive. It may already be in the main cache or may not exist.');
+ return;
+ }
+
+ recoveringEvent = true;
+ try {
+ const recovered = await recoverArchivedEventById(hexId);
+ if (recovered) {
+ await loadStats();
+ await loadArchiveStats();
+ await loadEvents(true);
+ alert('Event recovered successfully. It is now back in the main cache.');
+ eventIdToRecover = '';
+ } else {
+ alert('Failed to recover event');
+ }
+ } catch (error) {
+ alert('Failed to recover event');
+ } finally {
+ recoveringEvent = false;
+ }
+ }
+
async function loadEvents(reset = false) {
if (reset) {
offset = 0;
@@ -621,7 +682,7 @@
{#if archiveStats.oldestArchived}
- Oldest Archived: {new Date(archiveStats.oldestArchived).toLocaleDateString()}
+ Oldest Archived: {new Date(archiveStats.oldestArchived * 1000).toLocaleDateString()}
{/if}
@@ -633,6 +694,13 @@
>
{archiving ? 'Archiving...' : 'Archive Events Older Than 30 Days'}
+
Archived events are compressed to save space but remain accessible.
They are automatically decompressed when needed.
@@ -873,6 +963,38 @@
color: var(--fog-dark-text-light, #a8b8d0);
}
+ .recover-event-section {
+ margin-top: 1.5rem;
+ padding-top: 1.5rem;
+ border-top: 1px solid var(--fog-border, #e5e7eb);
+ }
+
+ :global(.dark) .recover-event-section {
+ border-top-color: var(--fog-dark-border, #475569);
+ }
+
+ .recover-event-section h3 {
+ margin: 0 0 1rem 0;
+ font-size: 1.1em;
+ color: var(--fog-text, #1f2937);
+ }
+
+ :global(.dark) .recover-event-section h3 {
+ color: var(--fog-dark-text, #f9fafb);
+ }
+
+ .recover-event-input-group {
+ display: flex;
+ gap: 0.75rem;
+ align-items: flex-start;
+ flex-wrap: wrap;
+ }
+
+ .recover-event-input-group .filter-input {
+ flex: 1;
+ min-width: 250px;
+ }
+
.bulk-actions-section,
.events-section {
margin-bottom: 2rem;
diff --git a/src/routes/find/+page.svelte b/src/routes/find/+page.svelte
index 051e611..d8ac3d2 100644
--- a/src/routes/find/+page.svelte
+++ b/src/routes/find/+page.svelte
@@ -1,25 +1,26 @@