mirror of
https://github.com/lumehq/lume.git
synced 2025-03-28 02:31:49 +01:00
feat(rail): edit title & open user notes
This commit is contained in:
parent
a93ebd3861
commit
2e23b3ae06
@ -13,9 +13,9 @@
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.prose :where(iframe):not(:where([class~="not-prose"] *)) {
|
||||
@apply aspect-video w-full h-auto mx-auto;
|
||||
}
|
||||
.prose :where(iframe):not(:where([class~='not-prose'] *)) {
|
||||
@apply mx-auto aspect-video h-auto w-full;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
@ -44,7 +44,7 @@ input::-ms-clear {
|
||||
}
|
||||
|
||||
.ProseMirror p.is-empty::before {
|
||||
@apply text-neutral-600 dark:text-neutral-400 float-left h-0 pointer-events-none content-[attr(data-placeholder)];
|
||||
@apply pointer-events-none float-left h-0 text-neutral-600 content-[attr(data-placeholder)] dark:text-neutral-400;
|
||||
}
|
||||
|
||||
.ProseMirror img.ProseMirror-selectednode {
|
||||
|
@ -65,7 +65,7 @@ export const ChatListItem = memo(function ChatListItem({ event }: { event: NDKEv
|
||||
{user?.name ||
|
||||
user?.display_name ||
|
||||
user?.displayName ||
|
||||
displayNpub(event.pubkey, 16)}
|
||||
displayNpub(event.pubkey)}
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="max-w-[10rem] truncate text-sm">{decryptedContent}</div>
|
||||
|
@ -47,7 +47,7 @@ export function MentionPopupItem({ pubkey, embed }: { pubkey: string; embed?: st
|
||||
{user?.display_name || user?.displayName || user?.name}
|
||||
</h5>
|
||||
<span className="text-sm leading-none text-neutral-600 dark:text-neutral-400">
|
||||
{displayNpub(pubkey, 16)}
|
||||
{displayNpub(pubkey)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -115,7 +115,7 @@ export function ArticleNoteScreen() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-span-4 border-l border-neutral-100 px-3 dark:border-neutral-900 xl:col-span-3">
|
||||
<div className="col-span-4 border-l border-neutral-100 px-3 xl:col-span-3 dark:border-neutral-900">
|
||||
<div className="mb-3 border-b border-neutral-100 pb-3 dark:border-neutral-900">
|
||||
<NoteReplyForm rootEvent={data} />
|
||||
</div>
|
||||
|
@ -104,7 +104,7 @@ export function UserProfile({ pubkey }: { pubkey: string }) {
|
||||
/>
|
||||
) : (
|
||||
<span className="max-w-[15rem] truncate text-sm text-neutral-500 dark:text-neutral-400">
|
||||
{displayNpub(pubkey, 16)}
|
||||
{displayNpub(pubkey)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
@ -330,6 +330,14 @@ export class Ark {
|
||||
if (res) return id;
|
||||
}
|
||||
|
||||
public async renameWidget(id: string, title: string) {
|
||||
const res = await this.#storage.execute(
|
||||
'UPDATE widgets SET title = $2 WHERE id = $1;',
|
||||
[id, title]
|
||||
);
|
||||
if (res) return res;
|
||||
}
|
||||
|
||||
public async createSetting(key: string, value: string | undefined) {
|
||||
if (value) {
|
||||
return await this.#storage.execute(
|
||||
|
@ -7,9 +7,14 @@ import { Link } from 'react-router-dom';
|
||||
|
||||
import { HorizontalDotsIcon } from '@shared/icons';
|
||||
|
||||
import { WIDGET_KIND } from '@utils/constants';
|
||||
import { useWidget } from '@utils/hooks/useWidget';
|
||||
|
||||
export function MoreActions({ id, pubkey }: { id: string; pubkey: string }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const { addWidget } = useWidget();
|
||||
|
||||
const copyID = async () => {
|
||||
await writeText(nip19.neventEncode({ id: id, author: pubkey } as EventPointer));
|
||||
setOpen(false);
|
||||
@ -49,6 +54,21 @@ export function MoreActions({ id, pubkey }: { id: string; pubkey: string }) {
|
||||
Copy ID
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex h-10 items-center px-4 text-sm text-neutral-900 hover:bg-neutral-200 focus:outline-none dark:text-neutral-100 dark:hover:bg-neutral-800"
|
||||
onClick={() =>
|
||||
addWidget.mutate({
|
||||
kind: WIDGET_KIND.user,
|
||||
title: pubkey,
|
||||
content: pubkey,
|
||||
})
|
||||
}
|
||||
>
|
||||
Open Notes
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item asChild>
|
||||
<Link
|
||||
to={`/users/${pubkey}`}
|
||||
|
@ -474,6 +474,8 @@ export const User = memo(function User({
|
||||
<span>{createdAt}</span>
|
||||
<span>·</span>
|
||||
<span>{fallbackName}</span>
|
||||
<div className="grow"></div>
|
||||
<MoreActions id={eventId} pubkey={pubkey} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -96,7 +96,7 @@ export function UserProfile({ pubkey }: { pubkey: string }) {
|
||||
/>
|
||||
) : (
|
||||
<span className="max-w-[15rem] truncate text-sm text-neutral-600 dark:text-neutral-400">
|
||||
{displayNpub(pubkey, 16)}
|
||||
{displayNpub(pubkey)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,13 +1,16 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useArk } from '@libs/ark';
|
||||
|
||||
import { CancelIcon } from '@shared/icons';
|
||||
import { CancelIcon, EditIcon, EnterIcon } from '@shared/icons';
|
||||
import { User } from '@shared/user';
|
||||
|
||||
import { cropText } from '@utils/formater';
|
||||
import { useWidget } from '@utils/hooks/useWidget';
|
||||
|
||||
export function TitleBar({
|
||||
id,
|
||||
title,
|
||||
title: aTitle,
|
||||
isLive,
|
||||
}: {
|
||||
id?: string;
|
||||
@ -15,7 +18,15 @@ export function TitleBar({
|
||||
isLive?: boolean;
|
||||
}) {
|
||||
const { ark } = useArk();
|
||||
const { removeWidget } = useWidget();
|
||||
|
||||
const [title, setTitle] = useState(aTitle);
|
||||
const [editing, setEditing] = useState(false);
|
||||
const { removeWidget, renameWidget } = useWidget();
|
||||
|
||||
const submitTitleChange = () => {
|
||||
renameWidget.mutate({ id, title });
|
||||
setEditing(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid h-11 w-full shrink-0 grid-cols-3 items-center px-3">
|
||||
@ -44,14 +55,50 @@ export function TitleBar({
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<h3 className="text-sm font-semibold text-neutral-900 dark:text-neutral-100">
|
||||
{title}
|
||||
) : !editing ? (
|
||||
<h3
|
||||
title={title}
|
||||
className="text-sm font-semibold text-neutral-900 dark:text-neutral-100"
|
||||
>
|
||||
{cropText(title)}
|
||||
</h3>
|
||||
) : (
|
||||
<input
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
onKeyUp={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
submitTitleChange();
|
||||
}
|
||||
if (event.key === 'Escape') {
|
||||
setTitle(aTitle);
|
||||
setEditing(false);
|
||||
}
|
||||
}}
|
||||
type="text"
|
||||
spellCheck={false}
|
||||
autoFocus={editing}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
placeholder="type here..."
|
||||
value={title}
|
||||
className="dark:transparent max-h-6 border-transparent bg-transparent px-3 text-sm placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
||||
></input>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-span-1 flex justify-end">
|
||||
{id !== '9999' && id !== '9998' ? (
|
||||
{id !== '9999' && id !== '9998' ? (
|
||||
<div className="col-span-1 flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => (editing ? submitTitleChange() : setEditing(true))}
|
||||
className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded text-neutral-900 backdrop-blur-xl hover:bg-neutral-100 dark:text-neutral-100 dark:hover:bg-neutral-900"
|
||||
>
|
||||
{editing ? (
|
||||
<EnterIcon className="h-3 w-3" />
|
||||
) : (
|
||||
<EditIcon className="h-3 w-3" />
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeWidget.mutate(id)}
|
||||
@ -59,8 +106,8 @@ export function TitleBar({
|
||||
>
|
||||
<CancelIcon className="h-3 w-3" />
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -43,9 +43,14 @@ export function formatCreatedAt(time: number, message: boolean = false) {
|
||||
return formated;
|
||||
}
|
||||
|
||||
export function displayNpub(pubkey: string, len: number, separator?: string) {
|
||||
export function displayNpub(pubkey: string, len: number = 16, separator?: string) {
|
||||
const npub = pubkey.startsWith('npub1') ? pubkey : (nip19.npubEncode(pubkey) as string);
|
||||
if (npub.length <= len) return npub;
|
||||
|
||||
return cropText(npub, len, separator);
|
||||
}
|
||||
|
||||
export function cropText(text: string, len: number = 16, separator?: string) {
|
||||
if (text.length <= len) return text;
|
||||
|
||||
separator = separator || ' ... ';
|
||||
|
||||
@ -54,7 +59,7 @@ export function displayNpub(pubkey: string, len: number, separator?: string) {
|
||||
frontChars = Math.ceil(charsToShow / 2),
|
||||
backChars = Math.floor(charsToShow / 2);
|
||||
|
||||
return npub.substr(0, frontChars) + separator + npub.substr(npub.length - backChars);
|
||||
return text.substr(0, frontChars) + separator + text.substr(text.length - backChars);
|
||||
}
|
||||
|
||||
// convert number to K, M, B, T, etc.
|
||||
|
@ -67,5 +67,29 @@ export function useWidget() {
|
||||
},
|
||||
});
|
||||
|
||||
return { addWidget, replaceWidget, removeWidget };
|
||||
const renameWidget = useMutation({
|
||||
mutationFn: async ({ id, title }: { id: string; title: string }) => {
|
||||
// Cancel any outgoing refetches
|
||||
await queryClient.cancelQueries({ queryKey: ['widgets'] });
|
||||
|
||||
// Snapshot the previous value
|
||||
const prevWidgets = queryClient.getQueryData(['widgets']);
|
||||
|
||||
// Optimistically update to the new value
|
||||
queryClient.setQueryData(['widgets'], (prev: Widget[]) =>
|
||||
prev.filter((t) => t.id !== id)
|
||||
);
|
||||
|
||||
// Update in database
|
||||
await ark.renameWidget(id, title);
|
||||
|
||||
// Return a context object with the snapshotted value
|
||||
return { prevWidgets };
|
||||
},
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['widgets'] });
|
||||
},
|
||||
});
|
||||
|
||||
return { addWidget, replaceWidget, removeWidget, renameWidget };
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user