mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-18 22:44:26 +01:00
update
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import type { Component, JSX } from 'solid-js';
|
||||
import { useHandleCommand } from '@/hooks/useCommandBus';
|
||||
|
||||
type ColumnProps = {
|
||||
export type ColumnProps = {
|
||||
name: string;
|
||||
columnIndex: number;
|
||||
lastColumn?: true;
|
||||
|
||||
@@ -142,6 +142,13 @@ const OtherConfig = () => {
|
||||
}));
|
||||
};
|
||||
|
||||
const toggleShowImage = () => {
|
||||
setConfig((current) => ({
|
||||
...current,
|
||||
showImage: !(current.showImage ?? true),
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 class="font-bold">その他</h3>
|
||||
@@ -153,6 +160,10 @@ const OtherConfig = () => {
|
||||
onClick={() => toggleKeepOpenPostForm()}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">画像をデフォルトで表示する</div>
|
||||
<ToggleButton value={config().showImage} onClick={() => toggleShowImage()} />
|
||||
</div>
|
||||
{/*
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">リアクションのデフォルト</div>
|
||||
|
||||
@@ -232,7 +232,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
|
||||
props.textAreaRef?.(el);
|
||||
}}
|
||||
name="text"
|
||||
class="rounded border-none"
|
||||
class="min-h-[40px] rounded border-none"
|
||||
rows={4}
|
||||
placeholder={placeholder(mode())}
|
||||
onInput={handleInput}
|
||||
@@ -260,7 +260,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
|
||||
'w-7': mode() === 'reply',
|
||||
}}
|
||||
type="button"
|
||||
area-label="コンテンツ警告を設定"
|
||||
aria-label="コンテンツ警告を設定"
|
||||
title="コンテンツ警告を設定"
|
||||
onClick={() => setContentWarning((e) => !e)}
|
||||
>
|
||||
@@ -278,7 +278,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
|
||||
}}
|
||||
type="button"
|
||||
title="画像を投稿"
|
||||
area-label="画像を投稿"
|
||||
aria-label="画像を投稿"
|
||||
disabled={fileUploadDisabled()}
|
||||
onClick={() => fileInputRef?.click()}
|
||||
>
|
||||
@@ -295,7 +295,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
|
||||
'w-7': mode() === 'reply',
|
||||
}}
|
||||
type="submit"
|
||||
area-label="投稿"
|
||||
aria-label="投稿"
|
||||
title="投稿"
|
||||
disabled={submitDisabled()}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Component, createMemo, Show } from 'solid-js';
|
||||
import { npubEncode } from 'nostr-tools/nip19';
|
||||
|
||||
import GlobeAlt from 'heroicons/24/outline/globe-alt.svg';
|
||||
import XMark from 'heroicons/24/outline/x-mark.svg';
|
||||
@@ -8,10 +7,11 @@ import Modal from '@/components/Modal';
|
||||
import Copy from '@/components/utils/Copy';
|
||||
|
||||
import useProfile from '@/nostr/useProfile';
|
||||
import useConfig from '@/nostr/useConfig';
|
||||
import npubEncodeFallback from '@/utils/npubEncodeFallback';
|
||||
|
||||
export type ProfileDisplayProps = {
|
||||
pubkey: string;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
|
||||
@@ -19,13 +19,17 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
|
||||
pubkey: props.pubkey,
|
||||
}));
|
||||
|
||||
const npub = createMemo(() => npubEncode(props.pubkey));
|
||||
const npub = createMemo(() => npubEncodeFallback(props.pubkey));
|
||||
|
||||
return (
|
||||
<Modal>
|
||||
<Modal onClose={() => props.onClose?.()}>
|
||||
<div class="max-h-full w-[640px] max-w-full overflow-scroll">
|
||||
<div class="flex justify-end">
|
||||
<button class="h-8 w-8 text-stone-700">
|
||||
<button
|
||||
class="h-8 w-8 text-stone-700"
|
||||
aria-label="Close"
|
||||
onClick={() => props.onClose?.()}
|
||||
>
|
||||
<XMark />
|
||||
</button>
|
||||
</div>
|
||||
@@ -38,16 +42,22 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
|
||||
)}
|
||||
</Show>
|
||||
</div>
|
||||
<div class="flex h-[64px] items-center gap-2 px-2">
|
||||
<div class="mt-[-64px] h-28 w-28 shrink-0 rounded-lg border-2 object-cover">
|
||||
<div class="flex h-[64px] items-center gap-4 px-4">
|
||||
<div class="mt-[-64px] h-28 w-28 shrink-0 rounded-lg bg-stone-400 shadow-md">
|
||||
<Show when={profile()?.picture} keyed>
|
||||
{(pictureUrl) => <img src={pictureUrl} alt="user icon" class="h-full w-full" />}
|
||||
{(pictureUrl) => (
|
||||
<img
|
||||
src={pictureUrl}
|
||||
alt="user icon"
|
||||
class="h-full w-full rounded-lg object-cover"
|
||||
/>
|
||||
)}
|
||||
</Show>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="truncate text-xl font-bold">{profile()?.display_name}</div>
|
||||
<div class="shrink-0 text-sm">@{profile()?.name}</div>
|
||||
<div class="shrink-0 text-sm font-bold">@{profile()?.name}</div>
|
||||
</div>
|
||||
<div class="flex gap-1">
|
||||
<div class="truncate text-xs">{npub()}</div>
|
||||
@@ -55,23 +65,27 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max-h-32 overflow-scroll whitespace-pre-wrap px-4 pt-1 text-sm">
|
||||
<div class="max-h-32 overflow-scroll whitespace-pre-wrap px-5 py-2 text-sm">
|
||||
{profile()?.about}
|
||||
</div>
|
||||
<ul class="px-4 py-2 text-xs">
|
||||
<ul class="border-t px-5 py-2 text-xs">
|
||||
<Show when={profile()?.website}>
|
||||
<li class="flex items-center gap-1">
|
||||
<span class="inline-block h-4 w-4" area-label="website" title="website">
|
||||
<GlobeAlt />
|
||||
</span>
|
||||
<a href={profile()?.website} target="_blank" rel="noreferrer noopener">
|
||||
<a
|
||||
class="text-blue-500 underline"
|
||||
href={profile()?.website}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
{profile()?.website}
|
||||
</a>
|
||||
</li>
|
||||
</Show>
|
||||
</ul>
|
||||
</Show>
|
||||
<div class="h-16 border" />
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component, Switch, Match } from 'solid-js';
|
||||
import { npubEncode } from 'nostr-tools/nip19';
|
||||
|
||||
import useProfile from '@/nostr/useProfile';
|
||||
import npubEncodeFallback from '@/utils/npubEncodeFallback';
|
||||
|
||||
type UserNameDisplayProps = {
|
||||
pubkey: string;
|
||||
@@ -13,7 +13,7 @@ const UserNameDisplay: Component<UserNameDisplayProps> = (props) => {
|
||||
}));
|
||||
|
||||
return (
|
||||
<Switch fallback={npubEncode(props.pubkey)}>
|
||||
<Switch fallback={npubEncodeFallback(props.pubkey)}>
|
||||
<Match when={(profile()?.display_name?.length ?? 0) > 0}>{profile()?.display_name}</Match>
|
||||
<Match when={(profile()?.name?.length ?? 0) > 0}>@{profile()?.name}</Match>
|
||||
</Switch>
|
||||
|
||||
@@ -8,7 +8,6 @@ import UserDisplayName from '@/components/UserDisplayName';
|
||||
|
||||
import useProfile from '@/nostr/useProfile';
|
||||
import useEvent from '@/nostr/useEvent';
|
||||
import { npubEncode } from 'nostr-tools/nip19';
|
||||
|
||||
type ReactionProps = {
|
||||
event: NostrEvent;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Show } from 'solid-js';
|
||||
import { npubEncode } from 'nostr-tools/nip19';
|
||||
|
||||
import useProfile from '@/nostr/useProfile';
|
||||
import npubEncodeFallback from '@/utils/npubEncodeFallback';
|
||||
|
||||
export type GeneralUserMentionDisplayProps = {
|
||||
pubkey: string;
|
||||
@@ -13,7 +13,10 @@ const GeneralUserMentionDisplay = (props: GeneralUserMentionDisplayProps) => {
|
||||
}));
|
||||
|
||||
return (
|
||||
<Show when={(profile()?.name?.length ?? 0) > 0} fallback={`@${npubEncode(props.pubkey)}`}>
|
||||
<Show
|
||||
when={(profile()?.name?.length ?? 0) > 0}
|
||||
fallback={`@${npubEncodeFallback(props.pubkey)}`}
|
||||
>
|
||||
@{profile()?.name ?? props.pubkey}
|
||||
</Show>
|
||||
);
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import type { MentionedUser } from '@/core/parseTextNote';
|
||||
import GeneralUserMentionDisplay from '@/components/textNote/GeneralUserMentionDisplay';
|
||||
import useModalState from '@/hooks/useModalState';
|
||||
|
||||
export type MentionedUserDisplayProps = {
|
||||
mentionedUser: MentionedUser;
|
||||
};
|
||||
|
||||
const MentionedUserDisplay = (props: MentionedUserDisplayProps) => {
|
||||
const { showProfile } = useModalState();
|
||||
|
||||
const handleClick = () => {
|
||||
showProfile(props.mentionedUser.pubkey);
|
||||
};
|
||||
return (
|
||||
<span class="text-blue-500 underline">
|
||||
<button class="inline text-blue-500 underline" onClick={handleClick}>
|
||||
<GeneralUserMentionDisplay pubkey={props.mentionedUser.pubkey} />
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import MentionedEventDisplay from '@/components/textNote/MentionedEventDisplay';
|
||||
import ImageDisplay from '@/components/textNote/ImageDisplay';
|
||||
import eventWrapper from '@/core/event';
|
||||
import { isImageUrl } from '@/utils/imageUrl';
|
||||
import useConfig from '@/nostr/useConfig';
|
||||
import EventLink from '../EventLink';
|
||||
import TextNoteDisplayById from './TextNoteDisplayById';
|
||||
|
||||
@@ -16,6 +17,7 @@ export type TextNoteContentDisplayProps = {
|
||||
};
|
||||
|
||||
const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => {
|
||||
const { config } = useConfig();
|
||||
const event = () => eventWrapper(props.event);
|
||||
return (
|
||||
<For each={parseTextNote(props.event)}>
|
||||
@@ -50,7 +52,9 @@ const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => {
|
||||
return (
|
||||
<ImageDisplay
|
||||
url={item.content}
|
||||
initialHidden={event().contentWarning().contentWarning || !props.embedding}
|
||||
initialHidden={
|
||||
!config().showImage || event().contentWarning().contentWarning || !props.embedding
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ import useDeprecatedReposts from '@/nostr/useDeprecatedReposts';
|
||||
import useFormatDate from '@/hooks/useFormatDate';
|
||||
|
||||
import ensureNonNull from '@/utils/ensureNonNull';
|
||||
import { npubEncode } from 'nostr-tools/nip19';
|
||||
import npubEncodeFallback from '@/utils/npubEncodeFallback';
|
||||
import useModalState from '@/hooks/useModalState';
|
||||
import UserNameDisplay from '../UserDisplayName';
|
||||
import TextNoteDisplayById from './TextNoteDisplayById';
|
||||
|
||||
@@ -39,6 +40,7 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
|
||||
const { config } = useConfig();
|
||||
const formatDate = useFormatDate();
|
||||
const pubkey = usePubkey();
|
||||
const { showProfile } = useModalState();
|
||||
|
||||
const [showReplyForm, setShowReplyForm] = createSignal(false);
|
||||
const closeReplyForm = () => setShowReplyForm(false);
|
||||
@@ -142,7 +144,10 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
|
||||
return (
|
||||
<div class="nostr-textnote flex flex-col">
|
||||
<div class="flex w-full gap-1">
|
||||
<div class="author-icon h-10 w-10 shrink-0 overflow-hidden object-cover">
|
||||
<button
|
||||
class="author-icon h-10 w-10 shrink-0 overflow-hidden object-cover"
|
||||
onClick={() => showProfile(event().pubkey)}
|
||||
>
|
||||
<Show when={author()?.picture}>
|
||||
{/* TODO 画像は脆弱性回避のためにimgじゃない方法で読み込みたい */}
|
||||
<img
|
||||
@@ -152,21 +157,27 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
|
||||
class="h-10 w-10 rounded"
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
</button>
|
||||
<div class="min-w-0 flex-auto">
|
||||
<div class="flex justify-between gap-1 text-xs">
|
||||
<div class="author flex min-w-0 truncate">
|
||||
<button
|
||||
class="author flex min-w-0 truncate"
|
||||
onClick={() => showProfile(event().pubkey)}
|
||||
>
|
||||
{/* TODO link to author */}
|
||||
<Show when={(author()?.display_name?.length ?? 0) > 0}>
|
||||
<div class="author-name truncate pr-1 font-bold">{author()?.display_name}</div>
|
||||
</Show>
|
||||
<div class="author-username truncate text-zinc-600">
|
||||
<Show when={author()?.name != null} fallback={`@${npubEncode(props.event.pubkey)}`}>
|
||||
<Show
|
||||
when={author()?.name != null}
|
||||
fallback={`@${npubEncodeFallback(event().pubkey)}`}
|
||||
>
|
||||
@{author()?.name}
|
||||
</Show>
|
||||
{/* TODO <Match when={author()?.nip05 != null}>@{author()?.nip05}</Match> */}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="created-at shrink-0">{createdAt()}</div>
|
||||
</div>
|
||||
<Show when={showReplyEvent()} keyed>
|
||||
@@ -180,9 +191,12 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
|
||||
<div class="text-xs">
|
||||
<For each={event().mentionedPubkeys()}>
|
||||
{(replyToPubkey: string) => (
|
||||
<span class="pr-1 text-blue-500 underline">
|
||||
<button
|
||||
class="pr-1 text-blue-500 underline"
|
||||
onClick={() => showProfile(replyToPubkey)}
|
||||
>
|
||||
<GeneralUserMentionDisplay pubkey={replyToPubkey} />
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
{'への返信'}
|
||||
|
||||
@@ -29,7 +29,7 @@ const Copy: Component<CopyProps> = (props) => {
|
||||
</button>
|
||||
<Show when={showPopup()}>
|
||||
<div
|
||||
class="absolute left-[-1rem] top-[-1.5rem] rounded-lg
|
||||
class="absolute left-[-1rem] top-[-1.5rem] rounded
|
||||
bg-rose-300 p-1 text-xs font-bold text-white shadow"
|
||||
>
|
||||
Copied!
|
||||
|
||||
Reference in New Issue
Block a user