partial support replaceable event

This commit is contained in:
Ren Amamiya 2023-09-16 16:06:01 +07:00
parent 11ad281d72
commit 1206486016
5 changed files with 144 additions and 102 deletions

View File

@ -1,7 +1,7 @@
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
import { writeText } from '@tauri-apps/api/clipboard'; import { writeText } from '@tauri-apps/api/clipboard';
import { nip19 } from 'nostr-tools'; import { nip19 } from 'nostr-tools';
import { EventPointer } from 'nostr-tools/lib/nip19'; import { AddressPointer, EventPointer } from 'nostr-tools/lib/nip19';
import { useRef, useState } from 'react'; import { useRef, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
@ -27,7 +27,9 @@ export function ArticleNoteScreen() {
const { id } = useParams(); const { id } = useParams();
const { db } = useStorage(); const { db } = useStorage();
const { status, data } = useEvent(id);
const naddr = id.startsWith('naddr') ? (nip19.decode(id).data as AddressPointer) : null;
const { status, data } = useEvent(id, naddr);
const [isCopy, setIsCopy] = useState(false); const [isCopy, setIsCopy] = useState(false);
@ -103,15 +105,15 @@ export function ArticleNoteScreen() {
<ThreadUser pubkey={data.pubkey} time={data.created_at} /> <ThreadUser pubkey={data.pubkey} time={data.created_at} />
<div className="mt-2">{renderKind(data)}</div> <div className="mt-2">{renderKind(data)}</div>
<div> <div>
<NoteActions id={id} pubkey={data.pubkey} extraButtons={false} /> <NoteActions id={data.id} pubkey={data.pubkey} extraButtons={false} />
<NoteStats id={id} /> <NoteStats id={data.id} />
</div> </div>
</div> </div>
</div> </div>
)} )}
<div ref={replyRef} className="px-3"> <div ref={replyRef} className="px-3">
<NoteReplyForm id={id} pubkey={db.account.pubkey} /> <NoteReplyForm id={data?.id} pubkey={db.account.pubkey} />
<RepliesList id={id} /> <RepliesList id={data?.id} />
</div> </div>
</div> </div>
<div className="col-span-1" /> <div className="col-span-1" />

View File

@ -1,89 +1,24 @@
import { Link } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { ArrowRightIcon } from '@shared/icons'; import { ArrowRightIcon } from '@shared/icons';
import { TitleBar } from '@shared/titleBar'; import { TitleBar } from '@shared/titleBar';
import { useResources } from '@stores/resources';
import { Widget } from '@utils/types'; import { Widget } from '@utils/types';
const resources = [
{
title: 'The Basics (provide by nostr.com)',
data: [
{
title: 'What is Nostr?',
image: '',
url: '',
},
{
title: 'Understanding keys',
image: '',
url: '',
},
{
title: "What's a client?",
image: '',
url: '',
},
{
title: 'What are relays?',
image: '',
url: '',
},
{
title: 'What is an event?',
image: '',
url: '',
},
{
title: 'How to help Nostr?',
image: '',
url: '',
},
],
},
{
title: 'Lume Tutorials',
data: [
{
title: 'How to use widget?',
image: '',
url: '',
},
{
title: 'How to send a post?',
image: '',
url: '',
},
{
title: 'How to find more people?',
image: '',
url: '',
},
{
title: 'How to edit your profile?',
image: '',
url: '',
},
{
title: 'How to use focus mode?',
image: '',
url: '',
},
{
title: 'Report an issue',
image: '',
url: '',
},
{
title: 'How to support Lume',
image: '',
url: '',
},
],
},
];
export function LearnNostrWidget({ params }: { params: Widget }) { export function LearnNostrWidget({ params }: { params: Widget }) {
const navigate = useNavigate();
const readResource = useResources((state) => state.readResource);
const resources = useResources((state) => state.resources);
const readed = useResources((state) => state.readed);
const open = (naddr: string) => {
readResource(naddr);
navigate(`/notes/article/${naddr}`);
};
return ( return (
<div className="relative shrink-0 grow-0 basis-[400px] bg-white/10 backdrop-blur-xl"> <div className="relative shrink-0 grow-0 basis-[400px] bg-white/10 backdrop-blur-xl">
<TitleBar id={params.id} title="The Joy of Nostr" /> <TitleBar id={params.id} title="The Joy of Nostr" />
@ -92,24 +27,37 @@ export function LearnNostrWidget({ params }: { params: Widget }) {
<div key={index} className="mb-6"> <div key={index} className="mb-6">
<h3 className="mb-2 font-medium text-white/50">{resource.title}</h3> <h3 className="mb-2 font-medium text-white/50">{resource.title}</h3>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
{resource.data.map((item, index) => ( {resource.data.length ? (
<Link resource.data.map((item, index) => (
key={index} <button
to={`/notes/articles/${item.url}`} key={index}
className="flex items-center justify-between rounded-xl bg-white/10 px-3 py-3 hover:bg-white/20" type="button"
> onClick={() => open(item.id)}
<div className="inline-flex items-center gap-2.5"> className="flex items-center justify-between rounded-xl bg-white/10 px-3 py-3 hover:bg-white/20"
<div className="h-10 w-10 shrink-0 rounded-md bg-white/10" /> >
<div className="flex flex-col gap-1"> <div className="inline-flex items-center gap-2.5">
<h5 className="font-semibold leading-none">{item.title}</h5> <div className="h-10 w-10 shrink-0 rounded-md bg-white/10" />
<p className="text-sm leading-none text-white/70">Unread</p> <div className="flex flex-col items-start gap-1">
<h5 className="font-semibold leading-none">{item.title}</h5>
{readed.has(item.id) ? (
<p className="text-sm leading-none text-green-500">Readed</p>
) : (
<p className="text-sm leading-none text-white/70">Unread</p>
)}
</div>
</div> </div>
</div> <button type="button">
<button type="button"> <ArrowRightIcon className="h-5 w-5 text-white" />
<ArrowRightIcon className="h-5 w-5 text-white" /> </button>
</button> </button>
</Link> ))
))} ) : (
<div className="flex h-14 items-center justify-center rounded-xl bg-white/10 px-3 py-3">
<p className="text-sm font-medium text-white">
More resources are coming, stay tuned.
</p>
</div>
)}
</div> </div>
</div> </div>
))} ))}

68
src/stores/resources.ts Normal file
View File

@ -0,0 +1,68 @@
import { create } from 'zustand';
import { createJSONStorage, persist } from 'zustand/middleware';
import { Resources } from '@utils/types';
const DEFAULT_RESOURCES: Array<Resources> = [
{
title: 'The Basics (provide by nostr.com)',
data: [
{
id: 'naddr1qqxnzd3exsurgwfnxgcnjve5qgsym7p8qvs805ny3z3vausedzzwnwk27cfe67r69nrxpqe8w0urmegrqsqqqa283wgxe0',
title: 'What is Nostr?',
image: '',
},
{
id: 'naddr1qqxnzd3exsurgwf48qcnvdfcqgsym7p8qvs805ny3z3vausedzzwnwk27cfe67r69nrxpqe8w0urmegrqsqqqa28cnv0yt',
title: 'Understanding keys',
image: '',
},
{
id: 'naddr1qqxnzd3exsurgwfcxgcrzwfjqgsym7p8qvs805ny3z3vausedzzwnwk27cfe67r69nrxpqe8w0urmegrqsqqqa28uccw5e',
title: "What's a client?",
image: '',
},
{
id: 'naddr1qqxnzd3exsurgwfexqersdp5qgsym7p8qvs805ny3z3vausedzzwnwk27cfe67r69nrxpqe8w0urmegrqsqqqa28jvlesq',
title: 'What are relays?',
image: '',
},
{
id: 'naddr1qqxnzd3exsur2vpjxserjveeqgsym7p8qvs805ny3z3vausedzzwnwk27cfe67r69nrxpqe8w0urmegrqsqqqa28rqy7mx',
title: 'What is an event?',
image: '',
},
{
id: 'naddr1qqxnzd3exsur2vp5xsmnywpnqgsym7p8qvs805ny3z3vausedzzwnwk27cfe67r69nrxpqe8w0urmegrqsqqqa28hxwx4e',
title: 'How to help Nostr?',
image: '',
},
],
},
{
title: 'Lume Tutorials',
data: [],
},
];
interface ResourceState {
resources: Array<Resources>;
readed: Set<string>;
readResource: (id: string) => void;
}
export const useResources = create<ResourceState>()(
persist(
(set) => ({
resources: DEFAULT_RESOURCES,
readed: new Set(),
readResource: (id: string) => {
set((state) => ({ readed: new Set(state.readed).add(id) }));
},
}),
{
name: 'resources',
storage: createJSONStorage(() => localStorage),
}
)
);

View File

@ -1,22 +1,35 @@
import { NDKEvent } from '@nostr-dev-kit/ndk'; import { NDKEvent } from '@nostr-dev-kit/ndk';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { AddressPointer } from 'nostr-tools/lib/nip19';
import { useNDK } from '@libs/ndk/provider'; import { useNDK } from '@libs/ndk/provider';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
import { toRawEvent } from '@utils/rawEvent'; import { toRawEvent } from '@utils/rawEvent';
export function useEvent(id: string, embed?: string) { export function useEvent(id: string, naddr?: AddressPointer, embed?: string) {
const { db } = useStorage(); const { db } = useStorage();
const { ndk } = useNDK(); const { ndk } = useNDK();
const { status, data } = useQuery( const { status, data } = useQuery(
['event', id], ['event', id],
async () => { async () => {
if (naddr) {
const rEvents = await ndk.fetchEvents({
kinds: [naddr.kind],
'#d': [naddr.identifier],
authors: [naddr.pubkey],
});
const rEvent = [...rEvents].slice(-1)[0];
return rEvent;
}
// return embed event (nostr.band api) or repost // return embed event (nostr.band api) or repost
if (embed) { if (embed) {
const event: NDKEvent = JSON.parse(embed); const event: NDKEvent = JSON.parse(embed);
return event; return event;
} }
// get event from db // get event from db
const dbEvent = await db.getEventByID(id); const dbEvent = await db.getEventByID(id);
if (dbEvent) return dbEvent; if (dbEvent) return dbEvent;

11
src/utils/types.d.ts vendored
View File

@ -111,3 +111,14 @@ export interface NostrBuildResponse extends Response {
}>; }>;
}; };
} }
export interface Resource {
id: string;
title: string;
image: string;
}
export interface Resources {
title: string;
data: Array<Resource>;
}