chore: remove signal

This commit is contained in:
reya 2023-12-22 14:49:00 +07:00
parent a882ead649
commit ee4e6b1ee6
9 changed files with 65 additions and 94 deletions

View File

@ -22,7 +22,6 @@
"@getalby/sdk": "^3.2.1", "@getalby/sdk": "^3.2.1",
"@nostr-dev-kit/ndk": "^2.3.1", "@nostr-dev-kit/ndk": "^2.3.1",
"@nostr-fetch/adapter-ndk": "^0.14.1", "@nostr-fetch/adapter-ndk": "^0.14.1",
"@preact/signals-react": "^2.0.0",
"@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-avatar": "^1.0.4",

View File

@ -17,9 +17,6 @@ dependencies:
'@nostr-fetch/adapter-ndk': '@nostr-fetch/adapter-ndk':
specifier: ^0.14.1 specifier: ^0.14.1
version: 0.14.1(@nostr-dev-kit/ndk@2.3.1)(nostr-fetch@0.14.1) version: 0.14.1(@nostr-dev-kit/ndk@2.3.1)(nostr-fetch@0.14.1)
'@preact/signals-react':
specifier: ^2.0.0
version: 2.0.0(react@18.2.0)
'@radix-ui/react-accordion': '@radix-ui/react-accordion':
specifier: ^1.1.2 specifier: ^1.1.2
version: 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) version: 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0)
@ -881,20 +878,6 @@ packages:
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
dev: false dev: false
/@preact/signals-core@1.5.1:
resolution: {integrity: sha512-dE6f+WCX5ZUDwXzUIWNMhhglmuLpqJhuy3X3xHrhZYI0Hm2LyQwOu0l9mdPiWrVNsE+Q7txOnJPgtIqHCYoBVA==}
dev: false
/@preact/signals-react@2.0.0(react@18.2.0):
resolution: {integrity: sha512-tMVi2SXFXlojaiPNWa8dlYaidR/XvEgMSp+iymKJgMssBM/QVtUQrodKZek1BJju+dkVHiyeuQHmkuLOI9oyNw==}
peerDependencies:
react: ^16.14.0 || 17.x || 18.x
dependencies:
'@preact/signals-core': 1.5.1
react: 18.2.0
use-sync-external-store: 1.2.0(react@18.2.0)
dev: false
/@radix-ui/primitive@1.0.1: /@radix-ui/primitive@1.0.1:
resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
dependencies: dependencies:
@ -5894,14 +5877,6 @@ packages:
tslib: 2.6.2 tslib: 2.6.2
dev: false dev: false
/use-sync-external-store@1.2.0(react@18.2.0):
resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
react: 18.2.0
dev: false
/utf-8-validate@5.0.10: /utf-8-validate@5.0.10:
resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==}
engines: {node: '>=6.14.2'} engines: {node: '>=6.14.2'}

View File

@ -1,5 +1,5 @@
import { NDKKind } from '@nostr-dev-kit/ndk'; import { NDKKind } from '@nostr-dev-kit/ndk';
import { useSignal } from '@preact/signals-react'; import { useState } from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { useArk } from '@libs/ark'; import { useArk } from '@libs/ark';
import { LoaderIcon, RunIcon } from '@shared/icons'; import { LoaderIcon, RunIcon } from '@shared/icons';
@ -7,11 +7,11 @@ import { User } from '@shared/user';
export function DepotContactCard() { export function DepotContactCard() {
const ark = useArk(); const ark = useArk();
const status = useSignal(false); const [status, setStatus] = useState(false);
const backupContact = async () => { const backupContact = async () => {
try { try {
status.value = true; setStatus(true);
const event = await ark.getEventByFilter({ const event = await ark.getEventByFilter({
filter: { authors: [ark.account.pubkey], kinds: [NDKKind.Contacts] }, filter: { authors: [ark.account.pubkey], kinds: [NDKKind.Contacts] },
@ -21,11 +21,11 @@ export function DepotContactCard() {
const publish = await event.publish(); const publish = await event.publish();
if (publish) { if (publish) {
status.value = false; setStatus(false);
toast.success('Backup contact list successfully.'); toast.success('Backup contact list successfully.');
} }
} catch (e) { } catch (e) {
status.value = false; setStatus(false);
toast.error(e); toast.error(e);
} }
}; };
@ -53,7 +53,7 @@ export function DepotContactCard() {
onClick={backupContact} onClick={backupContact}
className="inline-flex h-8 w-max items-center justify-center gap-2 rounded-md bg-blue-500 pl-2 pr-3 font-medium text-white shadow shadow-blue-500/50 hover:bg-blue-600" className="inline-flex h-8 w-max items-center justify-center gap-2 rounded-md bg-blue-500 pl-2 pr-3 font-medium text-white shadow shadow-blue-500/50 hover:bg-blue-600"
> >
{status.value ? ( {status ? (
<LoaderIcon className="size-4 animate-spin" /> <LoaderIcon className="size-4 animate-spin" />
) : ( ) : (
<RunIcon className="size-4" /> <RunIcon className="size-4" />

View File

@ -1,43 +1,42 @@
import { useSignal } from '@preact/signals-react';
import * as Dialog from '@radix-ui/react-dialog'; import * as Dialog from '@radix-ui/react-dialog';
import { resolveResource } from '@tauri-apps/api/path'; import { resolveResource } from '@tauri-apps/api/path';
import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs'; import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs';
import { nip19 } from 'nostr-tools'; import { nip19 } from 'nostr-tools';
import { useEffect } from 'react'; import { useEffect, useState } from 'react';
import { parse, stringify } from 'smol-toml'; import { parse, stringify } from 'smol-toml';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { CancelIcon, PlusIcon, UserAddIcon, UserRemoveIcon } from '@shared/icons'; import { CancelIcon, PlusIcon, UserAddIcon, UserRemoveIcon } from '@shared/icons';
import { User } from '@shared/user'; import { User } from '@shared/user';
export function DepotMembers() { export function DepotMembers() {
const members = useSignal<Set<string>>(null); const [members, setMembers] = useState<Set<string>>(null);
const tmpMembers = useSignal<Array<string>>([]); const [tmpMembers, setTmpMembers] = useState<Array<string>>([]);
const newMember = useSignal(''); const [newMember, setNewMember] = useState('');
const addMember = async () => { const addMember = async () => {
if (!newMember.value.startsWith('npub1')) if (!newMember.startsWith('npub1'))
return toast.error('You need to enter a valid npub'); return toast.error('You need to enter a valid npub');
try { try {
const pubkey = nip19.decode(newMember.value).data as string; const pubkey = nip19.decode(newMember).data as string;
tmpMembers.value.push(pubkey); setTmpMembers((prev) => [...prev, pubkey]);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
}; };
const removeMember = (member: string) => { const removeMember = (member: string) => {
tmpMembers.value = tmpMembers.value.filter((item) => item !== member); setTmpMembers((prev) => prev.filter((item) => item !== member));
}; };
const updateMembers = async () => { const updateMembers = async () => {
members.value = new Set(tmpMembers.value); setMembers(new Set(tmpMembers));
const defaultConfig = await resolveResource('resources/config.toml'); const defaultConfig = await resolveResource('resources/config.toml');
const config = await readTextFile(defaultConfig); const config = await readTextFile(defaultConfig);
const configContent = parse(config); const configContent = parse(config);
configContent.authorization['pubkey_whitelist'] = [...members.value]; configContent.authorization['pubkey_whitelist'] = [...members];
const newConfig = stringify(configContent); const newConfig = stringify(configContent);
@ -49,7 +48,7 @@ export function DepotMembers() {
const defaultConfig = await resolveResource('resources/config.toml'); const defaultConfig = await resolveResource('resources/config.toml');
const config = await readTextFile(defaultConfig); const config = await readTextFile(defaultConfig);
const configContent = parse(config); const configContent = parse(config);
tmpMembers.value = Array.from(configContent.authorization['pubkey_whitelist']); setTmpMembers(Array.from(configContent.authorization['pubkey_whitelist']));
} }
loadConfig(); loadConfig();
@ -66,12 +65,12 @@ export function DepotMembers() {
</div> </div>
<div className="inline-flex items-center gap-2"> <div className="inline-flex items-center gap-2">
<div className="isolate flex -space-x-2"> <div className="isolate flex -space-x-2">
{tmpMembers.value.slice(0, 5).map((item) => ( {tmpMembers.slice(0, 5).map((item) => (
<User key={item} pubkey={item} variant="stacked" /> <User key={item} pubkey={item} variant="stacked" />
))} ))}
{tmpMembers.value.length > 5 ? ( {tmpMembers.length > 5 ? (
<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-300 dark:bg-neutral-800 dark:text-neutral-100 dark:ring-neutral-700">
<span className="text-xs font-medium">+{tmpMembers.value.length}</span> <span className="text-xs font-medium">+{tmpMembers.length}</span>
</div> </div>
) : null} ) : null}
</div> </div>
@ -107,8 +106,8 @@ export function DepotMembers() {
<input <input
type="text" type="text"
spellCheck={false} spellCheck={false}
value={newMember.value} value={newMember}
onChange={(e) => (newMember.value = e.target.value)} onChange={(e) => setNewMember(e.target.value)}
placeholder="npub1..." placeholder="npub1..."
className="h-11 w-full rounded-lg border-transparent bg-neutral-100 pl-3 pr-20 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-900 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800" className="h-11 w-full rounded-lg border-transparent bg-neutral-100 pl-3 pr-20 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-900 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
/> />
@ -121,7 +120,7 @@ export function DepotMembers() {
Add Add
</button> </button>
</div> </div>
{tmpMembers.value.map((member) => ( {tmpMembers.map((member) => (
<div <div
key={member} key={member}
className="group flex items-center justify-between px-5 py-2 hover:bg-neutral-100 dark:hover:bg-neutral-900" className="group flex items-center justify-between px-5 py-2 hover:bg-neutral-100 dark:hover:bg-neutral-900"

View File

@ -1,5 +1,5 @@
import { NDKKind } from '@nostr-dev-kit/ndk'; import { NDKKind } from '@nostr-dev-kit/ndk';
import { useSignal } from '@preact/signals-react'; import { useState } from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { useArk } from '@libs/ark'; import { useArk } from '@libs/ark';
import { LoaderIcon, RunIcon } from '@shared/icons'; import { LoaderIcon, RunIcon } from '@shared/icons';
@ -7,11 +7,11 @@ import { User } from '@shared/user';
export function DepotProfileCard() { export function DepotProfileCard() {
const ark = useArk(); const ark = useArk();
const status = useSignal(false); const [status, setStatus] = useState(false);
const backupProfile = async () => { const backupProfile = async () => {
try { try {
status.value = true; setStatus(true);
const event = await ark.getEventByFilter({ const event = await ark.getEventByFilter({
filter: { authors: [ark.account.pubkey], kinds: [NDKKind.Metadata] }, filter: { authors: [ark.account.pubkey], kinds: [NDKKind.Metadata] },
@ -21,11 +21,11 @@ export function DepotProfileCard() {
const publish = await event.publish(); const publish = await event.publish();
if (publish) { if (publish) {
status.value = false; setStatus(false);
toast.success('Backup profile successfully.'); toast.success('Backup profile successfully.');
} }
} catch (e) { } catch (e) {
status.value = false; setStatus(false);
toast.error(JSON.stringify(e)); toast.error(JSON.stringify(e));
} }
}; };
@ -42,7 +42,7 @@ export function DepotProfileCard() {
onClick={backupProfile} onClick={backupProfile}
className="inline-flex h-8 w-max items-center justify-center gap-2 rounded-md bg-blue-500 pl-2 pr-3 font-medium text-white shadow shadow-blue-500/50 hover:bg-blue-600" className="inline-flex h-8 w-max items-center justify-center gap-2 rounded-md bg-blue-500 pl-2 pr-3 font-medium text-white shadow shadow-blue-500/50 hover:bg-blue-600"
> >
{status.value ? ( {status ? (
<LoaderIcon className="size-4 animate-spin" /> <LoaderIcon className="size-4 animate-spin" />
) : ( ) : (
<RunIcon className="size-4" /> <RunIcon className="size-4" />

View File

@ -1,18 +1,18 @@
import { NDKKind } from '@nostr-dev-kit/ndk'; import { NDKKind } from '@nostr-dev-kit/ndk';
import { useSignal } from '@preact/signals-react'; import { useEffect, useState } from 'react';
import { useEffect } from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { useArk } from '@libs/ark'; import { useArk } from '@libs/ark';
import { LoaderIcon, RunIcon } from '@shared/icons'; import { LoaderIcon, RunIcon } from '@shared/icons';
export function DepotRelaysCard() { export function DepotRelaysCard() {
const ark = useArk(); const ark = useArk();
const status = useSignal(false);
const relaySize = useSignal(0); const [status, setStatus] = useState(false);
const [relaySize, setRelaySize] = useState(0);
const backupRelays = async () => { const backupRelays = async () => {
try { try {
status.value = true; setStatus(true);
const event = await ark.getEventByFilter({ const event = await ark.getEventByFilter({
filter: { authors: [ark.account.pubkey], kinds: [NDKKind.RelayList] }, filter: { authors: [ark.account.pubkey], kinds: [NDKKind.RelayList] },
@ -22,11 +22,11 @@ export function DepotRelaysCard() {
const publish = await event.publish(); const publish = await event.publish();
if (publish) { if (publish) {
status.value = false; setStatus(false);
toast.success('Backup profile successfully.'); toast.success('Backup profile successfully.');
} }
} catch (e) { } catch (e) {
status.value = false; setStatus(false);
toast.error(JSON.stringify(e)); toast.error(JSON.stringify(e));
} }
}; };
@ -36,7 +36,7 @@ export function DepotRelaysCard() {
const event = await ark.getEventByFilter({ const event = await ark.getEventByFilter({
filter: { authors: [ark.account.pubkey], kinds: [NDKKind.RelayList] }, filter: { authors: [ark.account.pubkey], kinds: [NDKKind.RelayList] },
}); });
if (event) relaySize.value = event.tags.length; if (event) setRelaySize(event.tags.length);
} }
loadRelays(); loadRelays();
@ -54,7 +54,7 @@ export function DepotRelaysCard() {
onClick={backupRelays} onClick={backupRelays}
className="inline-flex h-8 w-max items-center justify-center gap-2 rounded-md bg-blue-500 pl-2 pr-3 font-medium text-white shadow shadow-blue-500/50 hover:bg-blue-600" className="inline-flex h-8 w-max items-center justify-center gap-2 rounded-md bg-blue-500 pl-2 pr-3 font-medium text-white shadow shadow-blue-500/50 hover:bg-blue-600"
> >
{status.value ? ( {status ? (
<LoaderIcon className="size-4 animate-spin" /> <LoaderIcon className="size-4 animate-spin" />
) : ( ) : (
<RunIcon className="size-4" /> <RunIcon className="size-4" />

View File

@ -1,9 +1,8 @@
import { NDKKind } from '@nostr-dev-kit/ndk'; import { NDKKind } from '@nostr-dev-kit/ndk';
import { useSignal } from '@preact/signals-react';
import * as Collapsible from '@radix-ui/react-collapsible'; import * as Collapsible from '@radix-ui/react-collapsible';
import { appConfigDir } from '@tauri-apps/api/path'; import { appConfigDir } from '@tauri-apps/api/path';
import { invoke } from '@tauri-apps/api/primitives'; import { invoke } from '@tauri-apps/api/primitives';
import { useEffect } from 'react'; import { useEffect, useState } from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { DepotContactCard } from '@app/depot/components/contact'; import { DepotContactCard } from '@app/depot/components/contact';
import { DepotMembers } from '@app/depot/components/members'; import { DepotMembers } from '@app/depot/components/members';
@ -14,22 +13,23 @@ import { ChevronDownIcon, DepotIcon, GossipIcon } from '@shared/icons';
export function DepotScreen() { export function DepotScreen() {
const ark = useArk(); const ark = useArk();
const dataPath = useSignal('');
const tunnelUrl = useSignal(''); const [dataPath, setDataPath] = useState('');
const [tunnelUrl, setTunnelUrl] = useState('');
const openFolder = async () => { const openFolder = async () => {
await invoke('show_in_folder', { await invoke('show_in_folder', {
path: dataPath.value + '/nostr.db', path: dataPath + '/nostr.db',
}); });
}; };
const updateRelayList = async () => { const updateRelayList = async () => {
try { try {
if (tunnelUrl.value.length < 1) return toast.info('Please enter a valid relay url'); if (tunnelUrl.length < 1) return toast.info('Please enter a valid relay url');
if (!tunnelUrl.value.startsWith('ws')) if (!tunnelUrl.startsWith('ws'))
return toast.info('Please enter a valid relay url'); return toast.info('Please enter a valid relay url');
const relayUrl = new URL(tunnelUrl.value.replace(/\s/g, '')); const relayUrl = new URL(tunnelUrl.replace(/\s/g, ''));
if (!/^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/.test(relayUrl.host)) return; if (!/^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/.test(relayUrl.host)) return;
const relayEvent = await ark.getEventByFilter({ const relayEvent = await ark.getEventByFilter({
@ -41,12 +41,12 @@ export function DepotScreen() {
if (!relayEvent) { if (!relayEvent) {
publish = await ark.createEvent({ publish = await ark.createEvent({
kind: NDKKind.RelayList, kind: NDKKind.RelayList,
tags: [['r', tunnelUrl.value, '']], tags: [['r', tunnelUrl, '']],
}); });
} }
const newTags = relayEvent.tags ?? []; const newTags = relayEvent.tags ?? [];
newTags.push(['r', tunnelUrl.value, '']); newTags.push(['r', tunnelUrl, '']);
publish = await ark.createEvent({ publish = await ark.createEvent({
kind: NDKKind.RelayList, kind: NDKKind.RelayList,
@ -54,10 +54,10 @@ export function DepotScreen() {
}); });
if (publish) { if (publish) {
await ark.createSetting('tunnel_url', tunnelUrl.value); await ark.createSetting('tunnel_url', tunnelUrl);
toast.success('Update relay list successfully.'); toast.success('Update relay list successfully.');
tunnelUrl.value = ''; setTunnelUrl('');
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@ -68,7 +68,7 @@ export function DepotScreen() {
useEffect(() => { useEffect(() => {
async function loadConfig() { async function loadConfig() {
const appDir = await appConfigDir(); const appDir = await appConfigDir();
dataPath.value = appDir; setDataPath(appDir);
} }
loadConfig(); loadConfig();
@ -170,8 +170,8 @@ export function DepotScreen() {
<div className="mt-2 inline-flex w-full items-center gap-2"> <div className="mt-2 inline-flex w-full items-center gap-2">
<input <input
type="text" type="text"
value={tunnelUrl.value} value={tunnelUrl}
onChange={(e) => (tunnelUrl.value = e.target.value)} onChange={(e) => setTunnelUrl(e.target.value)}
spellCheck={false} spellCheck={false}
placeholder="wss://" placeholder="wss://"
className="h-10 flex-1 rounded-lg border-transparent bg-neutral-100 px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-900 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800" className="h-10 flex-1 rounded-lg border-transparent bg-neutral-100 px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-900 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"

View File

@ -1,9 +1,8 @@
import { useSignal } from '@preact/signals-react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { useRef } from 'react'; import { useRef, useState } from 'react';
import { VList, VListHandle } from 'virtua'; import { VList, VListHandle } from 'virtua';
import { useArk } from '@libs/ark'; import { useArk } from '@libs/ark';
import { LoaderIcon, PlusIcon } from '@shared/icons'; import { LoaderIcon } from '@shared/icons';
import { import {
ArticleWidget, ArticleWidget,
FileWidget, FileWidget,
@ -12,7 +11,6 @@ import {
NewsfeedWidget, NewsfeedWidget,
NotificationWidget, NotificationWidget,
ThreadWidget, ThreadWidget,
ToggleWidgetList,
TopicWidget, TopicWidget,
TrendingAccountsWidget, TrendingAccountsWidget,
TrendingNotesWidget, TrendingNotesWidget,
@ -25,7 +23,6 @@ import { WidgetProps } from '@utils/types';
export function HomeScreen() { export function HomeScreen() {
const ark = useArk(); const ark = useArk();
const ref = useRef<VListHandle>(null); const ref = useRef<VListHandle>(null);
const index = useSignal(-1);
const { isLoading, data } = useQuery({ const { isLoading, data } = useQuery({
queryKey: ['widgets'], queryKey: ['widgets'],
@ -54,6 +51,8 @@ export function HomeScreen() {
staleTime: Infinity, staleTime: Infinity,
}); });
const [selectedIndex, setSelectedIndex] = useState(-1);
const renderItem = (widget: WidgetProps) => { const renderItem = (widget: WidgetProps) => {
switch (widget.kind) { switch (widget.kind) {
case WIDGET_KIND.notification: case WIDGET_KIND.notification:
@ -107,8 +106,8 @@ export function HomeScreen() {
case 'ArrowUp': case 'ArrowUp':
case 'ArrowLeft': { case 'ArrowLeft': {
e.preventDefault(); e.preventDefault();
const prevIndex = Math.max(index.peek() - 1, 0); const prevIndex = Math.max(selectedIndex - 1, 0);
index.value = prevIndex; setSelectedIndex(prevIndex);
ref.current.scrollToIndex(prevIndex, { ref.current.scrollToIndex(prevIndex, {
align: 'center', align: 'center',
smooth: true, smooth: true,
@ -118,8 +117,8 @@ export function HomeScreen() {
case 'ArrowDown': case 'ArrowDown':
case 'ArrowRight': { case 'ArrowRight': {
e.preventDefault(); e.preventDefault();
const nextIndex = Math.min(index.peek() + 1, data.length - 1); const nextIndex = Math.min(selectedIndex + 1, data.length - 1);
index.value = nextIndex; setSelectedIndex(nextIndex);
ref.current.scrollToIndex(nextIndex, { ref.current.scrollToIndex(nextIndex, {
align: 'center', align: 'center',
smooth: true, smooth: true,

View File

@ -1,6 +1,5 @@
import { useSignal } from '@preact/signals-react';
import { Resizable } from 're-resizable'; import { Resizable } from 're-resizable';
import { ReactNode } from 'react'; import { ReactNode, useState } from 'react';
import { twMerge } from 'tailwind-merge'; import { twMerge } from 'tailwind-merge';
export function WidgetRoot({ export function WidgetRoot({
@ -10,14 +9,14 @@ export function WidgetRoot({
children: ReactNode; children: ReactNode;
className?: string; className?: string;
}) { }) {
const width = useSignal(420); const [width, setWidth] = useState(420);
return ( return (
<Resizable <Resizable
size={{ width: width.value, height: '100%' }} size={{ width, height: '100%' }}
onResizeStart={(e) => e.preventDefault()} onResizeStart={(e) => e.preventDefault()}
onResizeStop={(_e, _direction, _ref, d) => { onResizeStop={(_e, _direction, _ref, d) => {
width.value = width.peek() + d.width; setWidth((prevWidth) => prevWidth + d.width);
}} }}
minWidth={420} minWidth={420}
maxWidth={600} maxWidth={600}