mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-18 14:34:25 +01:00
feat: add specific relay as a column
This commit is contained in:
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ export default {
|
|||||||
back: '戻る',
|
back: '戻る',
|
||||||
loadLatest: '最新の投稿を読み込む',
|
loadLatest: '最新の投稿を読み込む',
|
||||||
loadOld: '古い投稿を読み込む',
|
loadOld: '古い投稿を読み込む',
|
||||||
|
addRelayColumn: {
|
||||||
|
add: '追加',
|
||||||
|
},
|
||||||
config: {
|
config: {
|
||||||
columnWidth: 'カラム幅',
|
columnWidth: 'カラム幅',
|
||||||
widest: '特大',
|
widest: '特大',
|
||||||
|
|||||||
Reference in New Issue
Block a user