fix the mess I started

This commit is contained in:
Ren Amamiya
2023-04-28 09:52:12 +07:00
parent 233a5bd2ad
commit a71502d19e
35 changed files with 197 additions and 380 deletions

View File

@@ -4,7 +4,7 @@ import Navigation from '@lume/shared/navigation';
export function LayoutChannel({ 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="h-screen w-screen bg-zinc-50 text-zinc-900 dark:bg-zinc-950 dark:text-white">
<div className="flex h-screen w-full flex-col">
<div
data-tauri-drag-region

View File

@@ -1,9 +1,7 @@
import { AccountContext } from '@lume/shared/accountProvider';
import { ChannelBlackList } from '@lume/shared/channels/channelBlackList';
import { ChannelProfile } from '@lume/shared/channels/channelProfile';
import { UpdateChannelModal } from '@lume/shared/channels/updateChannelModal';
import { FormChannel } from '@lume/shared/form/channel';
import { RelayContext } from '@lume/shared/relaysProvider';
import { channelMessagesAtom, channelReplyAtom } from '@lume/stores/channel';
import { FULL_RELAYS } from '@lume/stores/constants';
import { dateToUnix, hoursAgo } from '@lume/utils/getDate';
@@ -12,18 +10,20 @@ import { arrayObjToPureArr } from '@lume/utils/transform';
import { useSetAtom } from 'jotai';
import { useResetAtom } from 'jotai/utils';
import { Suspense, lazy, useContext, useRef } from 'react';
import { RelayPool } from 'nostr-relaypool';
import { Suspense, lazy, useRef } from 'react';
import useSWRSubscription from 'swr/subscription';
const ChannelMessages = lazy(() => import('@lume/shared/channels/messages'));
let mutedList: any = [];
let activeAccount: any = {};
let activeMutedList: any = [];
let activeHidedList: any = [];
if (typeof window !== 'undefined') {
const { getBlacklist, getActiveBlacklist, getActiveAccount } = await import('@lume/utils/storage');
const activeAccount = await getActiveAccount();
activeAccount = await getActiveAccount();
activeHidedList = await getActiveBlacklist(activeAccount.id, 43);
activeMutedList = await getActiveBlacklist(activeAccount.id, 44);
mutedList = await getBlacklist(activeAccount.id, 44);
@@ -33,12 +33,9 @@ export function Page() {
const pageContext = usePageContext();
const searchParams: any = pageContext.urlParsed.search;
const id = searchParams.id;
const channelID = searchParams.id;
const channelPubkey = searchParams.pubkey;
const pool: any = useContext(RelayContext);
const activeAccount: any = useContext(AccountContext);
const setChannelMessages = useSetAtom(channelMessagesAtom);
const resetChannelMessages = useResetAtom(channelMessagesAtom);
const resetChannelReply = useResetAtom(channelReplyAtom);
@@ -47,16 +44,17 @@ export function Page() {
const hided = arrayObjToPureArr(activeHidedList);
const muted = arrayObjToPureArr(activeMutedList);
useSWRSubscription(id, () => {
useSWRSubscription(channelID, () => {
// reset channel reply
resetChannelReply();
// reset channel messages
resetChannelMessages();
// subscribe for new messages
const pool = new RelayPool(FULL_RELAYS);
const unsubscribe = pool.subscribe(
[
{
'#e': [id],
'#e': [channelID],
kinds: [42],
since: dateToUnix(hoursAgo(48, now.current)),
},
@@ -82,11 +80,11 @@ export function Page() {
<div className="flex h-full flex-col justify-between gap-2">
<div className="flex h-11 w-full shrink-0 items-center justify-between">
<div>
<ChannelProfile id={id} pubkey={channelPubkey} />
<ChannelProfile id={channelID} pubkey={channelPubkey} />
</div>
<div className="flex items-center gap-2">
<ChannelBlackList blacklist={mutedList} />
{activeAccount.pubkey === channelPubkey && <UpdateChannelModal id={id} />}
{activeAccount.pubkey === channelPubkey && <UpdateChannelModal id={activeAccount} />}
</div>
</div>
<div className="relative flex w-full flex-1 flex-col justify-between rounded-lg border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20">
@@ -94,7 +92,7 @@ export function Page() {
<ChannelMessages />
</Suspense>
<div className="shrink-0 p-3">
<FormChannel eventId={id} />
<FormChannel eventId={channelID} />
</div>
</div>
</div>

View File

@@ -4,7 +4,7 @@ import Navigation from '@lume/shared/navigation';
export function LayoutChat({ 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="h-screen w-screen bg-zinc-50 text-zinc-900 dark:bg-zinc-950 dark:text-white">
<div className="flex h-screen w-full flex-col">
<div
data-tauri-drag-region

View File

@@ -0,0 +1,56 @@
import { ImagePreview } from '@lume/app/newsfeed/components/note/preview/image';
import { VideoPreview } from '@lume/app/newsfeed/components/note/preview/video';
import { YoutubePreview } from '@lume/app/newsfeed/components/note/preview/youtube';
import { NoteQuote } from '@lume/app/newsfeed/components/note/quote';
import { NoteMentionUser } from '@lume/app/newsfeed/components/user/mention';
import destr from 'destr';
import reactStringReplace from 'react-string-replace';
export const contentParser = (noteContent: any, noteTags: any) => {
let parsedContent = noteContent.trim();
// get data tags
const tags = destr(noteTags);
// handle urls
parsedContent = reactStringReplace(parsedContent, /(https?:\/\/\S+)/g, (match, i) => {
if (match.match(/\.(jpg|jpeg|gif|png|webp)$/i)) {
// image url
return <ImagePreview key={match + i} url={match} size="large" />;
} else if (match.match(/(http:|https:)?(\/\/)?(www\.)?(youtube.com|youtu.be)\/(watch|embed)?(\?v=|\/)?(\S+)?/)) {
// youtube
return <YoutubePreview key={match + i} url={match} />;
} else if (match.match(/\.(mp4|webm)$/i)) {
// video
return <VideoPreview key={match + i} url={match} />;
} else {
return (
<a key={match + i} href={match} className="cursor-pointer text-fuchsia-500" target="_blank" rel="noreferrer">
{match}
</a>
);
}
});
// handle #-hashtags
parsedContent = reactStringReplace(parsedContent, /#(\w+)/g, (match, i) => (
<span key={match + i} className="cursor-pointer text-fuchsia-500">
#{match}
</span>
));
// handle mentions
if (tags && tags.length > 0) {
parsedContent = reactStringReplace(parsedContent, /\#\[(\d+)\]/gm, (match, i) => {
if (tags[match][0] === 'p') {
// @-mentions
return <NoteMentionUser key={tags[match][1] + i} pubkey={tags[match][1]} />;
} else if (tags[match][0] === 'e') {
// note-quotes
return <NoteQuote key={tags[match][1] + i} id={tags[match][1]} />;
} else {
return;
}
});
}
return parsedContent;
};

View File

@@ -1,32 +1,31 @@
import { AccountContext } from '@lume/shared/accountProvider';
import { ImagePicker } from '@lume/shared/form/imagePicker';
import { RelayContext } from '@lume/shared/relaysProvider';
import { WRITEONLY_RELAYS } from '@lume/stores/constants';
import { noteContentAtom } from '@lume/stores/note';
import { dateToUnix } from '@lume/utils/getDate';
import { useActiveAccount } from '@lume/utils/hooks/useActiveAccount';
import { useAtom } from 'jotai';
import { useResetAtom } from 'jotai/utils';
import { RelayPool } from 'nostr-relaypool';
import { getEventHash, signEvent } from 'nostr-tools';
import { useContext } from 'react';
export default function FormBase() {
const pool: any = useContext(RelayContext);
const activeAccount: any = useContext(AccountContext);
export default function NoteForm() {
const { account, isLoading, isError } = useActiveAccount();
const [value, setValue] = useAtom(noteContentAtom);
const resetValue = useResetAtom(noteContentAtom);
const submitEvent = () => {
if (!isLoading && !isError && account) {
const pool = new RelayPool(WRITEONLY_RELAYS);
const event: any = {
content: value,
created_at: dateToUnix(),
kind: 1,
pubkey: activeAccount.pubkey,
pubkey: account.pubkey,
tags: [],
};
event.id = getEventHash(event);
event.sig = signEvent(event, activeAccount.privkey);
event.sig = signEvent(event, account.privkey);
// publish note
pool.publish(event, WRITEONLY_RELAYS);
@@ -34,6 +33,9 @@ export default function FormBase() {
resetValue();
// send notification
// sendNotification('Note has been published successfully');
} else {
console.log('Cannot publish note');
}
};
return (

View File

@@ -1,7 +1,6 @@
import { NoteMetadata } from '@lume/shared/note/metadata';
import { NoteParent } from '@lume/shared/note/parent';
import { UserExtend } from '@lume/shared/user/extend';
import { contentParser } from '@lume/utils/parser';
import { contentParser } from '@lume/app/newsfeed/components/contentParser';
import { NoteParent } from '@lume/app/newsfeed/components/note/parent';
import { NoteDefaultUser } from '@lume/app/newsfeed/components/user/default';
import { memo } from 'react';
import { navigate } from 'vite-plugin-ssr/client/router';
@@ -40,19 +39,12 @@ export const NoteBase = memo(function NoteBase({ event }: { event: any }) {
{parentNote()}
<div className="relative z-10 flex flex-col">
<div onClick={(e) => openUserPage(e)}>
<UserExtend pubkey={event.pubkey} time={event.created_at} />
<NoteDefaultUser pubkey={event.pubkey} time={event.created_at} />
</div>
<div className="mt-1 pl-[52px]">
<div className="whitespace-pre-line break-words text-[15px] leading-tight text-zinc-100">{content}</div>
</div>
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
<NoteMetadata
eventID={event.event_id}
eventPubkey={event.pubkey}
eventContent={event.content}
eventTime={event.created_at}
/>
</div>
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]"></div>
</div>
</div>
);

View File

@@ -0,0 +1,24 @@
import { contentParser } from '@lume/app/newsfeed/components/contentParser';
import { NoteDefaultUser } from '@lume/app/newsfeed/components/user/default';
import { memo } from 'react';
export const NoteComment = memo(function NoteComment({ event }: { event: any }) {
const content = contentParser(event.content, event.tags);
return (
<div className="relative z-10 flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 px-3 py-5 hover:bg-black/20">
<div className="relative z-10 flex flex-col">
<NoteDefaultUser pubkey={event.pubkey} time={event.created_at} />
<div className="-mt-5 pl-[52px]">
<div className="flex flex-col gap-2">
<div className="prose prose-zinc max-w-none break-words text-[15px] leading-tight dark:prose-invert prose-p:m-0 prose-p:text-[15px] prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-img:m-0 prose-video:m-0">
{content}
</div>
</div>
</div>
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]"></div>
</div>
</div>
);
});

View File

@@ -1,15 +1,12 @@
import { NoteMetadata } from '@lume/shared/note/metadata';
import { RelayContext } from '@lume/shared/relaysProvider';
import { UserExtend } from '@lume/shared/user/extend';
import { contentParser } from '@lume/app/newsfeed/components/contentParser';
import { NoteDefaultUser } from '@lume/app/newsfeed/components/user/default';
import { READONLY_RELAYS } from '@lume/stores/constants';
import { contentParser } from '@lume/utils/parser';
import { memo, useContext } from 'react';
import { RelayPool } from 'nostr-relaypool';
import { memo } from 'react';
import useSWRSubscription from 'swr/subscription';
export const NoteParent = memo(function NoteParent({ id }: { id: string }) {
const pool: any = useContext(RelayContext);
const { data, error } = useSWRSubscription(
id
? [
@@ -20,6 +17,7 @@ export const NoteParent = memo(function NoteParent({ id }: { id: string }) {
]
: null,
(key, { next }) => {
const pool = new RelayPool(READONLY_RELAYS);
const unsubscribe = pool.subscribe(
key,
READONLY_RELAYS,
@@ -70,20 +68,13 @@ export const NoteParent = memo(function NoteParent({ id }: { id: string }) {
<>
<div className="absolute left-[21px] top-0 h-full w-0.5 bg-gradient-to-t from-zinc-800 to-zinc-600"></div>
<div className="relative z-10 flex flex-col">
<UserExtend pubkey={data.pubkey} time={data.created_at} />
<NoteDefaultUser pubkey={data.pubkey} time={data.created_at} />
<div className="mt-1 pl-[52px]">
<div className="whitespace-pre-line break-words text-[15px] leading-tight text-zinc-100">
{contentParser(data.content, data.tags)}
</div>
</div>
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
<NoteMetadata
eventID={data.event_id}
eventPubkey={data.pubkey}
eventContent={data.content}
eventTime={data.created_at}
/>
</div>
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]"></div>
</div>
</>
)}

View File

@@ -1,14 +1,12 @@
import { RelayContext } from '@lume/shared/relaysProvider';
import { UserExtend } from '@lume/shared/user/extend';
import { contentParser } from '@lume/app/newsfeed/components/contentParser';
import { NoteDefaultUser } from '@lume/app/newsfeed/components/user/default';
import { READONLY_RELAYS } from '@lume/stores/constants';
import { contentParser } from '@lume/utils/parser';
import { memo, useContext } from 'react';
import { RelayPool } from 'nostr-relaypool';
import { memo } from 'react';
import useSWRSubscription from 'swr/subscription';
export const NoteQuote = memo(function NoteQuote({ id }: { id: string }) {
const pool: any = useContext(RelayContext);
const { data, error } = useSWRSubscription(
id
? [
@@ -19,6 +17,7 @@ export const NoteQuote = memo(function NoteQuote({ id }: { id: string }) {
]
: null,
(key, { next }) => {
const pool = new RelayPool(READONLY_RELAYS);
const unsubscribe = pool.subscribe(
key,
READONLY_RELAYS,
@@ -45,7 +44,7 @@ export const NoteQuote = memo(function NoteQuote({ id }: { id: string }) {
<div className="h-6 w-full animate-pulse select-text flex-col rounded bg-zinc-800"></div>
) : (
<div className="relative z-10 flex flex-col">
<UserExtend pubkey={data.pubkey} time={data.created_at} />
<NoteDefaultUser pubkey={data.pubkey} time={data.created_at} />
<div className="mt-1 pl-[52px]">
<div className="whitespace-pre-line break-words text-[15px] leading-tight text-zinc-100">
{contentParser(data.content, data.tags)}

View File

@@ -1,5 +1,5 @@
import { RootNote } from '@lume/shared/note/rootNote';
import { UserQuoteRepost } from '@lume/shared/user/quoteRepost';
import { RootNote } from '@lume/app/newsfeed/components/note/rootNote';
import { NoteRepostUser } from '@lume/app/newsfeed/components/user/repost';
import { getQuoteID } from '@lume/utils/transform';
import { memo } from 'react';
@@ -11,7 +11,7 @@ export const NoteQuoteRepost = memo(function NoteQuoteRepost({ event }: { event:
<div className="relative z-10 flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 px-3 py-5 hover:bg-black/20">
<div className="relative z-10 flex flex-col pb-5">
<div className="absolute left-[21px] top-0 h-full w-0.5 bg-gradient-to-t from-zinc-800 to-zinc-600"></div>
<UserQuoteRepost pubkey={event.pubkey} time={event.created_at} />
<NoteRepostUser pubkey={event.pubkey} time={event.created_at} />
</div>
<RootNote id={rootID} />
</div>

View File

@@ -1,16 +1,13 @@
import { NoteMetadata } from '@lume/shared/note/metadata';
import { RelayContext } from '@lume/shared/relaysProvider';
import { UserExtend } from '@lume/shared/user/extend';
import { contentParser } from '@lume/app/newsfeed/components/contentParser';
import { NoteDefaultUser } from '@lume/app/newsfeed/components/user/default';
import { READONLY_RELAYS } from '@lume/stores/constants';
import { contentParser } from '@lume/utils/parser';
import { memo, useContext } from 'react';
import { RelayPool } from 'nostr-relaypool';
import { memo } from 'react';
import useSWRSubscription from 'swr/subscription';
import { navigate } from 'vite-plugin-ssr/client/router';
export const RootNote = memo(function RootNote({ id }: { id: string }) {
const pool: any = useContext(RelayContext);
const openThread = (e) => {
const selection = window.getSelection();
if (selection.toString().length === 0) {
@@ -30,6 +27,7 @@ export const RootNote = memo(function RootNote({ id }: { id: string }) {
]
: null,
(key, { next }) => {
const pool = new RelayPool(READONLY_RELAYS);
const unsubscribe = pool.subscribe(
key,
READONLY_RELAYS,
@@ -56,20 +54,13 @@ export const RootNote = memo(function RootNote({ id }: { id: string }) {
<div className="h-6 w-full animate-pulse select-text flex-col rounded bg-zinc-800"></div>
) : (
<div onClick={(e) => openThread(e)} className="relative z-10 flex flex-col">
<UserExtend pubkey={data.pubkey} time={data.created_at} />
<NoteDefaultUser pubkey={data.pubkey} time={data.created_at} />
<div className="mt-1 pl-[52px]">
<div className="whitespace-pre-line break-words text-[15px] leading-tight text-zinc-100">
{contentParser(data.content, data.tags)}
</div>
</div>
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
<NoteMetadata
eventID={data.id}
eventPubkey={data.pubkey}
eventContent={data.content}
eventTime={data.created_at}
/>
</div>
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]"></div>
</div>
)}
</>

View File

@@ -7,7 +7,7 @@ import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(relativeTime);
export const UserExtend = ({ pubkey, time }: { pubkey: string; time: number }) => {
export const NoteDefaultUser = ({ pubkey, time }: { pubkey: string; time: number }) => {
const profile = useProfile(pubkey);
return (

View File

@@ -1,7 +1,7 @@
import { useProfile } from '@lume/utils/hooks/useProfile';
import { shortenKey } from '@lume/utils/shortenKey';
export const UserMention = ({ pubkey }: { pubkey: string }) => {
export const NoteMentionUser = ({ pubkey }: { pubkey: string }) => {
const profile = useProfile(pubkey);
return (
<span className="cursor-pointer text-fuchsia-500">@{profile?.name || profile?.username || shortenKey(pubkey)}</span>

View File

@@ -7,7 +7,7 @@ import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(relativeTime);
export const UserQuoteRepost = ({ pubkey, time }: { pubkey: string; time: number }) => {
export const NoteRepostUser = ({ pubkey, time }: { pubkey: string; time: number }) => {
const profile = useProfile(pubkey);
return (

View File

@@ -4,7 +4,7 @@ import Navigation from '@lume/shared/navigation';
export function LayoutNewsfeed({ 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="h-screen w-screen bg-zinc-50 text-zinc-900 dark:bg-zinc-950 dark:text-white">
<div className="flex h-screen w-full flex-col">
<div
data-tauri-drag-region

View File

@@ -1,7 +1,7 @@
import FormBase from '@lume/shared/form/base';
import { NoteBase } from '@lume/shared/note/base';
import { Placeholder } from '@lume/shared/note/placeholder';
import { NoteQuoteRepost } from '@lume/shared/note/quoteRepost';
import NoteForm from '@lume/app/newsfeed/components/form';
import { NoteBase } from '@lume/app/newsfeed/components/note/base';
import { Placeholder } from '@lume/app/newsfeed/components/note/placeholder';
import { NoteQuoteRepost } from '@lume/app/newsfeed/components/note/quoteRepost';
import { hasNewerNoteAtom } from '@lume/stores/note';
import { countTotalNotes, getNotes } from '@lume/utils/storage';
@@ -73,7 +73,7 @@ export function Page() {
<div>{error.message}</div>
) : (
<div ref={parentRef} className="scrollbar-hide h-full w-full overflow-y-auto" style={{ contain: 'strict' }}>
<FormBase />
<NoteForm />
<div
className="relative w-full"
style={{

View File

@@ -94,8 +94,9 @@ export function Page() {
const submit = async () => {
setLoading(true);
const followsIncludeSelf = follows.concat([onboarding.pubkey]);
// insert to database
createAccount(onboarding.pubkey, onboarding.privkey, onboarding.metadata, arrayToNIP02(follows), 1)
createAccount(onboarding.pubkey, onboarding.privkey, onboarding.metadata, arrayToNIP02(followsIncludeSelf), 1)
.then((res) => {
if (res) {
for (const tag of follows) {

View File

@@ -53,8 +53,9 @@ export function Page() {
// show loading indicator
setLoading(true);
const follows = onboarding.follows.concat([['p', pubkey]]);
// insert to database
createAccount(pubkey, onboarding.privkey, onboarding.metadata, onboarding.follows, 1)
createAccount(pubkey, onboarding.privkey, onboarding.metadata, follows, 1)
.then((res) => {
if (res) {
for (const tag of onboarding.follows) {

View File

@@ -45,10 +45,9 @@ export default function AppHeader({ collector }: { collector: boolean }) {
</button>
</div>
<div data-tauri-drag-region className="flex h-full w-full items-center justify-between">
<div className="flex h-full items-center divide-x divide-zinc-900 px-4 pt-px">
<div className="flex h-full items-center divide-x divide-zinc-900 px-4 pt-px"></div>
{collector && <EventCollector />}
</div>
</div>
</div>
);
}

View File

@@ -1,24 +1,26 @@
import { AccountContext } from '@lume/shared/accountProvider';
import { NetworkStatusIndicator } from '@lume/shared/networkStatusIndicator';
import { RelayContext } from '@lume/shared/relaysProvider';
import { READONLY_RELAYS } from '@lume/stores/constants';
import { hasNewerNoteAtom } from '@lume/stores/note';
import { dateToUnix } from '@lume/utils/getDate';
import { useActiveAccount } from '@lume/utils/hooks/useActiveAccount';
import { createChat, createNote, updateAccount } from '@lume/utils/storage';
import { getParentID, nip02ToArray } from '@lume/utils/transform';
import { useSetAtom } from 'jotai';
import { useCallback, useContext, useEffect, useRef } from 'react';
import { RelayPool } from 'nostr-relaypool';
import { useRef } from 'react';
import useSWRSubscription from 'swr/subscription';
export default function EventCollector() {
const pool: any = useContext(RelayContext);
const activeAccount: any = useContext(AccountContext);
const setHasNewerNote = useSetAtom(hasNewerNoteAtom);
const now = useRef(new Date());
const subscribe = useCallback(async () => {
const follows = activeAccount.follows ? JSON.parse(activeAccount.follows) : [];
const { account, isLoading, isError } = useActiveAccount();
useSWRSubscription(!isLoading && !isError ? account : null, () => {
const follows = nip02ToArray(JSON.parse(account.follows));
const pool = new RelayPool(READONLY_RELAYS);
const unsubscribe = pool.subscribe(
[
{
@@ -28,16 +30,16 @@ export default function EventCollector() {
},
{
kinds: [0, 3],
authors: [activeAccount.pubkey],
authors: [account.pubkey],
},
{
kinds: [4],
'#p': [activeAccount.pubkey],
'#p': [account.pubkey],
since: dateToUnix(now.current),
},
],
READONLY_RELAYS,
(event: { kind: number; tags: string[]; id: string; pubkey: string; content: string; created_at: number }) => {
(event: any) => {
switch (event.kind) {
// metadata
case 0:
@@ -48,7 +50,7 @@ export default function EventCollector() {
const parentID = getParentID(event.tags, event.id);
createNote(
event.id,
activeAccount.id,
account.id,
event.pubkey,
event.kind,
event.tags,
@@ -61,21 +63,20 @@ export default function EventCollector() {
break;
// contacts
case 3:
const arr = nip02ToArray(event.tags);
// update account's folllows with NIP-02 tag list
updateAccount('follows', arr, event.pubkey);
updateAccount('follows', event.tags, event.pubkey);
break;
// chat
case 4:
if (event.pubkey !== activeAccount.pubkey) {
createChat(activeAccount.id, event.pubkey, event.created_at);
if (event.pubkey !== account.pubkey) {
createChat(account.id, event.pubkey, event.created_at);
}
break;
// repost
case 6:
createNote(
event.id,
activeAccount.id,
account.id,
event.pubkey,
event.kind,
event.tags,
@@ -93,19 +94,7 @@ export default function EventCollector() {
return () => {
unsubscribe();
};
}, [activeAccount.id, activeAccount.pubkey, activeAccount.follows, pool, setHasNewerNote]);
useEffect(() => {
let ignore = false;
if (!ignore) {
subscribe();
}
return () => {
ignore = true;
};
}, [subscribe]);
});
return <NetworkStatusIndicator />;
}

View File

@@ -1,31 +0,0 @@
import AppHeader from '@lume/shared/appHeader';
import MultiAccounts from '@lume/shared/multiAccounts';
import Navigation from '@lume/shared/navigation';
export default function ChannelLayout({ 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 collector={true} />
</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 xl:col-span-4 xl:mr-1.5">
<div className="h-full w-full rounded-lg">{children}</div>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,29 +0,0 @@
import AppHeader from '@lume/shared/appHeader';
import MultiAccounts from '@lume/shared/multiAccounts';
import Navigation from '@lume/shared/navigation';
export default function NewsfeedLayout({ 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 collector={true} />
</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 xl:col-span-4">{children}</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,17 +0,0 @@
import AppHeader from '@lume/shared/appHeader';
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"
>
<AppHeader collector={false} />
</div>
<div className="relative flex min-h-0 w-full flex-1">{children}</div>
</div>
</div>
);
}

View File

@@ -24,7 +24,7 @@ export default function Navigation() {
</Disclosure.Button>
<Disclosure.Panel className="flex flex-col text-zinc-400">
<ActiveLink
href="/newsfeed/following"
href="/app/newsfeed/following"
className="flex h-8 items-center gap-2.5 rounded-md px-2.5 text-sm font-medium hover:text-zinc-200"
activeClassName="dark:bg-zinc-900 dark:text-zinc-100 hover:dark:bg-zinc-800"
>
@@ -32,7 +32,7 @@ export default function Navigation() {
<span>Following</span>
</ActiveLink>
<ActiveLink
href="/newsfeed/circle"
href="/app/newsfeed/circle"
className="flex h-8 items-center gap-2.5 rounded-md px-2.5 text-sm font-medium hover:text-zinc-200"
activeClassName="dark:bg-zinc-900 dark:text-zinc-100 hover:dark:bg-zinc-800"
>

View File

@@ -1,82 +0,0 @@
import { NoteMetadata } from '@lume/shared/note/metadata';
import { ImagePreview } from '@lume/shared/note/preview/image';
import { VideoPreview } from '@lume/shared/note/preview/video';
import { NoteQuote } from '@lume/shared/note/quote';
import { UserExtend } from '@lume/shared/user/extend';
import { UserMention } from '@lume/shared/user/mention';
import destr from 'destr';
import { memo, useMemo } from 'react';
import reactStringReplace from 'react-string-replace';
export const NoteComment = memo(function NoteComment({ event }: { event: any }) {
const content = useMemo(() => {
let parsedContent = event.content;
// get data tags
const tags = destr(event.tags);
// handle urls
parsedContent = reactStringReplace(parsedContent, /(https?:\/\/\S+)/g, (match, i) => {
if (match.match(/\.(jpg|jpeg|gif|png|webp)$/i)) {
// image url
return <ImagePreview key={match + i} url={match} size="large" />;
} else if (match.match(/(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/i)) {
// youtube
return <VideoPreview key={match + i} url={match} />;
} else if (match.match(/\.(mp4|webm)$/i)) {
// video
return <VideoPreview key={match + i} url={match} />;
} else {
return (
<a key={match + i} href={match} target="_blank" rel="noreferrer">
{match}
</a>
);
}
});
// handle #-hashtags
parsedContent = reactStringReplace(parsedContent, /#(\w+)/g, (match, i) => (
<span key={match + i} className="cursor-pointer text-fuchsia-500">
#{match}
</span>
));
// handle mentions
if (tags.length > 0) {
parsedContent = reactStringReplace(parsedContent, /\#\[(\d+)\]/gm, (match, i) => {
if (tags[match][0] === 'p') {
// @-mentions
return <UserMention key={match + i} pubkey={tags[match][1]} />;
} else if (tags[match][0] === 'e') {
// note-quotes
return <NoteQuote key={match + i} id={tags[match][1]} />;
} else {
return;
}
});
}
return parsedContent;
}, [event.content, event.tags]);
return (
<div className="relative z-10 flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 px-3 py-5 hover:bg-black/20">
<div className="relative z-10 flex flex-col">
<UserExtend pubkey={event.pubkey} time={event.created_at} />
<div className="-mt-5 pl-[52px]">
<div className="flex flex-col gap-2">
<div className="prose prose-zinc max-w-none break-words text-[15px] leading-tight dark:prose-invert prose-p:m-0 prose-p:text-[15px] prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-img:m-0 prose-video:m-0">
{content}
</div>
</div>
</div>
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
<NoteMetadata
eventID={event.event_id}
eventPubkey={event.pubkey}
eventContent={event.content}
eventTime={event.created_at}
/>
</div>
</div>
</div>
);
});

View File

@@ -1,82 +0,0 @@
import { NoteMetadata } from '@lume/shared/note/metadata';
import { ImagePreview } from '@lume/shared/note/preview/image';
import { VideoPreview } from '@lume/shared/note/preview/video';
import { NoteQuote } from '@lume/shared/note/quote';
import { UserLarge } from '@lume/shared/user/large';
import { UserMention } from '@lume/shared/user/mention';
import destr from 'destr';
import { memo, useMemo } from 'react';
import reactStringReplace from 'react-string-replace';
export const NoteExtend = memo(function NoteExtend({ event }: { event: any }) {
const content = useMemo(() => {
let parsedContent = event.content;
// get data tags
const tags = destr(event.tags);
// handle urls
parsedContent = reactStringReplace(parsedContent, /(https?:\/\/\S+)/g, (match, i) => {
if (match.match(/\.(jpg|jpeg|gif|png|webp)$/i)) {
// image url
return <ImagePreview key={match + i} url={match} size="large" />;
} else if (match.match(/(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/i)) {
// youtube
return <VideoPreview key={match + i} url={match} />;
} else if (match.match(/\.(mp4|webm)$/i)) {
// video
return <VideoPreview key={match + i} url={match} />;
} else {
return (
<a key={match + i} href={match} target="_blank" rel="noreferrer">
{match}
</a>
);
}
});
// handle #-hashtags
parsedContent = reactStringReplace(parsedContent, /#(\w+)/g, (match, i) => (
<span key={match + i} className="cursor-pointer text-fuchsia-500">
#{match}
</span>
));
// handle mentions
if (tags.length > 0) {
parsedContent = reactStringReplace(parsedContent, /\#\[(\d+)\]/gm, (match, i) => {
if (tags[match][0] === 'p') {
// @-mentions
return <UserMention key={match + i} pubkey={tags[match][1]} />;
} else if (tags[match][0] === 'e') {
// note-quotes
return <NoteQuote key={match + i} id={tags[match][1]} />;
} else {
return;
}
});
}
return parsedContent;
}, [event.content, event.tags]);
return (
<div className="relative z-10 flex h-min min-h-min w-full select-text flex-col">
<div className="relative z-10 flex flex-col">
<UserLarge pubkey={event.pubkey} time={event.created_at} />
<div className="mt-2">
<div className="flex flex-col gap-2">
<div className="prose prose-zinc max-w-none break-words text-[15px] leading-tight dark:prose-invert prose-p:m-0 prose-p:text-[15px] prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-img:m-0 prose-video:m-0">
{content}
</div>
</div>
</div>
<div className="mt-5 flex items-center border-b border-t border-zinc-800 py-2">
<NoteMetadata
eventID={event.event_id}
eventPubkey={event.pubkey}
eventContent={event.content}
eventTime={event.created_at}
/>
</div>
</div>
</div>
);
});

View File

@@ -0,0 +1,15 @@
import { getActiveAccount } from '@lume/utils/storage';
import useSWR from 'swr';
const fetcher = () => getActiveAccount();
export const useActiveAccount = () => {
const { data, error, isLoading } = useSWR('activeAcount', fetcher);
return {
account: data,
isLoading,
isError: error,
};
};

View File

@@ -1,7 +1,7 @@
import destr from 'destr';
// convert NIP-02 to array of pubkey
export const nip02ToArray = (tags: string[]) => {
export const nip02ToArray = (tags: any) => {
const arr = [];
tags.forEach((item) => {
arr.push(item[1]);