mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-18 06:24:25 +01:00
feat: show profile json
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Component, createMemo } from 'solid-js';
|
||||
import { Component, createMemo, Show } from 'solid-js';
|
||||
|
||||
import { type Event as NostrEvent } from 'nostr-tools/pure';
|
||||
|
||||
@@ -8,6 +8,7 @@ import usePool from '@/nostr/usePool';
|
||||
|
||||
export type EventDebugModalProps = {
|
||||
event: NostrEvent;
|
||||
extra?: string;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
@@ -22,17 +23,26 @@ const EventDebugModal: Component<EventDebugModalProps> = (props) => {
|
||||
|
||||
return (
|
||||
<BasicModal onClose={props.onClose}>
|
||||
<div class="p-2">
|
||||
<h2 class="text-lg font-bold">JSON</h2>
|
||||
<div class="p-4">
|
||||
<h2 class="text-lg font-bold">Event JSON</h2>
|
||||
<pre class="whitespace-pre-wrap break-all rounded-lg border border-border p-4 text-xs">
|
||||
{json()}
|
||||
</pre>
|
||||
<div class="flex justify-end">
|
||||
<Copy class="h-4 w-4 hover:text-primary" text={json()} />
|
||||
<Copy class="size-4 hover:text-primary" text={json()} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<Show when={props.extra}>
|
||||
<div class="p-4">
|
||||
<h2 class="text-lg font-bold">Extra</h2>
|
||||
<pre class="whitespace-pre-wrap break-all rounded-lg border border-border p-4 text-xs">
|
||||
{props.extra}
|
||||
</pre>
|
||||
</div>
|
||||
</Show>
|
||||
<div class="p-4">
|
||||
<h2 class="text-lg font-bold">Found in these relays</h2>
|
||||
<p>If this is empty, it is from the cache.</p>
|
||||
<pre class="whitespace-pre-wrap break-all rounded-lg border border-border p-2 text-xs">
|
||||
{seenOn()}
|
||||
</pre>
|
||||
|
||||
@@ -9,6 +9,7 @@ import ExclamationCircle from 'heroicons/24/solid/exclamation-circle.svg';
|
||||
|
||||
import TextNoteContentDisplay from '@/components/event/textNote/TextNoteContentDisplay';
|
||||
import BasicModal from '@/components/modal/BasicModal';
|
||||
import EventDebugModal from '@/components/modal/EventDebugModal';
|
||||
import UserList from '@/components/modal/UserList';
|
||||
import Timeline from '@/components/timeline/Timeline';
|
||||
import SafeLink from '@/components/utils/SafeLink';
|
||||
@@ -58,7 +59,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
|
||||
const [updatingContacts, setUpdatingContacts] = createSignal(false);
|
||||
const [hoverFollowButton, setHoverFollowButton] = createSignal(false);
|
||||
const [showFollowers, setShowFollowers] = createSignal(false);
|
||||
const [modal, setModal] = createSignal<'Following' | null>(null);
|
||||
const [modal, setModal] = createSignal<'Following' | 'EventDebugModal' | null>(null);
|
||||
const closeModal = () => setModal(null);
|
||||
|
||||
const {
|
||||
@@ -219,22 +220,33 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
|
||||
},
|
||||
},
|
||||
{
|
||||
content: i18n.t('profile.addUserColumn'),
|
||||
content: i18n.t('profile.showJSON'),
|
||||
onSelect: () => {
|
||||
const columnName = profile()?.name ?? npub();
|
||||
saveColumn(createPostsColumn({ name: columnName, pubkey: props.pubkey }));
|
||||
request({ command: 'moveToLastColumn' }).catch((err) => console.error(err));
|
||||
props.onClose?.();
|
||||
setModal('EventDebugModal');
|
||||
},
|
||||
},
|
||||
{
|
||||
content: i18n.t('profile.addUserHomeColumn'),
|
||||
onSelect: () => {
|
||||
const columnName = `${i18n.t('column.home')} / ${profile()?.name ?? npub()}`;
|
||||
saveColumn(createFollowingColumn({ name: columnName, pubkey: props.pubkey }));
|
||||
request({ command: 'moveToLastColumn' }).catch((err) => console.error(err));
|
||||
props.onClose?.();
|
||||
},
|
||||
content: i18n.t('profile.addColumn'),
|
||||
items: [
|
||||
{
|
||||
content: i18n.t('profile.addUserColumn'),
|
||||
onSelect: () => {
|
||||
const columnName = profile()?.name ?? npub();
|
||||
saveColumn(createPostsColumn({ name: columnName, pubkey: props.pubkey }));
|
||||
request({ command: 'moveToLastColumn' }).catch((err) => console.error(err));
|
||||
props.onClose?.();
|
||||
},
|
||||
},
|
||||
{
|
||||
content: i18n.t('profile.addUserHomeColumn'),
|
||||
onSelect: () => {
|
||||
const columnName = `${i18n.t('column.home')} / ${profile()?.name ?? npub()}`;
|
||||
saveColumn(createFollowingColumn({ name: columnName, pubkey: props.pubkey }));
|
||||
request({ command: 'moveToLastColumn' }).catch((err) => console.error(err));
|
||||
props.onClose?.();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
content: !isMuted() ? i18n.t('profile.mute') : i18n.t('profile.unmute'),
|
||||
@@ -461,6 +473,15 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
|
||||
<Match when={modal() === 'Following'}>
|
||||
<UserList data={userFollowingPubkeys()} pubkeyExtractor={(e) => e} onClose={closeModal} />
|
||||
</Match>
|
||||
<Match when={modal() === 'EventDebugModal' && profileQuery.data} keyed>
|
||||
{(event) => (
|
||||
<EventDebugModal
|
||||
event={event}
|
||||
extra={JSON.stringify(profile(), null, 2)}
|
||||
onClose={closeModal}
|
||||
/>
|
||||
)}
|
||||
</Match>
|
||||
</Switch>
|
||||
<ul class="border-t border-border p-1">
|
||||
<Timeline events={events()} />
|
||||
|
||||
@@ -1,23 +1,37 @@
|
||||
import { createMemo, For, type Component, type JSX } from 'solid-js';
|
||||
import { createMemo, For, Switch, Match, type Component, type JSX } from 'solid-js';
|
||||
|
||||
import ChevronRight from 'heroicons/24/outline/chevron-right.svg';
|
||||
|
||||
import usePopup, { type UsePopupProps } from '@/components/utils/usePopup';
|
||||
|
||||
export type MenuItem = {
|
||||
export type SelectableItem = {
|
||||
content: JSX.Element;
|
||||
when?: () => boolean;
|
||||
onSelect?: () => void;
|
||||
onSelect: () => void;
|
||||
};
|
||||
|
||||
export type SubMenu = Omit<SelectableItem, 'type' | 'onSelect'> & {
|
||||
content: JSX.Element;
|
||||
items: (SelectableItem | SubMenu)[];
|
||||
};
|
||||
|
||||
export type MenuItem = SelectableItem | SubMenu;
|
||||
|
||||
export type UseContextMenuProps = Omit<UsePopupProps, 'popup'> & {
|
||||
menu: MenuItem[];
|
||||
menu: (MenuItem | SubMenu)[];
|
||||
};
|
||||
|
||||
export type MenuItemDisplayProps = {
|
||||
item: MenuItem;
|
||||
export type SelectableItemDisplayProps = {
|
||||
item: SelectableItem;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const MenuItemDisplay: Component<MenuItemDisplayProps> = (props) => {
|
||||
export type SubMenuDisplayProps = {
|
||||
submenu: SubMenu;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const SelectableItemDisplay: Component<SelectableItemDisplayProps> = (props) => {
|
||||
const handleClick = () => {
|
||||
props.item?.onSelect?.();
|
||||
props.onClose();
|
||||
@@ -32,15 +46,59 @@ const MenuItemDisplay: Component<MenuItemDisplayProps> = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const SubMenuDisplay: Component<SubMenuDisplayProps> = (props) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
const contextMenuPopup = useContextMenu(() => ({
|
||||
menu: props.submenu.items,
|
||||
position: 'right',
|
||||
}));
|
||||
|
||||
const handleClick = () => {
|
||||
contextMenuPopup.open();
|
||||
};
|
||||
|
||||
return (
|
||||
<li ref={contextMenuPopup.targetRef} class="border-b border-border hover:bg-bg-tertiary">
|
||||
<button class="flex w-full items-center py-1 pe-2 ps-4 text-start" onClick={handleClick}>
|
||||
<span class="flex-1">{props.submenu.content}</span>
|
||||
<span class="inline-block size-4 shrink-0">
|
||||
<ChevronRight />
|
||||
</span>
|
||||
</button>
|
||||
{contextMenuPopup.popup()}
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
const ensureSelectableItem = (item: MenuItem): SelectableItem | null => {
|
||||
if ('onSelect' in item) return item;
|
||||
return null;
|
||||
};
|
||||
|
||||
const ensureSubMenu = (item: MenuItem): SubMenu | null => {
|
||||
if ('items' in item) return item;
|
||||
return null;
|
||||
};
|
||||
|
||||
const useContextMenu = (propsProvider: () => UseContextMenuProps) => {
|
||||
const props = createMemo(propsProvider);
|
||||
|
||||
const popup = usePopup(() => ({
|
||||
ensureWidth: 300,
|
||||
...props(),
|
||||
popup: (
|
||||
<ul class="min-w-[96px] rounded border border-border bg-bg shadow-md">
|
||||
<For each={props().menu.filter((e) => e.when == null || e.when())}>
|
||||
{(item: MenuItem) => <MenuItemDisplay item={item} onClose={() => popup.close()} />}
|
||||
{(item) => (
|
||||
<Switch>
|
||||
<Match when={ensureSelectableItem(item)} keyed>
|
||||
{(v) => <SelectableItemDisplay item={v} onClose={() => popup.close()} />}
|
||||
</Match>
|
||||
<Match when={ensureSubMenu(item)} keyed>
|
||||
{(v) => <SubMenuDisplay submenu={v} onClose={() => popup.close()} />}
|
||||
</Match>
|
||||
</Switch>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
),
|
||||
|
||||
@@ -59,10 +59,12 @@ export default {
|
||||
followingCurrently: 'Following',
|
||||
followsYou: 'follows you',
|
||||
copyPubkey: 'Copy ID',
|
||||
showJSON: 'Show JSON',
|
||||
mute: 'Mute',
|
||||
unmute: 'Unmute',
|
||||
followMyself: 'Follow myself',
|
||||
unfollowMyself: 'Unfollow myself',
|
||||
addColumn: 'Add column',
|
||||
addUserColumn: 'Add user column',
|
||||
addUserHomeColumn: 'Add home column',
|
||||
confirmUnfollow: 'Do you really want to unfollow?',
|
||||
|
||||
@@ -58,10 +58,12 @@ export default {
|
||||
followingCurrently: 'フォロー中',
|
||||
followsYou: 'フォローされています',
|
||||
copyPubkey: 'IDをコピー',
|
||||
showJSON: 'JSONを確認',
|
||||
mute: 'ミュート',
|
||||
unmute: 'ミュート解除',
|
||||
followMyself: '自分をフォロー',
|
||||
unfollowMyself: '自分をフォロー解除',
|
||||
addColumn: 'カラムを追加',
|
||||
addUserColumn: 'ユーザカラムを追加',
|
||||
addUserHomeColumn: 'ホームカラムを追加',
|
||||
confirmUnfollow: '本当にフォロー解除しますか?',
|
||||
|
||||
Reference in New Issue
Block a user