updated onboarding flow

This commit is contained in:
Ren Amamiya 2023-02-22 21:12:37 +07:00
parent c0889456a3
commit 247f28ae75
4 changed files with 182 additions and 29 deletions

View File

@ -0,0 +1,125 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import BaseLayout from '@layouts/baseLayout';
import OnboardingLayout from '@layouts/onboardingLayout';
import { motion } from 'framer-motion';
import { useRouter } from 'next/router';
import { useNostrEvents } from 'nostr-react';
import {
JSXElementConstructor,
ReactElement,
ReactFragment,
ReactPortal,
useEffect,
useState,
} from 'react';
import Database from 'tauri-plugin-sql-api';
export default function Page() {
const [follows, setFollows] = useState([null]);
const [loading, setLoading] = useState(false);
const router = useRouter();
const { pubkey }: any = router.query;
const { onEvent } = useNostrEvents({
filter: {
authors: [pubkey],
kinds: [3],
},
});
onEvent((rawMetadata) => {
try {
setFollows(rawMetadata.tags);
} catch (err) {
console.error(err, rawMetadata);
}
});
useEffect(() => {
setLoading(true);
const insertDB = async () => {
const db = await Database.load('sqlite:lume.db');
follows.forEach(async (item) => {
if (item) {
await db.execute(
`INSERT INTO followings (pubkey, account) VALUES ("${item[1]}", "${pubkey}")`
);
}
});
};
if (follows !== null && follows.length > 0) {
insertDB()
.then(() => {
setTimeout(() => {
setLoading(false);
router.push('/feed/following');
}, 1500);
})
.catch(console.error);
}
}, [follows, pubkey, router]);
return (
<div className="flex h-full flex-col justify-between px-8">
<div>{/* spacer */}</div>
<motion.div layoutId="form">
<div className="mb-8 flex flex-col gap-3">
<motion.h1
layoutId="title"
className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
Fetching your follows...
</motion.h1>
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
Not only profile, every nostr client can sync your follows list when you move to a new
client, so please keep your key safely (again)
</motion.h2>
</div>
</motion.div>
<motion.div layoutId="action" className="pb-5">
<div className="flex h-10 items-center">
{loading === true ? (
<svg
className="h-5 w-5 animate-spin text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24">
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
) : (
<></>
)}
</div>
</motion.div>
</div>
);
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return (
<BaseLayout>
<OnboardingLayout>{page}</OnboardingLayout>
</BaseLayout>
);
};

View File

@ -11,13 +11,14 @@ import {
ReactElement, ReactElement,
ReactFragment, ReactFragment,
ReactPortal, ReactPortal,
useCallback, useEffect,
useState, useState,
} from 'react'; } from 'react';
import Database from 'tauri-plugin-sql-api'; import Database from 'tauri-plugin-sql-api';
export default function Page() { export default function Page() {
const [account, setAccount] = useState(null); const [account, setAccount] = useState(null);
const [loading, setLoading] = useState(false);
const router = useRouter(); const router = useRouter();
const { privkey }: any = router.query; const { privkey }: any = router.query;
@ -42,19 +43,32 @@ export default function Page() {
} }
}); });
const insertAccount = useCallback(async () => { useEffect(() => {
// save account to database setLoading(true);
const db = await Database.load('sqlite:lume.db'); const insertDB = async () => {
await db.execute( // save account to database
`INSERT INTO accounts (privkey, pubkey, npub, nsec, metadata) VALUES ("${privkey}", "${pubkey}", "${npub}", "${nsec}", '${JSON.stringify( const db = await Database.load('sqlite:lume.db');
account await db.execute(
)}')` `INSERT INTO accounts (privkey, pubkey, npub, nsec, metadata) VALUES ("${privkey}", "${pubkey}", "${npub}", "${nsec}", '${JSON.stringify(
); account
await db.close(); )}')`
);
await db.close();
};
setTimeout(() => { if (account !== null) {
router.push('/feed/following'); insertDB()
}, 500); .then(() => {
setTimeout(() => {
setLoading(false);
router.push({
pathname: '/onboarding/fetch-follows',
query: { pubkey: pubkey },
});
}, 1500);
})
.catch(console.error);
}
}, [account, npub, nsec, privkey, pubkey, router]); }, [account, npub, nsec, privkey, pubkey, router]);
return ( return (
@ -65,26 +79,37 @@ export default function Page() {
<motion.h1 <motion.h1
layoutId="title" layoutId="title"
className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent"> className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
Getting your old profile Fetching your profile...
</motion.h1> </motion.h1>
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400"> <motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
As long as you have private key, you alway can recover your profile as well as follows As long as you have private key, you alway can sync your profile on every nostr client,
list when you move to new nostr client so please keep your key safely
</motion.h2> </motion.h2>
</div> </div>
<div className="flex flex-col gap-4">
<p>#TODO: show profile</p>
</div>
</motion.div> </motion.div>
<motion.div layoutId="action" className="pb-5"> <motion.div layoutId="action" className="pb-5">
<div className="flex h-10 items-center"> <div className="flex h-10 items-center">
<div className="relative shrink-0 before:pointer-events-none before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-blue-500 before:opacity-0 before:ring-2 before:ring-blue-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight after:shadow-white/5 after:transition focus-within:before:opacity-100 focus-within:after:shadow-blue-500/100 dark:focus-within:after:shadow-blue-500/20"> {loading === true ? (
<button <svg
onClick={() => insertAccount()} className="h-5 w-5 animate-spin text-white"
className="transform rounded-lg border border-white/10 bg-[radial-gradient(ellipse_at_bottom_right,_var(--tw-gradient-stops))] from-gray-300 via-fuchsia-600 to-orange-600 px-3.5 py-2 font-medium shadow-input shadow-black/5 active:translate-y-1 disabled:cursor-not-allowed disabled:opacity-50 dark:shadow-black/10"> xmlns="http://www.w3.org/2000/svg"
<span className="drop-shadow-lg">Finish </span> fill="none"
</button> viewBox="0 0 24 24">
</div> <circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
) : (
<></>
)}
</div> </div>
</motion.div> </motion.div>
</div> </div>

View File

@ -37,11 +37,14 @@ export default function Page() {
const onSubmit = async (data: any) => { const onSubmit = async (data: any) => {
let privKey = data['key']; let privKey = data['key'];
if (privKey.substring(0, 4) === 'nsec') { if (privKey.substring(0, 4) === 'nsec') {
privKey = nip19.decode(privKey).data; privKey = nip19.decode(privKey).data;
} }
try { try {
const pubKey = getPublicKey(privKey); const pubKey = getPublicKey(privKey);
if (pubKey) { if (pubKey) {
router.push({ router.push({
pathname: '/onboarding/fetch-profile', pathname: '/onboarding/fetch-profile',
@ -75,8 +78,8 @@ export default function Page() {
<div className="relative shrink-0 before:pointer-events-none before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-blue-500 before:opacity-0 before:ring-2 before:ring-blue-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight after:shadow-white/5 after:transition focus-within:before:opacity-100 focus-within:after:shadow-blue-500/100 dark:focus-within:after:shadow-blue-500/20"> <div className="relative shrink-0 before:pointer-events-none before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-blue-500 before:opacity-0 before:ring-2 before:ring-blue-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight after:shadow-white/5 after:transition focus-within:before:opacity-100 focus-within:after:shadow-blue-500/100 dark:focus-within:after:shadow-blue-500/20">
<input <input
{...register('key', { required: true, minLength: 32 })} {...register('key', { required: true, minLength: 32 })}
placeholder="Paste key here..." placeholder="Paste nsec or hex key here..."
className="relative w-full rounded-lg border border-black/5 px-3.5 py-2 shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-600" className="relative w-full rounded-lg border border-black/5 px-3.5 py-2 shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-500"
/> />
</div> </div>
<span className="text-sm text-red-400">{errors.key && <p>{errors.key.message}</p>}</span> <span className="text-sm text-red-400">{errors.key && <p>{errors.key.message}</p>}</span>

View File

@ -34,7 +34,7 @@ export default function Page() {
<Link <Link
href="/onboarding/import" href="/onboarding/import"
className="hover:bg-zinc-900/2.5 transform rounded-lg border border-black/5 bg-zinc-800 px-3.5 py-2 font-medium ring-1 ring-inset ring-zinc-900/10 hover:text-zinc-900 active:translate-y-1 dark:text-zinc-300 dark:ring-white/10 dark:hover:bg-zinc-700 dark:hover:text-white"> className="hover:bg-zinc-900/2.5 transform rounded-lg border border-black/5 bg-zinc-800 px-3.5 py-2 font-medium ring-1 ring-inset ring-zinc-900/10 hover:text-zinc-900 active:translate-y-1 dark:text-zinc-300 dark:ring-white/10 dark:hover:bg-zinc-700 dark:hover:text-white">
Login Login with private key
</Link> </Link>
</motion.div> </motion.div>
</div> </div>