fixed ssr errors

This commit is contained in:
Ren Amamiya 2023-04-23 08:25:27 +07:00
parent b9bafc851e
commit 8b6bffcff2
14 changed files with 101 additions and 45 deletions

View File

@ -1,6 +1,5 @@
import { platform } from '@tauri-apps/api/os';
import { ArrowLeft, ArrowRight, Refresh } from 'iconoir-react'; import { ArrowLeft, ArrowRight, Refresh } from 'iconoir-react';
import { useLayoutEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
export default function AppActions() { export default function AppActions() {
const [os, setOS] = useState(''); const [os, setOS] = useState('');
@ -17,13 +16,23 @@ export default function AppActions() {
window.location.reload(); window.location.reload();
}; };
useLayoutEffect(() => { const getPlatform = useCallback(async () => {
const getPlatform = async () => { const { platform } = await import('@tauri-apps/api/os');
const result = await platform(); const result = await platform();
setOS(result);
};
getPlatform().catch(console.error); setOS(result);
}, []);
useEffect(() => {
let ignore = false;
if (!ignore) {
getPlatform().catch(console.error);
}
return () => {
ignore = true;
};
}, []); }, []);
return ( return (

View File

@ -7,7 +7,7 @@ export const ChannelListItem = ({ data }: { data: any }) => {
return ( return (
<a <a
href={`channel?id=${data.event_id}`} href={`/channel?id=${data.event_id}`}
className="inline-flex items-center gap-2 rounded-md px-2.5 py-1.5 hover:bg-zinc-900" className="inline-flex items-center gap-2 rounded-md px-2.5 py-1.5 hover:bg-zinc-900"
> >
<div className="relative h-5 w-5 shrink-0 rounded"> <div className="relative h-5 w-5 shrink-0 rounded">

View File

@ -11,7 +11,7 @@ import { useEffect, useState } from 'react';
export default function ChatList() { export default function ChatList() {
const [list, setList] = useState([]); const [list, setList] = useState([]);
const [activeAccount]: any = useLocalStorage('account', {}); const [activeAccount]: any = useLocalStorage('account', {});
const profile = JSON.parse(activeAccount.metadata); const profile = activeAccount.metadata ? JSON.parse(activeAccount.metadata) : null;
useEffect(() => { useEffect(() => {
let ignore = false; let ignore = false;

View File

@ -13,7 +13,7 @@ import { useCallback, useContext, useEffect, useRef } from 'react';
export default function EventCollector() { export default function EventCollector() {
const [pool, relays]: any = useContext(RelayContext); const [pool, relays]: any = useContext(RelayContext);
const [activeAccount]: any = useLocalStorage('account', {}); const [activeAccount]: any = useLocalStorage('account', null);
const setHasNewerNote = useSetAtom(hasNewerNoteAtom); const setHasNewerNote = useSetAtom(hasNewerNoteAtom);
const follows = JSON.parse(activeAccount.follows); const follows = JSON.parse(activeAccount.follows);
@ -106,7 +106,15 @@ export default function EventCollector() {
}, [activeAccount.pubkey, activeAccount.id, follows, pool, relays, setHasNewerNote]); }, [activeAccount.pubkey, activeAccount.id, follows, pool, relays, setHasNewerNote]);
useEffect(() => { useEffect(() => {
subscribe(); let ignore = false;
if (!ignore) {
subscribe();
}
return () => {
ignore = true;
};
}, [setHasNewerNote, subscribe]); }, [setHasNewerNote, subscribe]);
return <NetworkStatusIndicator />; return <NetworkStatusIndicator />;

View File

@ -1,5 +1,5 @@
import { memo } from 'react'; import { memo } from 'react';
import ReactPlayer from 'react-player/lazy'; import ReactPlayer from 'react-player';
export const VideoPreview = memo(function VideoPreview({ url }: { url: string }) { export const VideoPreview = memo(function VideoPreview({ url }: { url: string }) {
return ( return (

View File

@ -18,7 +18,7 @@ import LumeSymbol from '@assets/icons/Lume';
import { writeStorage } from '@rehooks/local-storage'; import { writeStorage } from '@rehooks/local-storage';
import { useCallback, useContext, useEffect, useRef } from 'react'; import { useCallback, useContext, useEffect, useRef } from 'react';
import { navigate } from 'vite-plugin-ssr/client/router'; import { navigate, prefetch } from 'vite-plugin-ssr/client/router';
export function Page() { export function Page() {
const [pool, relays]: any = useContext(RelayContext); const [pool, relays]: any = useContext(RelayContext);
@ -123,7 +123,8 @@ export function Page() {
() => { () => {
updateLastLogin(dateToUnix(now.current)); updateLastLogin(dateToUnix(now.current));
timeout.current = setTimeout(() => { timeout.current = setTimeout(() => {
navigate('/newsfeed/following', { overwriteLastHistoryEntry: true }); prefetch('/newsfeed/following');
navigate('/newsfeed/following');
}, 5000); }, 5000);
}, },
{ {
@ -152,6 +153,7 @@ export function Page() {
// fetch data // fetch data
fetchData(account, account.follows); fetchData(account, account.follows);
} else { } else {
prefetch('/onboarding');
navigate('/onboarding', { overwriteLastHistoryEntry: true }); navigate('/onboarding', { overwriteLastHistoryEntry: true });
} }
}) })

View File

@ -10,20 +10,22 @@ import { createAccount, createPleb, updateAccount } from '@utils/storage';
import { nip02ToArray } from '@utils/transform'; import { nip02ToArray } from '@utils/transform';
import { getPublicKey } from 'nostr-tools'; import { getPublicKey } from 'nostr-tools';
import { useCallback, useContext, useEffect, useRef, useState } from 'react'; import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { navigate } from 'vite-plugin-ssr/client/router';
export function Page() { export function Page() {
const pageContext = usePageContext(); const pageContext = usePageContext();
const searchParams = pageContext.urlParsed.search; const searchParams = pageContext.urlParsed.search;
const privkey = searchParams.privkey; const privkey = searchParams.privkey;
const pubkey = getPublicKey(privkey); const pubkey = useMemo(() => getPublicKey(privkey), [privkey]);
const [pool, relays]: any = useContext(RelayContext); const [pool, relays]: any = useContext(RelayContext);
const [profile, setProfile] = useState({ metadata: null }); const [profile, setProfile] = useState({ metadata: null });
const [done, setDone] = useState(false); const [done, setDone] = useState(false);
const timeout = useRef(null); const timeout = useRef(null);
const nip02 = useRef(null);
const createPlebs = useCallback(async (tags: string[]) => { const createPlebs = useCallback(async (tags: string[]) => {
for (const tag of tags) { for (const tag of tags) {
@ -33,6 +35,16 @@ export function Page() {
} }
}, []); }, []);
const submit = () => {
// update account's folllows with NIP-02 tag list
const arr = nip02ToArray(nip02.current);
updateAccount('follows', arr, pubkey);
// create plebs (saved nostr profile)
createPlebs(nip02.current);
// redirect to splashscreen
navigate('/', { overwriteLastHistoryEntry: true });
};
useEffect(() => { useEffect(() => {
const unsubscribe = pool.subscribe( const unsubscribe = pool.subscribe(
[ [
@ -43,20 +55,20 @@ export function Page() {
], ],
relays, relays,
(event: any) => { (event: any) => {
if (event.kind === 0) { switch (event.kind) {
// create account case 0:
createAccount(pubkey, privkey, event.content); // create account
// update state createAccount(pubkey, privkey, event.content);
setProfile({ // update state
metadata: JSON.parse(event.content), setProfile({
}); metadata: JSON.parse(event.content),
} else { });
if (event.tags.length > 0) { break;
createPlebs(event.tags); case 3:
const arr = nip02ToArray(event.tags); nip02.current = event.tags;
// update account's folllows with NIP-02 tag list break;
updateAccount('follows', arr, pubkey); default:
} break;
} }
}, },
undefined, undefined,
@ -73,7 +85,7 @@ export function Page() {
unsubscribe(); unsubscribe();
clearTimeout(timeout.current); clearTimeout(timeout.current);
}; };
}, [pool, relays, pubkey, privkey, createPlebs]); }, [pool, relays, pubkey, privkey]);
return ( return (
<OnboardingLayout> <OnboardingLayout>
@ -128,12 +140,12 @@ export function Page() {
></path> ></path>
</svg> </svg>
) : ( ) : (
<a <button
href="/" onClick={() => submit()}
className="inline-flex w-full transform items-center justify-center rounded-lg bg-gradient-to-r from-fuchsia-300 via-orange-100 to-amber-300 px-3.5 py-2.5 font-medium text-zinc-800 active:translate-y-1 disabled:cursor-not-allowed disabled:opacity-30" className="inline-flex w-full transform items-center justify-center rounded-lg bg-gradient-to-r from-fuchsia-300 via-orange-100 to-amber-300 px-3.5 py-2.5 font-medium text-zinc-800 active:translate-y-1 disabled:cursor-not-allowed disabled:opacity-30"
> >
<span className="drop-shadow-lg">Done! Go to newsfeed</span> <span className="drop-shadow-lg">Done! Go to newsfeed</span>
</a> </button>
)} )}
</div> </div>
</div> </div>

View File

@ -2,19 +2,31 @@ import '@renderer/index.css';
import { Shell } from '@renderer/shell'; import { Shell } from '@renderer/shell';
import { PageContextClient } from '@renderer/types'; import { PageContextClient } from '@renderer/types';
import { hydrateRoot } from 'react-dom/client'; import { Root, createRoot, hydrateRoot } from 'react-dom/client';
export const clientRouting = true; export const clientRouting = true;
let root: Root;
export async function render(pageContext: PageContextClient) { export async function render(pageContext: PageContextClient) {
const { Page, pageProps } = pageContext; const { Page, pageProps } = pageContext;
if (!Page) throw new Error('Client-side render() hook expects pageContext.Page to be defined'); if (!Page) throw new Error('Client-side render() hook expects pageContext.Page to be defined');
hydrateRoot( const page = (
document.getElementById('app')!,
<Shell pageContext={pageContext}> <Shell pageContext={pageContext}>
<Page {...pageProps} /> <Page {...pageProps} />
</Shell> </Shell>
); );
const container = document.getElementById('app');
// SPA
if (container.innerHTML === '' || !pageContext.isHydration) {
if (!root) {
root = createRoot(container);
}
root.render(page);
// SSR
} else {
root = hydrateRoot(container, page);
}
} }

View File

@ -7,15 +7,22 @@ import { dangerouslySkipEscape, escapeInject } from 'vite-plugin-ssr/server';
export const passToClient = ['pageProps']; export const passToClient = ['pageProps'];
export function render(pageContext: PageContextServer) { export function render(pageContext: PageContextServer) {
const { Page, pageProps } = pageContext; let pageHtml: string;
if (!Page) throw new Error('My render() hook expects pageContext.Page to be defined'); if (!pageContext.Page) {
// SPA
pageHtml = '';
} else {
// SSR / HTML-only
const { Page, pageProps } = pageContext;
if (!Page) throw new Error('My render() hook expects pageContext.Page to be defined');
const pageHtml = ReactDOMServer.renderToString( pageHtml = ReactDOMServer.renderToString(
<Shell pageContext={pageContext}> <Shell pageContext={pageContext}>
<Page {...pageProps} /> <Page {...pageProps} />
</Shell> </Shell>
); );
}
return escapeInject`<!DOCTYPE html> return escapeInject`<!DOCTYPE html>
<html lang="en" class="dark"> <html lang="en" class="dark">

View File

@ -6,6 +6,7 @@ export const nip02ToArray = (tags: string[]) => {
tags.forEach((item) => { tags.forEach((item) => {
arr.push(item[1]); arr.push(item[1]);
}); });
return arr; return arr;
}; };

View File

@ -5,4 +5,9 @@ import viteTsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({ export default defineConfig({
plugins: [react(), ssr({ prerender: true }), viteTsconfigPaths()], plugins: [react(), ssr({ prerender: true }), viteTsconfigPaths()],
define: {
global: {
window: {},
},
},
}); });