update onboarding

This commit is contained in:
Ren Amamiya 2023-09-15 08:58:09 +07:00
parent 8f8617d8f9
commit d3db6492d9
8 changed files with 105 additions and 80 deletions

View File

@ -205,13 +205,6 @@ const router = createBrowserRouter([
return { Component: OnboardStep2Screen };
},
},
{
path: 'step-3',
async lazy() {
const { OnboardStep3Screen } = await import('@app/auth/onboarding/step-3');
return { Component: OnboardStep3Screen };
},
},
],
},
{
@ -235,13 +228,6 @@ const router = createBrowserRouter([
return { Component: ResetScreen };
},
},
{
path: 'hard-reset',
async lazy() {
const { HardResetScreen } = await import('@app/auth/hardReset');
return { Component: HardResetScreen };
},
},
],
},
{

View File

@ -11,7 +11,7 @@ export function User({ pubkey, fallback }: { pubkey: string; fallback?: string }
if (status === 'loading') {
return (
<div className="flex items-center gap-2">
<div className="relative h-11 w-11 shrink-0 animate-pulse rounded-md bg-white/10 backdrop-blur-xl" />
<div className="relative h-14 w-14 shrink-0 animate-pulse rounded-md bg-white/10 backdrop-blur-xl" />
<div className="flex w-full flex-1 flex-col items-start gap-1 text-start">
<span className="h-4 w-1/2 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
<span className="h-3 w-1/3 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
@ -21,30 +21,33 @@ export function User({ pubkey, fallback }: { pubkey: string; fallback?: string }
}
return (
<div className="flex items-start gap-2.5 py-2">
<div className="flex h-full w-full flex-col gap-2.5">
<Image
src={user?.picture || user?.image}
alt={pubkey}
className="h-11 w-11 shrink-0 rounded-lg object-cover"
className="h-14 w-14 shrink-0 rounded-lg object-cover"
/>
<div className="flex w-full flex-col items-start gap-1 text-start">
<div className="inline-flex items-center gap-2">
<p className="max-w-[15rem] truncate font-semibold leading-none text-white">
<div className="flex h-full flex-col items-start justify-between">
<div className="flex flex-col items-start gap-1 text-start">
<p className="max-w-[15rem] truncate text-lg font-semibold leading-none text-white">
{user?.name || user?.display_name}
</p>
<p className="line-clamp-6 break-all text-white/70">
{user?.about || user?.bio || 'No bio'}
</p>
</div>
<div className="flex flex-col gap-2">
{user?.website ? (
<Link
to={user.website}
to={user?.website}
target="_blank"
className="border-l border-white/10 pl-2"
className="inline-flex items-center gap-2 text-sm text-white/70"
>
<WorldIcon className="h-4 w-4 text-white/70 hover:text-white" />{' '}
<WorldIcon className="h-4 w-4" />
<p className="max-w-[10rem] truncate">{user.website}</p>
</Link>
) : null}
</div>
<div className="line-clamp-4 break-all text-sm text-white/70">
{user?.about || user?.bio || 'No bio'}
</div>
</div>
</div>
);

View File

@ -1,7 +0,0 @@
export function HardResetScreen() {
return (
<div>
<p>hard reset</p>
</div>
);
}

View File

@ -51,7 +51,7 @@ export function ImportStep3Screen() {
<div className="mx-auto w-full max-w-md">
<div className="mb-4 pb-4">
<h1 className="text-center text-2xl font-semibold text-white">
{loading ? 'Prefetching data...' : 'Your Nostr profile'}
{loading ? 'Downloading...' : 'Your Nostr profile'}
</h1>
</div>
<div className="flex flex-col gap-3">

View File

@ -68,8 +68,8 @@ export function OnboardStep1Screen() {
}, []);
return (
<div className="mx-auto w-full max-w-md">
<div className="mb-4 border-b border-white/10 pb-4">
<div className="flex h-full w-full flex-col justify-center">
<div className="mx-auto mb-4 w-full max-w-md border-b border-white/10 pb-4">
<h1 className="mb-2 text-center text-2xl font-semibold text-white">
{loading ? 'Prefetching data...' : 'Enrich your network'}
</h1>
@ -79,32 +79,30 @@ export function OnboardStep1Screen() {
add them later.
</p>
</div>
<div className="flex flex-col gap-4">
<div className="scrollbar-hide flex h-[450px] w-full flex-col divide-y divide-white/5 overflow-y-auto rounded-xl bg-white/20 backdrop-blur-xl">
{status === 'loading' ? (
<div className="flex h-full w-full items-center justify-center">
<LoaderIcon className="h-4 w-4 animate-spin text-white" />
</div>
) : (
data?.profiles.map(
(item: { pubkey: string; profile: { content: string } }) => (
<button
key={item.pubkey}
type="button"
onClick={() => toggleFollow(item.pubkey)}
className="relative px-4 py-2 hover:bg-white/10"
>
<User pubkey={item.pubkey} fallback={item.profile?.content} />
{follows.includes(item.pubkey) && (
<div className="absolute right-2 top-2">
<CheckCircleIcon className="h-4 w-4 text-green-400" />
</div>
)}
</button>
)
)
)}
</div>
<div className="scrollbar-hide flex w-full flex-nowrap items-center gap-4 overflow-x-auto px-4">
{status === 'loading' ? (
<div className="flex h-full w-full items-center justify-center">
<LoaderIcon className="h-4 w-4 animate-spin text-white" />
</div>
) : (
data?.profiles.map((item: { pubkey: string; profile: { content: string } }) => (
<button
key={item.pubkey}
type="button"
onClick={() => toggleFollow(item.pubkey)}
className="relative h-[300px] shrink-0 grow-0 basis-[250px] rounded-lg border-t border-white/10 bg-white/20 px-4 py-4 hover:bg-white/30"
>
<User pubkey={item.pubkey} fallback={item.profile?.content} />
{follows.includes(item.pubkey) && (
<div className="absolute right-2 top-2">
<CheckCircleIcon className="h-4 w-4 text-green-400" />
</div>
)}
</button>
))
)}
</div>
<div className="mx-auto mt-4 w-full max-w-md">
<div className="flex flex-col gap-2">
<button
type="button"
@ -133,7 +131,12 @@ export function OnboardStep1Screen() {
>
Skip, you can add later
</Link>
) : null}
) : (
<span className="text-center text-sm text-white/50">
By clicking &apos;Continue&apos;, Lume will download all events related to
your follows from the last 24 hours. It may take a bit
</span>
)}
</div>
</div>
</div>

View File

@ -1,6 +1,6 @@
import { message } from '@tauri-apps/api/dialog';
import { useEffect, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import { useStorage } from '@libs/storage/provider';
@ -34,8 +34,8 @@ const data = [
export function OnboardStep2Screen() {
const navigate = useNavigate();
const setStep = useOnboarding((state) => state.setStep);
const [setStep, clearStep] = useOnboarding((state) => [state.setStep, state.clearStep]);
const [loading, setLoading] = useState(false);
const [tags, setTags] = useState(new Set<string>());
@ -53,6 +53,16 @@ export function OnboardStep2Screen() {
}
};
const skip = async () => {
// update last login
await db.updateLastLogin();
// clear local storage
clearStep();
navigate('/', { replace: true });
};
const submit = async () => {
try {
setLoading(true);
@ -61,8 +71,15 @@ export function OnboardStep2Screen() {
await db.createWidget(WidgetKinds.global.hashtag, tag, tag.replace('#', ''));
}
navigate('/auth/onboarding/step-3', { replace: true });
// update last login
await db.updateLastLogin();
// clear local storage
clearStep();
navigate('/', { replace: true });
} catch (e) {
setLoading(false);
await message(e, { title: 'Lume', type: 'error' });
}
};
@ -123,12 +140,13 @@ export function OnboardStep2Screen() {
)}
</button>
{!loading ? (
<Link
to="/auth/onboarding/step-3"
<button
type="button"
onClick={() => skip()}
className="inline-flex h-12 w-full items-center justify-center rounded-lg border-t border-white/10 bg-white/20 font-medium leading-none text-white backdrop-blur-xl hover:bg-white/30 focus:outline-none"
>
Skip, you can add later
</Link>
</button>
) : null}
</div>
</div>

View File

@ -4,7 +4,7 @@ import { Resolver, useForm } from 'react-hook-form';
import { Link, useNavigate } from 'react-router-dom';
import { Stronghold } from 'tauri-plugin-stronghold-api';
import { User } from '@app/auth/components/user';
import { UserImport } from '@app/auth/components/userImport';
import { useStorage } from '@libs/storage/provider';
@ -74,20 +74,22 @@ export function UnlockScreen() {
return (
<div className="flex h-full w-full items-center justify-center">
<div className="mx-auto w-full max-w-md">
<div className="mb-6 text-center">
<h1 className="text-2xl font-semibold text-white">Enter password to unlock</h1>
<div className="mb-4 pb-4">
<h1 className="text-center text-2xl font-semibold text-white">
Enter password to unlock
</h1>
</div>
<form onSubmit={handleSubmit(onSubmit)} className="mb-0 flex flex-col">
<div className="flex flex-col rounded-lg bg-white/5">
<div className="w-full rounded-t-lg border-b border-white/10 bg-white/5 p-4">
<User pubkey={db.account.pubkey} />
<UserImport pubkey={db.account.pubkey} />
</div>
<div className="relative">
<input
{...register('password', { required: true, minLength: 4 })}
type={showPassword ? 'text' : 'password'}
placeholder="Password"
className="relative h-12 w-full rounded-b-lg bg-white/10 py-1 text-center text-white !outline-none backdrop-blur-xl placeholder:text-white/50"
className="relative h-12 w-full rounded-b-lg bg-white/10 py-1 text-center tracking-widest text-white !outline-none backdrop-blur-xl placeholder:text-white/50"
/>
<button
type="button"
@ -109,12 +111,12 @@ export function UnlockScreen() {
<button
type="submit"
disabled={!isDirty || !isValid}
className="inline-flex h-11 w-full items-center justify-between gap-2 rounded-lg bg-fuchsia-500 px-6 font-medium leading-none text-white hover:bg-fuchsia-600 focus:outline-none disabled:opacity-50"
className="inline-flex h-12 w-full items-center justify-between gap-2 rounded-lg bg-fuchsia-500 px-6 font-medium leading-none text-white hover:bg-fuchsia-600 focus:outline-none disabled:opacity-50"
>
{loading ? (
<>
<span className="w-5" />
<span>Decryting...</span>
<span>Unlocking...</span>
<LoaderIcon className="h-5 w-5 animate-spin text-white" />
</>
) : (
@ -127,7 +129,7 @@ export function UnlockScreen() {
</button>
<Link
to="/auth/reset"
className="mt-1 inline-flex h-11 w-full items-center justify-center rounded-lg text-center text-white/50 hover:bg-white/10"
className="mt-1 inline-flex h-12 w-full items-center justify-center rounded-lg text-center text-white/70 hover:bg-white/20"
>
Reset password
</Link>

View File

@ -57,6 +57,24 @@ export function useNostr() {
const follows = new Set<string>(preFollows || []);
const lruNetwork = new LRUCache<string, string, void>({ max: 300 });
// fetch user's relays
const relayEvents = await ndk.fetchEvents({
kinds: [NDKKind.RelayList],
authors: [db.account.pubkey],
});
if (relayEvents) {
const latestRelayEvent = [...relayEvents].sort(
(a, b) => b.created_at - a.created_at
)[0];
if (latestRelayEvent) {
for (const item of latestRelayEvent.tags) {
await db.createRelay(item[1], item[2]);
}
}
}
// fetch user's follows
if (!preFollows) {
const user = ndk.getUser({ hexpubkey: db.account.pubkey });
@ -67,20 +85,22 @@ export function useNostr() {
}
// build user's network
const events = await ndk.fetchEvents({
kinds: [3],
const followEvents = await ndk.fetchEvents({
kinds: [NDKKind.Contacts],
authors: [...follows],
limit: 300,
});
events.forEach((event: NDKEvent) => {
followEvents.forEach((event: NDKEvent) => {
event.tags.forEach((tag) => {
if (tag[0] === 'p') lruNetwork.set(tag[1], tag[1]);
});
});
// get lru values
const network = [...lruNetwork.values()] as string[];
// update db
await db.updateAccount('follows', [...follows]);
await db.updateAccount('network', [...new Set([...follows, ...network])]);