wip: complete new onboarding

This commit is contained in:
reya
2023-10-17 16:33:41 +07:00
parent 3aa4f294f9
commit 7fa1e89dc8
44 changed files with 580 additions and 732 deletions
@@ -0,0 +1,50 @@
import { isPermissionGranted, requestPermission } from '@tauri-apps/plugin-notification';
import { CheckCircleIcon } from '@shared/icons';
import { useOnboarding } from '@stores/onboarding';
export function AllowNotification() {
const [notification, setNotification] = useOnboarding((state) => [
state.notification,
state.toggleNotification,
]);
const allow = async () => {
let permissionGranted = await isPermissionGranted();
if (!permissionGranted) {
const permission = await requestPermission();
permissionGranted = permission === 'granted';
}
if (permissionGranted) {
setNotification();
}
};
return (
<div className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200">
<div className="flex items-start justify-between gap-2">
<div>
<h5 className="font-semibold">Allow notification</h5>
<p className="text-sm">
By allowing Lume to send notifications in your OS settings, you will receive
notification messages when someone interacts with you or your content.
</p>
</div>
{notification ? (
<div className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-teal-500 text-white">
<CheckCircleIcon className="h-4 w-4" />
</div>
) : (
<button
type="button"
onClick={allow}
className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-neutral-200 font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-800"
>
Allow
</button>
)}
</div>
</div>
);
}
@@ -1,21 +0,0 @@
export function CustomRelay() {
return (
<div className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200">
<div className="flex items-start justify-between">
<div>
<h5 className="font-semibold">Personalize relay list</h5>
<p className="text-sm">
Lume offers some default relays for users who are not familiar with Nostr, but
you can consider adding more relays to discover more content.
</p>
</div>
<button
type="button"
className="mt-1 h-9 w-24 shrink-0 rounded-lg bg-neutral-200 font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-800"
>
Custom
</button>
</div>
</div>
);
}
@@ -0,0 +1,99 @@
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
import { LRUCache } from 'lru-cache';
import { useState } from 'react';
import { toast } from 'sonner';
import { useNDK } from '@libs/ndk/provider';
import { useStorage } from '@libs/storage/provider';
import { CheckCircleIcon, LoaderIcon } from '@shared/icons';
import { useOnboarding } from '@stores/onboarding';
export function Circle() {
const { db } = useStorage();
const { ndk } = useNDK();
const [circle, setCircle] = useOnboarding((state) => [
state.circle,
state.toggleCircle,
]);
const [loading, setLoading] = useState(false);
const enableLinks = async () => {
setLoading(true);
const users = ndk.getUser({ hexpubkey: db.account.pubkey });
const follows = await users.follows();
if (follows.size === 0) {
setLoading(false);
return toast('You need to follow at least 1 account');
}
const lru = new LRUCache<string, string, void>({ max: 300 });
const followsAsArr = [];
// add user's follows to lru
follows.forEach((user) => {
lru.set(user.pubkey, user.pubkey);
followsAsArr.push(user.pubkey);
});
// get follows from follows
const events = await ndk.fetchEvents({
kinds: [NDKKind.Contacts],
authors: followsAsArr,
limit: 300,
});
events.forEach((event: NDKEvent) => {
event.tags.forEach((tag) => {
if (tag[0] === 'p') lru.set(tag[1], tag[1]);
});
});
// get lru values
const circleList = [...lru.values()] as string[];
// update db
await db.updateAccount('follows', JSON.stringify(followsAsArr));
await db.updateAccount('circles', JSON.stringify(circleList));
db.account.follows = followsAsArr;
db.account.circles = circleList;
// clear lru
lru.clear();
// done
setCircle();
};
return (
<div className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200">
<div className="flex items-start justify-between gap-2">
<div>
<h5 className="font-semibold">Enable Circle</h5>
<p className="text-sm">
Beside newsfeed from your follows, you will see more content from all people
that followed by your follows.
</p>
</div>
{circle ? (
<div className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-teal-500 text-white">
<CheckCircleIcon className="h-4 w-4" />
</div>
) : (
<button
type="button"
onClick={enableLinks}
className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-neutral-200 font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-800"
>
{loading ? <LoaderIcon className="h-4 w-4 animate-spin" /> : 'Enable'}
</button>
)}
</div>
</div>
);
}
@@ -0,0 +1,47 @@
import { useStorage } from '@libs/storage/provider';
import { CheckCircleIcon } from '@shared/icons';
import { useOnboarding } from '@stores/onboarding';
export function OutboxModel() {
const { db } = useStorage();
const [outbox, setOutbox] = useOnboarding((state) => [
state.outbox,
state.toggleOutbox,
]);
const enableOutbox = async () => {
await db.createSetting('outbox', '1');
setOutbox();
};
return (
<div className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200">
<div className="flex items-start justify-between gap-2">
<div>
<h5 className="font-semibold">Enable Outbox (experiment)</h5>
<p className="text-sm">
When you request information about a user, Lume will automatically query the
user&apos;s outbox relays and subsequent queries will favour using those
relays for queries with that user&apos;s pubkey.
</p>
</div>
{outbox ? (
<div className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-teal-500 text-white">
<CheckCircleIcon className="h-4 w-4" />
</div>
) : (
<button
type="button"
onClick={enableOutbox}
className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-neutral-200 font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-800"
>
Enable
</button>
)}
</div>
</div>
);
}
@@ -1,4 +1,12 @@
import { Link } from 'react-router-dom';
import { CheckCircleIcon } from '@shared/icons';
import { useOnboarding } from '@stores/onboarding';
export function FavoriteHashtag() {
const hashtag = useOnboarding((state) => state.hashtag);
return (
<div className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200">
<div className="flex items-start justify-between">
@@ -9,12 +17,18 @@ export function FavoriteHashtag() {
hashtag as a column
</p>
</div>
<button
type="button"
className="mt-1 h-9 w-24 shrink-0 rounded-lg bg-neutral-200 font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-800"
>
Add
</button>
{hashtag ? (
<div className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-teal-500 text-white">
<CheckCircleIcon className="h-4 w-4" />
</div>
) : (
<Link
to="/auth/onboarding/hashtag"
className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-neutral-200 font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-800"
>
Add
</Link>
)}
</div>
</div>
);
@@ -1,3 +1,58 @@
import { useQuery } from '@tanstack/react-query';
import { useNDK } from '@libs/ndk/provider';
import { useStorage } from '@libs/storage/provider';
import { LoaderIcon } from '@shared/icons';
import { User } from '@shared/user';
export function FollowList() {
return <div></div>;
const { db } = useStorage();
const { ndk } = useNDK();
const { status, data } = useQuery(
['follows'],
async () => {
const user = ndk.getUser({ hexpubkey: db.account.pubkey });
const follows = await user.follows();
const followsAsArr = [];
follows.forEach((user) => {
followsAsArr.push(user.pubkey);
});
// update db
await db.updateAccount('follows', JSON.stringify(followsAsArr));
await db.updateAccount('circles', JSON.stringify(followsAsArr));
db.account.follows = followsAsArr;
db.account.circles = followsAsArr;
return followsAsArr;
},
{
refetchOnWindowFocus: false,
}
);
return (
<div className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200">
<h5 className="font-semibold">Your follows</h5>
<div className="mt-2 flex w-full items-center justify-center">
{status === 'loading' ? (
<LoaderIcon className="h-4 w-4 animate-spin text-neutral-900 dark:text-neutral-100" />
) : (
<div className="isolate flex -space-x-2">
{data.slice(0, 16).map((item) => (
<User key={item} pubkey={item} variant="stacked" />
))}
{data.length > 16 ? (
<div className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-neutral-200 text-neutral-900 ring-1 ring-neutral-300 dark:bg-neutral-800 dark:text-neutral-100 dark:ring-neutral-700">
<span className="text-xs font-medium">+{data.length}</span>
</div>
) : null}
</div>
)}
</div>
</div>
);
}
@@ -1,21 +0,0 @@
export function LinkList() {
return (
<div className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200">
<div className="flex items-start justify-between">
<div>
<h5 className="font-semibold">Enable Links</h5>
<p className="text-sm">
Beside newsfeed from your follows, you will see more content from all people
that followed by your follows.
</p>
</div>
<button
type="button"
className="mt-1 h-9 w-24 shrink-0 rounded-lg bg-neutral-200 font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-800"
>
Enable
</button>
</div>
</div>
);
}
@@ -1,21 +0,0 @@
export function NIP04() {
return (
<div className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200">
<div className="flex items-start justify-between">
<div>
<h5 className="font-semibold">Enable direct message (Deprecated)</h5>
<p className="text-sm">
Send direct message to other user (NIP-04), all messages will be encrypted,
but your metadata will be leaked.
</p>
</div>
<button
type="button"
className="mt-1 h-9 w-24 shrink-0 rounded-lg bg-neutral-200 font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-800"
>
Enable
</button>
</div>
</div>
);
}
@@ -1,6 +1,12 @@
import { Link } from 'react-router-dom';
import { CheckCircleIcon } from '@shared/icons';
import { useOnboarding } from '@stores/onboarding';
export function SuggestFollow() {
const enrich = useOnboarding((state) => state.enrich);
return (
<div className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200">
<div className="flex items-start justify-between">
@@ -11,12 +17,18 @@ export function SuggestFollow() {
world.
</p>
</div>
<Link
to="/auth/onboarding/enrich"
className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-neutral-200 font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-800"
>
Check
</Link>
{enrich ? (
<div className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-teal-500 text-white">
<CheckCircleIcon className="h-4 w-4" />
</div>
) : (
<Link
to="/auth/onboarding/enrich"
className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-neutral-200 font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-800"
>
Check
</Link>
)}
</div>
</div>
);