mirror of
https://github.com/lumehq/lume.git
synced 2025-03-18 05:41:53 +01:00
refactor initial database and state management
This commit is contained in:
parent
964343ccc8
commit
458f826958
11
.prettierrc
11
.prettierrc
@ -8,16 +8,7 @@
|
||||
"endOfLine": "lf",
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": true,
|
||||
"importOrder": [
|
||||
"^@layouts/(.*)$",
|
||||
"^@pages/(.*)$",
|
||||
"^@components/(.*)$",
|
||||
"^@utils/(.*)$",
|
||||
"^@stores/(.*)$",
|
||||
"^@assets/(.*)$",
|
||||
"<THIRD_PARTY_MODULES>",
|
||||
"^[./]"
|
||||
],
|
||||
"importOrder": ["^@layouts/(.*)$", "^@pages/(.*)$", "^@components/(.*)$", "^@utils/(.*)$", "^@stores/(.*)$", "<THIRD_PARTY_MODULES>", "^[./]"],
|
||||
"importOrderSeparation": true,
|
||||
"importOrderSortSpecifiers": true,
|
||||
"plugins": ["@trivago/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss"],
|
||||
|
@ -12,11 +12,10 @@
|
||||
"**/*": "prettier --write --ignore-unknown"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nanostores/persistent": "^0.7.0",
|
||||
"@nanostores/react": "^0.4.1",
|
||||
"@radix-ui/react-dialog": "^1.0.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.3",
|
||||
"@radix-ui/react-icons": "^1.2.0",
|
||||
"@rehooks/local-storage": "^2.4.4",
|
||||
"@tauri-apps/api": "^1.2.0",
|
||||
"@uiw/react-markdown-preview": "^4.1.9",
|
||||
"@uiw/react-md-editor": "^3.20.5",
|
||||
|
43
pnpm-lock.yaml
generated
43
pnpm-lock.yaml
generated
@ -1,11 +1,10 @@
|
||||
lockfileVersion: 5.4
|
||||
|
||||
specifiers:
|
||||
'@nanostores/persistent': ^0.7.0
|
||||
'@nanostores/react': ^0.4.1
|
||||
'@radix-ui/react-dialog': ^1.0.2
|
||||
'@radix-ui/react-dropdown-menu': ^2.0.3
|
||||
'@radix-ui/react-icons': ^1.2.0
|
||||
'@rehooks/local-storage': ^2.4.4
|
||||
'@tailwindcss/typography': ^0.5.9
|
||||
'@tauri-apps/api': ^1.2.0
|
||||
'@tauri-apps/cli': ^1.2.3
|
||||
@ -54,11 +53,10 @@ specifiers:
|
||||
ws: ^8.12.1
|
||||
|
||||
dependencies:
|
||||
'@nanostores/persistent': 0.7.0_nanostores@0.7.4
|
||||
'@nanostores/react': 0.4.1_nkfnbc2tpc77iht7asm3uqwau4
|
||||
'@radix-ui/react-dialog': 1.0.2_zula6vjvt3wdocc4mwcxqa6nzi
|
||||
'@radix-ui/react-dropdown-menu': 2.0.3_zula6vjvt3wdocc4mwcxqa6nzi
|
||||
'@radix-ui/react-icons': 1.2.0_react@18.2.0
|
||||
'@rehooks/local-storage': 2.4.4_react@18.2.0
|
||||
'@tauri-apps/api': 1.2.0
|
||||
'@uiw/react-markdown-preview': 4.1.9_zula6vjvt3wdocc4mwcxqa6nzi
|
||||
'@uiw/react-md-editor': 3.20.5_zula6vjvt3wdocc4mwcxqa6nzi
|
||||
@ -467,27 +465,6 @@ packages:
|
||||
'@jridgewell/sourcemap-codec': 1.4.14
|
||||
dev: false
|
||||
|
||||
/@nanostores/persistent/0.7.0_nanostores@0.7.4:
|
||||
resolution: { integrity: sha512-4PAInL/T1hbftZUJ0cmgdFHBMalUoq7BUXFBy7QfyMv/8X3LPTYNh/yxspL7+J+XM3UNvVI7IFRMMs6FBasjhQ== }
|
||||
engines: { node: ^14.0.0 || ^16.0.0 || >=18.0.0 }
|
||||
peerDependencies:
|
||||
nanostores: ^0.7.0
|
||||
dependencies:
|
||||
nanostores: 0.7.4
|
||||
dev: false
|
||||
|
||||
/@nanostores/react/0.4.1_nkfnbc2tpc77iht7asm3uqwau4:
|
||||
resolution: { integrity: sha512-lsv0CYrMxczbXtoV/mxFVEoL/uVjEjseoP89srO/5yNAOkJka+dSFS7LYyWEbuvCPO7EgbtkvRpO5V+OztKQOw== }
|
||||
engines: { node: ^14.0.0 || ^16.0.0 || >=18.0.0 }
|
||||
peerDependencies:
|
||||
nanostores: ^0.7.0
|
||||
react: '>=18.0.0'
|
||||
dependencies:
|
||||
nanostores: 0.7.4
|
||||
react: 18.2.0
|
||||
use-sync-external-store: 1.2.0_react@18.2.0
|
||||
dev: false
|
||||
|
||||
/@next/env/13.2.1:
|
||||
resolution: { integrity: sha512-Hq+6QZ6kgmloCg8Kgrix+4F0HtvLqVK3FZAnlAoS0eonaDemHe1Km4kwjSWRE3JNpJNcKxFHF+jsZrYo0SxWoQ== }
|
||||
dev: false
|
||||
@ -1009,6 +986,14 @@ packages:
|
||||
'@babel/runtime': 7.21.0
|
||||
dev: false
|
||||
|
||||
/@rehooks/local-storage/2.4.4_react@18.2.0:
|
||||
resolution: { integrity: sha512-zE+kfOkG59n/1UTxdmbwktIosclr67Nlbf2MzUJ9mNtCSypVscNHeD1qT6JCSo5Pjj8DO893IKWNLJqKKzDL/Q== }
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@rushstack/eslint-patch/1.2.0:
|
||||
resolution: { integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== }
|
||||
dev: true
|
||||
@ -5374,14 +5359,6 @@ packages:
|
||||
tslib: 2.5.0
|
||||
dev: false
|
||||
|
||||
/use-sync-external-store/1.2.0_react@18.2.0:
|
||||
resolution: { integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== }
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/util-deprecate/1.0.2:
|
||||
resolution: { integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== }
|
||||
dev: true
|
||||
|
@ -1,4 +1,28 @@
|
||||
-- Add migration script here
|
||||
-- create relays
|
||||
CREATE TABLE
|
||||
relays (
|
||||
id INTEGER PRIMARY KEY,
|
||||
relay_url TEXT NOT NULL,
|
||||
relay_status INTEGER NOT NULL DEFAULT 1,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
relays (relay_url, relay_status)
|
||||
VALUES
|
||||
("wss://relay.damus.io", "1"),
|
||||
("wss://relay.uselume.xyz", "0"),
|
||||
("wss://nostr-pub.wellorder.net", "1"),
|
||||
("wss://nostr.bongbong.com", "1"),
|
||||
("wss://nostr.zebedee.cloud", "1"),
|
||||
("wss://nostr.fmt.wiz.biz", "1"),
|
||||
("wss://nostr.walletofsatoshi.com", "1"),
|
||||
("wss://relay.snort.social", "1"),
|
||||
("wss://offchain.pub", "1"),
|
||||
("wss://nos.lol", "1");
|
||||
|
||||
-- create accounts
|
||||
CREATE TABLE
|
||||
accounts (
|
||||
@ -6,6 +30,7 @@ CREATE TABLE
|
||||
privkey TEXT NOT NULL,
|
||||
npub TEXT NOT NULL,
|
||||
nsec TEXT NOT NULL,
|
||||
is_active INTEGER NOT NULL DEFAULT 0,
|
||||
metadata JSON
|
||||
);
|
||||
|
||||
@ -40,5 +65,6 @@ CREATE TABLE
|
||||
kind INTEGER NOT NULL DEFAULT 1,
|
||||
tags TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
relay TEXT,
|
||||
is_multi BOOLEAN DEFAULT 0
|
||||
);
|
@ -1,19 +1,16 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Account } from '@components/accountBar/account';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
|
||||
import LumeSymbol from '@assets/icons/Lume';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { PlusIcon } from '@radix-ui/react-icons';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import Link from 'next/link';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import Database from 'tauri-plugin-sql-api';
|
||||
|
||||
export default function AccountBar() {
|
||||
const [users, setUsers] = useState([]);
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
|
||||
const getAccounts = useCallback(async () => {
|
||||
const db = await Database.load('sqlite:lume.db');
|
||||
@ -30,7 +27,7 @@ export default function AccountBar() {
|
||||
<div className="flex h-full flex-col items-center justify-between px-2 pt-12 pb-4">
|
||||
<div className="flex flex-col gap-4">
|
||||
{users.map((user, index) => (
|
||||
<Account key={index} user={user} current={$currentUser.pubkey} />
|
||||
<Account key={index} user={user} current={currentUser.pubkey} />
|
||||
))}
|
||||
<Link
|
||||
href="/onboarding"
|
||||
|
@ -4,10 +4,7 @@ import { RelayContext } from '@components/contexts/relay';
|
||||
|
||||
import { dateToUnix, hoursAgo } from '@utils/getDate';
|
||||
|
||||
import { follows } from '@stores/follows';
|
||||
import { relays } from '@stores/relays';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import { memo, useCallback, useContext, useRef } from 'react';
|
||||
|
||||
export const NoteConnector = memo(function NoteConnector() {
|
||||
@ -16,8 +13,8 @@ export const NoteConnector = memo(function NoteConnector() {
|
||||
|
||||
const now = useRef(new Date());
|
||||
|
||||
const $follows = useStore(follows);
|
||||
const $relays = useStore(relays);
|
||||
const [follows]: any = useLocalStorage('follows');
|
||||
const [relays]: any = useLocalStorage('relays');
|
||||
|
||||
const insertDB = useCallback(
|
||||
async (event: any) => {
|
||||
@ -35,11 +32,11 @@ export const NoteConnector = memo(function NoteConnector() {
|
||||
[
|
||||
{
|
||||
kinds: [1],
|
||||
authors: $follows,
|
||||
authors: follows,
|
||||
since: dateToUnix(hoursAgo(12, now.current)),
|
||||
},
|
||||
],
|
||||
$relays,
|
||||
relays,
|
||||
(event: any) => {
|
||||
insertDB(event).catch(console.error);
|
||||
},
|
||||
|
@ -1,13 +1,56 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { createContext } from 'react';
|
||||
import { writeStorage } from '@rehooks/local-storage';
|
||||
import { createContext, useEffect, useMemo } from 'react';
|
||||
import Database from 'tauri-plugin-sql-api';
|
||||
|
||||
export const DatabaseContext = createContext({});
|
||||
|
||||
const db = typeof window !== 'undefined' ? await Database.load('sqlite:lume.db') : null;
|
||||
const initDB = typeof window !== 'undefined' ? await Database.load('sqlite:lume.db') : null;
|
||||
|
||||
export default function DatabaseProvider({ children }: { children: React.ReactNode }) {
|
||||
const value = db;
|
||||
const db = useMemo(() => initDB, []);
|
||||
|
||||
return <DatabaseContext.Provider value={value}>{children}</DatabaseContext.Provider>;
|
||||
useEffect(() => {
|
||||
const getRelays = async () => {
|
||||
const arr = [];
|
||||
const result: any[] = await db.select('SELECT relay_url FROM relays WHERE relay_status = "1"');
|
||||
|
||||
result.forEach((item: { relay_url: string }) => {
|
||||
arr.push(item.relay_url);
|
||||
});
|
||||
|
||||
writeStorage('relays', arr);
|
||||
};
|
||||
|
||||
const getAccount = async () => {
|
||||
const result = await db.select(`SELECT * FROM accounts LIMIT 1`);
|
||||
writeStorage('current-user', result[0]);
|
||||
|
||||
return result[0];
|
||||
};
|
||||
|
||||
const getFollows = async (id: string) => {
|
||||
const arr = [];
|
||||
const result: any[] = await db.select(`SELECT pubkey FROM follows WHERE account = "${id}"`);
|
||||
|
||||
result.forEach((item: { pubkey: string }) => {
|
||||
arr.push(item.pubkey);
|
||||
});
|
||||
|
||||
writeStorage('follows', arr);
|
||||
};
|
||||
|
||||
if (db !== null) {
|
||||
getRelays().catch(console.error);
|
||||
getAccount()
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
getFollows(res.id).catch(console.error);
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
}, [db]);
|
||||
|
||||
return <DatabaseContext.Provider value={{ db }}>{children}</DatabaseContext.Provider>;
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import * as commands from '@uiw/react-md-editor/lib/commands';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { dateToUnix, useNostr } from 'nostr-react';
|
||||
@ -17,9 +15,9 @@ export default function CreatePost() {
|
||||
const { publish } = useNostr();
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const pubkey = $currentUser.pubkey;
|
||||
const privkey = $currentUser.privkey;
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
const pubkey = currentUser.pubkey;
|
||||
const privkey = currentUser.privkey;
|
||||
|
||||
const postButton = {
|
||||
name: 'post',
|
||||
@ -27,9 +25,7 @@ export default function CreatePost() {
|
||||
buttonProps: { className: 'cta-btn', 'aria-label': 'Post a message' },
|
||||
icon: (
|
||||
<div className="relative inline-flex h-10 w-16 transform cursor-pointer overflow-hidden rounded bg-zinc-900 px-2.5 ring-zinc-500/50 ring-offset-zinc-900 will-change-transform focus:outline-none focus:ring-1 focus:ring-offset-2 active:translate-y-1">
|
||||
<span className="absolute inset-px z-10 inline-flex items-center justify-center rounded bg-zinc-900 text-zinc-200">
|
||||
Post
|
||||
</span>
|
||||
<span className="absolute inset-px z-10 inline-flex items-center justify-center rounded bg-zinc-900 text-zinc-200">Post</span>
|
||||
<span className="absolute inset-0 z-0 scale-x-[2.0] blur before:absolute before:inset-0 before:top-1/2 before:aspect-square before:animate-disco before:bg-gradient-conic before:from-gray-300 before:via-fuchsia-600 before:to-orange-600"></span>
|
||||
</div>
|
||||
),
|
||||
|
@ -4,14 +4,12 @@ import { NoteConnector } from '@components/connectors/note';
|
||||
import CreatePost from '@components/navigatorBar/createPost';
|
||||
import { ProfileMenu } from '@components/navigatorBar/profileMenu';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { PlusIcon } from '@radix-ui/react-icons';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
|
||||
export default function NavigatorBar() {
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const profile = $currentUser.metadata !== undefined ? JSON.parse($currentUser.metadata) : { display_name: null, username: null };
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
const profile = currentUser.metadata !== undefined ? JSON.parse(currentUser.metadata) : { display_name: null, username: null };
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col flex-wrap justify-between overflow-hidden px-2 pt-3 pb-4">
|
||||
@ -25,7 +23,7 @@ export default function NavigatorBar() {
|
||||
<div className="flex flex-col p-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<h5 className="font-semibold leading-tight text-zinc-100">{profile.display_name || ''}</h5>
|
||||
<ProfileMenu pubkey={$currentUser.pubkey} />
|
||||
<ProfileMenu pubkey={currentUser.pubkey} />
|
||||
</div>
|
||||
<span className="text-sm leading-tight text-zinc-500">@{profile.username || ''}</span>
|
||||
</div>
|
||||
|
@ -1,26 +1,18 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { HeartFilledIcon, HeartIcon } from '@radix-ui/react-icons';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import { dateToUnix, useNostr, useNostrEvents } from 'nostr-react';
|
||||
import { getEventHash, signEvent } from 'nostr-tools';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function Reaction({
|
||||
eventID,
|
||||
eventPubkey,
|
||||
}: {
|
||||
eventID: string;
|
||||
eventPubkey: string;
|
||||
}) {
|
||||
export default function Reaction({ eventID, eventPubkey }: { eventID: string; eventPubkey: string }) {
|
||||
const { publish } = useNostr();
|
||||
const [reaction, setReaction] = useState(0);
|
||||
const [isReact, setIsReact] = useState(false);
|
||||
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const pubkey = $currentUser.pubkey;
|
||||
const privkey = $currentUser.privkey;
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
const pubkey = currentUser.pubkey;
|
||||
const privkey = currentUser.privkey;
|
||||
|
||||
const { onEvent } = useNostrEvents({
|
||||
filter: {
|
||||
@ -65,15 +57,9 @@ export default function Reaction({
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={(e) => handleReaction(e)}
|
||||
className="group flex w-16 items-center gap-1.5 text-sm text-zinc-500">
|
||||
<button onClick={(e) => handleReaction(e)} className="group flex w-16 items-center gap-1.5 text-sm text-zinc-500">
|
||||
<div className="rounded-lg p-1 group-hover:bg-zinc-600">
|
||||
{isReact ? (
|
||||
<HeartFilledIcon className="h-4 w-4 group-hover:text-red-400" />
|
||||
) : (
|
||||
<HeartIcon className="h-4 w-4 text-zinc-500" />
|
||||
)}
|
||||
{isReact ? <HeartFilledIcon className="h-4 w-4 group-hover:text-red-400" /> : <HeartIcon className="h-4 w-4 text-zinc-500" />}
|
||||
</div>
|
||||
<span>{reaction}</span>
|
||||
</button>
|
||||
|
@ -2,12 +2,10 @@
|
||||
import AccountBar from '@components/accountBar';
|
||||
import ActiveLink from '@components/activeLink';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
|
||||
export default function UserLayout({ children }: { children: React.ReactNode }) {
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-row">
|
||||
@ -27,13 +25,13 @@ export default function UserLayout({ children }: { children: React.ReactNode })
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 text-zinc-500">
|
||||
<ActiveLink
|
||||
href={`/profile/${$currentUser.pubkey}`}
|
||||
href={`/profile/${currentUser.pubkey}`}
|
||||
activeClassName="ring-1 ring-white/10 dark:bg-zinc-900 dark:text-white"
|
||||
className="flex h-10 items-center gap-1 rounded-lg px-2.5 text-sm font-medium hover:bg-zinc-900">
|
||||
<span>Personal Page</span>
|
||||
</ActiveLink>
|
||||
<ActiveLink
|
||||
href={`/profile/update?pubkey=${$currentUser.pubkey}`}
|
||||
href={`/profile/update?pubkey=${currentUser.pubkey}`}
|
||||
activeClassName="ring-1 ring-white/10 dark:bg-zinc-900 dark:text-white"
|
||||
className="flex h-10 items-center gap-1 rounded-lg px-2.5 text-sm font-medium hover:bg-zinc-900">
|
||||
<span>Update Profile</span>
|
||||
|
@ -2,9 +2,7 @@
|
||||
import DatabaseProvider from '@components/contexts/database';
|
||||
import RelayProvider from '@components/contexts/relay';
|
||||
|
||||
import { relays } from '@stores/relays';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import type { NextPage } from 'next';
|
||||
import type { AppProps } from 'next/app';
|
||||
import { ReactElement, ReactNode } from 'react';
|
||||
@ -23,12 +21,12 @@ type AppPropsWithLayout = AppProps & {
|
||||
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
// Use the layout defined at the page level, if available
|
||||
const getLayout = Component.getLayout ?? ((page) => page);
|
||||
// Get all relays
|
||||
const $relays = useStore(relays);
|
||||
// Get relays from localstorage
|
||||
const [relays] = useLocalStorage('relays');
|
||||
|
||||
return (
|
||||
<DatabaseProvider>
|
||||
<RelayProvider relays={$relays}>{getLayout(<Component {...pageProps} />)}</RelayProvider>
|
||||
<RelayProvider relays={relays}>{getLayout(<Component {...pageProps} />)}</RelayProvider>
|
||||
</DatabaseProvider>
|
||||
);
|
||||
}
|
||||
|
@ -2,90 +2,32 @@
|
||||
import BaseLayout from '@layouts/baseLayout';
|
||||
import FullLayout from '@layouts/fullLayout';
|
||||
|
||||
import { DatabaseContext } from '@components/contexts/database';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
import { follows } from '@stores/follows';
|
||||
|
||||
import LumeSymbol from '@assets/icons/Lume';
|
||||
|
||||
import { isPermissionGranted, requestPermission, sendNotification } from '@tauri-apps/api/notification';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useRouter } from 'next/router';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useEffect, useState } from 'react';
|
||||
|
||||
export default function Page() {
|
||||
const db: any = useContext(DatabaseContext);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const requestNotification = useCallback(async () => {
|
||||
// NOTE: notification don't work in dev mode (only affect MacOS)
|
||||
// ref: https://github.com/tauri-apps/tauri/issues/4965
|
||||
let permissionGranted = await isPermissionGranted();
|
||||
if (!permissionGranted) {
|
||||
const permission = await requestPermission();
|
||||
permissionGranted = permission === 'granted';
|
||||
}
|
||||
if (permissionGranted) {
|
||||
sendNotification({ title: 'Lume', body: 'Nostr is awesome' });
|
||||
}
|
||||
|
||||
return permissionGranted;
|
||||
}, []);
|
||||
|
||||
const getAccount = useCallback(async () => {
|
||||
const result = await db.select(`SELECT * FROM accounts ASC LIMIT 1`);
|
||||
return result;
|
||||
}, [db]);
|
||||
|
||||
const getFollows = useCallback(
|
||||
async (account: { id: string }) => {
|
||||
const arr = [];
|
||||
const result: any = await db.select(`SELECT pubkey FROM follows WHERE account = "${account.id}"`);
|
||||
|
||||
result.forEach((item: { pubkey: string }) => {
|
||||
arr.push(item.pubkey);
|
||||
});
|
||||
|
||||
return arr;
|
||||
},
|
||||
[db]
|
||||
);
|
||||
|
||||
// Explain:
|
||||
// Step 1: request allow notification from system
|
||||
// Step 2: get first account. #TODO: get last used account instead (part of multi account feature)
|
||||
// Step 3: get follows by account
|
||||
useEffect(() => {
|
||||
requestNotification().then(() => {
|
||||
getAccount()
|
||||
.then((res: any) => {
|
||||
if (res.length === 0) {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/onboarding');
|
||||
}, 1500);
|
||||
} else {
|
||||
// store current user in localstorage
|
||||
currentUser.set(res[0]);
|
||||
getFollows(res[0])
|
||||
.then(async (res) => {
|
||||
// store follows in localstorage
|
||||
follows.set(res);
|
||||
// redirect to newsfeed
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/feed/following');
|
||||
}, 1500);
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
});
|
||||
}, [requestNotification, getAccount, getFollows, router]);
|
||||
console.log(currentUser);
|
||||
if (!currentUser) {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/onboarding');
|
||||
}, 1500);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/feed/following');
|
||||
}, 1500);
|
||||
}
|
||||
}, [currentUser, router]);
|
||||
|
||||
return (
|
||||
<div className="relative flex h-full flex-col items-center justify-between">
|
||||
|
@ -5,16 +5,13 @@ import OnboardingLayout from '@layouts/onboardingLayout';
|
||||
import { DatabaseContext } from '@components/contexts/database';
|
||||
import { RelayContext } from '@components/contexts/relay';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
import { relays } from '@stores/relays';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { EyeClosedIcon, EyeOpenIcon } from '@radix-ui/react-icons';
|
||||
import { useLocalStorage, writeStorage } from '@rehooks/local-storage';
|
||||
import { motion } from 'framer-motion';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
import { generatePrivateKey, getEventHash, getPublicKey, nip19, signEvent } from 'nostr-tools';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useState } from 'react';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useCallback, useContext, useMemo, useState } from 'react';
|
||||
import { Config, names, uniqueNamesGenerator } from 'unique-names-generator';
|
||||
|
||||
const config: Config = {
|
||||
@ -22,11 +19,12 @@ const config: Config = {
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
const db: any = useContext(DatabaseContext);
|
||||
const router = useRouter();
|
||||
|
||||
const { db }: any = useContext(DatabaseContext);
|
||||
const relayPool: any = useContext(RelayContext);
|
||||
const $relays = useStore(relays);
|
||||
|
||||
const [relays] = useLocalStorage('relays');
|
||||
|
||||
const [type, setType] = useState('password');
|
||||
const [loading, setLoading] = useState(false);
|
||||
@ -47,13 +45,22 @@ export default function Page() {
|
||||
};
|
||||
|
||||
// auto-generated profile
|
||||
const data = {
|
||||
display_name: name,
|
||||
name: name,
|
||||
username: name.toLowerCase(),
|
||||
picture: 'https://bafybeidfsbrzqbvontmucteomoz2rkrxugu462l5hyhh6uioslkfzzs4oq.ipfs.w3s.link/avatar-11.png',
|
||||
banner: 'https://bafybeiacwit7hjmdefqggxqtgh6ht5dhth7ndptwn2msl5kpkodudsr7py.ipfs.w3s.link/banner-1.jpg',
|
||||
};
|
||||
const data = useMemo(
|
||||
() => ({
|
||||
display_name: name,
|
||||
name: name,
|
||||
username: name.toLowerCase(),
|
||||
picture: 'https://bafybeidfsbrzqbvontmucteomoz2rkrxugu462l5hyhh6uioslkfzzs4oq.ipfs.w3s.link/avatar-11.png',
|
||||
banner: 'https://bafybeiacwit7hjmdefqggxqtgh6ht5dhth7ndptwn2msl5kpkodudsr7py.ipfs.w3s.link/banner-1.jpg',
|
||||
}),
|
||||
[name]
|
||||
);
|
||||
|
||||
const insertDB = useCallback(async () => {
|
||||
await db.execute(
|
||||
`INSERT INTO accounts (id, privkey, npub, nsec, metadata) VALUES ("${pubKey}", "${privKey}", "${npub}", "${nsec}", '${JSON.stringify(data)}')`
|
||||
);
|
||||
}, [data, db, npub, nsec, privKey, pubKey]);
|
||||
|
||||
const createAccount = async () => {
|
||||
setLoading(true);
|
||||
@ -68,27 +75,25 @@ export default function Page() {
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, privKey);
|
||||
// publish to relays
|
||||
relayPool.publish(event, $relays);
|
||||
|
||||
// save account to database
|
||||
await db.execute(
|
||||
`INSERT INTO accounts (id, privkey, npub, nsec, metadata) VALUES ("${pubKey}", "${privKey}", "${npub}", "${nsec}", '${JSON.stringify(data)}')`
|
||||
);
|
||||
|
||||
// set currentUser in global state
|
||||
currentUser.set({
|
||||
metadata: JSON.stringify(data),
|
||||
npub: npub,
|
||||
privkey: privKey,
|
||||
pubkey: pubKey,
|
||||
});
|
||||
|
||||
// redirect to pre-follow
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/onboarding/following');
|
||||
}, 1500);
|
||||
insertDB()
|
||||
.then(() => {
|
||||
// publish to relays
|
||||
relayPool.publish(event, relays);
|
||||
// set currentUser in global state
|
||||
writeStorage('current-user', {
|
||||
metadata: JSON.stringify(data),
|
||||
npub: npub,
|
||||
privkey: privKey,
|
||||
pubkey: pubKey,
|
||||
});
|
||||
// redirect to pre-follow
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/onboarding/create/pre-follows');
|
||||
}, 1500);
|
||||
})
|
||||
.catch(console.error);
|
||||
};
|
||||
|
||||
return (
|
@ -6,28 +6,25 @@ import { DatabaseContext } from '@components/contexts/database';
|
||||
|
||||
import { truncate } from '@utils/truncate';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
|
||||
import data from '@assets/directory.json';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { CheckCircledIcon } from '@radix-ui/react-icons';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import { motion } from 'framer-motion';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useState } from 'react';
|
||||
|
||||
const shuffle = (arr: { name: string; avatar: string; npub: string }[]) => [...arr].sort(() => Math.random() - 0.5);
|
||||
|
||||
export default function Page() {
|
||||
const db: any = useContext(DatabaseContext);
|
||||
const router = useRouter();
|
||||
|
||||
const shuffle = (arr) => [...arr].sort(() => Math.random() - 0.5);
|
||||
|
||||
const [follow, setFollow] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [list] = useState(shuffle(data));
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
|
||||
const followUser = (e) => {
|
||||
const npub = e.currentTarget.getAttribute('data-npub');
|
||||
@ -36,11 +33,11 @@ export default function Page() {
|
||||
|
||||
const insertDB = async () => {
|
||||
// self follow
|
||||
await db.execute(`INSERT INTO follows (pubkey, account, kind) VALUES ("${$currentUser.pubkey}", "${$currentUser.pubkey}", "0")`);
|
||||
await db.execute(`INSERT INTO follows (pubkey, account, kind) VALUES ("${currentUser.pubkey}", "${currentUser.pubkey}", "0")`);
|
||||
// follow selected
|
||||
follow.forEach(async (npub) => {
|
||||
const { data } = nip19.decode(npub);
|
||||
await db.execute(`INSERT INTO follows (pubkey, account, kind) VALUES ("${data}", "${$currentUser.pubkey}", "0")`);
|
||||
await db.execute(`INSERT INTO follows (pubkey, account, kind) VALUES ("${data}", "${currentUser.pubkey}", "0")`);
|
||||
});
|
||||
};
|
||||
|
@ -1,116 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import BaseLayout from '@layouts/baseLayout';
|
||||
import OnboardingLayout from '@layouts/onboardingLayout';
|
||||
|
||||
import { DatabaseContext } from '@components/contexts/database';
|
||||
import { RelayContext } from '@components/contexts/relay';
|
||||
|
||||
import { relays } from '@stores/relays';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useRouter } from 'next/router';
|
||||
import { getPublicKey, nip19 } from 'nostr-tools';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useCallback, useContext, useEffect, useState } from 'react';
|
||||
|
||||
export default function Page() {
|
||||
const db: any = useContext(DatabaseContext);
|
||||
const relayPool: any = useContext(RelayContext);
|
||||
const $relays = useStore(relays);
|
||||
|
||||
const router = useRouter();
|
||||
const { privkey }: any = router.query;
|
||||
|
||||
const [account, setAccount] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const pubkey = privkey ? getPublicKey(privkey) : null;
|
||||
const npub = privkey ? nip19.npubEncode(pubkey) : null;
|
||||
const nsec = privkey ? nip19.nsecEncode(privkey) : null;
|
||||
|
||||
relayPool.subscribe(
|
||||
[
|
||||
{
|
||||
authors: [pubkey],
|
||||
kinds: [0],
|
||||
},
|
||||
],
|
||||
$relays,
|
||||
(event: any) => {
|
||||
const metadata = JSON.parse(event.content);
|
||||
setAccount(metadata);
|
||||
},
|
||||
undefined,
|
||||
(events: any, relayURL: any) => {
|
||||
console.log(events, relayURL);
|
||||
}
|
||||
);
|
||||
|
||||
const insertDB = useCallback(async () => {
|
||||
// save account to database
|
||||
const metadata = JSON.stringify(account);
|
||||
await db.execute(
|
||||
`INSERT INTO accounts (id, privkey, npub, nsec, metadata) VALUES ("${pubkey}", "${privkey}", "${npub}", "${nsec}", '${metadata}')`
|
||||
);
|
||||
await db.close();
|
||||
}, [account, db, npub, nsec, privkey, pubkey]);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
|
||||
if (account !== null) {
|
||||
insertDB()
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push({
|
||||
pathname: '/onboarding/fetch-follows',
|
||||
query: { pubkey: pubkey },
|
||||
});
|
||||
}, 1500);
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
}, [account, insertDB, npub, nsec, privkey, 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 profile...
|
||||
</motion.h1>
|
||||
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
|
||||
As long as you have private key, you alway can sync your profile on every nostr client, so please keep your key safely
|
||||
</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>
|
||||
);
|
||||
};
|
@ -10,9 +10,7 @@ export default function Page() {
|
||||
<div className="flex h-full flex-col justify-between px-8">
|
||||
<div>{/* spacer */}</div>
|
||||
<div className="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">
|
||||
<motion.h1 layoutId="title" className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
Other social network require email/password
|
||||
<br />
|
||||
nostr use{' '}
|
||||
@ -21,8 +19,8 @@ export default function Page() {
|
||||
</span>
|
||||
</motion.h1>
|
||||
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
|
||||
If you have used nostr before, you can import your own private key. Otherwise, you can
|
||||
create a new key or use auto-generated account created by system.
|
||||
If you have used nostr before, you can import your own private key. Otherwise, you can create a new key or use auto-generated account
|
||||
created by system.
|
||||
</motion.h2>
|
||||
<motion.div layoutId="form"></motion.div>
|
||||
<motion.div layoutId="action" className="mt-4 flex gap-2">
|
||||
@ -32,7 +30,7 @@ export default function Page() {
|
||||
Create new key
|
||||
</Link>
|
||||
<Link
|
||||
href="/onboarding/import"
|
||||
href="/onboarding/login"
|
||||
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 with private key
|
||||
</Link>
|
||||
@ -44,13 +42,7 @@ export default function Page() {
|
||||
}
|
||||
|
||||
Page.getLayout = function getLayout(
|
||||
page:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
||||
| ReactFragment
|
||||
| ReactPortal
|
||||
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||
) {
|
||||
return (
|
||||
<BaseLayout>
|
||||
|
@ -5,35 +5,69 @@ import OnboardingLayout from '@layouts/onboardingLayout';
|
||||
import { DatabaseContext } from '@components/contexts/database';
|
||||
import { RelayContext } from '@components/contexts/relay';
|
||||
|
||||
import { relays } from '@stores/relays';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import { motion } from 'framer-motion';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useEffect, useState } from 'react';
|
||||
import { getPublicKey, nip19 } from 'nostr-tools';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useCallback, useContext, useMemo, useState } from 'react';
|
||||
|
||||
export default function Page() {
|
||||
const db: any = useContext(DatabaseContext);
|
||||
const { db }: any = useContext(DatabaseContext);
|
||||
const relayPool: any = useContext(RelayContext);
|
||||
const $relays = useStore(relays);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [relays] = useLocalStorage('relays');
|
||||
|
||||
const router = useRouter();
|
||||
const { pubkey }: any = router.query;
|
||||
const { privkey }: any = router.query;
|
||||
|
||||
const [follows, setFollows] = useState([null]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const pubkey = useMemo(() => (privkey ? getPublicKey(privkey) : null), [privkey]);
|
||||
|
||||
// save account to database
|
||||
const insertAccount = useCallback(
|
||||
async (metadata) => {
|
||||
if (loading === false) {
|
||||
const npub = privkey ? nip19.npubEncode(pubkey) : null;
|
||||
const nsec = privkey ? nip19.nsecEncode(privkey) : null;
|
||||
await db.execute(
|
||||
`INSERT OR IGNORE INTO accounts (id, privkey, npub, nsec, metadata) VALUES ("${pubkey}", "${privkey}", "${npub}", "${nsec}", '${metadata}')`
|
||||
);
|
||||
setLoading(true);
|
||||
}
|
||||
},
|
||||
[db, privkey, pubkey, loading]
|
||||
);
|
||||
|
||||
// save follows to database
|
||||
const insertFollows = useCallback(
|
||||
async (follows) => {
|
||||
follows.forEach(async (item) => {
|
||||
if (item) {
|
||||
await db.execute(`INSERT OR IGNORE INTO follows (pubkey, account, kind) VALUES ("${item[1]}", "${pubkey}", "0")`);
|
||||
}
|
||||
});
|
||||
},
|
||||
[db, pubkey]
|
||||
);
|
||||
|
||||
relayPool.subscribe(
|
||||
[
|
||||
{
|
||||
authors: [pubkey],
|
||||
kinds: [0],
|
||||
kinds: [0, 3],
|
||||
since: 0,
|
||||
},
|
||||
],
|
||||
$relays,
|
||||
relays,
|
||||
(event: any) => {
|
||||
setFollows(event.tags);
|
||||
if (event.kind === 0) {
|
||||
insertAccount(event.content);
|
||||
} else {
|
||||
if (event.tags.length > 0) {
|
||||
insertFollows(event.tags);
|
||||
}
|
||||
}
|
||||
},
|
||||
undefined,
|
||||
(events: any, relayURL: any) => {
|
||||
@ -41,39 +75,16 @@ export default function Page() {
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
|
||||
const insertDB = async () => {
|
||||
follows.forEach(async (item) => {
|
||||
if (item) {
|
||||
await db.execute(`INSERT OR IGNORE INTO follows (pubkey, account, kind) VALUES ("${item[1]}", "${pubkey}", "0")`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (follows !== null && follows.length > 0) {
|
||||
insertDB()
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/');
|
||||
}, 1500);
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
}, [db, 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...
|
||||
Fetching your profile...
|
||||
</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)
|
||||
As long as you have private key, you alway can sync your profile and follows list on every nostr client, so please keep your key safely
|
||||
</motion.h2>
|
||||
</div>
|
||||
</motion.div>
|
||||
@ -88,7 +99,11 @@ export default function Page() {
|
||||
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>
|
||||
) : (
|
||||
<></>
|
||||
<Link
|
||||
href="/"
|
||||
className="transform rounded-lg 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 active:translate-y-1 disabled:cursor-not-allowed disabled:opacity-30">
|
||||
<span className="drop-shadow-lg">Finish</span>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
@ -44,7 +44,7 @@ export default function Page() {
|
||||
|
||||
try {
|
||||
router.push({
|
||||
pathname: '/onboarding/fetch-profile',
|
||||
pathname: '/onboarding/login/fetch',
|
||||
query: { privkey: privkey },
|
||||
});
|
||||
} catch (error) {
|
@ -2,9 +2,7 @@
|
||||
import BaseLayout from '@layouts/baseLayout';
|
||||
import UserLayout from '@layouts/userLayout';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import { useRouter } from 'next/router';
|
||||
import { dateToUnix, useNostr } from 'nostr-react';
|
||||
import { getEventHash, signEvent } from 'nostr-tools';
|
||||
@ -28,11 +26,8 @@ export default function Page() {
|
||||
const { publish } = useNostr();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const profile =
|
||||
$currentUser.metadata !== undefined
|
||||
? JSON.parse($currentUser.metadata)
|
||||
: { display_name: null, username: null };
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
const profile = currentUser.metadata !== undefined ? JSON.parse(currentUser.metadata) : { display_name: null, username: null };
|
||||
|
||||
const {
|
||||
register,
|
||||
@ -48,28 +43,24 @@ export default function Page() {
|
||||
content: JSON.stringify(data),
|
||||
created_at: dateToUnix(),
|
||||
kind: 0,
|
||||
pubkey: $currentUser.pubkey,
|
||||
pubkey: currentUser.pubkey,
|
||||
tags: [],
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, $currentUser.privkey);
|
||||
event.sig = signEvent(event, currentUser.privkey);
|
||||
publish(event);
|
||||
|
||||
// save account to database
|
||||
const db = await Database.load('sqlite:lume.db');
|
||||
await db.execute(
|
||||
`UPDATE accounts SET metadata = '${JSON.stringify(data)}' WHERE pubkey = "${
|
||||
$currentUser.pubkey
|
||||
}"`
|
||||
);
|
||||
await db.execute(`UPDATE accounts SET metadata = '${JSON.stringify(data)}' WHERE pubkey = "${currentUser.pubkey}"`);
|
||||
await db.close();
|
||||
|
||||
// set currentUser in global state
|
||||
currentUser.set({
|
||||
metadata: JSON.stringify(data),
|
||||
npub: $currentUser.npub,
|
||||
privkey: $currentUser.privkey,
|
||||
pubkey: $currentUser.pubkey,
|
||||
npub: currentUser.npub,
|
||||
privkey: currentUser.privkey,
|
||||
pubkey: currentUser.pubkey,
|
||||
});
|
||||
|
||||
// redirect to newsfeed
|
||||
@ -80,16 +71,11 @@ export default function Page() {
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
className="flex h-full w-full flex-col justify-between px-6">
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="flex h-full w-full flex-col justify-between px-6">
|
||||
<div className="mb-8 flex flex-col gap-3 pt-8">
|
||||
<h1 className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
Update profile
|
||||
</h1>
|
||||
<h1 className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">Update profile</h1>
|
||||
<h2 className="w-3/4 text-zinc-400">
|
||||
Your profile will be published to all relays, as long as you have the private key, you
|
||||
always can recover your profile in any client
|
||||
Your profile will be published to all relays, as long as you have the private key, you always can recover your profile in any client
|
||||
</h2>
|
||||
</div>
|
||||
<fieldset className="flex flex-col gap-2">
|
||||
@ -105,9 +91,7 @@ export default function Page() {
|
||||
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>
|
||||
<span className="text-sm text-red-400">
|
||||
{errors.display_name && <p>{errors.display_name.message}</p>}
|
||||
</span>
|
||||
<span className="text-sm text-red-400">{errors.display_name && <p>{errors.display_name.message}</p>}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4">
|
||||
@ -122,9 +106,7 @@ export default function Page() {
|
||||
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>
|
||||
<span className="text-sm text-red-400">
|
||||
{errors.name && <p>{errors.name.message}</p>}
|
||||
</span>
|
||||
<span className="text-sm text-red-400">{errors.name && <p>{errors.name.message}</p>}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4">
|
||||
@ -139,9 +121,7 @@ export default function Page() {
|
||||
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>
|
||||
<span className="text-sm text-red-400">
|
||||
{errors.username && <p>{errors.username.message}</p>}
|
||||
</span>
|
||||
<span className="text-sm text-red-400">{errors.username && <p>{errors.username.message}</p>}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4">
|
||||
@ -156,9 +136,7 @@ export default function Page() {
|
||||
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>
|
||||
<span className="text-sm text-red-400">
|
||||
{errors.picture && <p>{errors.picture.message}</p>}
|
||||
</span>
|
||||
<span className="text-sm text-red-400">{errors.picture && <p>{errors.picture.message}</p>}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4">
|
||||
@ -173,9 +151,7 @@ export default function Page() {
|
||||
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>
|
||||
<span className="text-sm text-red-400">
|
||||
{errors.banner && <p>{errors.banner.message}</p>}
|
||||
</span>
|
||||
<span className="text-sm text-red-400">{errors.banner && <p>{errors.banner.message}</p>}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4">
|
||||
@ -190,27 +166,15 @@ export default function Page() {
|
||||
className="relative h-24 w-full resize-none 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>
|
||||
<span className="text-sm text-red-400">
|
||||
{errors.about && <p>{errors.about.message}</p>}
|
||||
</span>
|
||||
<span className="text-sm text-red-400">{errors.about && <p>{errors.about.message}</p>}</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div 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>
|
||||
<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"
|
||||
@ -231,13 +195,7 @@ export default function Page() {
|
||||
}
|
||||
|
||||
Page.getLayout = function getLayout(
|
||||
page:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
||||
| ReactFragment
|
||||
| ReactPortal
|
||||
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||
) {
|
||||
return (
|
||||
<BaseLayout>
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { persistentAtom } from '@nanostores/persistent';
|
||||
|
||||
export const currentUser = persistentAtom(
|
||||
'currentUser',
|
||||
{},
|
||||
{
|
||||
encode: JSON.stringify,
|
||||
decode: JSON.parse,
|
||||
}
|
||||
);
|
@ -1,10 +0,0 @@
|
||||
import { persistentAtom } from '@nanostores/persistent';
|
||||
|
||||
export const follows = persistentAtom('follows', [], {
|
||||
encode(value) {
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
decode(value) {
|
||||
return JSON.parse(value);
|
||||
},
|
||||
});
|
@ -1,25 +0,0 @@
|
||||
import { persistentAtom } from '@nanostores/persistent';
|
||||
|
||||
export const relays = persistentAtom(
|
||||
'relays',
|
||||
[
|
||||
'wss://relay.uselume.xyz',
|
||||
'wss://nostr-pub.wellorder.net',
|
||||
'wss://nostr.bongbong.com',
|
||||
'wss://nostr.zebedee.cloud',
|
||||
'wss://nostr.fmt.wiz.biz',
|
||||
'wss://nostr.walletofsatoshi.com',
|
||||
'wss://relay.snort.social',
|
||||
'wss://offchain.pub',
|
||||
'wss://nos.lol',
|
||||
'wss://relay.damus.io',
|
||||
],
|
||||
{
|
||||
encode(value) {
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
decode(value) {
|
||||
return JSON.parse(value);
|
||||
},
|
||||
}
|
||||
);
|
@ -6,7 +6,6 @@
|
||||
"@layouts/*": ["src/layouts/*"],
|
||||
"@components/*": ["src/components/*"],
|
||||
"@utils/*": ["src/utils/*"],
|
||||
"@stores/*": ["src/stores/*"],
|
||||
"@assets/*": ["src/assets/*"]
|
||||
},
|
||||
"target": "es2017",
|
||||
|
Loading…
x
Reference in New Issue
Block a user