migrated to nextjs 13 app dir

This commit is contained in:
Ren Amamiya 2023-04-15 09:06:29 +07:00
parent da38ced72c
commit d59cc041d5
56 changed files with 617 additions and 807 deletions

View File

@ -9,7 +9,7 @@
"bracketSpacing": true,
"bracketSameLine": false,
"importOrder": [
"^@layouts/(.*)$",
"^@app/(.*)$",
"^@pages/(.*)$",
"^@components/(.*)$",
"^@stores/(.*)$",

View File

@ -3,6 +3,7 @@
*/
const nextConfig = {
output: 'export',
reactStrictMode: false,
swcMinify: true,
images: {
@ -11,6 +12,10 @@ const nextConfig = {
typescript: {
ignoreBuildErrors: true,
},
experimental: {
appDir: true,
scrollRestoration: true,
},
webpack: (config) => {
config.experiments = { ...config.experiments, topLevelAwait: true };
return config;

View File

@ -23,23 +23,23 @@
"@radix-ui/react-tabs": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.5",
"@rehooks/local-storage": "^2.4.4",
"@supabase/supabase-js": "^2.15.0",
"@supabase/supabase-js": "^2.20.0",
"@tauri-apps/api": "^1.2.0",
"dayjs": "^1.11.7",
"destr": "^1.2.2",
"emoji-mart": "^5.5.2",
"framer-motion": "^9.1.7",
"iconoir-react": "^6.6.0",
"jotai": "^2.0.3",
"jotai": "^2.0.4",
"next": "^13.3.0",
"nostr-relaypool": "^0.5.18",
"nostr-tools": "^1.8.2",
"nostr-tools": "^1.9.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.9",
"react-player": "^2.12.0",
"react-string-replace": "^1.1.0",
"react-virtuoso": "^4.2.0",
"react-virtuoso": "^4.2.1",
"unique-names-generator": "^4.7.1",
"ws": "^8.13.0"
},
@ -48,19 +48,20 @@
"@tauri-apps/cli": "^1.2.3",
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
"@types/node": "^18.15.11",
"@types/react": "^18.0.33",
"@types/react": "^18.0.35",
"@types/react-dom": "^18.0.11",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0",
"autoprefixer": "^10.4.14",
"csstype": "^3.1.2",
"eslint": "^8.37.0",
"encoding": "^0.1.13",
"eslint": "^8.38.0",
"eslint-config-next": "^13.3.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"husky": "^8.0.3",
"lint-staged": "^13.2.0",
"lint-staged": "^13.2.1",
"postcss": "^8.4.21",
"prettier": "^2.8.7",
"prettier-plugin-tailwindcss": "^0.2.7",

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
"beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm build",
"devPath": "http://localhost:1420",
"distDir": "../dist",
"distDir": "../out",
"withGlobalTauri": true
},
"package": {

View File

@ -1,5 +1,4 @@
import BaseLayout from '@layouts/base';
import WithSidebarLayout from '@layouts/withSidebar';
'use client';
import { ChannelMessages } from '@components/channels/messages/index';
import FormChannelMessage from '@components/form/channelMessage';
@ -9,24 +8,11 @@ import { channelReplyAtom } from '@stores/channel';
import useLocalStorage from '@rehooks/local-storage';
import { useResetAtom } from 'jotai/utils';
import { useRouter } from 'next/router';
import {
JSXElementConstructor,
ReactElement,
ReactFragment,
ReactPortal,
useContext,
useEffect,
useRef,
useState,
} from 'react';
import { useContext, useEffect, useRef, useState } from 'react';
export default function Page() {
export default function Page({ params }: { params: { id: string } }) {
const [pool, relays]: any = useContext(RelayContext);
const router = useRouter();
const id: string | string[] = router.query.id || null;
const [messages, setMessages] = useState([]);
const [activeAccount]: any = useLocalStorage('activeAccount', {});
const resetChannelReply = useResetAtom(channelReplyAtom);
@ -46,7 +32,7 @@ export default function Page() {
since: 0,
},
{
'#e': [id],
'#e': [params.id],
kinds: [42],
since: 0,
},
@ -70,32 +56,16 @@ export default function Page() {
);
return () => {
unsubscribe;
unsubscribe();
};
}, [id, pool, relays, activeAccount.pubkey, resetChannelReply]);
}, [pool, relays, activeAccount.pubkey, params.id, resetChannelReply]);
return (
<div className="flex h-full w-full flex-col justify-between">
<ChannelMessages data={messages.sort((a, b) => a.created_at - b.created_at)} />
<div className="shrink-0 p-3">
<FormChannelMessage eventId={id} />
<FormChannelMessage eventId={params.id} />
</div>
</div>
);
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return (
<BaseLayout>
<WithSidebarLayout>{page}</WithSidebarLayout>
</BaseLayout>
);
};

View File

@ -0,0 +1,39 @@
import AppHeader from '@components/appHeader';
import MultiAccounts from '@components/multiAccounts';
import Navigation from '@components/navigation';
export default function ChannelsLayout({ children }: { children: React.ReactNode }) {
return (
<div className="h-screen w-screen bg-zinc-50 text-zinc-900 dark:bg-black dark:text-white">
<div className="flex h-screen w-full flex-col">
<div
data-tauri-drag-region
className="relative h-11 shrink-0 border-b border-zinc-100 bg-white dark:border-zinc-900 dark:bg-black"
>
<AppHeader />
</div>
<div className="relative flex min-h-0 w-full flex-1">
<div className="relative w-[68px] shrink-0 border-r border-zinc-900">
<MultiAccounts />
</div>
<div className="grid w-full grid-cols-4 xl:grid-cols-5">
<div className="scrollbar-hide col-span-1 overflow-y-auto overflow-x-hidden border-r border-zinc-900">
<Navigation />
</div>
<div className="col-span-3 m-3 overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20 xl:col-span-2 xl:mr-1.5">
<div className="h-full w-full rounded-lg">{children}</div>
</div>
<div className="col-span-3 m-3 hidden overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20 xl:col-span-2 xl:ml-1.5 xl:flex">
<div className="flex h-full w-full items-center justify-center">
<p className="select-text p-8 text-center text-zinc-400">
This feature hasn&apos;t implemented yet, so resize Lume to the initial size for a better experience.
I&apos;m sorry for this inconvenience, and I swear I will add it soon 😁
</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -1,9 +1,8 @@
import BaseLayout from '@layouts/base';
import WithSidebarLayout from '@layouts/withSidebar';
'use client';
import { BrowseChannelItem } from '@components/channels/browseChannelItem';
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
export default function Page() {
const [list, setList] = useState([]);
@ -27,19 +26,3 @@ export default function Page() {
</div>
);
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return (
<BaseLayout>
<WithSidebarLayout>{page}</WithSidebarLayout>
</BaseLayout>
);
};

View File

@ -1,28 +1,15 @@
import BaseLayout from '@layouts/base';
import WithSidebarLayout from '@layouts/withSidebar';
'use client';
import { MessageList } from '@components/chats/messageList';
import FormChat from '@components/form/chat';
import { RelayContext } from '@components/relaysProvider';
import useLocalStorage from '@rehooks/local-storage';
import { useRouter } from 'next/router';
import {
JSXElementConstructor,
ReactElement,
ReactFragment,
ReactPortal,
useContext,
useEffect,
useState,
} from 'react';
import { useContext, useEffect, useState } from 'react';
export default function Page() {
export default function Page({ params }: { params: { pubkey: string } }) {
const [pool, relays]: any = useContext(RelayContext);
const router = useRouter();
const pubkey: any = router.query.pubkey || null;
const [activeAccount]: any = useLocalStorage('activeAccount', {});
const [messages, setMessages] = useState([]);
@ -31,13 +18,13 @@ export default function Page() {
[
{
kinds: [4],
authors: [pubkey],
authors: [params.pubkey],
'#p': [activeAccount.pubkey],
},
{
kinds: [4],
authors: [activeAccount.pubkey],
'#p': [pubkey],
'#p': [params.pubkey],
},
],
relays,
@ -47,32 +34,16 @@ export default function Page() {
);
return () => {
unsubscribe;
unsubscribe();
};
}, [pool, relays, pubkey, activeAccount.pubkey]);
}, [pool, relays, params.pubkey, activeAccount.pubkey]);
return (
<div className="flex h-full w-full flex-col justify-between">
<MessageList data={messages.sort((a, b) => a.created_at - b.created_at)} />
<div className="shrink-0 p-3">
<FormChat receiverPubkey={pubkey} />
<FormChat receiverPubkey={params.pubkey} />
</div>
</div>
);
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return (
<BaseLayout>
<WithSidebarLayout>{page}</WithSidebarLayout>
</BaseLayout>
);
};

39
src/app/chats/layout.tsx Normal file
View File

@ -0,0 +1,39 @@
import AppHeader from '@components/appHeader';
import MultiAccounts from '@components/multiAccounts';
import Navigation from '@components/navigation';
export default function ChatsLayout({ children }: { children: React.ReactNode }) {
return (
<div className="h-screen w-screen bg-zinc-50 text-zinc-900 dark:bg-black dark:text-white">
<div className="flex h-screen w-full flex-col">
<div
data-tauri-drag-region
className="relative h-11 shrink-0 border-b border-zinc-100 bg-white dark:border-zinc-900 dark:bg-black"
>
<AppHeader />
</div>
<div className="relative flex min-h-0 w-full flex-1">
<div className="relative w-[68px] shrink-0 border-r border-zinc-900">
<MultiAccounts />
</div>
<div className="grid w-full grid-cols-4 xl:grid-cols-5">
<div className="scrollbar-hide col-span-1 overflow-y-auto overflow-x-hidden border-r border-zinc-900">
<Navigation />
</div>
<div className="col-span-3 m-3 overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20 xl:col-span-2 xl:mr-1.5">
<div className="h-full w-full rounded-lg">{children}</div>
</div>
<div className="col-span-3 m-3 hidden overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20 xl:col-span-2 xl:ml-1.5 xl:flex">
<div className="flex h-full w-full items-center justify-center">
<p className="select-text p-8 text-center text-zinc-400">
This feature hasn&apos;t implemented yet, so resize Lume to the initial size for a better experience.
I&apos;m sorry for this inconvenience, and I swear I will add it soon 😁
</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

3
src/app/explore/page.tsx Normal file
View File

@ -0,0 +1,3 @@
export default function Page() {
return <></>;
}

13
src/app/layout.tsx Normal file
View File

@ -0,0 +1,13 @@
import '@assets/global.css';
import { Providers } from './providers';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className="dark">
<body className="cursor-default select-none overflow-hidden font-sans antialiased">
<Providers>{children}</Providers>
</body>
</html>
);
}

View File

@ -0,0 +1,5 @@
'use client';
export default function Page({ params }: { params: { id: string } }) {
return <div className="scrollbar-hide flex h-full flex-col gap-2 overflow-y-auto py-3">{params.id}</div>;
}

View File

@ -0,0 +1,7 @@
export default function Page() {
return (
<div className="flex h-full w-full items-center justify-center">
<p className="text-sm text-zinc-400">Sorry, this feature under development, it will come in the next version</p>
</div>
);
}

View File

@ -1,5 +1,4 @@
import BaseLayout from '@layouts/base';
import WithSidebarLayout from '@layouts/withSidebar';
'use client';
import FormBase from '@components/form/base';
import { NoteBase } from '@components/note/base';
@ -12,16 +11,7 @@ import { filterDuplicateParentID } from '@utils/transform';
import { ArrowUp } from 'iconoir-react';
import { useAtom } from 'jotai';
import {
JSXElementConstructor,
ReactElement,
ReactFragment,
ReactPortal,
useCallback,
useEffect,
useRef,
useState,
} from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { Virtuoso } from 'react-virtuoso';
export default function Page() {
@ -121,19 +111,3 @@ const COMPONENTS = {
EmptyPlaceholder: () => <Placeholder />,
ScrollSeekPlaceholder: () => <Placeholder />,
};
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return (
<BaseLayout>
<WithSidebarLayout>{page}</WithSidebarLayout>
</BaseLayout>
);
};

View File

@ -0,0 +1,39 @@
import AppHeader from '@components/appHeader';
import MultiAccounts from '@components/multiAccounts';
import Navigation from '@components/navigation';
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div className="h-screen w-screen bg-zinc-50 text-zinc-900 dark:bg-black dark:text-white">
<div className="flex h-screen w-full flex-col">
<div
data-tauri-drag-region
className="relative h-11 shrink-0 border-b border-zinc-100 bg-white dark:border-zinc-900 dark:bg-black"
>
<AppHeader />
</div>
<div className="relative flex min-h-0 w-full flex-1">
<div className="relative w-[68px] shrink-0 border-r border-zinc-900">
<MultiAccounts />
</div>
<div className="grid w-full grid-cols-4 xl:grid-cols-5">
<div className="scrollbar-hide col-span-1 overflow-y-auto overflow-x-hidden border-r border-zinc-900">
<Navigation />
</div>
<div className="col-span-3 m-3 overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20 xl:col-span-2 xl:mr-1.5">
<div className="h-full w-full rounded-lg">{children}</div>
</div>
<div className="col-span-3 m-3 hidden overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20 xl:col-span-2 xl:ml-1.5 xl:flex">
<div className="flex h-full w-full items-center justify-center">
<p className="select-text p-8 text-center text-zinc-400">
This feature hasn&apos;t implemented yet, so resize Lume to the initial size for a better experience.
I&apos;m sorry for this inconvenience, and I swear I will add it soon 😁
</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -1,4 +1,4 @@
import BaseLayout from '@layouts/base';
'use client';
import { RelayContext } from '@components/relaysProvider';
import { UserBase } from '@components/user/base';
@ -8,19 +8,9 @@ import { followsTag } from '@utils/transform';
import { createClient } from '@supabase/supabase-js';
import { CheckCircle } from 'iconoir-react';
import { useRouter } from 'next/router';
import { useRouter } from 'next/navigation';
import { getEventHash, signEvent } from 'nostr-tools';
import {
JSXElementConstructor,
Key,
ReactElement,
ReactFragment,
ReactPortal,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
import { Key, useCallback, useContext, useEffect, useState } from 'react';
const supabase = createClient(
'https://niwaazauwnrwiwmnocnn.supabase.co',
@ -62,11 +52,9 @@ const initialList = [
{ pubkey: 'ff04a0e6cd80c141b0b55825fed127d4532a6eecdb7e743a38a3c28bf9f44609' },
];
export default function Page() {
const [pool, relays]: any = useContext(RelayContext);
export default function Page({ params }: { params: { id: string; pubkey: string; privkey: string } }) {
const router = useRouter();
const { id, pubkey, privkey }: any = router.query || '';
const [pool, relays]: any = useContext(RelayContext);
const [loading, setLoading] = useState(false);
const [list, setList]: any = useState(initialList);
@ -86,11 +74,11 @@ export default function Page() {
for (const follow of follows) {
const metadata: any = await fetchMetadata(follow);
createPleb({
pleb_id: follow + '-lume' + id,
pleb_id: follow + '-lume' + params.id,
pubkey: follow,
kind: 0,
metadata: metadata.content,
account_id: parseInt(id),
account_id: parseInt(params.id),
}).catch(console.error);
}
@ -99,15 +87,15 @@ export default function Page() {
content: '',
created_at: Math.floor(Date.now() / 1000),
kind: 3,
pubkey: pubkey,
pubkey: params.pubkey,
tags: followsTag(follows),
};
event.id = getEventHash(event);
event.sig = signEvent(event, privkey);
event.sig = signEvent(event, params.privkey);
pool.publish(event, relays);
router.replace('/');
}, [follows, id, pool, pubkey, privkey, relays, router]);
}, [params.pubkey, params.privkey, params.id, follows, pool, relays, router]);
useEffect(() => {
const fetchData = async () => {
@ -182,15 +170,3 @@ export default function Page() {
</div>
);
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return <BaseLayout>{page}</BaseLayout>;
};

View File

@ -1,21 +1,12 @@
import BaseLayout from '@layouts/base';
'use client';
import { RelayContext } from '@components/relaysProvider';
import { ArrowLeft, EyeClose, EyeEmpty } from 'iconoir-react';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { useRouter } from 'next/navigation';
import { generatePrivateKey, getEventHash, getPublicKey, nip19, signEvent } from 'nostr-tools';
import {
JSXElementConstructor,
ReactElement,
ReactFragment,
ReactPortal,
useCallback,
useContext,
useMemo,
useState,
} from 'react';
import { useCallback, useContext, useMemo, useState } from 'react';
import { Config, names, uniqueNamesGenerator } from 'unique-names-generator';
const config: Config = {
@ -80,10 +71,7 @@ export default function Page() {
createAccount({ pubkey: pubKey, privkey: privKey, metadata: metadata })
.then((res) => {
pool.publish(event, relays);
router.push({
pathname: '/onboarding/create/step-2',
query: { id: res.id, pubkey: res.pubkey, privkey: res.privkey },
});
router.push(`/onboarding/create/${res.id}/${res.pubkey}/${res.privkey}`);
})
.catch(console.error);
}, [pool, pubKey, privKey, metadata, relays, router]);
@ -193,15 +181,3 @@ export default function Page() {
</div>
);
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return <BaseLayout>{page}</BaseLayout>;
};

View File

@ -0,0 +1,13 @@
export default function OnboardingLayout({ children }: { children: React.ReactNode }) {
return (
<div className="h-screen w-screen bg-zinc-50 text-zinc-900 dark:bg-black dark:text-white">
<div className="flex h-screen w-full flex-col">
<div
data-tauri-drag-region
className="relative h-11 shrink-0 border-b border-zinc-100 bg-white dark:border-zinc-900 dark:bg-black"
></div>
<div className="relative flex min-h-0 w-full flex-1">{children}</div>
</div>
</div>
);
}

View File

@ -1,4 +1,4 @@
import BaseLayout from '@layouts/base';
'use client';
import { RelayContext } from '@components/relaysProvider';
@ -8,26 +8,15 @@ import { fetchMetadata } from '@utils/metadata';
import { truncate } from '@utils/truncate';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { useRouter } from 'next/navigation';
import { getPublicKey } from 'nostr-tools';
import {
JSXElementConstructor,
ReactElement,
ReactFragment,
ReactPortal,
useCallback,
useContext,
useEffect,
useRef,
useState,
} from 'react';
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
export default function Page() {
export default function Page({ params }: { params: { privkey: string } }) {
const router = useRouter();
const [pool, relays]: any = useContext(RelayContext);
const router = useRouter();
const privkey: any = router.query.privkey || null;
const pubkey = privkey ? getPublicKey(privkey) : null;
const pubkey = useMemo(() => (params.privkey ? getPublicKey(params.privkey) : null), [params.privkey]);
const [profile, setProfile] = useState({ id: null, metadata: null });
const [done, setDone] = useState(false);
@ -75,7 +64,7 @@ export default function Page() {
relays,
(event: any) => {
if (event.kind === 0) {
insertAccountToStorage(pubkey, privkey, event.content);
insertAccountToStorage(pubkey, params.privkey, event.content);
} else {
if (event.tags.length > 0) {
insertFollowsToStorage(event.tags);
@ -91,7 +80,7 @@ export default function Page() {
return () => {
unsubscribe;
};
}, [insertAccountToStorage, insertFollowsToStorage, pool, relays, privkey, pubkey]);
}, [insertAccountToStorage, insertFollowsToStorage, pool, relays, pubkey, params.privkey]);
// submit then redirect to home
const submit = () => {
@ -168,15 +157,3 @@ export default function Page() {
</div>
);
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return <BaseLayout>{page}</BaseLayout>;
};

View File

@ -1,9 +1,8 @@
import BaseLayout from '@layouts/base';
'use client';
import { ArrowLeft, CableTag } from 'iconoir-react';
import { useRouter } from 'next/router';
import { useRouter } from 'next/navigation';
import { nip19 } from 'nostr-tools';
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal } from 'react';
import { Resolver, useForm } from 'react-hook-form';
type FormValues = {
@ -45,10 +44,7 @@ export default function Page() {
}
try {
router.push({
pathname: '/onboarding/login/step-2',
query: { privkey: privkey },
});
router.push(`/onboarding/login/${privkey}`);
} catch (error) {
setError('key', {
type: 'custom',
@ -134,15 +130,3 @@ export default function Page() {
</div>
);
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return <BaseLayout>{page}</BaseLayout>;
};

View File

@ -1,9 +1,6 @@
import BaseLayout from '@layouts/base';
import { ArrowRight } from 'iconoir-react';
import Image from 'next/image';
import Link from 'next/link';
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal } from 'react';
const PLEBS = [
'https://133332.xyz/p.jpg',
@ -128,15 +125,3 @@ export default function Page() {
</div>
);
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return <BaseLayout>{page}</BaseLayout>;
};

View File

@ -1,4 +1,4 @@
import BaseLayout from '@layouts/base';
'use client';
import { RelayContext } from '@components/relaysProvider';
@ -8,17 +8,8 @@ import { getParentID, pubkeyArray } from '@utils/transform';
import LumeSymbol from '@assets/icons/Lume';
import useLocalStorage, { writeStorage } from '@rehooks/local-storage';
import { useRouter } from 'next/router';
import {
JSXElementConstructor,
ReactElement,
ReactFragment,
ReactPortal,
useCallback,
useContext,
useEffect,
useRef,
} from 'react';
import { useRouter } from 'next/navigation';
import { useCallback, useContext, useEffect, useRef } from 'react';
export default function Page() {
const [pool, relays]: any = useContext(RelayContext);
@ -174,50 +165,40 @@ export default function Page() {
}, [fetchActiveAccount, fetchPlebsByAccount, totalNotes, fetchData, router]);
return (
<div className="relative h-full overflow-hidden">
{/* dragging area */}
<div data-tauri-drag-region className="absolute left-0 top-0 z-20 h-16 w-full bg-transparent" />
{/* end dragging area */}
<div className="relative flex h-full flex-col items-center justify-center">
<div className="flex flex-col items-center gap-2">
<LumeSymbol className="h-16 w-16 text-black dark:text-white" />
<div className="text-center">
<h3 className="text-lg font-semibold leading-tight text-zinc-900 dark:text-zinc-100">
Here&apos;s an interesting fact:
</h3>
<p className="font-medium text-zinc-300 dark:text-zinc-600">
Bitcoin and Nostr can be used by anyone, and no one can stop you!
</p>
<div className="h-screen w-screen bg-zinc-50 text-zinc-900 dark:bg-black dark:text-white">
<div className="relative h-full overflow-hidden">
{/* dragging area */}
<div data-tauri-drag-region className="absolute left-0 top-0 z-20 h-16 w-full bg-transparent" />
{/* end dragging area */}
<div className="relative flex h-full flex-col items-center justify-center">
<div className="flex flex-col items-center gap-2">
<LumeSymbol className="h-16 w-16 text-black dark:text-white" />
<div className="text-center">
<h3 className="text-lg font-semibold leading-tight text-zinc-900 dark:text-zinc-100">
Here&apos;s an interesting fact:
</h3>
<p className="font-medium text-zinc-300 dark:text-zinc-600">
Bitcoin and Nostr can be used by anyone, and no one can stop you!
</p>
</div>
</div>
<div className="absolute bottom-16 left-1/2 -translate-x-1/2 transform">
<svg
className="h-5 w-5 animate-spin text-black dark: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>
</div>
<div className="absolute bottom-16 left-1/2 -translate-x-1/2 transform">
<svg
className="h-5 w-5 animate-spin text-black dark: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>
</div>
</div>
);
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return <BaseLayout>{page}</BaseLayout>;
};

7
src/app/providers.tsx Normal file
View File

@ -0,0 +1,7 @@
'use client';
import RelayProvider from '@components/relaysProvider';
export function Providers({ children }: { children: React.ReactNode }) {
return <RelayProvider>{children}</RelayProvider>;
}

View File

@ -1,5 +1,4 @@
import BaseLayout from '@layouts/base';
import WithSidebarLayout from '@layouts/withSidebar';
'use client';
import ProfileFollowers from '@components/profile/followers';
import ProfileFollows from '@components/profile/follows';
@ -7,16 +6,11 @@ import ProfileMetadata from '@components/profile/metadata';
import ProfileNotes from '@components/profile/notes';
import * as Tabs from '@radix-ui/react-tabs';
import { useRouter } from 'next/router';
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal } from 'react';
export default function Page() {
const router = useRouter();
const id: any = router.query.id || '';
export default function Page({ params }: { params: { id: string } }) {
return (
<div className="scrollbar-hide h-full w-full overflow-y-auto">
<ProfileMetadata id={id} />
<ProfileMetadata id={params.id} />
<Tabs.Root className="flex w-full flex-col" defaultValue="notes">
<Tabs.List className="flex border-b border-zinc-800">
<Tabs.Trigger
@ -39,31 +33,15 @@ export default function Page() {
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="notes">
<ProfileNotes id={id} />
<ProfileNotes id={params.id} />
</Tabs.Content>
<Tabs.Content value="followers">
<ProfileFollowers id={id} />
<ProfileFollowers id={params.id} />
</Tabs.Content>
<Tabs.Content value="following">
<ProfileFollows id={id} />
<ProfileFollows id={params.id} />
</Tabs.Content>
</Tabs.Root>
</div>
);
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return (
<BaseLayout>
<WithSidebarLayout>{page}</WithSidebarLayout>
</BaseLayout>
);
};

39
src/app/users/layout.tsx Normal file
View File

@ -0,0 +1,39 @@
import AppHeader from '@components/appHeader';
import MultiAccounts from '@components/multiAccounts';
import Navigation from '@components/navigation';
export default function UsersLayout({ children }: { children: React.ReactNode }) {
return (
<div className="h-screen w-screen bg-zinc-50 text-zinc-900 dark:bg-black dark:text-white">
<div className="flex h-screen w-full flex-col">
<div
data-tauri-drag-region
className="relative h-11 shrink-0 border-b border-zinc-100 bg-white dark:border-zinc-900 dark:bg-black"
>
<AppHeader />
</div>
<div className="relative flex min-h-0 w-full flex-1">
<div className="relative w-[68px] shrink-0 border-r border-zinc-900">
<MultiAccounts />
</div>
<div className="grid w-full grid-cols-4 xl:grid-cols-5">
<div className="scrollbar-hide col-span-1 overflow-y-auto overflow-x-hidden border-r border-zinc-900">
<Navigation />
</div>
<div className="col-span-3 m-3 overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20 xl:col-span-2 xl:mr-1.5">
<div className="h-full w-full rounded-lg">{children}</div>
</div>
<div className="col-span-3 m-3 hidden overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20 xl:col-span-2 xl:ml-1.5 xl:flex">
<div className="flex h-full w-full items-center justify-center">
<p className="select-text p-8 text-center text-zinc-400">
This feature hasn&apos;t implemented yet, so resize Lume to the initial size for a better experience.
I&apos;m sorry for this inconvenience, and I swear I will add it soon 😁
</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -1,39 +0,0 @@
import Link, { LinkProps } from 'next/link';
import { useRouter } from 'next/router';
import { PropsWithChildren, memo, useEffect, useState } from 'react';
type ActiveLinkProps = LinkProps & {
className?: string;
activeClassName: string;
};
const ActiveLink = ({ children, activeClassName, className, ...props }: PropsWithChildren<ActiveLinkProps>) => {
const { asPath, isReady } = useRouter();
const [computedClassName, setComputedClassName] = useState(className);
useEffect(() => {
// Check if the router fields are updated client-side
if (isReady) {
// Dynamic route will be matched via props.as
// Static route will be matched via props.href
const linkPathname = new URL((props.as || props.href) as string, location.href).pathname;
// Using URL().pathname to get rid of query and hash
const activePathname = new URL(asPath, location.href).pathname;
const newClassName = linkPathname === activePathname ? `${className} ${activeClassName}`.trim() : className;
if (newClassName !== computedClassName) {
setComputedClassName(newClassName);
}
}
}, [asPath, isReady, props.as, props.href, activeClassName, className, computedClassName]);
return (
<Link className={computedClassName} {...props}>
{children}
</Link>
);
};
export default memo(ActiveLink);

View File

@ -1,6 +1,8 @@
'use client';
import { platform } from '@tauri-apps/api/os';
import { ArrowLeft, ArrowRight, Refresh } from 'iconoir-react';
import { useRouter } from 'next/router';
import { useRouter } from 'next/navigation';
import { useLayoutEffect, useState } from 'react';
export default function AppActions() {
@ -12,11 +14,11 @@ export default function AppActions() {
};
const goForward = () => {
window.history.forward();
router.forward();
};
const reload = () => {
router.reload();
router.refresh();
};
useLayoutEffect(() => {

View File

@ -2,7 +2,7 @@ import { ImageWithFallback } from '@components/imageWithFallback';
import { DEFAULT_AVATAR } from '@stores/constants';
import { useRouter } from 'next/router';
import { useRouter } from 'next/navigation';
import { useCallback } from 'react';
export const BrowseChannelItem = ({ data }: { data: any }) => {
@ -11,10 +11,7 @@ export const BrowseChannelItem = ({ data }: { data: any }) => {
const openChannel = useCallback(
(id: string) => {
router.push({
pathname: '/channels/[id]',
query: { id: id },
});
router.push(`/channels/${id}`);
},
[router]
);

View File

@ -2,17 +2,14 @@ import { ImageWithFallback } from '@components/imageWithFallback';
import { DEFAULT_AVATAR } from '@stores/constants';
import { useRouter } from 'next/router';
import { useRouter } from 'next/navigation';
export const ChannelListItem = ({ data }: { data: any }) => {
const router = useRouter();
const channel = JSON.parse(data.content);
const openChannel = (id: string) => {
router.push({
pathname: '/channels/[id]',
query: { id: id },
});
router.push(`/channels/${id}`);
};
return (

View File

@ -5,7 +5,7 @@ import { ImageWithFallback } from '@components/imageWithFallback';
import { DEFAULT_AVATAR } from '@stores/constants';
import useLocalStorage from '@rehooks/local-storage';
import { useRouter } from 'next/router';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
export default function ChatList() {
@ -16,10 +16,7 @@ export default function ChatList() {
const profile = activeAccount.metadata ? JSON.parse(activeAccount.metadata) : null;
const openSelfChat = () => {
router.push({
pathname: '/chats/[pubkey]',
query: { pubkey: activeAccount.pubkey },
});
router.push(`/chats/${activeAccount.pubkey}`);
};
useEffect(() => {

View File

@ -5,17 +5,14 @@ import { DEFAULT_AVATAR } from '@stores/constants';
import { useMetadata } from '@utils/metadata';
import { truncate } from '@utils/truncate';
import { useRouter } from 'next/router';
import { useRouter } from 'next/navigation';
export const ChatListItem = ({ pubkey }: { pubkey: string }) => {
const router = useRouter();
const profile = useMetadata(pubkey);
const openChat = () => {
router.push({
pathname: '/chats/[pubkey]',
query: { pubkey: pubkey },
});
router.push(`/chats/${pubkey}`);
};
return (

View File

@ -4,17 +4,14 @@ import { DEFAULT_AVATAR } from '@stores/constants';
import { truncate } from '@utils/truncate';
import { useRouter } from 'next/router';
import { useRouter } from 'next/navigation';
export const ChatModalUser = ({ data }: { data: any }) => {
const router = useRouter();
const profile = JSON.parse(data.metadata);
const openNewChat = () => {
router.push({
pathname: '/chats/[pubkey]',
query: { pubkey: data.pubkey },
});
router.push(`/chats/${data.pubkey}`);
};
return (

View File

@ -1,3 +1,5 @@
'use client';
import { NetworkStatusIndicator } from '@components/networkStatusIndicator';
import { RelayContext } from '@components/relaysProvider';

View File

@ -1,10 +1,12 @@
'use client';
import { DEFAULT_AVATAR } from '@stores/constants';
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import { writeText } from '@tauri-apps/api/clipboard';
import { LogOut, ProfileCircle, Settings } from 'iconoir-react';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { useRouter } from 'next/navigation';
import { nip19 } from 'nostr-tools';
export const ActiveAccount = ({ user }: { user: any }) => {

View File

@ -1,3 +1,5 @@
'use client';
import { ActiveAccount } from '@components/multiAccounts/activeAccount';
import { InactiveAccount } from '@components/multiAccounts/inactiveAccount';

View File

@ -1,3 +1,5 @@
'use client';
import ChannelList from '@components/channels/channelList';
import * as Collapsible from '@radix-ui/react-collapsible';

View File

@ -1,3 +1,5 @@
'use client';
import ChatList from '@components/chats/chatList';
import * as Collapsible from '@radix-ui/react-collapsible';

View File

@ -1,7 +1,8 @@
import ActiveLink from '@components/activeLink';
'use client';
import * as Collapsible from '@radix-ui/react-collapsible';
import { NavArrowUp } from 'iconoir-react';
import Link from 'next/link';
import { useState } from 'react';
export default function Newsfeed() {
@ -21,20 +22,20 @@ export default function Newsfeed() {
<h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">Newsfeed</h3>
</Collapsible.Trigger>
<Collapsible.Content className="flex flex-col text-zinc-400">
<ActiveLink
<Link
href={`/newsfeed/following`}
activeClassName="dark:bg-zinc-900 dark:text-zinc-100 hover:dark:bg-zinc-800"
//activeClassName="dark:bg-zinc-900 dark:text-zinc-100 hover:dark:bg-zinc-800"
className="flex h-8 items-center gap-2.5 rounded-md px-2.5 text-sm font-medium hover:text-zinc-200"
>
<span>Following</span>
</ActiveLink>
<ActiveLink
</Link>
<Link
href={`/newsfeed/circle`}
activeClassName="dark:bg-zinc-900 dark:text-zinc-100 hover:dark:bg-zinc-800"
//activeClassName="dark:bg-zinc-900 dark:text-zinc-100 hover:dark:bg-zinc-800"
className="flex h-8 items-center gap-2.5 rounded-md px-2.5 text-sm font-medium hover:text-zinc-200"
>
<span>Circle</span>
</ActiveLink>
</Link>
</Collapsible.Content>
</div>
</Collapsible.Root>

View File

@ -7,7 +7,7 @@ import { UserExtend } from '@components/user/extend';
import { UserMention } from '@components/user/mention';
import destr from 'destr';
import { useRouter } from 'next/router';
import { useRouter } from 'next/navigation';
import { memo, useMemo } from 'react';
import reactStringReplace from 'react-string-replace';

View File

@ -7,7 +7,7 @@ import { dateToUnix } from '@utils/getDate';
import * as Dialog from '@radix-ui/react-dialog';
import useLocalStorage from '@rehooks/local-storage';
import { MultiBubble, OpenNewWindow } from 'iconoir-react';
import { useRouter } from 'next/router';
import { useRouter } from 'next/navigation';
import { getEventHash, signEvent } from 'nostr-tools';
import { memo, useContext, useState } from 'react';

View File

@ -1,3 +1,5 @@
'use client';
import { RelayContext } from '@components/relaysProvider';
import { UserFollow } from '@components/user/follow';

View File

@ -1,3 +1,5 @@
'use client';
import { RelayContext } from '@components/relaysProvider';
import { UserFollow } from '@components/user/follow';

View File

@ -1,3 +1,5 @@
'use client';
import { ImageWithFallback } from '@components/imageWithFallback';
import { RelayContext } from '@components/relaysProvider';

View File

@ -1,3 +1,5 @@
'use client';
import { NoteBase } from '@components/note/base';
import { RelayContext } from '@components/relaysProvider';

View File

@ -1,3 +1,5 @@
'use client';
import { RelayPool } from 'nostr-relaypool';
import { createContext, useMemo } from 'react';

View File

@ -1,3 +0,0 @@
export default function BaseLayout({ children }: { children: React.ReactNode }) {
return <div className="h-screen w-screen bg-zinc-50 text-zinc-900 dark:bg-black dark:text-white">{children}</div>;
}

View File

@ -1,37 +0,0 @@
import AppHeader from '@components/appHeader';
import MultiAccounts from '@components/multiAccounts';
import Navigation from '@components/navigation';
export default function WithSidebarLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex h-screen w-full flex-col">
<div
data-tauri-drag-region
className="relative h-11 shrink-0 border-b border-zinc-100 bg-white dark:border-zinc-900 dark:bg-black"
>
<AppHeader />
</div>
<div className="relative flex min-h-0 w-full flex-1">
<div className="relative w-[68px] shrink-0 border-r border-zinc-900">
<MultiAccounts />
</div>
<div className="grid w-full grid-cols-4 xl:grid-cols-5">
<div className="scrollbar-hide col-span-1 overflow-y-auto overflow-x-hidden border-r border-zinc-900">
<Navigation />
</div>
<div className="col-span-3 m-3 overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20 xl:col-span-2 xl:mr-1.5">
<div className="h-full w-full rounded-lg">{children}</div>
</div>
<div className="col-span-3 m-3 hidden overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20 xl:col-span-2 xl:ml-1.5 xl:flex">
<div className="flex h-full w-full items-center justify-center">
<p className="select-text p-8 text-center text-zinc-400">
This feature hasn&apos;t implemented yet, so resize Lume to the initial size for a better experience.
I&apos;m sorry for this inconvenience, and I swear I will add it soon 😁
</p>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -1,25 +0,0 @@
import RelayProvider from '@components/relaysProvider';
import type { NextPage } from 'next';
import type { AppProps } from 'next/app';
import { useRouter } from 'next/router';
import { ReactElement, ReactNode } from 'react';
import '../App.css';
// eslint-disable-next-line @typescript-eslint/ban-types
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode;
};
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
};
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
const router = useRouter();
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout ?? ((page) => page);
return <RelayProvider>{getLayout(<Component key={router.asPath} {...pageProps} />)}</RelayProvider>;
}

View File

@ -1,13 +0,0 @@
import { Head, Html, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html>
<Head />
<body className="cursor-default select-none overflow-hidden font-sans antialiased">
<Main />
<NextScript />
</body>
</Html>
);
}

View File

@ -1,24 +0,0 @@
import BaseLayout from '@layouts/base';
import WithSidebarLayout from '@layouts/withSidebar';
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal } from 'react';
export default function Page() {
return <></>;
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return (
<BaseLayout>
<WithSidebarLayout>{page}</WithSidebarLayout>
</BaseLayout>
);
};

View File

@ -1,24 +0,0 @@
import BaseLayout from '@layouts/base';
import WithSidebarLayout from '@layouts/withSidebar';
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal } from 'react';
export default function Page() {
return <div className="scrollbar-hide flex h-full flex-col gap-2 overflow-y-auto py-3"></div>;
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return (
<BaseLayout>
<WithSidebarLayout>{page}</WithSidebarLayout>
</BaseLayout>
);
};

View File

@ -1,28 +0,0 @@
import BaseLayout from '@layouts/base';
import WithSidebarLayout from '@layouts/withSidebar';
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal } from 'react';
export default function Page() {
return (
<div className="flex h-full w-full items-center justify-center">
<p className="text-sm text-zinc-400">Sorry, this feature under development, it will come in the next version</p>
</div>
);
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return (
<BaseLayout>
<WithSidebarLayout>{page}</WithSidebarLayout>
</BaseLayout>
);
};

View File

@ -2,6 +2,7 @@
module.exports = {
content: ['./src/**/*.{js,ts,jsx,tsx}'],
darkMode: 'class',
theme: {
extend: {
boxShadow: {

View File

@ -2,8 +2,7 @@
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@pages/*": ["src/pages/*"],
"@layouts/*": ["src/layouts/*"],
"@app/*": ["src/app/*"],
"@components/*": ["src/components/*"],
"@stores/*": ["src/stores/*"],
"@utils/*": ["src/utils/*"],
@ -27,7 +26,8 @@
{
"name": "next"
}
]
],
"strictNullChecks": false
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]