diff --git a/src/app.css b/src/app.css
index fac2b4d3..db27fba9 100644
--- a/src/app.css
+++ b/src/app.css
@@ -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 {
diff --git a/src/app/chats/components/chatListItem.tsx b/src/app/chats/components/chatListItem.tsx
index 2fa56470..6aa7ab34 100644
--- a/src/app/chats/components/chatListItem.tsx
+++ b/src/app/chats/components/chatListItem.tsx
@@ -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)}
{decryptedContent}
diff --git a/src/app/new/components/mentionPopupItem.tsx b/src/app/new/components/mentionPopupItem.tsx
index 46645bff..1dde4d29 100644
--- a/src/app/new/components/mentionPopupItem.tsx
+++ b/src/app/new/components/mentionPopupItem.tsx
@@ -47,7 +47,7 @@ export function MentionPopupItem({ pubkey, embed }: { pubkey: string; embed?: st
{user?.display_name || user?.displayName || user?.name}
- {displayNpub(pubkey, 16)}
+ {displayNpub(pubkey)}
diff --git a/src/app/notes/article.tsx b/src/app/notes/article.tsx
index 912489db..4a573637 100644
--- a/src/app/notes/article.tsx
+++ b/src/app/notes/article.tsx
@@ -115,7 +115,7 @@ export function ArticleNoteScreen() {
)}
-
+
diff --git a/src/app/users/components/profile.tsx b/src/app/users/components/profile.tsx
index 7fb7129b..0990beb6 100644
--- a/src/app/users/components/profile.tsx
+++ b/src/app/users/components/profile.tsx
@@ -104,7 +104,7 @@ export function UserProfile({ pubkey }: { pubkey: string }) {
/>
) : (
- {displayNpub(pubkey, 16)}
+ {displayNpub(pubkey)}
)}
diff --git a/src/libs/ark/ark.ts b/src/libs/ark/ark.ts
index 456f8771..db85648f 100644
--- a/src/libs/ark/ark.ts
+++ b/src/libs/ark/ark.ts
@@ -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(
diff --git a/src/shared/notes/actions/more.tsx b/src/shared/notes/actions/more.tsx
index d2f4d9e9..354aa7fc 100644
--- a/src/shared/notes/actions/more.tsx
+++ b/src/shared/notes/actions/more.tsx
@@ -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
+
+
+
{createdAt}
ยท
{fallbackName}
+
+
diff --git a/src/shared/widgets/other/userProfile.tsx b/src/shared/widgets/other/userProfile.tsx
index 4305018d..30debe8d 100644
--- a/src/shared/widgets/other/userProfile.tsx
+++ b/src/shared/widgets/other/userProfile.tsx
@@ -96,7 +96,7 @@ export function UserProfile({ pubkey }: { pubkey: string }) {
/>
) : (
- {displayNpub(pubkey, 16)}
+ {displayNpub(pubkey)}
)}
diff --git a/src/shared/widgets/titleBar.tsx b/src/shared/widgets/titleBar.tsx
index 46dfa48b..50d19e7b 100644
--- a/src/shared/widgets/titleBar.tsx
+++ b/src/shared/widgets/titleBar.tsx
@@ -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 (
@@ -44,14 +55,50 @@ export function TitleBar({
) : null}
- ) : (
-
- {title}
+ ) : !editing ? (
+
+ {cropText(title)}
+ ) : (
+ 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"
+ >
)}
-
- {id !== '9999' && id !== '9998' ? (
+ {id !== '9999' && id !== '9998' ? (
+
+
- ) : null}
-
+
+ ) : null}
);
}
diff --git a/src/utils/formater.ts b/src/utils/formater.ts
index b33fcbf7..477dc4f7 100644
--- a/src/utils/formater.ts
+++ b/src/utils/formater.ts
@@ -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.
diff --git a/src/utils/hooks/useWidget.ts b/src/utils/hooks/useWidget.ts
index c64d5519..3d91d37b 100644
--- a/src/utils/hooks/useWidget.ts
+++ b/src/utils/hooks/useWidget.ts
@@ -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 };
}