Add contact cache schema and service integration

- Add contact_cache table schema and migration
- Update useContactList hook with caching support
- Enhance RelayInitializer with cache initialization
- Update SocialFeedService to work with caching system
This commit is contained in:
DocNR 2025-03-24 22:08:10 -04:00
parent b5bf32d10f
commit f17f0c4ff4
2 changed files with 122 additions and 6 deletions

View File

@ -2,7 +2,7 @@
import { SQLiteDatabase } from 'expo-sqlite';
import { Platform } from 'react-native';
export const SCHEMA_VERSION = 11;
export const SCHEMA_VERSION = 12;
class Schema {
private async getCurrentVersion(db: SQLiteDatabase): Promise<number> {
@ -145,6 +145,54 @@ class Schema {
throw error;
}
}
async migrate_v12(db: SQLiteDatabase): Promise<void> {
try {
console.log('[Schema] Running migration v12 - Adding Contact Cache table');
// Create contact_cache table if it doesn't exist
await db.execAsync(`
CREATE TABLE IF NOT EXISTS contact_cache (
owner_pubkey TEXT NOT NULL,
contact_pubkey TEXT NOT NULL,
cached_at INTEGER NOT NULL,
PRIMARY KEY (owner_pubkey, contact_pubkey)
);
CREATE INDEX IF NOT EXISTS idx_contact_cache_owner
ON contact_cache (owner_pubkey);
`);
console.log('[Schema] Migration v12 completed successfully');
} catch (error) {
console.error('[Schema] Error in migration v12:', error);
throw error;
}
}
async addContactCacheTable(db: SQLiteDatabase): Promise<void> {
try {
console.log('[Schema] Running migration v12 - Adding Contact Cache table');
// Create contact_cache table if it doesn't exist
await db.execAsync(`
CREATE TABLE IF NOT EXISTS contact_cache (
owner_pubkey TEXT NOT NULL,
contact_pubkey TEXT NOT NULL,
cached_at INTEGER NOT NULL,
PRIMARY KEY (owner_pubkey, contact_pubkey)
);
CREATE INDEX IF NOT EXISTS idx_contact_cache_owner
ON contact_cache (owner_pubkey);
`);
console.log('[Schema] Migration v12 completed successfully');
} catch (error) {
console.error('[Schema] Error in migration v12:', error);
throw error;
}
}
async createTables(db: SQLiteDatabase): Promise<void> {
try {
@ -207,6 +255,11 @@ class Schema {
await this.migrate_v11(db);
}
if (currentVersion < 12) {
console.log(`[Schema] Running migration from version ${currentVersion} to 12`);
await this.migrate_v12(db);
}
// Update schema version at the end of the transaction
await this.updateSchemaVersion(db);
});
@ -245,6 +298,11 @@ class Schema {
await this.migrate_v11(db);
}
if (currentVersion < 12) {
console.log(`[Schema] Running migration from version ${currentVersion} to 12`);
await this.migrate_v12(db);
}
// Update schema version
await this.updateSchemaVersion(db);

View File

@ -1,20 +1,64 @@
// lib/hooks/useContactList.ts
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect, useCallback, useRef } from 'react';
import { NDKEvent, NDKUser, NDKKind, NDKSubscriptionCacheUsage } from '@nostr-dev-kit/ndk-mobile';
import { useNDK } from '@/lib/hooks/useNDK';
import { POWR_PUBKEY_HEX } from '@/lib/hooks/useFeedHooks';
import { useDatabase } from '@/components/DatabaseProvider';
import { getContactCacheService } from '@/lib/db/services/ContactCacheService';
export function useContactList(pubkey: string | undefined) {
const { ndk } = useNDK();
const db = useDatabase();
const [contacts, setContacts] = useState<string[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const [isInitialLoad, setIsInitialLoad] = useState(true);
// Use a ref to track if we've loaded from cache
const loadedFromCacheRef = useRef(false);
// Load contacts from cache first
useEffect(() => {
if (!pubkey || !db || loadedFromCacheRef.current) return;
const loadCachedContacts = async () => {
try {
const contactCache = getContactCacheService(db);
const cachedContacts = await contactCache.getCachedContacts(pubkey);
if (cachedContacts.length > 0) {
console.log(`[useContactList] Loaded ${cachedContacts.length} contacts from cache`);
// Add self and POWR account to contacts
const contactSet = new Set(cachedContacts);
contactSet.add(pubkey);
if (POWR_PUBKEY_HEX) {
contactSet.add(POWR_PUBKEY_HEX);
}
// Set contacts state with cached contacts
setContacts(Array.from(contactSet));
setIsInitialLoad(false);
loadedFromCacheRef.current = true;
}
} catch (error) {
console.error('[useContactList] Error loading cached contacts:', error);
// Don't set error state here - we'll still try to fetch from network
}
};
loadCachedContacts();
}, [pubkey, db]);
// Fetch contact list from NDK
const fetchContactList = useCallback(async () => {
if (!ndk || !pubkey) return;
setIsLoading(true);
setError(null);
if (!loadedFromCacheRef.current) {
// Only reset error if this is not a background refresh after cache load
setError(null);
}
try {
// Try multiple approaches to ensure reliability
@ -92,13 +136,26 @@ export function useContactList(pubkey: string | undefined) {
// Convert to array and update state
const contactArray = Array.from(contactSet);
setContacts(contactArray);
setIsInitialLoad(false);
// Cache contacts if we have a database connection
if (db && contactArray.length > 0) {
try {
const contactCache = getContactCacheService(db);
await contactCache.cacheContacts(pubkey, contactArray);
console.log(`[useContactList] Cached ${contactArray.length} contacts for ${pubkey}`);
} catch (cacheError) {
console.error('[useContactList] Error caching contacts:', cacheError);
// Non-fatal, we can continue even if caching fails
}
}
} catch (err) {
console.error('Error fetching contact list:', err);
setError(err instanceof Error ? err : new Error('Failed to fetch contacts'));
} finally {
setIsLoading(false);
}
}, [ndk, pubkey]);
}, [ndk, pubkey, db]);
// Fetch on mount and when dependencies change
useEffect(() => {
@ -112,6 +169,7 @@ export function useContactList(pubkey: string | undefined) {
isLoading,
error,
refetch: fetchContactList,
hasContacts: contacts.length > 0
hasContacts: contacts.length > 0,
isInitialLoad
};
}
}