mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-19 11:43:30 +00:00
update onboarding
This commit is contained in:
parent
8f8617d8f9
commit
d3db6492d9
14
src/app.tsx
14
src/app.tsx
@ -205,13 +205,6 @@ const router = createBrowserRouter([
|
|||||||
return { Component: OnboardStep2Screen };
|
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 };
|
return { Component: ResetScreen };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'hard-reset',
|
|
||||||
async lazy() {
|
|
||||||
const { HardResetScreen } = await import('@app/auth/hardReset');
|
|
||||||
return { Component: HardResetScreen };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,7 @@ export function User({ pubkey, fallback }: { pubkey: string; fallback?: string }
|
|||||||
if (status === 'loading') {
|
if (status === 'loading') {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<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">
|
<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-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" />
|
<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 (
|
return (
|
||||||
<div className="flex items-start gap-2.5 py-2">
|
<div className="flex h-full w-full flex-col gap-2.5">
|
||||||
<Image
|
<Image
|
||||||
src={user?.picture || user?.image}
|
src={user?.picture || user?.image}
|
||||||
alt={pubkey}
|
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="flex h-full flex-col items-start justify-between">
|
||||||
<div className="inline-flex items-center gap-2">
|
<div className="flex flex-col items-start gap-1 text-start">
|
||||||
<p className="max-w-[15rem] truncate font-semibold leading-none text-white">
|
<p className="max-w-[15rem] truncate text-lg font-semibold leading-none text-white">
|
||||||
{user?.name || user?.display_name}
|
{user?.name || user?.display_name}
|
||||||
</p>
|
</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 ? (
|
{user?.website ? (
|
||||||
<Link
|
<Link
|
||||||
to={user.website}
|
to={user?.website}
|
||||||
target="_blank"
|
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>
|
</Link>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="line-clamp-4 break-all text-sm text-white/70">
|
|
||||||
{user?.about || user?.bio || 'No bio'}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
export function HardResetScreen() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<p>hard reset</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -51,7 +51,7 @@ export function ImportStep3Screen() {
|
|||||||
<div className="mx-auto w-full max-w-md">
|
<div className="mx-auto w-full max-w-md">
|
||||||
<div className="mb-4 pb-4">
|
<div className="mb-4 pb-4">
|
||||||
<h1 className="text-center text-2xl font-semibold text-white">
|
<h1 className="text-center text-2xl font-semibold text-white">
|
||||||
{loading ? 'Prefetching data...' : 'Your Nostr profile'}
|
{loading ? 'Downloading...' : 'Your Nostr profile'}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
|
@ -68,8 +68,8 @@ export function OnboardStep1Screen() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-md">
|
<div className="flex h-full w-full flex-col justify-center">
|
||||||
<div className="mb-4 border-b border-white/10 pb-4">
|
<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">
|
<h1 className="mb-2 text-center text-2xl font-semibold text-white">
|
||||||
{loading ? 'Prefetching data...' : 'Enrich your network'}
|
{loading ? 'Prefetching data...' : 'Enrich your network'}
|
||||||
</h1>
|
</h1>
|
||||||
@ -79,32 +79,30 @@ export function OnboardStep1Screen() {
|
|||||||
add them later.
|
add them later.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4">
|
<div className="scrollbar-hide flex w-full flex-nowrap items-center gap-4 overflow-x-auto px-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' ? (
|
||||||
{status === 'loading' ? (
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<LoaderIcon className="h-4 w-4 animate-spin text-white" />
|
||||||
<LoaderIcon className="h-4 w-4 animate-spin text-white" />
|
</div>
|
||||||
</div>
|
) : (
|
||||||
) : (
|
data?.profiles.map((item: { pubkey: string; profile: { content: string } }) => (
|
||||||
data?.profiles.map(
|
<button
|
||||||
(item: { pubkey: string; profile: { content: string } }) => (
|
key={item.pubkey}
|
||||||
<button
|
type="button"
|
||||||
key={item.pubkey}
|
onClick={() => toggleFollow(item.pubkey)}
|
||||||
type="button"
|
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"
|
||||||
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) && (
|
||||||
<User pubkey={item.pubkey} fallback={item.profile?.content} />
|
<div className="absolute right-2 top-2">
|
||||||
{follows.includes(item.pubkey) && (
|
<CheckCircleIcon className="h-4 w-4 text-green-400" />
|
||||||
<div className="absolute right-2 top-2">
|
</div>
|
||||||
<CheckCircleIcon className="h-4 w-4 text-green-400" />
|
)}
|
||||||
</div>
|
</button>
|
||||||
)}
|
))
|
||||||
</button>
|
)}
|
||||||
)
|
</div>
|
||||||
)
|
<div className="mx-auto mt-4 w-full max-w-md">
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -133,7 +131,12 @@ export function OnboardStep1Screen() {
|
|||||||
>
|
>
|
||||||
Skip, you can add later
|
Skip, you can add later
|
||||||
</Link>
|
</Link>
|
||||||
) : null}
|
) : (
|
||||||
|
<span className="text-center text-sm text-white/50">
|
||||||
|
By clicking 'Continue', Lume will download all events related to
|
||||||
|
your follows from the last 24 hours. It may take a bit
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { message } from '@tauri-apps/api/dialog';
|
import { message } from '@tauri-apps/api/dialog';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Link, useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { useStorage } from '@libs/storage/provider';
|
import { useStorage } from '@libs/storage/provider';
|
||||||
|
|
||||||
@ -34,8 +34,8 @@ const data = [
|
|||||||
|
|
||||||
export function OnboardStep2Screen() {
|
export function OnboardStep2Screen() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const setStep = useOnboarding((state) => state.setStep);
|
|
||||||
|
|
||||||
|
const [setStep, clearStep] = useOnboarding((state) => [state.setStep, state.clearStep]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [tags, setTags] = useState(new Set<string>());
|
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 () => {
|
const submit = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@ -61,8 +71,15 @@ export function OnboardStep2Screen() {
|
|||||||
await db.createWidget(WidgetKinds.global.hashtag, tag, tag.replace('#', ''));
|
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) {
|
} catch (e) {
|
||||||
|
setLoading(false);
|
||||||
await message(e, { title: 'Lume', type: 'error' });
|
await message(e, { title: 'Lume', type: 'error' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -123,12 +140,13 @@ export function OnboardStep2Screen() {
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{!loading ? (
|
{!loading ? (
|
||||||
<Link
|
<button
|
||||||
to="/auth/onboarding/step-3"
|
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"
|
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
|
Skip, you can add later
|
||||||
</Link>
|
</button>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,7 +4,7 @@ import { Resolver, useForm } from 'react-hook-form';
|
|||||||
import { Link, useNavigate } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
import { Stronghold } from 'tauri-plugin-stronghold-api';
|
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';
|
import { useStorage } from '@libs/storage/provider';
|
||||||
|
|
||||||
@ -74,20 +74,22 @@ export function UnlockScreen() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<div className="mx-auto w-full max-w-md">
|
<div className="mx-auto w-full max-w-md">
|
||||||
<div className="mb-6 text-center">
|
<div className="mb-4 pb-4">
|
||||||
<h1 className="text-2xl font-semibold text-white">Enter password to unlock</h1>
|
<h1 className="text-center text-2xl font-semibold text-white">
|
||||||
|
Enter password to unlock
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="mb-0 flex flex-col">
|
<form onSubmit={handleSubmit(onSubmit)} className="mb-0 flex flex-col">
|
||||||
<div className="flex flex-col rounded-lg bg-white/5">
|
<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">
|
<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>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
{...register('password', { required: true, minLength: 4 })}
|
{...register('password', { required: true, minLength: 4 })}
|
||||||
type={showPassword ? 'text' : 'password'}
|
type={showPassword ? 'text' : 'password'}
|
||||||
placeholder="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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -109,12 +111,12 @@ export function UnlockScreen() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={!isDirty || !isValid}
|
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 ? (
|
{loading ? (
|
||||||
<>
|
<>
|
||||||
<span className="w-5" />
|
<span className="w-5" />
|
||||||
<span>Decryting...</span>
|
<span>Unlocking...</span>
|
||||||
<LoaderIcon className="h-5 w-5 animate-spin text-white" />
|
<LoaderIcon className="h-5 w-5 animate-spin text-white" />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@ -127,7 +129,7 @@ export function UnlockScreen() {
|
|||||||
</button>
|
</button>
|
||||||
<Link
|
<Link
|
||||||
to="/auth/reset"
|
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
|
Reset password
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -57,6 +57,24 @@ export function useNostr() {
|
|||||||
const follows = new Set<string>(preFollows || []);
|
const follows = new Set<string>(preFollows || []);
|
||||||
const lruNetwork = new LRUCache<string, string, void>({ max: 300 });
|
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
|
// fetch user's follows
|
||||||
if (!preFollows) {
|
if (!preFollows) {
|
||||||
const user = ndk.getUser({ hexpubkey: db.account.pubkey });
|
const user = ndk.getUser({ hexpubkey: db.account.pubkey });
|
||||||
@ -67,20 +85,22 @@ export function useNostr() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// build user's network
|
// build user's network
|
||||||
const events = await ndk.fetchEvents({
|
const followEvents = await ndk.fetchEvents({
|
||||||
kinds: [3],
|
kinds: [NDKKind.Contacts],
|
||||||
authors: [...follows],
|
authors: [...follows],
|
||||||
limit: 300,
|
limit: 300,
|
||||||
});
|
});
|
||||||
|
|
||||||
events.forEach((event: NDKEvent) => {
|
followEvents.forEach((event: NDKEvent) => {
|
||||||
event.tags.forEach((tag) => {
|
event.tags.forEach((tag) => {
|
||||||
if (tag[0] === 'p') lruNetwork.set(tag[1], tag[1]);
|
if (tag[0] === 'p') lruNetwork.set(tag[1], tag[1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// get lru values
|
||||||
const network = [...lruNetwork.values()] as string[];
|
const network = [...lruNetwork.values()] as string[];
|
||||||
|
|
||||||
|
// update db
|
||||||
await db.updateAccount('follows', [...follows]);
|
await db.updateAccount('follows', [...follows]);
|
||||||
await db.updateAccount('network', [...new Set([...follows, ...network])]);
|
await db.updateAccount('network', [...new Set([...follows, ...network])]);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user