This commit is contained in:
2023-10-18 14:49:20 +07:00
parent 489ab6bd0b
commit 939a72f945
47 changed files with 389 additions and 293 deletions

View File

@@ -46,7 +46,7 @@ export function FollowList() {
<User key={item} pubkey={item} variant="stacked" />
))}
{data.length > 16 ? (
<div className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-neutral-200 text-neutral-900 ring-1 ring-neutral-300 dark:bg-neutral-800 dark:text-neutral-100 dark:ring-neutral-700">
<div className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-neutral-200 text-neutral-900 ring-1 ring-neutral-200 dark:bg-neutral-800 dark:text-neutral-100 dark:ring-neutral-800">
<span className="text-xs font-medium">+{data.length}</span>
</div>
) : null}

View File

@@ -1,3 +1,4 @@
import { readText } from '@tauri-apps/plugin-clipboard-manager';
import { motion } from 'framer-motion';
import { nip19 } from 'nostr-tools';
import { useState } from 'react';
@@ -46,6 +47,11 @@ export function ImportAccountScreen() {
}
};
const pasteNsec = async () => {
const tempNsec = await readText();
setNsec(tempNsec);
};
const submitNsec = async () => {
if (savedPrivkey) return;
if (nsec.length > 50 && nsec.startsWith('nsec1')) {
@@ -61,7 +67,7 @@ export function ImportAccountScreen() {
return (
<div className="relative flex h-full w-full items-center justify-center">
<div className="absolute left-[8px] top-4">
<div className="absolute left-[8px] top-2">
{!created ? (
<button
onClick={() => navigate(-1)}
@@ -159,30 +165,45 @@ export function ImportAccountScreen() {
Enter your private key (optional):
</label>
<div className="inline-flex w-full items-center gap-2">
<input
name="nsec"
type="text"
value={nsec}
onChange={(e) => setNsec(e.target.value)}
spellCheck={false}
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
placeholder="nsec1"
className="h-11 flex-1 rounded-lg bg-neutral-200 px-3 placeholder:text-neutral-500 dark:bg-neutral-800 dark:placeholder:text-neutral-400"
/>
<button
type="button"
onClick={submitNsec}
className={twMerge(
'h-11 w-24 shrink-0 rounded-lg font-semibold text-white',
!savedPrivkey
? 'bg-blue-500 hover:bg-blue-600'
: 'bg-teal-500 hover:bg-teal-600'
)}
>
{savedPrivkey ? 'Saved' : 'Save'}
</button>
<div className="relative flex-1">
<input
name="nsec"
type="text"
value={nsec}
onChange={(e) => setNsec(e.target.value)}
spellCheck={false}
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
placeholder="nsec1"
className="h-11 w-full rounded-lg bg-neutral-200 px-3 placeholder:text-neutral-500 dark:bg-neutral-800 dark:placeholder:text-neutral-400"
/>
{nsec.length < 5 ? (
<div className="absolute right-0 top-0 inline-flex h-11 items-center justify-center px-2">
<button
type="button"
onClick={pasteNsec}
className="rounded-md bg-neutral-300 px-2 py-1 text-sm font-medium hover:bg-neutral-400 dark:bg-neutral-700 dark:hover:bg-neutral-600"
>
Paste
</button>
</div>
) : null}
</div>
{nsec.length > 5 ? (
<button
type="button"
onClick={submitNsec}
className={twMerge(
'h-11 w-24 shrink-0 rounded-lg font-semibold text-white',
!savedPrivkey
? 'bg-blue-500 hover:bg-blue-600'
: 'bg-teal-500 hover:bg-teal-600'
)}
>
{savedPrivkey ? 'Saved' : 'Save'}
</button>
) : null}
</div>
</div>
<div className="mt-3 select-text">
@@ -195,12 +216,14 @@ export function ImportAccountScreen() {
1. In case you store private key in Lume
</h5>
<p className="text-sm">
Lume will put your nsec to{' '}
{db.platform === 'macos'
? 'Apple Keychain (macOS)'
: db.platform === 'windows'
? 'Credential Manager (Windows)'
: 'Secret Service (Linux)'}
Lume will put your private key to{' '}
<b>
{db.platform === 'macos'
? 'Apple Keychain (macOS)'
: db.platform === 'windows'
? 'Credential Manager (Windows)'
: 'Secret Service (Linux)'}
</b>
, it will be secured by your OS
</p>
<h5 className="mt-2 font-semibold">

View File

@@ -29,12 +29,7 @@ export function ChatScreen() {
const renderItem = useCallback(
(message: NDKEvent) => {
return (
<ChatMessage
message={message}
userPubkey={db.account.pubkey}
userPrivkey={''}
self={message.pubkey === db.account.pubkey}
/>
<ChatMessage message={message} self={message.pubkey === db.account.pubkey} />
);
},
[data]
@@ -91,17 +86,12 @@ export function ChatScreen() {
</p>
</div>
) : (
<VList
ref={listRef}
className="h-full scrollbar-none"
mode="reverse"
shift={true}
>
<VList ref={listRef} className="h-full scrollbar-none" shift={true} reverse>
{data.map((message) => renderItem(message))}
</VList>
)}
</div>
<div className="shrink-0 rounded-b-lg border-t border-neutral-300 bg-neutral-200 p-3 backdrop-blur-xl dark:border-neutral-700 dark:bg-neutral-800">
<div className="shrink-0 rounded-b-lg border-t border-neutral-300 bg-neutral-200 p-3 dark:border-neutral-700 dark:bg-neutral-800">
<ChatForm
receiverPubkey={pubkey}
userPubkey={db.account.pubkey}

View File

@@ -57,12 +57,12 @@ export function ChatForm({
autoCorrect="off"
autoCapitalize="off"
placeholder="Message"
className="h-10 flex-1 resize-none bg-transparent px-3 text-neutral-900 placeholder:text-neutral-500 focus:outline-none dark:text-neutral-100 dark:placeholder:text-neutral-300"
className="h-10 flex-1 resize-none bg-transparent px-3 text-neutral-900 placeholder:text-neutral-600 focus:outline-none dark:text-neutral-100 dark:placeholder:text-neutral-300"
/>
<button
type="button"
onClick={submit}
className="inline-flex shrink-0 items-center gap-1.5 text-sm font-medium text-neutral-500 dark:text-neutral-300"
className="inline-flex shrink-0 items-center gap-1.5 text-sm font-medium text-neutral-600 dark:text-neutral-300"
>
<EnterIcon className="h-5 w-5" />
Send

View File

@@ -29,7 +29,7 @@ export function MediaUploader({
<button
type="button"
onClick={() => uploadMedia()}
className="group inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-neutral-300 text-neutral-500 hover:bg-neutral-400 dark:bg-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-600"
className="group inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-neutral-300 text-neutral-600 hover:bg-neutral-400 dark:bg-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-600"
>
{loading ? (
<LoaderIcon className="h-4 w-4 animate-spin" />

View File

@@ -7,18 +7,8 @@ import { ImagePreview, LinkPreview, MentionNote, VideoPreview } from '@shared/no
import { parser } from '@utils/parser';
export function ChatMessage({
message,
userPubkey,
userPrivkey,
self,
}: {
message: NDKEvent;
userPubkey: string;
userPrivkey: string;
self: boolean;
}) {
const decryptedContent = useDecryptMessage(message, userPubkey, userPrivkey);
export function ChatMessage({ message, self }: { message: NDKEvent; self: boolean }) {
const decryptedContent = useDecryptMessage(message);
const richContent = parser(decryptedContent) ?? null;
return (
@@ -26,17 +16,15 @@ export function ChatMessage({
className={twMerge(
'my-2 w-max max-w-[400px] rounded-t-xl px-3 py-3',
self
? 'ml-auto rounded-l-xl bg-blue-500'
: 'rounded-r-xl bg-neutral-200 dark:bg-neutral-800'
? 'ml-auto rounded-l-xl bg-blue-500 text-white'
: 'rounded-r-xl bg-neutral-200 text-neutral-900 dark:bg-neutral-800 dark:text-neutral-100'
)}
>
{!richContent ? (
<p className="text-neutral-900 dark:text-neutral-100">Decrypting...</p>
<p>Decrypting...</p>
) : (
<div>
<p className="select-text whitespace-pre-line text-neutral-900 dark:text-neutral-100">
{richContent.parsed}
</p>
<p className="select-text whitespace-pre-line">{richContent.parsed}</p>
<div>
{richContent.images.length > 0 && <ImagePreview urls={richContent.images} />}
{richContent.videos.length > 0 && <VideoPreview urls={richContent.videos} />}

View File

@@ -11,7 +11,7 @@ export function useDecryptMessage(message: NDKEvent) {
useEffect(() => {
async function decryptContent() {
try {
const privkey = await db.secureLoad();
const privkey = await db.secureLoad(db.account.pubkey);
const sender =
db.account.pubkey === message.pubkey
? message.tags.find((el) => el[0] === 'p')[1]

View File

@@ -36,9 +36,9 @@ export function ChatsScreen() {
<div className="col-span-1 h-full overflow-y-auto border-r border-neutral-200 scrollbar-none dark:border-neutral-800">
<div
data-tauri-drag-region
className="flex h-11 w-full shrink-0 items-center border-b border-white/5 px-3"
className="flex h-11 w-full shrink-0 items-center border-b border-neutral-200 px-3 dark:border-neutral-800"
>
<h3 className="bg-gradient-to-r from-blue-400 via-red-400 to-orange-500 bg-clip-text font-semibold text-transparent dark:from-blue-200 dark:via-red-200 dark:to-orange-300">
<h3 className="font-semibold text-neutral-950 dark:text-neutral-50">
All chats
</h3>
</div>

View File

@@ -55,19 +55,21 @@ export function UserLatestPosts({ pubkey }: { pubkey: string }) {
);
return (
<div className="mt-4 border-t border-white/5 pt-3">
<h3 className="mb-4 px-3 font-semibold text-white">Latest post</h3>
<div className="mt-4 border-t border-neutral-300 pt-3 dark:border-neutral-700">
<h3 className="mb-4 px-3 text-lg font-semibold text-neutral-900 dark:text-neutral-100">
Latest post
</h3>
<div>
{status === 'loading' ? (
<div className="px-3">
<div className="inline-flex h-16 w-full items-center justify-center gap-1.5 rounded-lg bg-white/10 text-sm font-medium text-white/70">
<LoaderIcon className="h-4 w-4 animate-spin text-white" />
<div className="inline-flex h-16 w-full items-center justify-center gap-1.5 rounded-lg bg-neutral-300 text-sm font-medium dark:bg-neutral-700">
<LoaderIcon className="h-4 w-4 animate-spin" />
Loading latest posts...
</div>
</div>
) : data.length < 1 ? (
<div className="px-3">
<div className="inline-flex h-16 w-full items-center justify-center rounded-lg bg-white/10 text-sm font-medium text-white/70">
<div className="inline-flex h-16 w-full items-center justify-center rounded-lg bg-neutral-300 text-sm font-medium dark:bg-neutral-700">
No posts from 24 hours ago
</div>
</div>

View File

@@ -4,7 +4,6 @@ import { Link } from 'react-router-dom';
import { useStorage } from '@libs/storage/provider';
import { Image } from '@shared/image';
import { NIP05 } from '@shared/nip05';
import { TextNote } from '@shared/notes';
import { User } from '@shared/user';
@@ -61,7 +60,7 @@ export const UserWithDrawer = memo(function UserWithDrawer({
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Content className="fixed right-0 top-0 z-50 flex h-full w-[400px] animate-slideRightAndFade items-center justify-center px-4 pb-4 pt-16 transition-all">
<div className="h-full w-full overflow-y-auto rounded-lg border-t border-white/10 bg-white/20 py-3 backdrop-blur-3xl">
<div className="h-full w-full overflow-y-auto rounded-lg border border-neutral-300 bg-neutral-200 py-3 dark:border-neutral-700 dark:bg-neutral-800">
{status === 'loading' ? (
<div>
<p>Loading...</p>
@@ -69,35 +68,40 @@ export const UserWithDrawer = memo(function UserWithDrawer({
) : (
<>
<div className="flex flex-col gap-3 px-3">
<Image
<img
src={user?.picture || user?.image}
alt={pubkey}
loading="lazy"
decoding="async"
style={{ contentVisibility: 'auto' }}
className="h-12 w-12 rounded-lg"
/>
<div className="flex flex-1 flex-col gap-2">
<div className="flex flex-col gap-1.5">
<h5 className="text-lg font-semibold leading-none">
{user?.displayName || user?.name || 'No name'}
</h5>
{user?.nip05 ? (
<NIP05
pubkey={pubkey}
nip05={user?.nip05}
className="max-w-[15rem] truncate text-sm leading-none text-white/50"
/>
) : (
<span className="max-w-[15rem] truncate text-sm leading-none text-white/50">
{displayNpub(pubkey, 16)}
</span>
)}
<div>
<h5 className="text-lg font-semibold">
{user?.name || user?.display_name || user?.displayName}
</h5>
{user?.nip05 ? (
<NIP05
pubkey={pubkey}
nip05={user?.nip05}
className="max-w-[15rem] truncate text-sm text-neutral-600 dark:text-neutral-400"
/>
) : (
<span className="max-w-[15rem] truncate text-sm text-neutral-600 dark:text-neutral-400">
{displayNpub(pubkey, 16)}
</span>
)}
</div>
{user?.about ? <TextNote content={user?.about} /> : null}
</div>
<div className="mt-3 inline-flex items-center gap-2">
<div className="inline-flex items-center gap-2">
{followed ? (
<button
type="button"
onClick={() => unfollowUser(pubkey)}
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-white/10 text-sm font-medium backdrop-blur-xl hover:bg-blue-600"
className="inline-flex h-9 w-36 items-center justify-center rounded-lg bg-neutral-300 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-700"
>
Unfollow
</button>
@@ -105,14 +109,14 @@ export const UserWithDrawer = memo(function UserWithDrawer({
<button
type="button"
onClick={() => followUser(pubkey)}
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-white/10 text-sm font-medium backdrop-blur-xl hover:bg-blue-600"
className="inline-flex h-9 w-36 items-center justify-center rounded-lg bg-neutral-300 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-700"
>
Follow
</button>
)}
<Link
to={`/chats/${pubkey}`}
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-white/10 text-sm font-medium backdrop-blur-xl hover:bg-blue-600"
className="inline-flex h-9 w-36 items-center justify-center rounded-lg bg-neutral-300 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-700"
>
Message
</Link>

View File

@@ -1,7 +1,7 @@
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
import { nip19 } from 'nostr-tools';
import { AddressPointer, EventPointer } from 'nostr-tools/lib/nip19';
import { AddressPointer, EventPointer } from 'nostr-tools/lib/types/nip19';
import { useRef, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
@@ -58,7 +58,7 @@ export function ArticleNoteScreen() {
};
return (
<div className="scrollbar-none h-full w-full overflow-y-auto scroll-smooth">
<div className="h-full w-full overflow-y-auto scroll-smooth scrollbar-none">
<div className="container mx-auto px-4 pt-16 sm:px-6 lg:px-8">
<div className="grid grid-cols-5">
<div className="col-span-1 pr-8">
@@ -66,20 +66,20 @@ export function ArticleNoteScreen() {
<button
type="button"
onClick={() => navigate(-1)}
className="inline-flex h-12 w-12 items-center justify-center rounded-xl border border-white/10 bg-white/5"
className="inline-flex h-12 w-12 items-center justify-center rounded-xl bg-neutral-200 dark:bg-neutral-800"
>
<ArrowLeftIcon className="h-5 w-5 text-white" />
<ArrowLeftIcon className="h-5 w-5" />
</button>
<div className="flex flex-col divide-y divide-white/5 rounded-xl border border-white/10 bg-white/5">
<div className="flex flex-col divide-y divide-neutral-300 rounded-xl bg-neutral-200 dark:divide-neutral-700 dark:bg-neutral-800">
<button
type="button"
onClick={share}
className="sticky top-16 inline-flex h-12 w-12 items-center justify-center rounded-t-xl "
className="sticky top-16 inline-flex h-12 w-12 items-center justify-center rounded-t-xl"
>
{isCopy ? (
<CheckCircleIcon className="h-5 w-5 text-green-500" />
<CheckCircleIcon className="h-5 w-5 text-teal-500" />
) : (
<ShareIcon className="h-5 w-5 text-white" />
<ShareIcon className="h-5 w-5" />
)}
</button>
<button
@@ -87,7 +87,7 @@ export function ArticleNoteScreen() {
onClick={scrollToReply}
className="sticky top-16 inline-flex h-12 w-12 items-center justify-center rounded-b-xl"
>
<ReplyIcon className="h-5 w-5 text-white" />
<ReplyIcon className="h-5 w-5" />
</button>
</div>
</div>
@@ -101,19 +101,19 @@ export function ArticleNoteScreen() {
</div>
) : (
<>
<div className="h-min w-full px-3">
<div className="rounded-xl border-t border-white/10 bg-white/20 px-3 pt-3">
<div className="flex h-min w-full flex-col gap-1 px-3">
<div>
<User pubkey={data.pubkey} time={data.created_at} variant="thread" />
<div className="mt-2">{renderKind(data)}</div>
<div>
<div className="mb-3 mt-3">
<NoteActions
id={data.id}
pubkey={data.pubkey}
extraButtons={false}
/>
<NoteStats id={data.id} />
</div>
</div>
<NoteStats id={data.id} />
</div>
<div ref={replyRef} className="px-3">
<NoteReplyForm id={data.id} pubkey={db.account.pubkey} />

View File

@@ -1,7 +1,7 @@
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
import { nip19 } from 'nostr-tools';
import { EventPointer } from 'nostr-tools/lib/nip19';
import { EventPointer } from 'nostr-tools/lib/types/nip19';
import { useRef, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
@@ -18,7 +18,6 @@ import {
UnknownNote,
} from '@shared/notes';
import { RepliesList } from '@shared/notes/replies/list';
import { NoteSkeleton } from '@shared/notes/skeleton';
import { User } from '@shared/user';
import { useEvent } from '@utils/hooks/useEvent';
@@ -28,6 +27,9 @@ export function TextNoteScreen() {
const replyRef = useRef(null);
const { id } = useParams();
console.log(id);
const { db } = useStorage();
const { status, data } = useEvent(id);
@@ -62,7 +64,7 @@ export function TextNoteScreen() {
};
return (
<div className="scrollbar-none h-full w-full overflow-y-auto scroll-smooth">
<div className="h-full w-full overflow-y-auto scroll-smooth scrollbar-none">
<div className="container mx-auto px-4 pt-16 sm:px-6 lg:px-8">
<div className="grid grid-cols-5">
<div className="col-span-1 pr-8">
@@ -70,20 +72,20 @@ export function TextNoteScreen() {
<button
type="button"
onClick={() => navigate(-1)}
className="inline-flex h-12 w-12 items-center justify-center rounded-xl border border-white/10 bg-white/5"
className="inline-flex h-12 w-12 items-center justify-center rounded-xl bg-neutral-200 dark:bg-neutral-800"
>
<ArrowLeftIcon className="h-5 w-5 text-white" />
<ArrowLeftIcon className="h-5 w-5" />
</button>
<div className="flex flex-col divide-y divide-white/5 rounded-xl border border-white/10 bg-white/5">
<div className="flex flex-col divide-y divide-neutral-300 rounded-xl bg-neutral-200 dark:divide-neutral-700 dark:bg-neutral-800">
<button
type="button"
onClick={share}
className="sticky top-16 inline-flex h-12 w-12 items-center justify-center rounded-t-xl "
className="sticky top-16 inline-flex h-12 w-12 items-center justify-center rounded-t-xl"
>
{isCopy ? (
<CheckCircleIcon className="h-5 w-5 text-green-500" />
<CheckCircleIcon className="h-5 w-5 text-teal-500" />
) : (
<ShareIcon className="h-5 w-5 text-white" />
<ShareIcon className="h-5 w-5" />
)}
</button>
<button
@@ -91,7 +93,7 @@ export function TextNoteScreen() {
onClick={scrollToReply}
className="sticky top-16 inline-flex h-12 w-12 items-center justify-center rounded-b-xl"
>
<ReplyIcon className="h-5 w-5 text-white" />
<ReplyIcon className="h-5 w-5" />
</button>
</div>
</div>
@@ -99,20 +101,20 @@ export function TextNoteScreen() {
<div className="col-span-3 flex flex-col gap-1.5">
{status === 'loading' ? (
<div className="px-3 py-1.5">
<div className="rounded-xl bg-white/10 px-3 py-3 backdrop-blur-xl">
<NoteSkeleton />
<div className="rounded-xl bg-neutral-100 px-3 py-3 dark:bg-neutral-900">
Loading...
</div>
</div>
) : (
<div className="h-min w-full px-3">
<div className="rounded-xl bg-white/10 px-3 pt-3 backdrop-blur-xl">
<div className="flex h-min w-full flex-col px-3">
<div className="rounded-xl bg-neutral-100 px-3 pt-3 dark:bg-neutral-900">
<User pubkey={data.pubkey} time={data.created_at} variant="thread" />
<div className="mt-2">{renderKind(data)}</div>
<div>
<div className="mb-3">
<NoteActions id={id} pubkey={data.pubkey} extraButtons={false} />
<NoteStats id={id} />
</div>
</div>
<NoteStats id={id} />
</div>
)}
<div ref={replyRef} className="px-3">

View File

@@ -56,24 +56,26 @@ export function NWCAlby() {
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
<div className="flex items-center justify-between">
<div className="inline-flex items-center gap-2.5">
<div className="inline-flex h-11 w-11 items-center justify-center rounded-md bg-orange-200">
<div className="inline-flex h-11 w-11 items-center justify-center rounded-md bg-neutral-200">
<AlbyIcon className="h-8 w-8" />
</div>
<div>
<h5 className="font-semibold leading-tight text-white">Alby</h5>
<p className="text-sm leading-tight text-white/50">Require alby account</p>
<h5 className="font-semibold text-neutral-900 dark:text-neutral-100">Alby</h5>
<p className="text-sm text-neutral-600 dark:text-neutral-400">
Require alby account
</p>
</div>
</div>
<Dialog.Trigger asChild>
<button
type="button"
className="inline-flex h-9 w-min items-center justify-center rounded-md border-t border-white/10 bg-white/20 px-3 text-sm font-medium text-white hover:bg-green-500"
className="inline-flex h-9 w-min items-center justify-center rounded-md bg-neutral-300 px-3 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-700"
>
Connect
</button>
</Dialog.Trigger>
</div>
<Dialog.Portal className="relative z-10">
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-2xl" />
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<div className="relative h-min w-full max-w-xl rounded-xl bg-white/10 backdrop-blur-xl">

View File

@@ -71,12 +71,14 @@ export function NWCOther() {
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
<div className="flex items-center justify-between pt-4">
<div className="inline-flex items-center gap-2.5">
<div className="inline-flex h-11 w-11 items-center justify-center rounded-md bg-white/10">
<div className="inline-flex h-11 w-11 items-center justify-center rounded-lg bg-neutral-200 dark:bg-neutral-700">
<WorldIcon className="h-5 w-5" />
</div>
<div>
<h5 className="font-semibold leading-tight text-white">URI String</h5>
<p className="text-sm leading-tight text-white/50">
<h5 className="font-semibold text-neutral-900 dark:text-neutral-100">
URI String
</h5>
<p className="text-sm text-neutral-600 dark:text-neutral-400">
Using format nostr+walletconnect:
</p>
</div>
@@ -84,14 +86,14 @@ export function NWCOther() {
<Dialog.Trigger asChild>
<button
type="button"
className="inline-flex h-9 w-min items-center justify-center rounded-md border-t border-white/10 bg-white/20 px-3 text-sm font-medium text-white hover:bg-green-500"
className="inline-flex h-9 w-min items-center justify-center rounded-md bg-neutral-300 px-3 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-700"
>
Connect
</button>
</Dialog.Trigger>
</div>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-2xl" />
<Dialog.Overlay className="fixed inset-0 z-50 bg-white dark:bg-black" />
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<div className="relative h-min w-full max-w-xl rounded-xl bg-white/10 backdrop-blur-xl">
<div className="h-min w-full shrink-0 rounded-t-xl border-b border-white/10 bg-white/5 px-5 py-5">

View File

@@ -1,36 +1,50 @@
import { useEffect, useState } from 'react';
import { NWCAlby } from '@app/nwc/components/alby';
import { NWCOther } from '@app/nwc/components/other';
import { useStorage } from '@libs/storage/provider';
import { CheckCircleIcon } from '@shared/icons';
export function NWCScreen() {
const walletConnectURL = 'test';
const { db } = useStorage();
const [walletConnectURL, setWalletConnectURL] = useState<null | string>(null);
const remove = async () => {
// setWalletConnectURL('');
// await db.secureSave('walletConnectURL', '', 'nwc');
await db.secureRemove('nwc');
setWalletConnectURL(null);
};
useEffect(() => {
async function getNWC() {
const nwc = await db.secureLoad('nwc');
if (nwc) setWalletConnectURL(nwc);
}
getNWC();
}, []);
return (
<div className="flex h-full w-full items-center justify-center bg-white/5">
<div className="flex h-full w-full items-center justify-center">
<div className="flex w-full flex-col gap-5">
<div className="text-center">
<h3 className="text-2xl font-bold leading-tight">
Nostr Wallet Connect (Beta)
</h3>
<p className="leading-tight text-white/70">
<p className="leading-tight text-neutral-600 dark:text-neutral-400">
Sending tips easily via Bitcoin Lightning.
</p>
</div>
<div className="mx-auto max-w-lg">
{!walletConnectURL ? (
<div className="flex w-full flex-col gap-4 divide-y divide-white/5 rounded-xl border-t border-white/10 bg-white/20 p-3">
<div className="flex w-full flex-col gap-4 divide-y divide-neutral-200 rounded-xl bg-neutral-100 p-3 dark:divide-neutral-800 dark:bg-neutral-900">
<NWCAlby />
<NWCOther />
</div>
) : (
<div className="flex w-full flex-col rounded-xl border-t border-white/10 bg-white/20 p-3">
<div className="mb-1 inline-flex items-center gap-1.5 text-sm text-green-500">
<div className="flex w-full flex-col rounded-xl bg-neutral-100 p-3 dark:bg-neutral-900">
<div className="mb-1 inline-flex items-center gap-1.5 text-sm text-teal-500">
<CheckCircleIcon className="h-4 w-4" />
<p>You&apos;re using nostr wallet connect</p>
</div>
@@ -38,12 +52,12 @@ export function NWCScreen() {
<textarea
readOnly
value={walletConnectURL.substring(0, 120) + '****'}
className="relative h-40 w-full resize-none rounded-lg bg-white/20 px-3 py-1 text-white !outline-none backdrop-blur-xl placeholder:text-white/50"
className="relative h-40 w-full resize-none rounded-lg bg-neutral-200 px-3 py-1 text-neutral-900 !outline-none placeholder:text-neutral-600 dark:bg-neutral-800 dark:text-neutral-100 dark:placeholder:text-neutral-400"
/>
<button
type="button"
onClick={() => remove()}
className="inline-flex h-11 w-full items-center justify-center gap-2 rounded-lg bg-white/10 px-6 font-medium leading-none text-red-500 hover:bg-white/20 focus:outline-none disabled:opacity-50"
className="inline-flex h-11 w-full items-center justify-center gap-2 rounded-lg bg-neutral-200 px-6 font-medium text-red-500 hover:bg-red-500 hover:text-white focus:outline-none dark:bg-neutral-800 dark:text-neutral-100"
>
Remove connection
</button>
@@ -52,17 +66,19 @@ export function NWCScreen() {
)}
<div className="mt-5 flex flex-col gap-4">
<div className="flex flex-col gap-1.5">
<h5 className="text-sm font-bold text-white">Introduction</h5>
<p className="text-sm text-white/70">
<h5 className="font-semibold text-neutral-900 dark:text-neutral-100">
Introduction
</h5>
<p className="text-sm text-neutral-600 dark:text-neutral-400">
Nostr Wallet Connect (NWC) is a way for applications like Nostr clients to
access a remote Lightning wallet through a standardized protocol.
</p>
<p className="text-sm text-white/70">
<p className="text-sm text-neutral-600 dark:text-neutral-400">
To learn more about the details have a look at{' '}
<a
href="https://github.com/getAlby/nips/blob/7-wallet-connect-patch/47.md"
target="_blank"
className="text-blue-300"
className="text-blue-500"
rel="noreferrer"
>
the specs (NIP47)
@@ -70,38 +86,42 @@ export function NWCScreen() {
</p>
</div>
<div className="flex flex-col gap-1.5">
<h5 className="text-sm font-bold text-white">About tipping</h5>
<p className="text-sm text-white/70">
<h5 className="font-semibold text-neutral-900 dark:text-neutral-100">
About tipping
</h5>
<p className="text-sm text-neutral-600 dark:text-neutral-400">
Also known as Zap in other Nostr client.
</p>
<p className="text-sm text-white/70">
<p className="text-sm text-neutral-600 dark:text-neutral-400">
Lume doesn&apos;t take any commission or platform fees when you tip
someone.
</p>
<p className="text-sm text-white/70">Lume doesn&apos;t hold your Bitcoin</p>
<p className="text-sm text-neutral-600 dark:text-neutral-400">
Lume doesn&apos;t hold your Bitcoin
</p>
</div>
<div className="flex flex-col gap-1.5">
<h5 className="text-sm font-bold text-white">
<h5 className="font-semibold text-neutral-900 dark:text-neutral-100">
Recommend wallet that support NWC
</h5>
<p className="text-sm text-white/70">
<p className="text-sm text-neutral-600 dark:text-neutral-400">
Mutiny Wallet:{' '}
<a
href="https://www.mutinywallet.com/"
target="_blank"
rel="noreferrer"
className="text-blue-300"
className="text-blue-500"
>
website
</a>
</p>
<p className="text-sm text-white/70">
<p className="text-sm text-neutral-600 dark:text-neutral-400">
Self hosted NWC on Umbrel :{' '}
<a
href="https://apps.umbrel.com/app/alby-nostr-wallet-connect"
target="_blank"
rel="noreferrer"
className="text-blue-300"
className="text-blue-500"
>
website
</a>

View File

@@ -53,9 +53,9 @@ export function RelayList() {
</div>
</div>
) : (
<VList className="mt-20 h-full scrollbar-none">
<VList className="h-full scrollbar-none">
<div className="inline-flex h-16 w-full items-center border-b border-neutral-100 px-3 dark:border-neutral-900">
<h3 className="bg-gradient-to-r from-blue-200 via-red-200 to-orange-300 bg-clip-text font-semibold text-transparent">
<h3 className="font-semibold text-neutral-950 dark:text-neutral-50">
All relays used by your follows
</h3>
</div>

View File

@@ -40,7 +40,7 @@ export function UserRelay() {
{relayUrls.includes(item) ? (
<span className="relative flex h-2 w-2">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"></span>
<span className="relative inline-flex h-2 w-2 rounded-full bg-green-500"></span>
<span className="relative inline-flex h-2 w-2 rounded-full bg-teal-500"></span>
</span>
) : (
<span className="relative flex h-2 w-2">

View File

@@ -25,23 +25,27 @@ export function RelayScreen() {
return (
<div className="grid h-full w-full grid-cols-3">
<div className="col-span-2 border-r border-white/5">
<div className="inline-flex h-16 w-full items-center gap-2.5 border-b border-white/5 px-3">
<div className="col-span-2 border-r border-neutral-100 dark:border-neutral-900">
<div className="inline-flex h-16 w-full items-center gap-2.5 border-b border-neutral-100 px-3 dark:border-neutral-900">
<button type="button" onClick={() => navigate(-1)}>
<ArrowLeftIcon className="h-5 w-5 text-white/70 hover:text-white" />
<ArrowLeftIcon className="h-5 w-5 text-neutral-500 hover:text-neutral-600 dark:text-neutral-600 dark:hover:text-neutral-500" />
</button>
<h3 className="font-semibold text-white">Global events</h3>
<h3 className="font-semibold text-neutral-950 dark:text-neutral-50">
Global events
</h3>
</div>
<RelayEventList relayUrl={url} />
</div>
<div className="col-span-1">
<div className="inline-flex h-16 w-full items-center border-b border-white/5 px-3">
<h3 className="font-semibold text-white">Information</h3>
<div className="inline-flex h-16 w-full items-center border-b border-neutral-100 px-3 dark:border-neutral-900">
<h3 className="font-semibold text-neutral-900 dark:text-neutral-100">
Information
</h3>
</div>
<div className="mt-4 px-3">
<Suspense
fallback={
<div className="flex items-center gap-2 text-sm font-medium text-white">
<div className="flex items-center gap-2 text-sm font-medium text-neutral-900 dark:text-neutral-100">
<LoaderIcon className="h-4 w-4 animate-spin" />
Loading...
</div>
@@ -58,24 +62,28 @@ export function RelayScreen() {
{(resolvedRelay) => (
<div className="flex flex-col gap-5">
<div>
<h3 className="font-semibold leading-tight text-white">
<h3 className="font-semibold leading-tight text-neutral-900 dark:text-neutral-100">
{resolvedRelay.name}
</h3>
<p className="text-sm font-medium text-white/70">
<p className="text-sm font-medium text-neutral-600 dark:text-neutral-500">
{resolvedRelay.description}
</p>
</div>
{resolvedRelay.pubkey ? (
<div className="flex flex-col gap-1">
<h5 className="text-sm font-semibold text-white/70">Owner:</h5>
<div className="w-full rounded-lg bg-white/10 px-2 py-2">
<h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400">
Owner:
</h5>
<div className="w-full rounded-lg bg-neutral-100 px-2 py-2 dark:bg-neutral-900">
<User pubkey={resolvedRelay.pubkey} variant="simple" />
</div>
</div>
) : null}
{resolvedRelay.contact ? (
<div>
<h5 className="text-sm font-semibold text-white/70">Contact:</h5>
<h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400">
Contact:
</h5>
<a
href={`mailto:${resolvedRelay.contact}`}
target="_blank"
@@ -87,7 +95,9 @@ export function RelayScreen() {
</div>
) : null}
<div>
<h5 className="text-sm font-semibold text-white/70">Software:</h5>
<h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400">
Software:
</h5>
<a
href={resolvedRelay.software}
target="_blank"
@@ -100,7 +110,7 @@ export function RelayScreen() {
</a>
</div>
<div>
<h5 className="text-sm font-semibold text-white/70">
<h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400">
Supported NIPs:
</h5>
<div className="mt-2 grid grid-cols-7 gap-2">
@@ -110,7 +120,7 @@ export function RelayScreen() {
href={`https://nips.be/${item}`}
target="_blank"
rel="noreferrer"
className="inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg bg-white/10 text-sm font-medium hover:bg-blue-600"
className="inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg bg-neutral-100 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-900"
>
{item}
</a>
@@ -119,7 +129,9 @@ export function RelayScreen() {
</div>
{resolvedRelay.limitation ? (
<div>
<h5 className="text-sm font-semibold text-white/70">Limitation</h5>
<h5 className="text-sm font-semibold text-neutral-600 dark:text-neutral-400">
Limitation
</h5>
<div className="flex flex-col gap-2 divide-y divide-white/5">
{Object.keys(resolvedRelay.limitation).map((key, index) => {
return (
@@ -127,10 +139,10 @@ export function RelayScreen() {
key={index}
className="flex items-baseline justify-between pt-2"
>
<p className="text-sm font-medium text-white">
<p className="text-sm font-medium text-neutral-900 dark:text-neutral-100">
{titleCase(key)}:
</p>
<p className="text-sm font-medium text-white/70">
<p className="text-sm font-medium text-neutral-600 dark:text-neutral-400">
{resolvedRelay.limitation[key].toString()}
</p>
</div>
@@ -149,7 +161,7 @@ export function RelayScreen() {
>
Open payment website
</a>
<span className="text-center text-xs text-white/70">
<span className="text-center text-xs text-neutral-600 dark:text-neutral-400">
You need to make a payment to connect this relay
</span>
</div>