This commit is contained in:
Shusui MOYATANI
2023-03-18 02:45:14 +09:00
parent 3f19aa120d
commit f1cd3f85aa
7 changed files with 85 additions and 10 deletions

View File

@@ -106,6 +106,53 @@ const DateFormatConfig = () => {
); );
}; };
const ToggleButton = (props: {
value: boolean;
onClick: JSX.EventHandler<HTMLButtonElement, MouseEvent>;
}) => {
return (
<button
class="flex h-[24px] w-[48px] items-center rounded-full border border-primary px-1"
classList={{
'bg-white': !props.value,
'justify-start': !props.value,
'bg-rose-300': props.value,
'justify-end': props.value,
}}
area-label={props.value}
onClick={props.onClick}
>
<span class="m-[-2px] inline-block h-5 w-5 rounded-full border border-primary bg-white shadow" />
</button>
);
};
const OtherConfig = () => {
const { config, setConfig } = useConfig();
const toggleKeepOpenPostForm = () => {
setConfig((current) => ({
...current,
keepOpenPostForm: !(current.keepOpenPostForm ?? false),
}));
};
return (
<div>
<h3 class="font-bold"></h3>
<div class="flex flex-col justify-evenly gap-2 sm:flex-row">
<div class="flex w-full">
<div class="flex-1">稿</div>
<ToggleButton
value={config().keepOpenPostForm}
onClick={() => toggleKeepOpenPostForm()}
/>
</div>
</div>
</div>
);
};
const ConfigUI = (props: ConfigProps) => { const ConfigUI = (props: ConfigProps) => {
let containerRef: HTMLDivElement | undefined; let containerRef: HTMLDivElement | undefined;
@@ -131,6 +178,7 @@ const ConfigUI = (props: ConfigProps) => {
</div> </div>
<RelayConfig /> <RelayConfig />
<DateFormatConfig /> <DateFormatConfig />
<OtherConfig />
</div> </div>
</div> </div>
); );

View File

@@ -22,19 +22,21 @@ import eventWrapper from '@/core/event';
import useConfig from '@/nostr/useConfig'; import useConfig from '@/nostr/useConfig';
import useCommands from '@/nostr/useCommands'; import useCommands from '@/nostr/useCommands';
import usePubkey from '@/nostr/usePubkey'; import usePubkey from '@/nostr/usePubkey';
import { useHandleCommand } from '@/hooks/useCommandBus';
type NotePostFormProps = { type NotePostFormProps = {
replyTo?: NostrEvent; replyTo?: NostrEvent;
mode?: 'normal' | 'reply'; mode?: 'normal' | 'reply';
onClose: () => void; onClose: () => void;
onPost?: () => void;
textAreaRef?: (textAreaRef: HTMLTextAreaElement) => void;
}; };
const placeholder = (mode: NotePostFormProps['mode']) => { const placeholder = (mode: NotePostFormProps['mode']) => {
switch (mode) { switch (mode) {
case 'normal':
return 'いまどうしてる?';
case 'reply': case 'reply':
return '返信を投稿'; return '返信を投稿';
case 'normal':
default: default:
return 'いまどうしてる?'; return 'いまどうしてる?';
} }
@@ -59,7 +61,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
onSuccess: () => { onSuccess: () => {
console.log('succeeded to post'); console.log('succeeded to post');
clearText(); clearText();
props?.onClose(); props.onPost?.();
}, },
onError: (err) => { onError: (err) => {
console.error('error', err); console.error('error', err);
@@ -99,6 +101,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey)) { if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey)) {
submit(); submit();
} else if (ev.key === 'Escape') { } else if (ev.key === 'Escape') {
textAreaRef?.blur();
props.onClose(); props.onClose();
} }
}; };
@@ -129,7 +132,10 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
</Show> </Show>
<form class="flex flex-col gap-1" onSubmit={handleSubmit}> <form class="flex flex-col gap-1" onSubmit={handleSubmit}>
<textarea <textarea
ref={textAreaRef} ref={(el) => {
textAreaRef = el;
props.textAreaRef?.(el);
}}
name="text" name="text"
class="rounded border-none" class="rounded border-none"
rows={4} rows={4}

View File

@@ -7,8 +7,12 @@ import NotePostForm from '@/components/NotePostForm';
import Config from '@/components/Config'; import Config from '@/components/Config';
import { useHandleCommand } from '@/hooks/useCommandBus'; import { useHandleCommand } from '@/hooks/useCommandBus';
import useConfig from '@/nostr/useConfig';
const SideBar: Component = () => { const SideBar: Component = () => {
let textAreaRef: HTMLTextAreaElement | undefined;
const { config } = useConfig();
const [formOpened, setFormOpened] = createSignal(false); const [formOpened, setFormOpened] = createSignal(false);
const [configOpened, setConfigOpened] = createSignal(false); const [configOpened, setConfigOpened] = createSignal(false);
@@ -17,7 +21,10 @@ const SideBar: Component = () => {
useHandleCommand(() => ({ useHandleCommand(() => ({
commandType: 'openPostForm', commandType: 'openPostForm',
handler: (cmd) => openForm(), handler: () => {
openForm();
setTimeout(() => textAreaRef?.focus?.(), 100);
},
})); }));
return ( return (
@@ -48,8 +55,13 @@ const SideBar: Component = () => {
</button> </button>
</div> </div>
</div> </div>
<Show when={formOpened()}> <Show when={formOpened() || config().keepOpenPostForm}>
<NotePostForm onClose={closeForm} /> <NotePostForm
textAreaRef={(el) => {
textAreaRef = el;
}}
onClose={closeForm}
/>
</Show> </Show>
<Show when={configOpened()}> <Show when={configOpened()}>
<Config onClose={() => setConfigOpened(false)} /> <Config onClose={() => setConfigOpened(false)} />

View File

@@ -42,6 +42,7 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
const pubkey = usePubkey(); const pubkey = usePubkey();
const [showReplyForm, setShowReplyForm] = createSignal(false); const [showReplyForm, setShowReplyForm] = createSignal(false);
const closeReplyForm = () => setShowReplyForm(false);
const [showMenu, setShowMenu] = createSignal(false); const [showMenu, setShowMenu] = createSignal(false);
const event = createMemo(() => eventWrapper(props.event)); const event = createMemo(() => eventWrapper(props.event));
@@ -255,7 +256,12 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
</div> </div>
</div> </div>
<Show when={showReplyForm()}> <Show when={showReplyForm()}>
<NotePostForm mode="reply" replyTo={props.event} onClose={() => setShowReplyForm(false)} /> <NotePostForm
mode="reply"
replyTo={props.event}
onClose={closeReplyForm}
onPost={closeReplyForm}
/>
</Show> </Show>
</div> </div>
); );

View File

@@ -112,7 +112,7 @@ export const parseTextNote = (event: NostrEvent): ParsedTextNote => {
} }
} else if (match.groups?.nip19 && match.index >= pos) { } else if (match.groups?.nip19 && match.index >= pos) {
try { try {
const decoded = decode(match[0].toLowerCase()); const decoded = decode(match[0]);
const bech32Entity: Bech32Entity = { const bech32Entity: Bech32Entity = {
type: 'Bech32Entity', type: 'Bech32Entity',
content: match[0], content: match[0],

View File

@@ -7,6 +7,7 @@ import {
export type Config = { export type Config = {
relayUrls: string[]; relayUrls: string[];
dateFormat: 'relative' | 'absolute-long' | 'absolute-short'; dateFormat: 'relative' | 'absolute-long' | 'absolute-short';
keepOpenPostForm: boolean;
}; };
type UseConfig = { type UseConfig = {
@@ -29,6 +30,7 @@ const InitialConfig: Config = {
'wss://nostr.holybea.com', 'wss://nostr.holybea.com',
], ],
dateFormat: 'relative', dateFormat: 'relative',
keepOpenPostForm: false,
}; };
const serializer = (config: Config): string => JSON.stringify(config); const serializer = (config: Config): string => JSON.stringify(config);

View File

@@ -1,5 +1,6 @@
import { createSignal, createEffect, onMount, type Component, onCleanup } from 'solid-js'; import { createSignal, createEffect, onMount, type Component, onCleanup } from 'solid-js';
import { useNavigate } from '@solidjs/router'; import { useNavigate } from '@solidjs/router';
import uniq from 'lodash/uniq';
import Column from '@/components/Column'; import Column from '@/components/Column';
import SideBar from '@/components/SideBar'; import SideBar from '@/components/SideBar';
@@ -48,7 +49,7 @@ const Home: Component = () => {
filters: [ filters: [
{ {
kinds: [1, 6], kinds: [1, 6],
authors: followingPubkeys(), authors: uniq([...followingPubkeys(), pubkeyNonNull]),
limit: 25, limit: 25,
since: Math.floor(Date.now() / 1000) - 12 * 60 * 60, since: Math.floor(Date.now() / 1000) - 12 * 60 * 60,
}, },