make settings use rxjs

This commit is contained in:
hzrd149 2023-02-07 17:04:18 -06:00
parent 012466383d
commit 02eb95b59b
13 changed files with 88 additions and 93 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"cSpell.words": ["Chakra", "nostr", "pubkeys"]
}

View File

@ -1,19 +1,12 @@
import React from "react";
import { Spinner } from "@chakra-ui/react";
import { Route, Routes } from "react-router-dom";
import { HomeView } from "./views/home";
import { UserView } from "./views/user";
import { ErrorBoundary } from "./components/error-boundary";
import { SettingsView } from "./views/settings";
import { GlobalView } from "./views/global";
import { useRelays } from "./providers/relay-provider";
import { Page } from "./components/page";
export const App = () => {
const { loading } = useRelays();
if (loading) return <Spinner size="xl" />;
return (
<ErrorBoundary>
<Routes>

View File

@ -19,12 +19,15 @@ import moment from "moment";
import { PostModal } from "./post-modal";
import { NostrEvent } from "../types/nostr-event";
import { useUserMetadata } from "../hooks/use-user-metadata";
import useSubject from "../hooks/use-subject";
import settings from "../services/settings";
export type PostProps = {
event: NostrEvent;
};
export const Post = React.memo(({ event }: PostProps) => {
const { isOpen, onClose, onOpen } = useDisclosure();
const corsProxy = useSubject(settings.corsProxy);
const userMetadata = useUserMetadata(event.pubkey);
const isLong = event.content.length > 800;
@ -35,8 +38,8 @@ export const Post = React.memo(({ event }: PostProps) => {
<HStack spacing="4">
<Flex flex="1" gap="4" alignItems="center" flexWrap="wrap">
<Avatar
name={userMetadata?.name}
src="https://bit.ly/sage-adebayo"
name={userMetadata?.display_name ?? userMetadata?.name}
src={userMetadata?.picture}
/>
<Box>

11
src/hooks/use-subject.ts Normal file
View File

@ -0,0 +1,11 @@
import { useObservable } from "react-use";
import { BehaviorSubject, Subject } from "rxjs";
function useSubject<T>(subject: BehaviorSubject<T>): T;
function useSubject<T>(subject: Subject<T>): T | undefined {
if (subject instanceof BehaviorSubject) {
return useObservable(subject, subject.getValue());
} else return useObservable(subject);
}
export default useSubject;

View File

@ -1,13 +1,10 @@
import React from "react";
import { ChakraProvider } from "@chakra-ui/react";
import { RelaysProvider } from "./relay-provider";
import { HashRouter } from "react-router-dom";
import theme from "../theme";
export const Providers = ({ children }: { children: React.ReactNode }) => (
<ChakraProvider theme={theme}>
<HashRouter>
<RelaysProvider>{children}</RelaysProvider>
</HashRouter>
<HashRouter>{children}</HashRouter>
</ChakraProvider>
);

View File

@ -1,52 +0,0 @@
import React, {
createContext,
useCallback,
useContext,
useEffect,
useState,
} from "react";
import settingsService from "../services/settings";
const relaysContext = createContext({
relays: [] as string[],
loading: true,
overwriteRelays: (urls: string[]) => {},
});
export function useRelays() {
return useContext(relaysContext);
}
export const RelaysProvider = ({ children }: { children: React.ReactNode }) => {
const [relays, setRelays] = useState<string[]>([]);
const [loading, setLoading] = useState(true);
const update = useCallback(async () => {
setRelays(await settingsService.getRelays());
setLoading(false);
}, [setRelays]);
const overwriteRelays = useCallback(
async (urls: string[]) => {
if (urls) await settingsService.setRelays(urls);
await update();
},
[update]
);
useEffect(() => {
update();
}, [update]);
return (
<relaysContext.Provider
value={{
relays,
loading,
overwriteRelays,
}}
>
{children}
</relaysContext.Provider>
);
};

View File

@ -1,6 +1,6 @@
import { Subject } from "rxjs";
import settings from "../settings";
import { Relay } from "./relay";
import settingsService from "../settings";
export class RelayPool {
relays = new Map<string, Relay>();
@ -72,8 +72,9 @@ if (import.meta.env.DEV) {
window.relayPool = relayPool;
}
// TODO: move this somewhere where it makes sense
setTimeout(async () => {
const urls = await settingsService.getRelays();
const urls = settings.relays.getValue();
for (const url of urls) {
relayPool.requestRelay(url);

View File

@ -1,20 +1,36 @@
import { BehaviorSubject } from "rxjs";
import db from "./db";
export async function getRelays(): Promise<string[]> {
return await db.get("settings", "relays");
}
export async function setRelays(relays: string[] = []) {
await db.put("settings", relays, "relays");
function log(message: string) {
console.log(`Settings: ${message}`);
}
const settingsService = {
getRelays,
setRelays,
const settings = {
relays: new BehaviorSubject<string[]>([]),
corsProxy: new BehaviorSubject<string>(""),
};
if (import.meta.env.DEV) {
// @ts-ignore
window.settingsService = settingsService;
}
async function loadSettings() {
let loading = true;
const relays = await db.get("settings", "relays");
if (relays) settings.relays.next(relays);
settings.relays.subscribe((newUrls) => {
if (loading) return;
log("saving relay urls");
db.put("settings", newUrls, "relays");
});
export default settingsService;
const corsProxy = await db.get("settings", "cors-proxy");
if (corsProxy) settings.corsProxy.next(corsProxy);
settings.corsProxy.subscribe((newUrl) => {
if (loading) return;
log("saving cors-proxy url");
db.put("settings", newUrl, "cors-proxy");
});
loading = false;
log("loaded");
}
await loadSettings();
export default settings;

View File

@ -1,7 +1,7 @@
import { BehaviorSubject } from "rxjs";
import { Kind0ParsedContent } from "../types/nostr-event";
import db from "./db";
import settingsService from "./settings";
import settings from "./settings";
import { Subscription } from "./subscriptions";
class UserMetadata {
@ -89,7 +89,7 @@ class UserMetadata {
}
}
const userMetadata = new UserMetadata(await settingsService.getRelays());
const userMetadata = new UserMetadata(settings.relays.getValue());
if (import.meta.env.DEV) {
// @ts-ignore

View File

@ -13,5 +13,8 @@ export type IncomingNostrEvent =
| ["NOTICE", string];
export type Kind0ParsedContent = {
name: string;
name?: string;
display_name?: string;
about?: string;
picture?: string;
};

View File

@ -1,13 +1,14 @@
import React, { useEffect, useState } from "react";
import { SkeletonText } from "@chakra-ui/react";
import { useSubscription } from "../../hooks/use-subscription";
import { useRelays } from "../../providers/relay-provider";
import { Post } from "../../components/post";
import moment from "moment/moment";
import { NostrEvent } from "../../types/nostr-event";
import settings from "../../services/settings";
import useSubject from "../../hooks/use-subject";
export const GlobalView = () => {
const { relays } = useRelays();
const relays = useSubject(settings.relays);
const [events, setEvents] = useState<Record<string, NostrEvent>>({});
const sub = useSubscription(

View File

@ -3,15 +3,19 @@ import {
FormControl,
FormHelperText,
FormLabel,
Input,
Stack,
Textarea,
} from "@chakra-ui/react";
import React, { SyntheticEvent, useState } from "react";
import { useRelays } from "../providers/relay-provider";
import { SyntheticEvent, useState } from "react";
import useSubject from "../hooks/use-subject";
import settings from "../services/settings";
export const SettingsView = () => {
const { relays, overwriteRelays } = useRelays();
const relays = useSubject(settings.relays);
// const corsProxy = useSubject(settings.corsProxy);
const [relayUrls, setRelayUrls] = useState(relays.join("\n"));
// const [newCorsProxy, setNewCorsProxy] = useState(corsProxy);
const handleSubmit = async (event: SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
@ -21,12 +25,18 @@ export const SettingsView = () => {
.map((url) => url.trim());
if (newRelays.length > 0) {
await overwriteRelays(newRelays);
settings.relays.next(newRelays);
}
// try {
// const corsUrl = new URL("https://cors.rdfriedl.com").toString();
// settings.corsProxy.next(corsUrl);
// } catch (e) {}
};
const resetForm = async () => {
setRelayUrls(relays.join("\n"));
// setNewCorsProxy(corsProxy);
};
return (
@ -43,6 +53,15 @@ export const SettingsView = () => {
/>
<FormHelperText>One relay per line</FormHelperText>
</FormControl>
{/* <FormControl>
<FormLabel>CORS Proxy</FormLabel>
<Input
value={newCorsProxy}
onChange={(e) => setNewCorsProxy(e.target.value)}
required
/>
<FormHelperText>One relay per line</FormHelperText>
</FormControl> */}
<Stack direction="row" spacing={4}>
<Button onClick={resetForm}>Reset</Button>
<Button type="submit">Save</Button>

View File

@ -1,17 +1,17 @@
import React, { useEffect, useState } from "react";
import { SkeletonText } from "@chakra-ui/react";
import settingsService from "../../services/settings";
import { useSubscription } from "../../hooks/use-subscription";
import { Post } from "../../components/post";
import { NostrEvent } from "../../types/nostr-event";
const relayUrls = await settingsService.getRelays();
import settings from "../../services/settings";
import useSubject from "../../hooks/use-subject";
export const UserPostsTab = ({ pubkey }: { pubkey: string }) => {
const relays = useSubject(settings.relays);
const [events, setEvents] = useState<Record<string, NostrEvent>>({});
const sub = useSubscription(
relayUrls,
relays,
{ authors: [pubkey], kinds: [1] },
`${pubkey} posts`
);