review / cleanup

This commit is contained in:
2023-02-09 12:26:54 +00:00
parent a03b385e55
commit dbae89837f
129 changed files with 678 additions and 2303 deletions

View File

@ -16,9 +16,7 @@ declare global {
nostr: {
getPublicKey: () => Promise<HexKey>;
signEvent: (event: RawEvent) => Promise<RawEvent>;
getRelays: () => Promise<
Record<string, { read: boolean; write: boolean }>
>;
getRelays: () => Promise<Record<string, { read: boolean; write: boolean }>>;
nip04: {
encrypt: (pubkey: HexKey, content: string) => Promise<string>;
decrypt: (pubkey: HexKey, content: string) => Promise<string>;
@ -28,26 +26,17 @@ declare global {
}
export default function useEventPublisher() {
const pubKey = useSelector<RootState, HexKey | undefined>(
(s) => s.login.publicKey
);
const privKey = useSelector<RootState, HexKey | undefined>(
(s) => s.login.privateKey
);
const follows = useSelector<RootState, HexKey[]>((s) => s.login.follows);
const pubKey = useSelector<RootState, HexKey | undefined>(s => s.login.publicKey);
const privKey = useSelector<RootState, HexKey | undefined>(s => s.login.privateKey);
const follows = useSelector<RootState, HexKey[]>(s => s.login.follows);
const relays = useSelector((s: RootState) => s.login.relays);
const hasNip07 = "nostr" in window;
async function signEvent(ev: NEvent): Promise<NEvent> {
if (hasNip07 && !privKey) {
ev.Id = await ev.CreateId();
const tmpEv = (await barrierNip07(() =>
window.nostr.signEvent(ev.ToObject())
)) as TaggedRawEvent;
if (!tmpEv.relays) {
tmpEv.relays = [];
}
return new NEvent(tmpEv);
const tmpEv = (await barrierNip07(() => window.nostr.signEvent(ev.ToObject()))) as RawEvent;
return new NEvent(tmpEv as TaggedRawEvent);
} else if (privKey) {
await ev.Sign(privKey);
} else {
@ -125,17 +114,15 @@ export default function useEventPublisher() {
const ev = NEvent.ForPubKey(pubKey);
ev.Kind = EventKind.Lists;
ev.Tags.push(new Tag(["d", Lists.Muted], ev.Tags.length));
keys.forEach((p) => {
keys.forEach(p => {
ev.Tags.push(new Tag(["p", p], ev.Tags.length));
});
let content = "";
if (priv.length > 0) {
const ps = priv.map((p) => ["p", p]);
const ps = priv.map(p => ["p", p]);
const plaintext = JSON.stringify(ps);
if (hasNip07 && !privKey) {
content = await barrierNip07(() =>
window.nostr.nip04.encrypt(pubKey, plaintext)
);
content = await barrierNip07(() => window.nostr.nip04.encrypt(pubKey, plaintext));
} else if (privKey) {
content = await ev.EncryptData(plaintext, pubKey, privKey);
}
@ -165,11 +152,11 @@ export default function useEventPublisher() {
const ev = NEvent.ForPubKey(pubKey);
ev.Kind = EventKind.ZapRequest;
if (note) {
ev.Tags.push(new Tag(["e", note], 0));
ev.Tags.push(new Tag(["e", note], ev.Tags.length));
}
ev.Tags.push(new Tag(["p", author], 0));
ev.Tags.push(new Tag(["p", author], ev.Tags.length));
const relayTag = ["relays", ...Object.keys(relays).slice(0, 10)];
ev.Tags.push(new Tag(relayTag, 0));
ev.Tags.push(new Tag(relayTag, ev.Tags.length));
processContent(ev, msg || "");
return await signEvent(ev);
}
@ -185,17 +172,7 @@ export default function useEventPublisher() {
const thread = replyTo.Thread;
if (thread) {
if (thread.Root || thread.ReplyTo) {
ev.Tags.push(
new Tag(
[
"e",
thread.Root?.Event ?? thread.ReplyTo?.Event ?? "",
"",
"root",
],
ev.Tags.length
)
);
ev.Tags.push(new Tag(["e", thread.Root?.Event ?? thread.ReplyTo?.Event ?? "", "", "root"], ev.Tags.length));
}
ev.Tags.push(new Tag(["e", replyTo.Id, "", "reply"], ev.Tags.length));
@ -243,17 +220,14 @@ export default function useEventPublisher() {
return await signEvent(ev);
}
},
addFollow: async (
pkAdd: HexKey | HexKey[],
newRelays?: Record<string, RelaySettings>
) => {
addFollow: async (pkAdd: HexKey | HexKey[], newRelays?: Record<string, RelaySettings>) => {
if (pubKey) {
const ev = NEvent.ForPubKey(pubKey);
ev.Kind = EventKind.ContactList;
ev.Content = JSON.stringify(newRelays ?? relays);
const temp = new Set(follows);
if (Array.isArray(pkAdd)) {
pkAdd.forEach((a) => temp.add(a));
pkAdd.forEach(a => temp.add(a));
} else {
temp.add(pkAdd);
}
@ -309,21 +283,14 @@ export default function useEventPublisher() {
},
decryptDm: async (note: NEvent): Promise<string | undefined> => {
if (pubKey) {
if (
note.PubKey !== pubKey &&
!note.Tags.some((a) => a.PubKey === pubKey)
) {
if (note.PubKey !== pubKey && !note.Tags.some(a => a.PubKey === pubKey)) {
return "<CANT DECRYPT>";
}
try {
const otherPubKey =
note.PubKey === pubKey
? unwrap(note.Tags.filter((a) => a.Key === "p")[0].PubKey)
: note.PubKey;
note.PubKey === pubKey ? unwrap(note.Tags.filter(a => a.Key === "p")[0].PubKey) : note.PubKey;
if (hasNip07 && !privKey) {
return await barrierNip07(() =>
window.nostr.nip04.decrypt(otherPubKey, note.Content)
);
return await barrierNip07(() => window.nostr.nip04.decrypt(otherPubKey, note.Content));
} else if (privKey) {
await note.DecryptDm(privKey, otherPubKey);
return note.Content;
@ -343,9 +310,7 @@ export default function useEventPublisher() {
try {
if (hasNip07 && !privKey) {
const cx: string = await barrierNip07(() =>
window.nostr.nip04.encrypt(to, content)
);
const cx: string = await barrierNip07(() => window.nostr.nip04.encrypt(to, content));
ev.Content = cx;
return await signEvent(ev);
} else if (privKey) {
@ -363,7 +328,7 @@ export default function useEventPublisher() {
let isNip07Busy = false;
const delay = (t: number) => {
return new Promise((resolve) => {
return new Promise(resolve => {
setTimeout(resolve, t);
});
};

View File

@ -18,11 +18,7 @@ export default function useFollowsFeed(pubkey: HexKey) {
}
export function getFollowers(feed: NoteStore, pubkey: HexKey) {
const contactLists = feed?.notes.filter(
(a) => a.kind === EventKind.ContactList && a.pubkey === pubkey
);
const pTags = contactLists?.map((a) =>
a.tags.filter((b) => b[0] === "p").map((c) => c[1])
);
const contactLists = feed?.notes.filter(a => a.kind === EventKind.ContactList && a.pubkey === pubkey);
const pTags = contactLists?.map(a => a.tags.filter(b => b[0] === "p").map(c => c[1]));
return [...new Set(pTags?.flat())];
}

View File

@ -11,9 +11,7 @@ export interface ImgProxySettings {
}
export default function useImgProxy() {
const settings = useSelector(
(s: RootState) => s.login.preferences.imgProxyConfig
);
const settings = useSelector((s: RootState) => s.login.preferences.imgProxyConfig);
const te = new TextEncoder();
function urlSafe(s: string) {
@ -34,9 +32,7 @@ export default function useImgProxy() {
if (!settings) return url;
const opt = resize ? `rs:fit:${resize}:${resize}` : "";
const urlBytes = te.encode(url);
const urlEncoded = urlSafe(
base64.encode(urlBytes, 0, urlBytes.byteLength)
);
const urlEncoded = urlSafe(base64.encode(urlBytes, 0, urlBytes.byteLength));
const path = `/${opt}/${urlEncoded}`;
const sig = await signUrl(path);
return `${new URL(settings.url).toString()}${sig}${path}`;

View File

@ -23,6 +23,7 @@ import { barrierNip07 } from "Feed/EventPublisher";
import { getMutedKeys, getNewest } from "Feed/MuteList";
import useModeration from "Hooks/useModeration";
import { unwrap } from "Util";
import { AnyAction, ThunkDispatch } from "@reduxjs/toolkit";
/**
* Managed loading data for the current logged in user
@ -103,23 +104,19 @@ export default function useLoginFeed() {
const mutedFeed = useSubscription(subMuted, { leaveOpen: true, cache: true });
useEffect(() => {
const contactList = metadataFeed.store.notes.filter(
(a) => a.kind === EventKind.ContactList
);
const metadata = metadataFeed.store.notes.filter(
(a) => a.kind === EventKind.SetMetadata
);
const contactList = metadataFeed.store.notes.filter(a => a.kind === EventKind.ContactList);
const metadata = metadataFeed.store.notes.filter(a => a.kind === EventKind.SetMetadata);
const profiles = metadata
.map((a) => mapEventToProfile(a))
.filter((a) => a !== undefined)
.map((a) => unwrap(a));
.map(a => mapEventToProfile(a))
.filter(a => a !== undefined)
.map(a => unwrap(a));
for (const cl of contactList) {
if (cl.content !== "" && cl.content !== "{}") {
const relays = JSON.parse(cl.content);
dispatch(setRelays({ relays, createdAt: cl.created_at }));
}
const pTags = cl.tags.filter((a) => a[0] === "p").map((a) => a[1]);
const pTags = cl.tags.filter(a => a[0] === "p").map(a => a[1]);
dispatch(setFollows({ keys: pTags, createdAt: cl.created_at }));
}
@ -145,17 +142,13 @@ export default function useLoginFeed() {
useEffect(() => {
const replies = notificationFeed.store.notes.filter(
(a) =>
a.kind === EventKind.TextNote &&
!isMuted(a.pubkey) &&
a.created_at > readNotifications
a => a.kind === EventKind.TextNote && !isMuted(a.pubkey) && a.created_at > readNotifications
);
replies.forEach((nx) => {
replies.forEach(nx => {
dispatch(setLatestNotifications(nx.created_at));
makeNotification(db, nx).then((notification) => {
makeNotification(db, nx).then(notification => {
if (notification) {
// @ts-expect-error This is typed wrong, but I don't have the time to fix it right now
dispatch(sendNotification(notification));
(dispatch as ThunkDispatch<RootState, undefined, AnyAction>)(sendNotification(notification));
}
});
});
@ -166,19 +159,12 @@ export default function useLoginFeed() {
dispatch(setMuted(muted));
const newest = getNewest(mutedFeed.store.notes);
if (
newest &&
newest.content.length > 0 &&
pubKey &&
newest.created_at > latestMuted
) {
if (newest && newest.content.length > 0 && pubKey && newest.created_at > latestMuted) {
decryptBlocked(newest, pubKey, privKey)
.then((plaintext) => {
.then(plaintext => {
try {
const blocked = JSON.parse(plaintext);
const keys = blocked
.filter((p: string) => p && p.length === 2 && p[0] === "p")
.map((p: string) => p[1]);
const keys = blocked.filter((p: string) => p && p.length === 2 && p[0] === "p").map((p: string) => p[1]);
dispatch(
setBlocked({
keys,
@ -189,29 +175,21 @@ export default function useLoginFeed() {
console.debug("Couldn't parse JSON");
}
})
.catch((error) => console.warn(error));
.catch(error => console.warn(error));
}
}, [dispatch, mutedFeed.store]);
useEffect(() => {
const dms = dmsFeed.store.notes.filter(
(a) => a.kind === EventKind.DirectMessage
);
const dms = dmsFeed.store.notes.filter(a => a.kind === EventKind.DirectMessage);
dispatch(addDirectMessage(dms));
}, [dispatch, dmsFeed.store]);
}
async function decryptBlocked(
raw: TaggedRawEvent,
pubKey: HexKey,
privKey?: HexKey
) {
async function decryptBlocked(raw: TaggedRawEvent, pubKey: HexKey, privKey?: HexKey) {
const ev = new Event(raw);
if (pubKey && privKey) {
return await ev.DecryptData(raw.content, privKey, pubKey);
} else {
return await barrierNip07(() =>
window.nostr.nip04.decrypt(pubKey, raw.content)
);
return await barrierNip07(() => window.nostr.nip04.decrypt(pubKey, raw.content));
}
}

View File

@ -34,7 +34,7 @@ export function getMutedKeys(rawNotes: TaggedRawEvent[]): {
const newest = getNewest(rawNotes);
if (newest) {
const { created_at, tags } = newest;
const keys = tags.filter((t) => t[0] === "p").map((t) => t[1]);
const keys = tags.filter(t => t[0] === "p").map(t => t[1]);
return {
keys,
createdAt: created_at,
@ -44,8 +44,6 @@ export function getMutedKeys(rawNotes: TaggedRawEvent[]): {
}
export function getMuted(feed: NoteStore, pubkey: HexKey): HexKey[] {
const lists = feed?.notes.filter(
(a) => a.kind === EventKind.Lists && a.pubkey === pubkey
);
const lists = feed?.notes.filter(a => a.kind === EventKind.Lists && a.pubkey === pubkey);
return getMutedKeys(lists).keys;
}

View File

@ -4,7 +4,7 @@ import { useKey, useKeys } from "State/Users/Hooks";
import { HexKey } from "Nostr";
import { System } from "Nostr/System";
export function useUserProfile(pubKey: HexKey): MetadataCache | undefined {
export function useUserProfile(pubKey?: HexKey): MetadataCache | undefined {
const users = useKey(pubKey);
useEffect(() => {
@ -17,9 +17,7 @@ export function useUserProfile(pubKey: HexKey): MetadataCache | undefined {
return users;
}
export function useUserProfiles(
pubKeys: Array<HexKey>
): Map<HexKey, MetadataCache> | undefined {
export function useUserProfiles(pubKeys?: Array<HexKey>): Map<HexKey, MetadataCache> | undefined {
const users = useKeys(pubKeys);
useEffect(() => {

View File

@ -25,7 +25,7 @@ function notesReducer(state: NoteStore, arg: ReducerArg) {
if (arg.type === "END") {
return {
notes: state.notes,
end: arg.end ?? false,
end: arg.end ?? true,
} as NoteStore;
}
@ -40,8 +40,8 @@ function notesReducer(state: NoteStore, arg: ReducerArg) {
if (!(evs instanceof Array)) {
evs = evs === undefined ? [] : [evs];
}
const existingIds = new Set(state.notes.map((a) => a.id));
evs = evs.filter((a) => !existingIds.has(a.id));
const existingIds = new Set(state.notes.map(a => a.id));
evs = evs.filter(a => !existingIds.has(a.id));
if (evs.length === 0) {
return state;
}
@ -99,7 +99,7 @@ export default function useSubscription(
if (useCache) {
// preload notes from db
PreloadNotes(subDebounce.Id)
.then((ev) => {
.then(ev => {
dispatch({
type: "EVENT",
ev: ev,
@ -107,7 +107,7 @@ export default function useSubscription(
})
.catch(console.warn);
}
subDebounce.OnEvent = (e) => {
subDebounce.OnEvent = e => {
dispatch({
type: "EVENT",
ev: e,
@ -117,7 +117,7 @@ export default function useSubscription(
}
};
subDebounce.OnEnd = (c) => {
subDebounce.OnEnd = c => {
if (!(options?.leaveOpen ?? false)) {
c.RemoveSubscription(subDebounce.Id);
if (subDebounce.IsFinished()) {
@ -149,7 +149,7 @@ export default function useSubscription(
useEffect(() => {
return debounce(DebounceMs, () => {
setDebounceOutput((s) => (s += 1));
setDebounceOutput(s => (s += 1));
});
}, [state]);
@ -175,23 +175,15 @@ const PreloadNotes = async (id: string): Promise<TaggedRawEvent[]> => {
const feed = await db.feeds.get(id);
if (feed) {
const events = await db.events.bulkGet(feed.ids);
return events.filter((a) => a !== undefined).map((a) => unwrap(a));
return events.filter(a => a !== undefined).map(a => unwrap(a));
}
return [];
};
const TrackNotesInFeed = async (id: string, notes: TaggedRawEvent[]) => {
const existing = await db.feeds.get(id);
const ids = Array.from(
new Set([...(existing?.ids || []), ...notes.map((a) => a.id)])
);
const since = notes.reduce(
(acc, v) => (acc > v.created_at ? v.created_at : acc),
+Infinity
);
const until = notes.reduce(
(acc, v) => (acc < v.created_at ? v.created_at : acc),
-Infinity
);
const ids = Array.from(new Set([...(existing?.ids || []), ...notes.map(a => a.id)]));
const since = notes.reduce((acc, v) => (acc > v.created_at ? v.created_at : acc), +Infinity);
const until = notes.reduce((acc, v) => (acc < v.created_at ? v.created_at : acc), -Infinity);
await db.feeds.put({ id, ids, since, until });
};

View File

@ -10,14 +10,12 @@ import { debounce } from "Util";
export default function useThreadFeed(id: u256) {
const [trackingEvents, setTrackingEvent] = useState<u256[]>([id]);
const pref = useSelector<RootState, UserPreferences>(
(s) => s.login.preferences
);
const pref = useSelector<RootState, UserPreferences>(s => s.login.preferences);
function addId(id: u256[]) {
setTrackingEvent((s) => {
setTrackingEvent(s => {
const orig = new Set(s);
if (id.some((a) => !orig.has(a))) {
if (id.some(a => !orig.has(a))) {
const tmp = new Set([...s, ...id]);
return Array.from(tmp);
} else {
@ -35,13 +33,7 @@ export default function useThreadFeed(id: u256) {
const subRelated = new Subscriptions();
subRelated.Kinds = new Set(
pref.enableReactions
? [
EventKind.Reaction,
EventKind.TextNote,
EventKind.Deletion,
EventKind.Repost,
EventKind.ZapReceipt,
]
? [EventKind.Reaction, EventKind.TextNote, EventKind.Deletion, EventKind.Repost, EventKind.ZapReceipt]
: [EventKind.TextNote]
);
subRelated.ETags = thisSub.Ids;
@ -55,15 +47,13 @@ export default function useThreadFeed(id: u256) {
useEffect(() => {
if (main.store) {
return debounce(200, () => {
const mainNotes = main.store.notes.filter(
(a) => a.kind === EventKind.TextNote
);
const mainNotes = main.store.notes.filter(a => a.kind === EventKind.TextNote);
const eTags = mainNotes
.filter((a) => a.kind === EventKind.TextNote)
.map((a) => a.tags.filter((b) => b[0] === "e").map((b) => b[1]))
.filter(a => a.kind === EventKind.TextNote)
.map(a => a.tags.filter(b => b[0] === "e").map(b => b[1]))
.flat();
const ids = mainNotes.map((a) => a.id);
const ids = mainNotes.map(a => a.id);
const allEvents = new Set([...eTags, ...ids]);
addId(Array.from(allEvents));
});

View File

@ -19,19 +19,14 @@ export interface TimelineSubject {
items: string[];
}
export default function useTimelineFeed(
subject: TimelineSubject,
options: TimelineFeedOptions
) {
export default function useTimelineFeed(subject: TimelineSubject, options: TimelineFeedOptions) {
const now = unixNow();
const [window] = useState<number>(options.window ?? 60 * 60);
const [until, setUntil] = useState<number>(now);
const [since, setSince] = useState<number>(now - window);
const [trackingEvents, setTrackingEvent] = useState<u256[]>([]);
const [trackingParentEvents, setTrackingParentEvents] = useState<u256[]>([]);
const pref = useSelector<RootState, UserPreferences>(
(s) => s.login.preferences
);
const pref = useSelector<RootState, UserPreferences>(s => s.login.preferences);
const createSub = useCallback(() => {
if (subject.type !== "global" && subject.items.length === 0) {
@ -116,12 +111,7 @@ export default function useTimelineFeed(
if (trackingEvents.length > 0 && pref.enableReactions) {
sub = new Subscriptions();
sub.Id = `timeline-related:${subject.type}`;
sub.Kinds = new Set([
EventKind.Reaction,
EventKind.Repost,
EventKind.Deletion,
EventKind.ZapReceipt,
]);
sub.Kinds = new Set([EventKind.Reaction, EventKind.Repost, EventKind.Deletion, EventKind.ZapReceipt]);
sub.ETags = new Set(trackingEvents);
}
return sub ?? null;
@ -143,21 +133,21 @@ export default function useTimelineFeed(
useEffect(() => {
if (main.store.notes.length > 0) {
setTrackingEvent((s) => {
const ids = main.store.notes.map((a) => a.id);
if (ids.some((a) => !s.includes(a))) {
setTrackingEvent(s => {
const ids = main.store.notes.map(a => a.id);
if (ids.some(a => !s.includes(a))) {
return Array.from(new Set([...s, ...ids]));
}
return s;
});
const reposts = main.store.notes
.filter((a) => a.kind === EventKind.Repost && a.content === "")
.map((a) => a.tags.find((b) => b[0] === "e"))
.filter((a) => a)
.map((a) => unwrap(a)[1]);
.filter(a => a.kind === EventKind.Repost && a.content === "")
.map(a => a.tags.find(b => b[0] === "e"))
.filter(a => a)
.map(a => unwrap(a)[1]);
if (reposts.length > 0) {
setTrackingParentEvents((s) => {
if (reposts.some((a) => !s.includes(a))) {
setTrackingParentEvents(s => {
if (reposts.some(a => !s.includes(a))) {
const temp = new Set([...s, ...reposts]);
return Array.from(temp);
}
@ -175,14 +165,11 @@ export default function useTimelineFeed(
loadMore: () => {
console.debug("Timeline load more!");
if (options.method === "LIMIT_UNTIL") {
const oldest = main.store.notes.reduce(
(acc, v) => (acc = v.created_at < acc ? v.created_at : acc),
unixNow()
);
const oldest = main.store.notes.reduce((acc, v) => (acc = v.created_at < acc ? v.created_at : acc), unixNow());
setUntil(oldest);
} else {
setUntil((s) => s - window);
setSince((s) => s - window);
setUntil(s => s - window);
setSince(s => s - window);
}
},
showLatest: () => {