diff --git a/src/routes/$account/_layout/chats.lazy.tsx b/src/routes/$account/_layout/chats.lazy.tsx
index 681a43d..f8175d4 100644
--- a/src/routes/$account/_layout/chats.lazy.tsx
+++ b/src/routes/$account/_layout/chats.lazy.tsx
@@ -1,505 +1,506 @@
-import { commands } from '@/commands'
-import { ago, cn } from '@/commons'
-import { Spinner } from '@/components/spinner'
-import { User } from '@/components/user'
+import { commands } from "@/commands";
+import { ago, cn } from "@/commons";
+import { Spinner } from "@/components/spinner";
+import { User } from "@/components/user";
import {
- ArrowRight,
- CaretDown,
- CirclesFour,
- Plus,
- X,
-} from '@phosphor-icons/react'
-import * as Dialog from '@radix-ui/react-dialog'
-import * as Progress from '@radix-ui/react-progress'
-import * as ScrollArea from '@radix-ui/react-scroll-area'
-import { useQuery } from '@tanstack/react-query'
-import { Link, Outlet, createLazyFileRoute } from '@tanstack/react-router'
-import { listen } from '@tauri-apps/api/event'
-import { Menu, MenuItem, PredefinedMenuItem } from '@tauri-apps/api/menu'
-import { readText, writeText } from '@tauri-apps/plugin-clipboard-manager'
-import { message } from '@tauri-apps/plugin-dialog'
-import { open } from '@tauri-apps/plugin-shell'
-import { type NostrEvent, nip19 } from 'nostr-tools'
-import { useCallback, useEffect, useRef, useState, useTransition } from 'react'
-import { Virtualizer } from 'virtua'
+ ArrowRight,
+ CaretDown,
+ CirclesFour,
+ Plus,
+ X,
+} from "@phosphor-icons/react";
+import * as Dialog from "@radix-ui/react-dialog";
+import * as Progress from "@radix-ui/react-progress";
+import * as ScrollArea from "@radix-ui/react-scroll-area";
+import { useQuery } from "@tanstack/react-query";
+import { Link, Outlet, createLazyFileRoute } from "@tanstack/react-router";
+import { listen } from "@tauri-apps/api/event";
+import { Menu, MenuItem, PredefinedMenuItem } from "@tauri-apps/api/menu";
+import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager";
+import { message } from "@tauri-apps/plugin-dialog";
+import { open } from "@tauri-apps/plugin-shell";
+import { type NostrEvent, nip19 } from "nostr-tools";
+import { useCallback, useEffect, useRef, useState, useTransition } from "react";
+import { Virtualizer } from "virtua";
type EventPayload = {
- event: string
- sender: string
-}
+ event: string;
+ sender: string;
+};
-export const Route = createLazyFileRoute('/$account/_layout/chats')({
- component: Screen,
-})
+export const Route = createLazyFileRoute("/$account/_layout/chats")({
+ component: Screen,
+});
function Screen() {
- return (
-
- )
+ return (
+
+ );
}
function Header() {
- const { platform } = Route.useRouteContext()
- const { account } = Route.useParams()
+ const { platform } = Route.useRouteContext();
+ const { account } = Route.useParams();
- return (
-
- )
+ return (
+
+ );
}
function ChatList() {
- const { account } = Route.useParams()
- const { queryClient } = Route.useRouteContext()
- const { isLoading, data } = useQuery({
- queryKey: ['chats'],
- queryFn: async () => {
- const res = await commands.getChats()
+ const { account } = Route.useParams();
+ const { queryClient } = Route.useRouteContext();
+ const { isLoading, data } = useQuery({
+ queryKey: ["chats"],
+ queryFn: async () => {
+ const res = await commands.getChats();
- if (res.status === 'ok') {
- const raw = res.data
- const events = raw.map((item) => JSON.parse(item) as NostrEvent)
+ if (res.status === "ok") {
+ const raw = res.data;
+ const events = raw.map((item) => JSON.parse(item) as NostrEvent);
- return events
- } else {
- throw new Error(res.error)
- }
- },
- select: (data) => data.sort((a, b) => b.created_at - a.created_at),
- refetchOnMount: false,
- refetchOnWindowFocus: false,
- })
+ return events;
+ } else {
+ throw new Error(res.error);
+ }
+ },
+ select: (data) => data.sort((a, b) => b.created_at - a.created_at),
+ refetchOnMount: false,
+ refetchOnWindowFocus: false,
+ });
- const [isSync, setIsSync] = useState(false)
- const [progress, setProgress] = useState(0)
+ const [isSync, setIsSync] = useState(false);
+ const [progress, setProgress] = useState(0);
- useEffect(() => {
- const timer = setInterval(
- () => setProgress((prev) => (prev <= 100 ? prev + 4 : 100)),
- 1200,
- )
- return () => clearInterval(timer)
- }, [])
+ useEffect(() => {
+ const timer = setInterval(
+ () => setProgress((prev) => (prev <= 100 ? prev + 4 : 100)),
+ 1200,
+ );
+ return () => clearInterval(timer);
+ }, []);
- useEffect(() => {
- const unlisten = listen('synchronized', async () => {
- await queryClient.refetchQueries({ queryKey: ['chats'] })
- setIsSync(true)
- })
+ useEffect(() => {
+ const unlisten = listen("synchronized", async () => {
+ await queryClient.refetchQueries({ queryKey: ["chats"] });
+ setIsSync(true);
+ });
- return () => {
- unlisten.then((f) => f())
- }
- }, [])
+ return () => {
+ unlisten.then((f) => f());
+ };
+ }, []);
- useEffect(() => {
- const unlisten = listen('event', async (data) => {
- const event: NostrEvent = JSON.parse(data.payload.event)
- const chats: NostrEvent[] = await queryClient.getQueryData(['chats'])
+ useEffect(() => {
+ const unlisten = listen("event", async (data) => {
+ const event: NostrEvent = JSON.parse(data.payload.event);
+ const chats: NostrEvent[] = await queryClient.getQueryData(["chats"]);
- if (chats) {
- const index = chats.findIndex((item) => item.pubkey === event.pubkey)
+ if (chats) {
+ const index = chats.findIndex((item) => item.pubkey === event.pubkey);
- if (index === -1) {
- await queryClient.setQueryData(
- ['chats'],
- (prevEvents: NostrEvent[]) => {
- if (!prevEvents) return prevEvents
- if (event.pubkey === account) return
+ if (index === -1) {
+ await queryClient.setQueryData(
+ ["chats"],
+ (prevEvents: NostrEvent[]) => {
+ if (!prevEvents) return prevEvents;
+ if (event.pubkey === account) return;
- return [event, ...prevEvents]
- },
- )
- } else {
- const newEvents = [...chats]
- newEvents[index] = {
- ...event,
- }
+ return [event, ...prevEvents];
+ },
+ );
+ } else {
+ const newEvents = [...chats];
+ newEvents[index] = {
+ ...event,
+ };
- await queryClient.setQueryData(['chats'], newEvents)
- }
- }
- })
+ await queryClient.setQueryData(["chats"], newEvents);
+ }
+ }
+ });
- return () => {
- unlisten.then((f) => f())
- }
- }, [])
+ return () => {
+ unlisten.then((f) => f());
+ };
+ }, []);
- return (
-
-
- {isLoading ? (
- <>
- {[...Array(5).keys()].map((i) => (
-
- ))}
- >
- ) : isSync && !data.length ? (
-
- ) : (
- data.map((item) => (
-
- {({ isActive, isTransitioning }) => (
-
-
-
-
-
-
-
- {account === item.pubkey ? '(you)' : ''}
-
-
- {isTransitioning ? (
-
- ) : (
-
- {ago(item.created_at)}
-
- )}
-
-
-
- )}
-
- ))
- )}
-
- {!isSync ? : null}
-
-
-
-
-
- )
+ return (
+
+
+ {isLoading ? (
+ <>
+ {[...Array(5).keys()].map((i) => (
+
+ ))}
+ >
+ ) : isSync && !data.length ? (
+
+ ) : (
+ data.map((item) => (
+
+ {({ isActive, isTransitioning }) => (
+
+
+
+
+
+
+
+ {account === item.pubkey ? "(you)" : ""}
+
+
+ {isTransitioning ? (
+
+ ) : (
+
+ {ago(item.created_at)}
+
+ )}
+
+
+
+ )}
+
+ ))
+ )}
+
+ {!isSync ? : null}
+
+
+
+
+
+ );
}
function SyncPopup({ progress }: { progress: number }) {
- return (
-
-
-
-
-
-
Syncing message...
-
-
- )
+ return (
+
+
+
+
+
+
+
Syncing message...
+
+
+ );
}
function Compose() {
- const [isOpen, setIsOpen] = useState(false)
- const [target, setTarget] = useState('')
- const [newMessage, setNewMessage] = useState('')
- const [isPending, startTransition] = useTransition()
+ const [isOpen, setIsOpen] = useState(false);
+ const [target, setTarget] = useState("");
+ const [newMessage, setNewMessage] = useState("");
+ const [isPending, startTransition] = useTransition();
- const { account } = Route.useParams()
- const { isLoading, data: contacts } = useQuery({
- queryKey: ['contacts', account],
- queryFn: async () => {
- const res = await commands.getContactList()
+ const { account } = Route.useParams();
+ const { isLoading, data: contacts } = useQuery({
+ queryKey: ["contacts", account],
+ queryFn: async () => {
+ const res = await commands.getContactList();
- if (res.status === 'ok') {
- return res.data
- } else {
- return []
- }
- },
- refetchOnWindowFocus: false,
- enabled: isOpen,
- })
+ if (res.status === "ok") {
+ return res.data;
+ } else {
+ return [];
+ }
+ },
+ refetchOnWindowFocus: false,
+ enabled: isOpen,
+ });
- const navigate = Route.useNavigate()
- const scrollRef = useRef(null)
+ const navigate = Route.useNavigate();
+ const scrollRef = useRef(null);
- const pasteFromClipboard = async () => {
- const val = await readText()
- setTarget(val)
- }
+ const pasteFromClipboard = async () => {
+ const val = await readText();
+ setTarget(val);
+ };
- const sendMessage = () => {
- startTransition(async () => {
- if (!newMessage.length) return
- if (!target.length) return
- if (!target.startsWith('npub1')) {
- await message('You must enter the public key as npub', {
- title: 'Send Message',
- kind: 'error',
- })
- return
- }
+ const sendMessage = () => {
+ startTransition(async () => {
+ if (!newMessage.length) return;
+ if (!target.length) return;
+ if (!target.startsWith("npub1")) {
+ await message("You must enter the public key as npub", {
+ title: "Send Message",
+ kind: "error",
+ });
+ return;
+ }
- const decoded = nip19.decode(target)
- let id: string
+ const decoded = nip19.decode(target);
+ let id: string;
- if (decoded.type !== 'npub') {
- await message('You must enter the public key as npub', {
- title: 'Send Message',
- kind: 'error',
- })
- return
- } else {
- id = decoded.data
- }
+ if (decoded.type !== "npub") {
+ await message("You must enter the public key as npub", {
+ title: "Send Message",
+ kind: "error",
+ });
+ return;
+ } else {
+ id = decoded.data;
+ }
- // Connect to user's inbox relays
- const connect = await commands.connectInboxRelays(target, false)
+ // Connect to user's inbox relays
+ const connect = await commands.connectInboxRelays(target, false);
- // Send message
- if (connect.status === 'ok') {
- const res = await commands.sendMessage(id, newMessage)
+ // Send message
+ if (connect.status === "ok") {
+ const res = await commands.sendMessage(id, newMessage);
- if (res.status === 'ok') {
- setTarget('')
- setNewMessage('')
- setIsOpen(false)
+ if (res.status === "ok") {
+ setTarget("");
+ setNewMessage("");
+ setIsOpen(false);
- navigate({
- to: '/$account/chats/$id',
- params: { account, id },
- })
- } else {
- await message(res.error, { title: 'Send Message', kind: 'error' })
- return
- }
- } else {
- await message(connect.error, {
- title: 'Connect Inbox Relays',
- kind: 'error',
- })
- return
- }
- })
- }
+ navigate({
+ to: "/$account/chats/$id",
+ params: { account, id },
+ });
+ } else {
+ await message(res.error, { title: "Send Message", kind: "error" });
+ return;
+ }
+ } else {
+ await message(connect.error, {
+ title: "Connect Inbox Relays",
+ kind: "error",
+ });
+ return;
+ }
+ });
+ };
- return (
-
-
-
-
-
-
-
-
-
- Send to
-
-
-
-
-
-
To:
-
- setTarget(e.target.value)}
- disabled={isPending}
- className="w-full pr-14 h-9 bg-transparent focus:outline-none placeholder:text-neutral-400 dark:placeholder:text-neutral-600"
- />
-
-
-
-
-
Message:
-
setNewMessage(e.target.value)}
- disabled={isPending}
- className="flex-1 h-9 bg-transparent focus:outline-none placeholder:text-neutral-400 dark:placeholder:text-neutral-600"
- />
-
-
-
-
-
-
- {isLoading ? (
-
-
-
- ) : !contacts?.length ? (
-
- ) : (
- contacts?.map((contact) => (
-
- ))
- )}
-
-
-
-
-
-
-
-
-
-
- )
+ return (
+
+
+
+
+
+
+
+
+
+ Send to
+
+
+
+
+
+
To:
+
+ setTarget(e.target.value)}
+ disabled={isPending}
+ className="w-full pr-14 h-9 bg-transparent focus:outline-none placeholder:text-neutral-400 dark:placeholder:text-neutral-600"
+ />
+
+
+
+
+
Message:
+
setNewMessage(e.target.value)}
+ disabled={isPending}
+ className="flex-1 h-9 bg-transparent focus:outline-none placeholder:text-neutral-400 dark:placeholder:text-neutral-600"
+ />
+
+
+
+
+
+
+ {isLoading ? (
+
+
+
+ ) : !contacts?.length ? (
+
+ ) : (
+ contacts?.map((contact) => (
+
+ ))
+ )}
+
+
+
+
+
+
+
+
+
+
+ );
}
function CurrentUser() {
- const params = Route.useParams()
- const navigate = Route.useNavigate()
+ const params = Route.useParams();
+ const navigate = Route.useNavigate();
- const showContextMenu = useCallback(async (e: React.MouseEvent) => {
- e.preventDefault()
+ const showContextMenu = useCallback(async (e: React.MouseEvent) => {
+ e.preventDefault();
- const menuItems = await Promise.all([
- MenuItem.new({
- text: 'Copy Public Key',
- action: async () => {
- const npub = nip19.npubEncode(params.account)
- await writeText(npub)
- },
- }),
- MenuItem.new({
- text: 'Settings',
- action: () => navigate({ to: '/' }),
- }),
- MenuItem.new({
- text: 'Feedback',
- action: async () => await open('https://github.com/lumehq/coop/issues'),
- }),
- PredefinedMenuItem.new({ item: 'Separator' }),
- MenuItem.new({
- text: 'Switch account',
- action: () => navigate({ to: '/' }),
- }),
- ])
+ const menuItems = await Promise.all([
+ MenuItem.new({
+ text: "Copy Public Key",
+ action: async () => {
+ const npub = nip19.npubEncode(params.account);
+ await writeText(npub);
+ },
+ }),
+ MenuItem.new({
+ text: "Settings",
+ action: () => navigate({ to: "/" }),
+ }),
+ MenuItem.new({
+ text: "Feedback",
+ action: async () => await open("https://github.com/lumehq/coop/issues"),
+ }),
+ PredefinedMenuItem.new({ item: "Separator" }),
+ MenuItem.new({
+ text: "Switch account",
+ action: () => navigate({ to: "/" }),
+ }),
+ ]);
- const menu = await Menu.new({
- items: menuItems,
- })
+ const menu = await Menu.new({
+ items: menuItems,
+ });
- await menu.popup().catch((e) => console.error(e))
- }, [])
+ await menu.popup().catch((e) => console.error(e));
+ }, []);
- return (
-
- )
+ return (
+
+ );
}