useDb hook
This commit is contained in:
parent
48407d9198
commit
59ff5de651
@ -8,151 +8,149 @@ import Event from "Nostr/Event";
|
|||||||
import { Subscriptions } from "Nostr/Subscriptions";
|
import { Subscriptions } from "Nostr/Subscriptions";
|
||||||
import { addDirectMessage, setFollows, setRelays, setMuted, setBlocked, sendNotification } from "State/Login";
|
import { addDirectMessage, setFollows, setRelays, setMuted, setBlocked, sendNotification } from "State/Login";
|
||||||
import { RootState } from "State/Store";
|
import { RootState } from "State/Store";
|
||||||
import { mapEventToProfile, MetadataCache } from "State/Users";
|
import { mapEventToProfile, MetadataCache } from "State/Users";
|
||||||
import { getDb } from "State/Users/Db";
|
import { useDb } from "State/Users/Db";
|
||||||
import useSubscription from "Feed/Subscription";
|
import useSubscription from "Feed/Subscription";
|
||||||
import { getDisplayName } from "Element/ProfileImage";
|
|
||||||
import { barierNip07 } from "Feed/EventPublisher";
|
import { barierNip07 } from "Feed/EventPublisher";
|
||||||
import { getMutedKeys, getNewest } from "Feed/MuteList";
|
import { getMutedKeys, getNewest } from "Feed/MuteList";
|
||||||
import { MentionRegex } from "Const";
|
|
||||||
import useModeration from "Hooks/useModeration";
|
import useModeration from "Hooks/useModeration";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Managed loading data for the current logged in user
|
* Managed loading data for the current logged in user
|
||||||
*/
|
*/
|
||||||
export default function useLoginFeed() {
|
export default function useLoginFeed() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { publicKey: pubKey, privateKey: privKey } = useSelector((s: RootState) => s.login);
|
const { publicKey: pubKey, privateKey: privKey } = useSelector((s: RootState) => s.login);
|
||||||
const { isMuted } = useModeration();
|
const { isMuted } = useModeration();
|
||||||
|
const db = useDb();
|
||||||
|
|
||||||
const subMetadata = useMemo(() => {
|
const subMetadata = useMemo(() => {
|
||||||
if (!pubKey) return null;
|
if (!pubKey) return null;
|
||||||
|
|
||||||
let sub = new Subscriptions();
|
let sub = new Subscriptions();
|
||||||
sub.Id = `login:meta`;
|
sub.Id = `login:meta`;
|
||||||
sub.Authors = new Set([pubKey]);
|
sub.Authors = new Set([pubKey]);
|
||||||
sub.Kinds = new Set([EventKind.ContactList, EventKind.SetMetadata]);
|
sub.Kinds = new Set([EventKind.ContactList, EventKind.SetMetadata]);
|
||||||
sub.Limit = 2
|
sub.Limit = 2
|
||||||
|
|
||||||
return sub;
|
return sub;
|
||||||
}, [pubKey]);
|
}, [pubKey]);
|
||||||
|
|
||||||
const subNotification = useMemo(() => {
|
const subNotification = useMemo(() => {
|
||||||
if (!pubKey) return null;
|
if (!pubKey) return null;
|
||||||
|
|
||||||
let sub = new Subscriptions();
|
let sub = new Subscriptions();
|
||||||
sub.Id = "login:notifications";
|
sub.Id = "login:notifications";
|
||||||
sub.Kinds = new Set([EventKind.TextNote]);
|
sub.Kinds = new Set([EventKind.TextNote]);
|
||||||
sub.PTags = new Set([pubKey]);
|
sub.PTags = new Set([pubKey]);
|
||||||
sub.Limit = 1;
|
sub.Limit = 1;
|
||||||
return sub;
|
return sub;
|
||||||
}, [pubKey]);
|
}, [pubKey]);
|
||||||
|
|
||||||
const subMuted = useMemo(() => {
|
const subMuted = useMemo(() => {
|
||||||
if (!pubKey) return null;
|
if (!pubKey) return null;
|
||||||
|
|
||||||
let sub = new Subscriptions();
|
let sub = new Subscriptions();
|
||||||
sub.Id = "login:muted";
|
sub.Id = "login:muted";
|
||||||
sub.Kinds = new Set([EventKind.Lists]);
|
sub.Kinds = new Set([EventKind.Lists]);
|
||||||
sub.Authors = new Set([pubKey]);
|
sub.Authors = new Set([pubKey]);
|
||||||
sub.DTags = new Set([Lists.Muted]);
|
sub.DTags = new Set([Lists.Muted]);
|
||||||
sub.Limit = 1;
|
sub.Limit = 1;
|
||||||
|
|
||||||
return sub;
|
return sub;
|
||||||
}, [pubKey]);
|
}, [pubKey]);
|
||||||
|
|
||||||
const subDms = useMemo(() => {
|
const subDms = useMemo(() => {
|
||||||
if (!pubKey) return null;
|
if (!pubKey) return null;
|
||||||
|
|
||||||
let dms = new Subscriptions();
|
let dms = new Subscriptions();
|
||||||
dms.Id = "login:dms";
|
dms.Id = "login:dms";
|
||||||
dms.Kinds = new Set([EventKind.DirectMessage]);
|
dms.Kinds = new Set([EventKind.DirectMessage]);
|
||||||
dms.PTags = new Set([pubKey]);
|
dms.PTags = new Set([pubKey]);
|
||||||
|
|
||||||
let dmsFromME = new Subscriptions();
|
let dmsFromME = new Subscriptions();
|
||||||
dmsFromME.Authors = new Set([pubKey]);
|
dmsFromME.Authors = new Set([pubKey]);
|
||||||
dmsFromME.Kinds = new Set([EventKind.DirectMessage]);
|
dmsFromME.Kinds = new Set([EventKind.DirectMessage]);
|
||||||
dms.AddSubscription(dmsFromME);
|
dms.AddSubscription(dmsFromME);
|
||||||
|
|
||||||
return dms;
|
return dms;
|
||||||
}, [pubKey]);
|
}, [pubKey]);
|
||||||
|
|
||||||
const metadataFeed = useSubscription(subMetadata, { leaveOpen: true, cache: true });
|
const metadataFeed = useSubscription(subMetadata, { leaveOpen: true, cache: true });
|
||||||
const notificationFeed = useSubscription(subNotification, { leaveOpen: true, cache: true });
|
const notificationFeed = useSubscription(subNotification, { leaveOpen: true, cache: true });
|
||||||
const dmsFeed = useSubscription(subDms, { leaveOpen: true, cache: true });
|
const dmsFeed = useSubscription(subDms, { leaveOpen: true, cache: true });
|
||||||
const mutedFeed = useSubscription(subMuted, { leaveOpen: true, cache: true });
|
const mutedFeed = useSubscription(subMuted, { leaveOpen: true, cache: true });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let contactList = metadataFeed.store.notes.filter(a => a.kind === EventKind.ContactList);
|
let contactList = metadataFeed.store.notes.filter(a => a.kind === EventKind.ContactList);
|
||||||
let metadata = metadataFeed.store.notes.filter(a => a.kind === EventKind.SetMetadata);
|
let metadata = metadataFeed.store.notes.filter(a => a.kind === EventKind.SetMetadata);
|
||||||
let profiles = metadata.map(a => mapEventToProfile(a))
|
let profiles = metadata.map(a => mapEventToProfile(a))
|
||||||
.filter(a => a !== undefined)
|
.filter(a => a !== undefined)
|
||||||
.map(a => a!);
|
.map(a => a!);
|
||||||
|
|
||||||
for (let cl of contactList) {
|
for (let cl of contactList) {
|
||||||
if (cl.content !== "" && cl.content !== "{}") {
|
if (cl.content !== "" && cl.content !== "{}") {
|
||||||
let relays = JSON.parse(cl.content);
|
let relays = JSON.parse(cl.content);
|
||||||
dispatch(setRelays({ relays, createdAt: cl.created_at }));
|
dispatch(setRelays({ relays, createdAt: cl.created_at }));
|
||||||
}
|
|
||||||
let pTags = cl.tags.filter(a => a[0] === "p").map(a => a[1]);
|
|
||||||
dispatch(setFollows({ keys: pTags, createdAt: cl.created_at }));
|
|
||||||
}
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
let maxProfile = profiles.reduce((acc, v) => {
|
|
||||||
if (v.created > acc.created) {
|
|
||||||
acc.profile = v;
|
|
||||||
acc.created = v.created;
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, { created: 0, profile: null as MetadataCache | null });
|
|
||||||
if (maxProfile.profile) {
|
|
||||||
const db = getDb()
|
|
||||||
let existing = await db.find(maxProfile.profile.pubkey);
|
|
||||||
if ((existing?.created ?? 0) < maxProfile.created) {
|
|
||||||
await db.put(maxProfile.profile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})().catch(console.warn);
|
|
||||||
}, [dispatch, metadataFeed.store]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const replies = notificationFeed.store.notes.filter(a => a.kind === EventKind.TextNote && !isMuted(a.pubkey))
|
|
||||||
replies.forEach(nx => {
|
|
||||||
makeNotification(nx).then(notification => {
|
|
||||||
if (notification) {
|
|
||||||
// @ts-ignore
|
|
||||||
dispatch(sendNotification(notification))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}, [dispatch, notificationFeed.store]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const muted = getMutedKeys(mutedFeed.store.notes)
|
|
||||||
dispatch(setMuted(muted))
|
|
||||||
|
|
||||||
const newest = getNewest(mutedFeed.store.notes)
|
|
||||||
if (newest && newest.content.length > 0 && pubKey) {
|
|
||||||
decryptBlocked(newest, pubKey, privKey).then((plaintext) => {
|
|
||||||
try {
|
|
||||||
const blocked = JSON.parse(plaintext)
|
|
||||||
const keys = blocked.filter((p:any) => p && p.length === 2 && p[0] === "p").map((p: any) => p[1])
|
|
||||||
dispatch(setBlocked({
|
|
||||||
keys,
|
|
||||||
createdAt: newest.created_at,
|
|
||||||
}))
|
|
||||||
} catch(error) {
|
|
||||||
console.debug("Couldn't parse JSON")
|
|
||||||
}
|
|
||||||
}).catch((error) => console.warn(error))
|
|
||||||
}
|
}
|
||||||
}, [dispatch, mutedFeed.store])
|
let pTags = cl.tags.filter(a => a[0] === "p").map(a => a[1]);
|
||||||
|
dispatch(setFollows({ keys: pTags, createdAt: cl.created_at }));
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
(async () => {
|
||||||
let dms = dmsFeed.store.notes.filter(a => a.kind === EventKind.DirectMessage);
|
let maxProfile = profiles.reduce((acc, v) => {
|
||||||
dispatch(addDirectMessage(dms));
|
if (v.created > acc.created) {
|
||||||
}, [dispatch, dmsFeed.store]);
|
acc.profile = v;
|
||||||
|
acc.created = v.created;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, { created: 0, profile: null as MetadataCache | null });
|
||||||
|
if (maxProfile.profile) {
|
||||||
|
let existing = await db.find(maxProfile.profile.pubkey);
|
||||||
|
if ((existing?.created ?? 0) < maxProfile.created) {
|
||||||
|
await db.put(maxProfile.profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})().catch(console.warn);
|
||||||
|
}, [dispatch, metadataFeed.store, db]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const replies = notificationFeed.store.notes.filter(a => a.kind === EventKind.TextNote && !isMuted(a.pubkey))
|
||||||
|
replies.forEach(nx => {
|
||||||
|
makeNotification(db, nx).then(notification => {
|
||||||
|
if (notification) {
|
||||||
|
// @ts-ignore
|
||||||
|
dispatch(sendNotification(notification))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}, [dispatch, notificationFeed.store, db]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const muted = getMutedKeys(mutedFeed.store.notes)
|
||||||
|
dispatch(setMuted(muted))
|
||||||
|
|
||||||
|
const newest = getNewest(mutedFeed.store.notes)
|
||||||
|
if (newest && newest.content.length > 0 && pubKey) {
|
||||||
|
decryptBlocked(newest, pubKey, privKey).then((plaintext) => {
|
||||||
|
try {
|
||||||
|
const blocked = JSON.parse(plaintext)
|
||||||
|
const keys = blocked.filter((p: any) => p && p.length === 2 && p[0] === "p").map((p: any) => p[1])
|
||||||
|
dispatch(setBlocked({
|
||||||
|
keys,
|
||||||
|
createdAt: newest.created_at,
|
||||||
|
}))
|
||||||
|
} catch (error) {
|
||||||
|
console.debug("Couldn't parse JSON")
|
||||||
|
}
|
||||||
|
}).catch((error) => console.warn(error))
|
||||||
|
}
|
||||||
|
}, [dispatch, mutedFeed.store])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let dms = dmsFeed.store.notes.filter(a => a.kind === EventKind.DirectMessage);
|
||||||
|
dispatch(addDirectMessage(dms));
|
||||||
|
}, [dispatch, dmsFeed.store]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { HexKey, TaggedRawEvent } from "Nostr";
|
import { HexKey, TaggedRawEvent } from "Nostr";
|
||||||
import { getDb } from "State/Users/Db";
|
|
||||||
import { ProfileCacheExpire } from "Const";
|
import { ProfileCacheExpire } from "Const";
|
||||||
import { mapEventToProfile, MetadataCache } from "State/Users";
|
import { mapEventToProfile, MetadataCache, UsersDb } from "State/Users";
|
||||||
import Connection, { RelaySettings } from "Nostr/Connection";
|
import Connection, { RelaySettings } from "Nostr/Connection";
|
||||||
import Event from "Nostr/Event";
|
import Event from "Nostr/Event";
|
||||||
import EventKind from "Nostr/EventKind";
|
import EventKind from "Nostr/EventKind";
|
||||||
@ -31,6 +30,11 @@ export class NostrSystem {
|
|||||||
*/
|
*/
|
||||||
WantsMetadata: Set<HexKey>;
|
WantsMetadata: Set<HexKey>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User db store
|
||||||
|
*/
|
||||||
|
UserDb?: UsersDb;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.Sockets = new Map();
|
this.Sockets = new Map();
|
||||||
this.Subscriptions = new Map();
|
this.Subscriptions = new Map();
|
||||||
@ -166,54 +170,54 @@ export class NostrSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _FetchMetadata() {
|
async _FetchMetadata() {
|
||||||
let missing = new Set<HexKey>();
|
if (this.UserDb) {
|
||||||
const db = getDb()
|
let missing = new Set<HexKey>();
|
||||||
let meta = await db.bulkGet(Array.from(this.WantsMetadata));
|
let meta = await this.UserDb.bulkGet(Array.from(this.WantsMetadata));
|
||||||
let expire = new Date().getTime() - ProfileCacheExpire;
|
let expire = new Date().getTime() - ProfileCacheExpire;
|
||||||
for (let pk of this.WantsMetadata) {
|
for (let pk of this.WantsMetadata) {
|
||||||
let m = meta.find(a => a?.pubkey === pk);
|
let m = meta.find(a => a?.pubkey === pk);
|
||||||
if (!m || m.loaded < expire) {
|
if (!m || m.loaded < expire) {
|
||||||
missing.add(pk);
|
missing.add(pk);
|
||||||
// cap 100 missing profiles
|
// cap 100 missing profiles
|
||||||
if (missing.size >= 100) {
|
if (missing.size >= 100) {
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (missing.size > 0) {
|
|
||||||
console.debug("Wants profiles: ", missing);
|
|
||||||
|
|
||||||
let sub = new Subscriptions();
|
|
||||||
sub.Id = `profiles:${sub.Id}`;
|
|
||||||
sub.Kinds = new Set([EventKind.SetMetadata]);
|
|
||||||
sub.Authors = missing;
|
|
||||||
sub.OnEvent = async (e) => {
|
|
||||||
let profile = mapEventToProfile(e);
|
|
||||||
if (profile) {
|
|
||||||
let existing = await db.find(profile.pubkey);
|
|
||||||
if ((existing?.created ?? 0) < profile.created) {
|
|
||||||
await db.put(profile);
|
|
||||||
} else if (existing) {
|
|
||||||
await db.update(profile.pubkey, { loaded: new Date().getTime() });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let results = await this.RequestSubscription(sub);
|
|
||||||
let couldNotFetch = Array.from(missing).filter(a => !results.some(b => b.pubkey === a));
|
|
||||||
console.debug("No profiles: ", couldNotFetch);
|
|
||||||
await db.bulkPut(couldNotFetch.map(a => {
|
|
||||||
return {
|
|
||||||
pubkey: a,
|
|
||||||
loaded: new Date().getTime()
|
|
||||||
} as MetadataCache;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (missing.size > 0) {
|
||||||
|
console.debug("Wants profiles: ", missing);
|
||||||
|
|
||||||
|
let sub = new Subscriptions();
|
||||||
|
sub.Id = `profiles:${sub.Id}`;
|
||||||
|
sub.Kinds = new Set([EventKind.SetMetadata]);
|
||||||
|
sub.Authors = missing;
|
||||||
|
sub.OnEvent = async (e) => {
|
||||||
|
let profile = mapEventToProfile(e);
|
||||||
|
if (profile) {
|
||||||
|
let existing = await this.UserDb!.find(profile.pubkey);
|
||||||
|
if ((existing?.created ?? 0) < profile.created) {
|
||||||
|
await this.UserDb!.put(profile);
|
||||||
|
} else if (existing) {
|
||||||
|
await this.UserDb!.update(profile.pubkey, { loaded: new Date().getTime() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let results = await this.RequestSubscription(sub);
|
||||||
|
let couldNotFetch = Array.from(missing).filter(a => !results.some(b => b.pubkey === a));
|
||||||
|
console.debug("No profiles: ", couldNotFetch);
|
||||||
|
await this.UserDb!.bulkPut(couldNotFetch.map(a => {
|
||||||
|
return {
|
||||||
|
pubkey: a,
|
||||||
|
loaded: new Date().getTime()
|
||||||
|
} as MetadataCache;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
setTimeout(() => this._FetchMetadata(), 500);
|
setTimeout(() => this._FetchMetadata(), 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
async nip42Auth(challenge: string, relay:string): Promise<Event|undefined> {
|
async nip42Auth(challenge: string, relay: string): Promise<Event | undefined> {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,11 @@ import Nostrich from "nostrich.webp";
|
|||||||
import { TaggedRawEvent } from "Nostr";
|
import { TaggedRawEvent } from "Nostr";
|
||||||
import EventKind from "Nostr/EventKind";
|
import EventKind from "Nostr/EventKind";
|
||||||
import type { NotificationRequest } from "State/Login";
|
import type { NotificationRequest } from "State/Login";
|
||||||
import { MetadataCache } from "State/Users";
|
import { MetadataCache, UsersDb } from "State/Users";
|
||||||
import { getDb } from "State/Users/Db";
|
|
||||||
import { getDisplayName } from "Element/ProfileImage";
|
import { getDisplayName } from "Element/ProfileImage";
|
||||||
import { MentionRegex } from "Const";
|
import { MentionRegex } from "Const";
|
||||||
|
|
||||||
export async function makeNotification(ev: TaggedRawEvent): Promise<NotificationRequest | null> {
|
export async function makeNotification(db: UsersDb, ev: TaggedRawEvent): Promise<NotificationRequest | null> {
|
||||||
const db = getDb()
|
|
||||||
switch (ev.kind) {
|
switch (ev.kind) {
|
||||||
case EventKind.TextNote: {
|
case EventKind.TextNote: {
|
||||||
const pubkeys = new Set([ev.pubkey, ...ev.tags.filter(a => a[0] === "p").map(a => a[1]!)]);
|
const pubkeys = new Set([ev.pubkey, ...ev.tags.filter(a => a[0] === "p").map(a => a[1]!)]);
|
||||||
|
@ -17,6 +17,7 @@ import { totalUnread } from "Pages/MessagesPage";
|
|||||||
import { SearchRelays } from 'Const';
|
import { SearchRelays } from 'Const';
|
||||||
import useEventPublisher from "Feed/EventPublisher";
|
import useEventPublisher from "Feed/EventPublisher";
|
||||||
import useModeration from "Hooks/useModeration";
|
import useModeration from "Hooks/useModeration";
|
||||||
|
import { IndexedUDB, useDb } from "State/Users/Db";
|
||||||
|
|
||||||
|
|
||||||
export default function Layout() {
|
export default function Layout() {
|
||||||
@ -31,12 +32,17 @@ export default function Layout() {
|
|||||||
const { isMuted } = useModeration();
|
const { isMuted } = useModeration();
|
||||||
const filteredDms = dms.filter(a => !isMuted(a.pubkey))
|
const filteredDms = dms.filter(a => !isMuted(a.pubkey))
|
||||||
const prefs = useSelector<RootState, UserPreferences>(s => s.login.preferences);
|
const prefs = useSelector<RootState, UserPreferences>(s => s.login.preferences);
|
||||||
|
const usingDb = useDb();
|
||||||
const pub = useEventPublisher();
|
const pub = useEventPublisher();
|
||||||
useLoginFeed();
|
useLoginFeed();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
System.nip42Auth = pub.nip42Auth
|
System.nip42Auth = pub.nip42Auth
|
||||||
},[pub])
|
}, [pub])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
System.UserDb = usingDb;
|
||||||
|
}, [usingDb])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (relays) {
|
if (relays) {
|
||||||
@ -73,7 +79,14 @@ export default function Layout() {
|
|||||||
}, [prefs.theme]);
|
}, [prefs.theme]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(init());
|
// check DB support then init
|
||||||
|
IndexedUDB.isAvailable()
|
||||||
|
.then(a => {
|
||||||
|
const db = a ? "indexdDb" : "redux";
|
||||||
|
console.debug(`Using db: ${db}`);
|
||||||
|
dispatch(init(db));
|
||||||
|
})
|
||||||
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function goToNotifications(e: any) {
|
async function goToNotifications(e: any) {
|
||||||
|
@ -61,8 +61,14 @@ export interface UserPreferences {
|
|||||||
*/
|
*/
|
||||||
imgProxyConfig: ImgProxySettings | null
|
imgProxyConfig: ImgProxySettings | null
|
||||||
}
|
}
|
||||||
|
export type DbType = "indexdDb" | "redux";
|
||||||
|
|
||||||
export interface LoginStore {
|
export interface LoginStore {
|
||||||
|
/**
|
||||||
|
* Which db we will use to cache data
|
||||||
|
*/
|
||||||
|
useDb: DbType,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If there is no login
|
* If there is no login
|
||||||
*/
|
*/
|
||||||
@ -146,6 +152,7 @@ const DefaultImgProxy = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const InitState = {
|
export const InitState = {
|
||||||
|
useDb: "redux",
|
||||||
loggedOut: undefined,
|
loggedOut: undefined,
|
||||||
publicKey: undefined,
|
publicKey: undefined,
|
||||||
privateKey: undefined,
|
privateKey: undefined,
|
||||||
@ -186,7 +193,8 @@ const LoginSlice = createSlice({
|
|||||||
name: "Login",
|
name: "Login",
|
||||||
initialState: InitState,
|
initialState: InitState,
|
||||||
reducers: {
|
reducers: {
|
||||||
init: (state) => {
|
init: (state, action: PayloadAction<DbType>) => {
|
||||||
|
state.useDb = action.payload;
|
||||||
state.privateKey = window.localStorage.getItem(PrivateKeyItem) ?? undefined;
|
state.privateKey = window.localStorage.getItem(PrivateKeyItem) ?? undefined;
|
||||||
if (state.privateKey) {
|
if (state.privateKey) {
|
||||||
window.localStorage.removeItem(PublicKeyItem); // reset nip07 if using private key
|
window.localStorage.removeItem(PublicKeyItem); // reset nip07 if using private key
|
||||||
|
@ -2,9 +2,10 @@ import { HexKey } from "Nostr";
|
|||||||
import { db as idb } from "Db";
|
import { db as idb } from "Db";
|
||||||
|
|
||||||
import { UsersDb, MetadataCache, setUsers } from "State/Users";
|
import { UsersDb, MetadataCache, setUsers } from "State/Users";
|
||||||
import store from "State/Store";
|
import store, { RootState } from "State/Store";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
class IndexedDb implements UsersDb {
|
class IndexedUsersDb implements UsersDb {
|
||||||
ready: boolean = false;
|
ready: boolean = false;
|
||||||
|
|
||||||
isAvailable() {
|
isAvailable() {
|
||||||
@ -132,21 +133,13 @@ class ReduxUsersDb implements UsersDb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const IndexedUDB = new IndexedUsersDb();
|
||||||
|
export const ReduxUDB = new ReduxUsersDb();
|
||||||
|
|
||||||
const indexedDb = new IndexedDb()
|
export function useDb(): UsersDb {
|
||||||
export const inMemoryDb = new ReduxUsersDb()
|
const db = useSelector((s: RootState) => s.login.useDb);
|
||||||
|
switch (db) {
|
||||||
let db: UsersDb = inMemoryDb
|
case "indexdDb": return IndexedUDB
|
||||||
indexedDb.isAvailable().then((available) => {
|
default: return ReduxUDB
|
||||||
if (available) {
|
|
||||||
console.debug('Using Indexed DB')
|
|
||||||
indexedDb.ready = true;
|
|
||||||
db = indexedDb;
|
|
||||||
} else {
|
|
||||||
console.debug('Using in-memory DB')
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
export function getDb() {
|
|
||||||
return db
|
|
||||||
}
|
|
@ -1,27 +1,17 @@
|
|||||||
import { useSelector } from "react-redux"
|
import { useSelector } from "react-redux"
|
||||||
import { useLiveQuery } from "dexie-react-hooks";
|
import { useLiveQuery } from "dexie-react-hooks";
|
||||||
import { MetadataCache } from "State/Users";
|
import { MetadataCache } from "State/Users";
|
||||||
import { getDb, inMemoryDb } from "State/Users/Db";
|
|
||||||
import type { RootState } from "State/Store"
|
import type { RootState } from "State/Store"
|
||||||
import { HexKey } from "Nostr";
|
import { HexKey } from "Nostr";
|
||||||
|
import { useDb } from "./Db";
|
||||||
|
|
||||||
export function useQuery(query: string, limit: number = 5) {
|
export function useQuery(query: string, limit: number = 5) {
|
||||||
const db = getDb()
|
const db = useDb()
|
||||||
|
return useLiveQuery(async () => db.query(query), [query],)
|
||||||
const allUsers = useLiveQuery(
|
|
||||||
() => db.query(query)
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err)
|
|
||||||
return inMemoryDb.query(query)
|
|
||||||
}),
|
|
||||||
[query],
|
|
||||||
)
|
|
||||||
|
|
||||||
return allUsers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useKey(pubKey: HexKey) {
|
export function useKey(pubKey: HexKey) {
|
||||||
const db = getDb()
|
const db = useDb()
|
||||||
const { users } = useSelector((state: RootState) => state.users)
|
const { users } = useSelector((state: RootState) => state.users)
|
||||||
const defaultUser = users[pubKey]
|
const defaultUser = users[pubKey]
|
||||||
|
|
||||||
@ -40,7 +30,9 @@ export function useKey(pubKey: HexKey) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useKeys(pubKeys: HexKey[]): Map<HexKey, MetadataCache> {
|
export function useKeys(pubKeys: HexKey[]): Map<HexKey, MetadataCache> {
|
||||||
const db = getDb()
|
const db = useDb()
|
||||||
|
const { users } = useSelector((state: RootState) => state.users)
|
||||||
|
|
||||||
const dbUsers = useLiveQuery(async () => {
|
const dbUsers = useLiveQuery(async () => {
|
||||||
if (pubKeys) {
|
if (pubKeys) {
|
||||||
try {
|
try {
|
||||||
@ -48,12 +40,11 @@ export function useKeys(pubKeys: HexKey[]): Map<HexKey, MetadataCache> {
|
|||||||
return new Map(ret.map(a => [a.pubkey, a]))
|
return new Map(ret.map(a => [a.pubkey, a]))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
const ret = await inMemoryDb.bulkGet(pubKeys);
|
return new Map(pubKeys.map(a => [a, users[a]]))
|
||||||
return new Map(ret.map(a => [a.pubkey, a]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new Map()
|
return new Map()
|
||||||
}, [pubKeys]);
|
}, [pubKeys, users]);
|
||||||
|
|
||||||
return dbUsers!
|
return dbUsers!
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user