diff --git a/package.json b/package.json
index 4e6d7577..d1a1a22a 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
},
"dependencies": {
"@evilmartians/harmony": "^1.1.0",
+ "@formkit/auto-animate": "^0.8.0",
"@getalby/sdk": "^2.4.0",
"@nostr-dev-kit/ndk": "^2.0.2",
"@nostr-dev-kit/ndk-cache-dexie": "^2.0.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 599c558e..fbf4a139 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,6 +8,9 @@ dependencies:
'@evilmartians/harmony':
specifier: ^1.1.0
version: 1.1.0
+ '@formkit/auto-animate':
+ specifier: ^0.8.0
+ version: 0.8.0
'@getalby/sdk':
specifier: ^2.4.0
version: 2.4.0
@@ -838,6 +841,10 @@ packages:
resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==}
dev: false
+ /@formkit/auto-animate@0.8.0:
+ resolution: {integrity: sha512-G8f7489ka0mWyi+1IEZT+xgIwcpWtRMmE2x+IrVoQ+KM1cP6VDj/TbujZjwxdb0P8w8b16/qBfViRmydbYHwMw==}
+ dev: false
+
/@getalby/sdk@2.4.0:
resolution: {integrity: sha512-aIGNwLRF9coj6koxfq7P4GtFZbFjQbnIheix39x9176PwFw4dXOdGXHPXnqioJTmeq80y+vX1yd+u/f03YGoeg==}
engines: {node: '>=14'}
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index cd5e9525..3ad278f5 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -115,7 +115,7 @@ fn main() {
.plugin(
tauri_plugin_sql::Builder::default()
.add_migrations(
- "sqlite:lume.db",
+ "sqlite:lume_v2.db",
vec![
Migration {
version: 20230418013219,
diff --git a/src/app.tsx b/src/app.tsx
index c8d6e80d..e9ed5db0 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -3,7 +3,6 @@ import { fetch } from '@tauri-apps/plugin-http';
import { RouterProvider, createBrowserRouter, defer, redirect } from 'react-router-dom';
import { ReactFlowProvider } from 'reactflow';
-import { CreateAccountScreen } from '@app/auth/create';
import { OnboardingScreen } from '@app/auth/onboarding';
import { ChatsScreen } from '@app/chats';
import { ErrorScreen } from '@app/error';
@@ -24,17 +23,10 @@ export default function App() {
const accountLoader = async () => {
try {
- const totalAccount = await db.checkAccount();
-
- const onboarding = localStorage.getItem('onboarding');
- const step = onboarding ? JSON.parse(onboarding).state.step : null;
-
// redirect to welcome screen if none user exist
+ const totalAccount = await db.checkAccount();
if (totalAccount === 0) return redirect('/auth/welcome');
- // restart onboarding process
- if (step) return redirect(step);
-
return null;
} catch (e) {
await message(e, { title: 'An unexpected error has occurred', type: 'error' });
@@ -169,8 +161,10 @@ export default function App() {
},
{
path: 'create',
- element: ,
- errorElement: ,
+ async lazy() {
+ const { CreateAccountScreen } = await import('@app/auth/create');
+ return { Component: CreateAccountScreen };
+ },
},
{
path: 'import',
@@ -179,6 +173,13 @@ export default function App() {
return { Component: ImportAccountScreen };
},
},
+ {
+ path: 'complete',
+ async lazy() {
+ const { CompleteScreen } = await import('@app/auth/complete');
+ return { Component: CompleteScreen };
+ },
+ },
{
path: 'onboarding',
element: ,
@@ -187,30 +188,23 @@ export default function App() {
{
path: '',
async lazy() {
- const { OnboardStep1Screen } = await import(
- '@app/auth/onboarding/step-1'
+ const { OnboardingListScreen } = await import(
+ '@app/auth/onboarding/list'
);
- return { Component: OnboardStep1Screen };
+ return { Component: OnboardingListScreen };
},
},
{
- path: 'step-2',
+ path: 'enrich',
async lazy() {
- const { OnboardStep2Screen } = await import(
- '@app/auth/onboarding/step-2'
+ const { OnboardEnrichScreen } = await import(
+ '@app/auth/onboarding/enrich'
);
- return { Component: OnboardStep2Screen };
+ return { Component: OnboardEnrichScreen };
},
},
],
},
- {
- path: 'complete',
- async lazy() {
- const { CompleteScreen } = await import('@app/auth/complete');
- return { Component: CompleteScreen };
- },
- },
],
},
{
diff --git a/src/app/auth/components/features/customRelay.tsx b/src/app/auth/components/features/customRelay.tsx
new file mode 100644
index 00000000..9e9660bb
--- /dev/null
+++ b/src/app/auth/components/features/customRelay.tsx
@@ -0,0 +1,21 @@
+export function CustomRelay() {
+ return (
+
+
+
+
Personalize relay list
+
+ Lume offers some default relays for users who are not familiar with Nostr, but
+ you can consider adding more relays to discover more content.
+
+
+
+ Custom
+
+
+
+ );
+}
diff --git a/src/app/auth/components/features/favoriteHashtag.tsx b/src/app/auth/components/features/favoriteHashtag.tsx
new file mode 100644
index 00000000..6756b933
--- /dev/null
+++ b/src/app/auth/components/features/favoriteHashtag.tsx
@@ -0,0 +1,21 @@
+export function FavoriteHashtag() {
+ return (
+
+
+
+
Favorite hashtag
+
+ By adding favorite hashtag, Lume will display all contents related to this
+ hashtag as a column
+
+
+
+ Add
+
+
+
+ );
+}
diff --git a/src/app/auth/components/features/followList.tsx b/src/app/auth/components/features/followList.tsx
new file mode 100644
index 00000000..0b52715c
--- /dev/null
+++ b/src/app/auth/components/features/followList.tsx
@@ -0,0 +1,3 @@
+export function FollowList() {
+ return
;
+}
diff --git a/src/app/auth/components/features/linkList.tsx b/src/app/auth/components/features/linkList.tsx
new file mode 100644
index 00000000..87429b6e
--- /dev/null
+++ b/src/app/auth/components/features/linkList.tsx
@@ -0,0 +1,21 @@
+export function LinkList() {
+ return (
+
+
+
+
Enable Links
+
+ Beside newsfeed from your follows, you will see more content from all people
+ that followed by your follows.
+
+
+
+ Enable
+
+
+
+ );
+}
diff --git a/src/app/auth/components/features/nip04.tsx b/src/app/auth/components/features/nip04.tsx
new file mode 100644
index 00000000..79b4a108
--- /dev/null
+++ b/src/app/auth/components/features/nip04.tsx
@@ -0,0 +1,21 @@
+export function NIP04() {
+ return (
+
+
+
+
Enable direct message (Deprecated)
+
+ Send direct message to other user (NIP-04), all messages will be encrypted,
+ but your metadata will be leaked.
+
+
+
+ Enable
+
+
+
+ );
+}
diff --git a/src/app/auth/components/features/suggestFollow.tsx b/src/app/auth/components/features/suggestFollow.tsx
new file mode 100644
index 00000000..b731038a
--- /dev/null
+++ b/src/app/auth/components/features/suggestFollow.tsx
@@ -0,0 +1,23 @@
+import { Link } from 'react-router-dom';
+
+export function SuggestFollow() {
+ return (
+
+
+
+
Enrich your network
+
+ Follow more people to stay up to date with everything happening around the
+ world.
+
+
+
+ Check
+
+
+
+ );
+}
diff --git a/src/app/auth/create.tsx b/src/app/auth/create.tsx
new file mode 100644
index 00000000..47d1f145
--- /dev/null
+++ b/src/app/auth/create.tsx
@@ -0,0 +1,281 @@
+import { NDKEvent, NDKKind, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
+import { downloadDir } from '@tauri-apps/api/path';
+import { message, save } from '@tauri-apps/plugin-dialog';
+import { writeTextFile } from '@tauri-apps/plugin-fs';
+import { motion } from 'framer-motion';
+import { minidenticon } from 'minidenticons';
+import { generatePrivateKey, getPublicKey, nip19 } from 'nostr-tools';
+import { useState } from 'react';
+import { useForm } from 'react-hook-form';
+import { useNavigate } from 'react-router-dom';
+import { toast } from 'sonner';
+
+import { useNDK } from '@libs/ndk/provider';
+import { useStorage } from '@libs/storage/provider';
+
+import { AvatarUploader } from '@shared/avatarUploader';
+import { ArrowLeftIcon, LoaderIcon } from '@shared/icons';
+import { User } from '@shared/user';
+
+export function CreateAccountScreen() {
+ const [picture, setPicture] = useState('');
+ const [downloaded, setDownloaded] = useState(false);
+ const [loading, setLoading] = useState(false);
+ const [keys, setKeys] = useState(null);
+
+ const {
+ register,
+ handleSubmit,
+ formState: { isDirty, isValid },
+ } = useForm();
+ const { db } = useStorage();
+ const { ndk } = useNDK();
+
+ const navigate = useNavigate();
+
+ const svgURI =
+ 'data:image/svg+xml;utf8,' +
+ encodeURIComponent(minidenticon('lume new account', 90, 50));
+
+ const onSubmit = async (data: {
+ name: string;
+ display_name: string;
+ about: string;
+ }) => {
+ try {
+ setLoading(true);
+
+ const profile = {
+ ...data,
+ name: data.name,
+ display_name: data.display_name,
+ bio: data.about,
+ };
+
+ const userPrivkey = generatePrivateKey();
+ const userPubkey = getPublicKey(userPrivkey);
+ const userNpub = nip19.npubEncode(userPubkey);
+ const userNsec = nip19.nsecEncode(userPrivkey);
+
+ const event = new NDKEvent(ndk);
+ const signer = new NDKPrivateKeySigner(userPrivkey);
+
+ event.content = JSON.stringify(profile);
+ event.kind = NDKKind.Metadata;
+ event.created_at = Math.floor(Date.now() / 1000);
+ event.pubkey = userPubkey;
+ event.tags = [];
+
+ await event.sign(signer);
+ const publish = await event.publish();
+
+ if (publish) {
+ await db.createAccount(userNpub, userPubkey);
+ await db.secureSave(userPubkey, userPrivkey);
+ setKeys({
+ npub: userNpub,
+ nsec: userNsec,
+ pubkey: userPubkey,
+ privkey: userPrivkey,
+ });
+ setLoading(false);
+ } else {
+ toast('Create account failed');
+ setLoading(false);
+ }
+ } catch (e) {
+ return toast(e);
+ }
+ };
+
+ const download = async () => {
+ try {
+ const downloadPath = await downloadDir();
+ const fileName = `nostr_keys_${new Date().toISOString()}.txt`;
+ const filePath = await save({
+ defaultPath: downloadPath + '/' + fileName,
+ });
+
+ if (filePath) {
+ await writeTextFile(
+ filePath,
+ `Generated by Lume (lume.nu)\nPublic key: ${keys.npub}\nPrivate key: ${keys.nsec}`
+ );
+
+ setDownloaded(true);
+ } // else { user cancel action }
+ } catch (e) {
+ await message(e, { title: 'Cannot download account keys', type: 'error' });
+ }
+ };
+
+ return (
+
+
+ {!keys ? (
+
navigate(-1)}
+ className="inline-flex items-center gap-2 text-sm font-medium"
+ >
+
+ Back
+
+ ) : null}
+
+
+
+ Let's set up your Nostr account.
+
+
+ {!keys ? (
+
+ ) : (
+ <>
+
+
+
+
+
+
Backup account
+
+
+ Your private key is your password. If you lose this key, you will
+ lose access to your account! Copy it and keep it in a safe place.{' '}
+
+ There is no way to reset your private key.
+
+
+
+ Public key is used for sharing with other people so that they can
+ find you using the public key.
+
+
+
+ {!downloaded ? (
+
download()}
+ className="inline-flex h-9 w-full shrink-0 items-center justify-center rounded-lg bg-blue-500 font-semibold text-white hover:bg-blue-600"
+ >
+ Download account keys
+
+ ) : null}
+
+
+ >
+ )}
+ {downloaded ? (
+
navigate('/auth/onboarding', { state: { newuser: true } })}
+ >
+ Finish
+
+ ) : null}
+
+
+
+ );
+}
diff --git a/src/app/auth/create/index.tsx b/src/app/auth/create/index.tsx
deleted file mode 100644
index c7581843..00000000
--- a/src/app/auth/create/index.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { Outlet } from 'react-router-dom';
-
-export function CreateAccountScreen() {
- return ;
-}
diff --git a/src/app/auth/create/step-1.tsx b/src/app/auth/create/step-1.tsx
deleted file mode 100644
index 4955776f..00000000
--- a/src/app/auth/create/step-1.tsx
+++ /dev/null
@@ -1,170 +0,0 @@
-import { downloadDir } from '@tauri-apps/api/path';
-import { writeText } from '@tauri-apps/plugin-clipboard-manager';
-import { message, save } from '@tauri-apps/plugin-dialog';
-import { writeTextFile } from '@tauri-apps/plugin-fs';
-import { generatePrivateKey, getPublicKey, nip19 } from 'nostr-tools';
-import { useEffect, useMemo, useState } from 'react';
-import { useNavigate } from 'react-router-dom';
-
-import { useStorage } from '@libs/storage/provider';
-
-import { CopyIcon } from '@shared/icons';
-
-import { useOnboarding } from '@stores/onboarding';
-
-export function CreateStep1Screen() {
- const { db } = useStorage();
-
- const navigate = useNavigate();
- const setPubkey = useOnboarding((state) => state.setPubkey);
- const setStep = useOnboarding((state) => state.setStep);
-
- const [loading, setLoading] = useState(false);
- const [copied, setCopied] = useState(false);
- const [downloaded, setDownloaded] = useState(false);
-
- const privkey = useMemo(() => generatePrivateKey(), []);
- const pubkey = getPublicKey(privkey);
- const npub = nip19.npubEncode(pubkey);
- const nsec = nip19.nsecEncode(privkey);
-
- const copyPrivkey = async () => {
- try {
- await writeText(nsec);
- setCopied(true);
-
- setTimeout(() => setCopied(false), 2000);
- } catch (e) {
- await message(e, { title: 'Cannot copy private key', type: 'error' });
- }
- };
-
- const download = async () => {
- try {
- const downloadPath = await downloadDir();
- const fileName = `nostr_keys_${new Date().toISOString()}.txt`;
- const filePath = await save({
- defaultPath: downloadPath + '/' + fileName,
- });
-
- if (filePath) {
- await writeTextFile(
- filePath,
- `Generated by Lume (lume.nu)\nPublic key: ${npub}\nPrivate key: ${nsec}`
- );
-
- setDownloaded(true);
- } // else { user cancel action }
- } catch (e) {
- await message(e, { title: 'Cannot download account keys', type: 'error' });
- }
- };
-
- const submit = async () => {
- try {
- setLoading(true);
- setPubkey(pubkey);
-
- // save privkey
- await db.secureSave(privkey, pubkey);
-
- // save to database
- await db.createAccount(npub, pubkey);
-
- // redirect to next step
- navigate('/auth/create/step-2', { replace: true });
- } catch (e) {
- await message(e, { title: 'Something went wrong!', type: 'error' });
- }
- };
-
- useEffect(() => {
- // save current step, if user close app and reopen it
- setStep('/auth/create');
- }, []);
-
- return (
-
-
-
-
- This is your new Nostr account
-
-
- Your private key is your password. If you lose this key, you will lose access
- to your account! Copy it and keep it in a safe place.{' '}
-
- There is no way to reset your private key.
-
-
-
- Public key is used for sharing with other people so that they can find you
- using the public key.
-
-
-
-
-
-
- Private Key
-
-
-
- copyPrivkey()}
- className="group absolute right-2 top-1/2 inline-flex h-7 -translate-y-1/2 transform items-center gap-1.5 rounded-md bg-neutral-300 px-2.5 text-sm text-neutral-800 hover:bg-neutral-400 hover:text-neutral-900 dark:bg-neutral-700 dark:text-neutral-200 dark:hover:bg-neutral-600 dark:hover:text-neutral-100"
- >
-
- {copied ? 'Copied' : 'Copy'}
-
-
-
-
-
- Public Key
-
-
-
-
-
- download()}
- className="inline-flex h-10 w-full items-center justify-center rounded-lg bg-blue-500 px-6 font-medium leading-none text-white hover:bg-blue-600 focus:outline-none"
- >
- {downloaded ? 'Downloaded' : 'Download account keys'}
-
- submit()}
- className="inline-flex h-10 w-full items-center justify-center rounded-lg bg-neutral-200 px-6 font-medium leading-none text-neutral-900 hover:bg-neutral-300 focus:outline-none dark:bg-neutral-800 dark:text-neutral-100 dark:hover:bg-neutral-700"
- >
- {loading ? 'Creating...' : 'Continue'}
-
-
- By clicking 'Continue', you are ensuring that your keys are saved
- in a safe place. You cannot recover these keys if they are lost.
-
-
-
-
-
- );
-}
diff --git a/src/app/auth/create/step-2.tsx b/src/app/auth/create/step-2.tsx
deleted file mode 100644
index 8341d03c..00000000
--- a/src/app/auth/create/step-2.tsx
+++ /dev/null
@@ -1,174 +0,0 @@
-import { NDKKind } from '@nostr-dev-kit/ndk';
-import { useEffect, useState } from 'react';
-import { useForm } from 'react-hook-form';
-import { useNavigate } from 'react-router-dom';
-
-import { useStorage } from '@libs/storage/provider';
-
-import { AvatarUploader } from '@shared/avatarUploader';
-import { BannerUploader } from '@shared/bannerUploader';
-import { LoaderIcon } from '@shared/icons';
-import { ArrowRightCircleIcon } from '@shared/icons/arrowRightCircle';
-import { Image } from '@shared/image';
-
-import { useOnboarding } from '@stores/onboarding';
-import { WidgetKinds } from '@stores/widgets';
-
-import { useNostr } from '@utils/hooks/useNostr';
-
-export function CreateStep2Screen() {
- const navigate = useNavigate();
- const setStep = useOnboarding((state) => state.setStep);
-
- const [loading, setLoading] = useState(false);
- const [picture, setPicture] = useState('https://void.cat/d/5VKmKyuHyxrNMf9bWSVPih');
- const [banner, setBanner] = useState('');
-
- const { db } = useStorage();
- const { publish } = useNostr();
- const {
- register,
- handleSubmit,
- formState: { isDirty, isValid },
- } = useForm();
-
- const onSubmit = async (data: { name: string; about: string; website: string }) => {
- setLoading(true);
- try {
- const profile = {
- ...data,
- name: data.name,
- display_name: data.name,
- bio: data.about,
- website: data.website,
- };
-
- const event = await publish({
- content: JSON.stringify(profile),
- kind: NDKKind.Metadata,
- tags: [],
- });
-
- // create default widget
- await db.createWidget(WidgetKinds.other.learnNostr, 'Learn Nostr', '');
-
- if (event) {
- navigate('/auth/onboarding', { replace: true });
- }
- } catch (e) {
- console.log('error: ', e);
- setLoading(false);
- }
- };
-
- useEffect(() => {
- // save current step, if user close app and reopen it
- setStep('/auth/create/step-3');
- }, []);
-
- return (
-
-
-
- Personalize your Nostr profile
-
-
- Nostr profile is synchronous across all Nostr clients. If you create a profile
- on Lume, it will also work well with other Nostr clients. If you update your
- profile on another Nostr client, it will also sync to Lume.
-
-
-
-
-
-
-
-
- {banner ? (
-
- ) : (
-
- )}
-
-
-
-
-
-
-
-
-
- Name *
-
-
-
-
-
- Bio
-
-
-
-
-
- Website
-
-
-
-
- {loading ? (
- <>
-
- Creating...
-
- >
- ) : (
- <>
-
- Continue
-
- >
- )}
-
-
-
-
-
- );
-}
diff --git a/src/app/auth/import.tsx b/src/app/auth/import.tsx
index 25a7ecf6..ccbb2efa 100644
--- a/src/app/auth/import.tsx
+++ b/src/app/auth/import.tsx
@@ -7,6 +7,7 @@ import { twMerge } from 'tailwind-merge';
import { useStorage } from '@libs/storage/provider';
+import { ArrowLeftIcon } from '@shared/icons';
import { User } from '@shared/user';
export function ImportAccountScreen() {
@@ -58,24 +59,34 @@ export function ImportAccountScreen() {
}
};
- const finish = async () => {
- navigate('/auth/onboarding');
- };
-
return (
-
+
+
+ {!created ? (
+
navigate(-1)}
+ className="inline-flex items-center gap-2 text-sm font-medium"
+ >
+
+ Back
+
+ ) : null}
+
- Import your Nostr account
+ Import your Nostr account.
- Enter your nostr npub:
+ Enter your npub:
setNpub(e.target.value)}
@@ -105,7 +116,6 @@ export function ImportAccountScreen() {
opacity: 1,
y: 0,
}}
- transition={{ y: { velocity: -100 } }}
className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200"
>
Account found
@@ -142,15 +152,15 @@ export function ImportAccountScreen() {
opacity: 1,
y: 0,
}}
- transition={{ y: { velocity: -100 } }}
className="rounded-lg bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200"
>
-
- Enter your nostr nsec (optional):
+
+ Enter your nsec (optional):
setNsec(e.target.value)}
@@ -204,15 +214,16 @@ export function ImportAccountScreen() {
+ navigate('/auth/onboarding', { state: { newuser: false } })
+ }
>
Finish
diff --git a/src/app/auth/onboarding/step-1.tsx b/src/app/auth/onboarding/enrich.tsx
similarity index 50%
rename from src/app/auth/onboarding/step-1.tsx
rename to src/app/auth/onboarding/enrich.tsx
index 9431ea00..9b4d0f9b 100644
--- a/src/app/auth/onboarding/step-1.tsx
+++ b/src/app/auth/onboarding/enrich.tsx
@@ -1,21 +1,16 @@
import { useQuery } from '@tanstack/react-query';
-import { useEffect, useState } from 'react';
-import { Link, useNavigate } from 'react-router-dom';
+import { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
import { useStorage } from '@libs/storage/provider';
-import { ArrowRightCircleIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons';
+import { ArrowLeftIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons';
import { User } from '@shared/user';
-import { useOnboarding } from '@stores/onboarding';
-
import { useNostr } from '@utils/hooks/useNostr';
import { arrayToNIP02 } from '@utils/transform';
-export function OnboardStep1Screen() {
- const navigate = useNavigate();
- const setStep = useOnboarding((state) => state.setStep);
-
+export function OnboardEnrichScreen() {
const { publish, fetchUserData } = useNostr();
const { db } = useStorage();
const { status, data } = useQuery(['trending-profiles-widget'], async () => {
@@ -29,6 +24,8 @@ export function OnboardStep1Screen() {
const [loading, setLoading] = useState(false);
const [follows, setFollows] = useState([]);
+ const navigate = useNavigate();
+
// toggle follow state
const toggleFollow = (pubkey: string) => {
const arr = follows.includes(pubkey)
@@ -59,27 +56,28 @@ export function OnboardStep1Screen() {
}
};
- useEffect(() => {
- // save current step, if user close app and reopen it
- setStep('/auth/onboarding');
- }, []);
-
return (
-
-
-
+
+
+
navigate(-1)}
+ className="inline-flex items-center gap-2 text-sm font-medium"
+ >
+
+ Back
+
+
+
+
{loading ? 'Loading...' : 'Enrich your network'}
-
- Choose the account you want to follow. These accounts are trending in the last
- 24 hours. If none of the accounts interest you, you can explore more options and
- add them later.
-
{status === 'loading' ? (
-
+
) : (
data?.profiles.map((item: { pubkey: string; profile: { content: string } }) => (
@@ -87,7 +85,7 @@ export function OnboardStep1Screen() {
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"
+ className="relative h-[300px] shrink-0 grow-0 basis-[250px] overflow-hidden rounded-lg bg-neutral-200 px-4 py-4 hover:bg-neutral-300 dark:bg-neutral-800 dark:hover:bg-neutral-700"
>
{follows.includes(item.pubkey) && (
-
+
)}
))
)}
-
-
-
- {loading ? (
- <>
-
- It might take a bit, please patient...
-
- >
- ) : (
- <>
-
- Follow {follows.length} accounts & Continue
-
- >
- )}
-
-
- Skip, you can add later
-
-
+
+
+ {loading ? (
+ <>
+
+ It might take a bit, please patient...
+ >
+ ) : (
+ Follow {follows.length} accounts & Continue
+ )}
+
);
diff --git a/src/app/auth/onboarding/step-2.tsx b/src/app/auth/onboarding/hashtag.tsx
similarity index 90%
rename from src/app/auth/onboarding/step-2.tsx
rename to src/app/auth/onboarding/hashtag.tsx
index 879d4370..de592306 100644
--- a/src/app/auth/onboarding/step-2.tsx
+++ b/src/app/auth/onboarding/hashtag.tsx
@@ -1,12 +1,11 @@
import { message } from '@tauri-apps/plugin-dialog';
-import { useEffect, useState } from 'react';
+import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useStorage } from '@libs/storage/provider';
import { ArrowRightCircleIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons';
-import { useOnboarding } from '@stores/onboarding';
import { WidgetKinds } from '@stores/widgets';
const data = [
@@ -32,15 +31,13 @@ const data = [
{ hashtag: '#dev' },
];
-export function OnboardStep2Screen() {
+export function OnboardHashtagScreen() {
+ const { db } = useStorage();
const navigate = useNavigate();
- const [setStep, clearStep] = useOnboarding((state) => [state.setStep, state.clearStep]);
const [loading, setLoading] = useState(false);
const [tags, setTags] = useState(new Set
());
- const { db } = useStorage();
-
const toggleTag = (tag: string) => {
if (tags.has(tag)) {
setTags((prev) => {
@@ -57,9 +54,6 @@ export function OnboardStep2Screen() {
// update last login
await db.updateLastLogin();
- // clear local storage
- clearStep();
-
navigate('/auth/complete', { replace: true });
};
@@ -74,9 +68,6 @@ export function OnboardStep2Screen() {
// update last login
await db.updateLastLogin();
- // clear local storage
- clearStep();
-
navigate('/auth/complete', { replace: true });
} catch (e) {
setLoading(false);
@@ -84,11 +75,6 @@ export function OnboardStep2Screen() {
}
};
- useEffect(() => {
- // save current step, if user close app and reopen it
- setStep('/auth/onboarding/step-2');
- }, []);
-
return (
diff --git a/src/app/auth/onboarding/index.tsx b/src/app/auth/onboarding/index.tsx
index 8a9358a7..58fd243e 100644
--- a/src/app/auth/onboarding/index.tsx
+++ b/src/app/auth/onboarding/index.tsx
@@ -1,9 +1,5 @@
import { Outlet } from 'react-router-dom';
export function OnboardingScreen() {
- return (
-
-
-
- );
+ return
;
}
diff --git a/src/app/auth/onboarding/list.tsx b/src/app/auth/onboarding/list.tsx
new file mode 100644
index 00000000..e7cc6d88
--- /dev/null
+++ b/src/app/auth/onboarding/list.tsx
@@ -0,0 +1,41 @@
+import { Link, useLocation } from 'react-router-dom';
+
+import { CustomRelay } from '@app/auth/components/features/customRelay';
+import { FavoriteHashtag } from '@app/auth/components/features/favoriteHashtag';
+import { FollowList } from '@app/auth/components/features/followList';
+import { LinkList } from '@app/auth/components/features/linkList';
+import { NIP04 } from '@app/auth/components/features/nip04';
+import { SuggestFollow } from '@app/auth/components/features/suggestFollow';
+
+export function OnboardingListScreen() {
+ const { state } = useLocation();
+ const { newuser }: { newuser: boolean } = state;
+
+ return (
+
+
+
+
+ You're almost ready to use Lume.
+
+
+ Let's start personalizing your experience.
+
+
+
+ {newuser ? : }
+
+
+
+
+
+ Continue
+
+
+
+
+ );
+}
diff --git a/src/app/auth/onboarding/step-3.tsx b/src/app/auth/onboarding/relays.tsx
similarity index 94%
rename from src/app/auth/onboarding/step-3.tsx
rename to src/app/auth/onboarding/relays.tsx
index 08d52928..98664805 100644
--- a/src/app/auth/onboarding/step-3.tsx
+++ b/src/app/auth/onboarding/relays.tsx
@@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query';
-import { useEffect, useState } from 'react';
+import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNDK } from '@libs/ndk/provider';
@@ -9,14 +9,12 @@ import { ArrowRightCircleIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons
import { User } from '@shared/user';
import { FULL_RELAYS } from '@stores/constants';
-import { useOnboarding } from '@stores/onboarding';
import { useNostr } from '@utils/hooks/useNostr';
-export function OnboardStep3Screen() {
+export function OnboardRelaysScreen() {
const navigate = useNavigate();
- const [setStep, clearStep] = useOnboarding((state) => [state.setStep, state.clearStep]);
const [loading, setLoading] = useState(false);
const [relays, setRelays] = useState(new Set
());
@@ -81,7 +79,6 @@ export function OnboardStep3Screen() {
// update last login
await db.updateLastLogin();
- clearStep();
navigate('/', { replace: true });
} catch (e) {
setLoading(false);
@@ -89,11 +86,6 @@ export function OnboardStep3Screen() {
}
};
- useEffect(() => {
- // save current step, if user close app and reopen it
- setStep('/auth/onboarding/step-3');
- }, []);
-
return (
diff --git a/src/libs/ndk/provider.tsx b/src/libs/ndk/provider.tsx
index cb88ec90..a6b33d1f 100644
--- a/src/libs/ndk/provider.tsx
+++ b/src/libs/ndk/provider.tsx
@@ -28,12 +28,7 @@ const NDKProvider = ({ children }: PropsWithChildren
) => {
data-tauri-drag-region
className="flex h-screen w-screen items-center justify-center bg-neutral-50 dark:bg-neutral-950"
>
-
-
-
- Connecting...
-
-
+
);
}
diff --git a/src/libs/storage/instance.ts b/src/libs/storage/instance.ts
index 37cbb0d3..eded08dd 100644
--- a/src/libs/storage/instance.ts
+++ b/src/libs/storage/instance.ts
@@ -18,7 +18,7 @@ export class LumeStorage {
this.platform = platform;
}
- public async secureSave(value: string, key?: string) {
+ public async secureSave(key: string, value: string) {
return await invoke('secure_save', { key, value });
}
diff --git a/src/libs/storage/provider.tsx b/src/libs/storage/provider.tsx
index 0d5a1dbd..de1cdf6d 100644
--- a/src/libs/storage/provider.tsx
+++ b/src/libs/storage/provider.tsx
@@ -19,7 +19,7 @@ const StorageProvider = ({ children }: PropsWithChildren
) => {
const initLumeStorage = async () => {
try {
- const sqlite = await Database.load('sqlite:lume.db');
+ const sqlite = await Database.load('sqlite:lume_v2.db');
const platformName = await platform();
const dir = await appConfigDir();
diff --git a/src/shared/avatarUploader.tsx b/src/shared/avatarUploader.tsx
index e15f0034..113302f3 100644
--- a/src/shared/avatarUploader.tsx
+++ b/src/shared/avatarUploader.tsx
@@ -25,13 +25,14 @@ export function AvatarUploader({
uploadAvatar()}
- className="inline-flex h-full w-full items-center justify-center rounded-lg bg-black/50 hover:bg-black/60"
+ className="inline-flex items-center gap-1 rounded-lg border border-blue-200 bg-blue-100 px-1.5 py-1 text-sm font-medium text-blue-500 hover:bg-blue-200 dark:bg-blue-900 dark:text-blue-500 dark:hover:bg-blue-800"
>
{loading ? (
-
+
) : (
-
+
)}
+ Change avatar
);
}
diff --git a/src/shared/bannerUploader.tsx b/src/shared/bannerUploader.tsx
index f970c16f..83e3c329 100644
--- a/src/shared/bannerUploader.tsx
+++ b/src/shared/bannerUploader.tsx
@@ -25,14 +25,16 @@ export function BannerUploader({
uploadBanner()}
- className="inline-flex h-full w-full flex-col items-center justify-center gap-1 bg-black/40 hover:bg-black/50"
+ className="inline-flex h-full w-full flex-col items-center justify-center"
>
{loading ? (
-
+
) : (
-
+
)}
- Add a banner image
+
+ Add cover
+
);
}
diff --git a/src/shared/layouts/auth.tsx b/src/shared/layouts/auth.tsx
index f89dc00c..a4683905 100644
--- a/src/shared/layouts/auth.tsx
+++ b/src/shared/layouts/auth.tsx
@@ -8,7 +8,11 @@ export function AuthLayout() {
return (
- {db.platform !== 'macos' ?
: null}
+ {db.platform !== 'macos' ? (
+
+ ) : (
+
+ )}
diff --git a/src/shared/navigation.tsx b/src/shared/navigation.tsx
index 992fdaff..ee0eac8d 100644
--- a/src/shared/navigation.tsx
+++ b/src/shared/navigation.tsx
@@ -3,14 +3,7 @@ import { twMerge } from 'tailwind-merge';
import { ActiveAccount } from '@shared/accounts/active';
import { ComposerModal } from '@shared/composer';
-import {
- ChatsIcon,
- CommunityIcon,
- ExploreIcon,
- HomeIcon,
- NwcIcon,
- RelayIcon,
-} from '@shared/icons';
+import { ChatsIcon, ExploreIcon, HomeIcon, NwcIcon, RelayIcon } from '@shared/icons';
export function Navigation() {
return (
@@ -58,27 +51,6 @@ export function Navigation() {
>
)}
-
- {({ isActive }) => (
- <>
-
-
-
- Groups
- >
- )}
-
void;
- setTempPrivkey: (privkey: string) => void;
- setStep: (url: string) => void;
- clearStep: () => void;
-}
-
-export const useOnboarding = create()(
- persist(
- (set) => ({
- step: null,
- pubkey: null,
- tempPrivkey: null,
- setPubkey: (pubkey: string) => {
- set({ pubkey });
- },
- setTempPrivkey: (privkey: string) => {
- set({ tempPrivkey: privkey });
- },
- setStep: (url: string) => {
- set({ step: url });
- },
- clearStep: () => {
- set({ step: null, pubkey: null, tempPrivkey: null });
- },
- }),
- {
- name: 'onboarding',
- storage: createJSONStorage(() => localStorage),
- }
- )
-);