feat: add specific relay as a column

This commit is contained in:
Shusui MOYATANI
2024-02-22 03:00:15 +09:00
parent 9472738f90
commit 507796e849
2 changed files with 104 additions and 20 deletions

View File

@@ -1,10 +1,11 @@
import { Component, For } from 'solid-js'; import { Component, For, Switch, Match, createSignal, type JSX } from 'solid-js';
import Bell from 'heroicons/24/outline/bell.svg'; import Bell from 'heroicons/24/outline/bell.svg';
import GlobeAlt from 'heroicons/24/outline/globe-alt.svg'; import GlobeAlt from 'heroicons/24/outline/globe-alt.svg';
import Heart from 'heroicons/24/outline/heart.svg'; import Heart from 'heroicons/24/outline/heart.svg';
import Home from 'heroicons/24/outline/home.svg'; import Home from 'heroicons/24/outline/home.svg';
import MagnifyingGlass from 'heroicons/24/outline/magnifying-glass.svg'; import MagnifyingGlass from 'heroicons/24/outline/magnifying-glass.svg';
import Server from 'heroicons/24/outline/server.svg';
import User from 'heroicons/24/outline/user.svg'; import User from 'heroicons/24/outline/user.svg';
// import BookmarkIcon from 'heroicons/24/outline/bookmark.svg'; // import BookmarkIcon from 'heroicons/24/outline/bookmark.svg';
// import ChatBubbleLeftRight from 'heroicons/24/outline/chat-bubble-left-right.svg'; // import ChatBubbleLeftRight from 'heroicons/24/outline/chat-bubble-left-right.svg';
@@ -16,6 +17,7 @@ import {
createNotificationColumn, createNotificationColumn,
createPostsColumn, createPostsColumn,
createReactionsColumn, createReactionsColumn,
createRelaysColumn,
createSearchColumn, createSearchColumn,
} from '@/core/column'; } from '@/core/column';
import useConfig from '@/core/useConfig'; import useConfig from '@/core/useConfig';
@@ -23,17 +25,78 @@ import { useRequestCommand } from '@/hooks/useCommandBus';
import { useTranslation } from '@/i18n/useTranslation'; import { useTranslation } from '@/i18n/useTranslation';
import usePubkey from '@/nostr/usePubkey'; import usePubkey from '@/nostr/usePubkey';
import ensureNonNull from '@/utils/ensureNonNull'; import ensureNonNull from '@/utils/ensureNonNull';
import { isWebSocketUrl } from '@/utils/url';
type AddColumnProps = { type AddColumnProps = {
onClose: () => void; onClose: () => void;
}; };
type AddRelayColumnProps = {
addRelaysColumn: (relayUrls: string[]) => void;
};
const AddRelaysColumn: Component<AddRelayColumnProps> = (props) => {
const i18n = useTranslation();
const { config } = useConfig();
const [relayUrl, setRelayUrl] = createSignal<string>('');
const handleSubmit: JSX.EventHandler<HTMLFormElement, Event> = (ev) => {
ev.preventDefault();
const url = relayUrl();
if (!isWebSocketUrl(url)) {
window.alert('Invalid url');
return;
}
props.addRelaysColumn([url]);
};
return (
<div class="p-8">
<form class="flex gap-1" onSubmit={handleSubmit}>
<input
class="flex-1 rounded-md border-border bg-bg placeholder:text-fg-secondary focus:border-border focus:ring-primary"
type="text"
name="url"
placeholder="wss://..."
pattern="wss?:\/\/.*"
required
onChange={(ev) => setRelayUrl(ev.currentTarget.value)}
/>
<button
class="rounded border border-primary px-4 py-1 font-bold text-primary"
type="submit"
>
{i18n.t('column.addRelayColumn.add')}
</button>
</form>
<div class="flex flex-col items-start gap-1 pt-8">
<For each={config().relayUrls}>
{(url) => (
<button
type="button"
class="text-fg-secondary hover:text-fg"
onClick={() => props.addRelaysColumn([url])}
>
{url}
</button>
)}
</For>
</div>
</div>
);
};
const AddColumn: Component<AddColumnProps> = (props) => { const AddColumn: Component<AddColumnProps> = (props) => {
const i18n = useTranslation(); const i18n = useTranslation();
const pubkey = usePubkey(); const pubkey = usePubkey();
const { saveColumn } = useConfig(); const { saveColumn } = useConfig();
const request = useRequestCommand(); const request = useRequestCommand();
const [detailComponent, setDetailComponent] = createSignal<string | undefined>(undefined);
const finish = () => { const finish = () => {
props.onClose(); props.onClose();
request({ command: 'moveToLastColumn' }).catch((err) => console.error(err)); request({ command: 'moveToLastColumn' }).catch((err) => console.error(err));
@@ -58,6 +121,11 @@ const AddColumn: Component<AddColumnProps> = (props) => {
finish(); finish();
}; };
const addRelaysColumn = (relayUrls: string[]) => {
saveColumn(createRelaysColumn({ relayUrls }));
finish();
};
const addSearchColumn = () => { const addSearchColumn = () => {
saveColumn(createSearchColumn({ query: '' })); saveColumn(createSearchColumn({ query: '' }));
finish(); finish();
@@ -81,32 +149,37 @@ const AddColumn: Component<AddColumnProps> = (props) => {
{ {
name: () => i18n.t('column.home'), name: () => i18n.t('column.home'),
icon: () => <Home />, icon: () => <Home />,
onClick: addFollowingColumn, onSelect: addFollowingColumn,
}, },
{ {
name: () => i18n.t('column.notification'), name: () => i18n.t('column.notification'),
icon: () => <Bell />, icon: () => <Bell />,
onClick: addNotificationColumn, onSelect: addNotificationColumn,
},
{
name: () => i18n.t('column.relay'),
icon: () => <Server />,
onSelect: () => setDetailComponent('AddRelaysColumn'),
}, },
{ {
name: () => i18n.t('column.japanese'), name: () => i18n.t('column.japanese'),
icon: () => <GlobeAlt />, icon: () => <GlobeAlt />,
onClick: addJapanRelaysColumn, onSelect: addJapanRelaysColumn,
}, },
{ {
name: () => i18n.t('column.search'), name: () => i18n.t('column.search'),
icon: () => <MagnifyingGlass />, icon: () => <MagnifyingGlass />,
onClick: addSearchColumn, onSelect: addSearchColumn,
}, },
{ {
name: () => i18n.t('column.myPosts'), name: () => i18n.t('column.myPosts'),
icon: () => <User />, icon: () => <User />,
onClick: addMyPostsColumn, onSelect: addMyPostsColumn,
}, },
{ {
name: () => i18n.t('column.myReactions'), name: () => i18n.t('column.myReactions'),
icon: () => <Heart />, icon: () => <Heart />,
onClick: addMyReactionsColumn, onSelect: addMyReactionsColumn,
}, },
// TODO channel <ChatBubbleLeftRight /> // TODO channel <ChatBubbleLeftRight />
// TODO bookmark <BookmarkIcon /> // TODO bookmark <BookmarkIcon />
@@ -114,19 +187,27 @@ const AddColumn: Component<AddColumnProps> = (props) => {
return ( return (
<BasicModal onClose={props.onClose}> <BasicModal onClose={props.onClose}>
<div class="flex flex-wrap p-4"> <Switch
<For each={menu}> fallback={
{(menuItem) => ( <div class="flex flex-wrap p-4">
<button <For each={menu}>
class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4" {(menuItem) => (
onClick={menuItem.onClick} <button
> class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4"
<span class="inline-block size-8">{menuItem.icon()}</span> onClick={menuItem.onSelect}
{menuItem.name()} >
</button> <span class="inline-block size-8">{menuItem.icon()}</span>
)} {menuItem.name()}
</For> </button>
</div> )}
</For>
</div>
}
>
<Match when={detailComponent() === 'AddRelaysColumn'}>
<AddRelaysColumn addRelaysColumn={addRelaysColumn} />
</Match>
</Switch>
</BasicModal> </BasicModal>
); );
}; };

View File

@@ -37,6 +37,9 @@ export default {
back: '戻る', back: '戻る',
loadLatest: '最新の投稿を読み込む', loadLatest: '最新の投稿を読み込む',
loadOld: '古い投稿を読み込む', loadOld: '古い投稿を読み込む',
addRelayColumn: {
add: '追加',
},
config: { config: {
columnWidth: 'カラム幅', columnWidth: 'カラム幅',
widest: '特大', widest: '特大',