mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-29 16:30:55 +00:00
feat: migrate note component to i18n
This commit is contained in:
parent
b97676dd3e
commit
698bd78684
@ -32,6 +32,7 @@
|
||||
"re-resizable": "^6.9.11",
|
||||
"react": "^18.2.0",
|
||||
"react-currency-input-field": "^3.6.14",
|
||||
"react-i18next": "^14.0.1",
|
||||
"react-router-dom": "^6.21.3",
|
||||
"react-string-replace": "^1.1.1",
|
||||
"sonner": "^1.3.1",
|
||||
|
@ -1,11 +1,14 @@
|
||||
import { PinIcon } from "@lume/icons";
|
||||
import { COL_TYPES } from "@lume/utils";
|
||||
import * as Tooltip from "@radix-ui/react-tooltip";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useColumnContext } from "../../column/provider";
|
||||
import { useNoteContext } from "../provider";
|
||||
|
||||
export function NotePin() {
|
||||
const event = useNoteContext();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { addColumn } = useColumnContext();
|
||||
|
||||
return (
|
||||
@ -24,12 +27,12 @@ export function NotePin() {
|
||||
className="inline-flex items-center justify-center gap-2 pl-2 pr-3 text-sm font-medium rounded-full h-7 w-max bg-neutral-100 hover:bg-neutral-200 dark:hover:bg-neutral-800 dark:bg-neutral-900"
|
||||
>
|
||||
<PinIcon className="size-4" />
|
||||
Pin
|
||||
{t("note.buttons.pin")}
|
||||
</button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
||||
Pin note
|
||||
{t("note.buttons.pinTooltip")}
|
||||
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { ReplyIcon } from "@lume/icons";
|
||||
import * as Tooltip from "@radix-ui/react-tooltip";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNoteContext } from "../provider";
|
||||
|
||||
@ -7,6 +8,8 @@ export function NoteReply() {
|
||||
const event = useNoteContext();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Tooltip.Provider>
|
||||
<Tooltip.Root delayDuration={150}>
|
||||
@ -21,7 +24,7 @@ export function NoteReply() {
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
||||
View thread
|
||||
{t("note.menu.viewThread")}
|
||||
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
|
@ -5,6 +5,7 @@ import * as Tooltip from "@radix-ui/react-tooltip";
|
||||
import { useSetAtom } from "jotai";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { useNoteContext } from "../provider";
|
||||
|
||||
@ -13,6 +14,7 @@ export function NoteRepost() {
|
||||
const setEditorValue = useSetAtom(editorValueAtom);
|
||||
const setIsEditorOpen = useSetAtom(editorAtom);
|
||||
|
||||
const [t] = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isRepost, setIsRepost] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
@ -81,7 +83,7 @@ export function NoteRepost() {
|
||||
</DropdownMenu.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
||||
Repost
|
||||
{t("note.buttons.repost")}
|
||||
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
@ -96,7 +98,7 @@ export function NoteRepost() {
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
>
|
||||
<RepostIcon className="size-4" />
|
||||
Repost
|
||||
{t("note.buttons.repost")}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item asChild>
|
||||
@ -106,7 +108,7 @@ export function NoteRepost() {
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
>
|
||||
<ReplyIcon className="size-4" />
|
||||
Quote
|
||||
{t("note.buttons.quote")}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
|
@ -8,6 +8,7 @@ import * as Tooltip from "@radix-ui/react-tooltip";
|
||||
import { QRCodeSVG } from "qrcode.react";
|
||||
import { useState } from "react";
|
||||
import CurrencyInput from "react-currency-input-field";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { useProfile } from "../../../hooks/useProfile";
|
||||
import { useNoteContext } from "../provider";
|
||||
@ -23,6 +24,7 @@ export function NoteZap() {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [invoice, setInvoice] = useState<string>(null);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { user } = useProfile(event.pubkey);
|
||||
|
||||
const createZapRequest = async (instant?: boolean) => {
|
||||
@ -99,7 +101,7 @@ export function NoteZap() {
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
||||
Zap
|
||||
{t("note.zap.tooltip")}
|
||||
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
@ -124,7 +126,7 @@ export function NoteZap() {
|
||||
</Dialog.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
||||
Zap
|
||||
{t("note.zap.tooltip")}
|
||||
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
@ -145,7 +147,7 @@ export function NoteZap() {
|
||||
<div className="inline-flex items-center justify-center w-full px-5 py-3 shrink-0">
|
||||
<div className="w-6" />
|
||||
<Dialog.Title className="font-semibold text-center">
|
||||
Send zap to{" "}
|
||||
{t("note.zap.modalTitle")}{" "}
|
||||
{user?.name ||
|
||||
user?.displayName ||
|
||||
displayNpub(event.pubkey, 16)}
|
||||
@ -217,7 +219,7 @@ export function NoteZap() {
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
placeholder="Enter message (optional)"
|
||||
placeholder={t("note.zap.messagePlaceholder")}
|
||||
className="w-full resize-none rounded-lg border-transparent bg-neutral-100 px-3 py-3 !outline-none placeholder:text-neutral-600 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-950 dark:text-neutral-400"
|
||||
/>
|
||||
<div className="flex flex-col gap-2">
|
||||
@ -227,10 +229,10 @@ export function NoteZap() {
|
||||
className="inline-flex items-center justify-center w-full pb-[2px] font-semibold border-t rounded-lg border-neutral-900 dark:border-neutral-800 h-9 bg-neutral-950 text-neutral-50 dark:bg-neutral-900 hover:bg-neutral-900 dark:hover:bg-neutral-800"
|
||||
>
|
||||
{isCompleted
|
||||
? "Zapped"
|
||||
? t("note.zap.buttonFinish")
|
||||
: isLoading
|
||||
? "Processing..."
|
||||
: "Zap"}
|
||||
? t("note.zap.buttonLoading")
|
||||
: t("note.zap.zap")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -241,11 +243,11 @@ export function NoteZap() {
|
||||
<QRCodeSVG value={invoice} size={256} />
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<h3 className="text-lg font-medium">Scan to zap</h3>
|
||||
<h3 className="text-lg font-medium">
|
||||
{t("note.zap.invoiceButton")}
|
||||
</h3>
|
||||
<span className="text-center text-sm text-neutral-600 dark:text-neutral-400">
|
||||
You must use Bitcoin wallet which support Lightning
|
||||
<br />
|
||||
such as: Blue Wallet, Bitkit, Phoenix,...
|
||||
{t("note.zap.invoiceFooter")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { NOSTR_MENTIONS } from "@lume/utils";
|
||||
import { nanoid } from "nanoid";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { ReactNode, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import reactStringReplace from "react-string-replace";
|
||||
import { useEvent } from "../../hooks/useEvent";
|
||||
@ -13,6 +13,7 @@ export function NoteChild({
|
||||
eventId,
|
||||
isRoot,
|
||||
}: { eventId: string; isRoot?: boolean }) {
|
||||
const { t } = useTranslation();
|
||||
const { isLoading, isError, data } = useEvent(eventId);
|
||||
|
||||
const richContent = useMemo(() => {
|
||||
@ -91,7 +92,7 @@ export function NoteChild({
|
||||
return (
|
||||
<div className="relative flex gap-3">
|
||||
<div className="relative flex-1 rounded-md bg-neutral-200 px-2 py-2 dark:bg-neutral-800">
|
||||
Failed to fetch event
|
||||
{t("note.error")}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -111,7 +112,7 @@ export function NoteChild({
|
||||
<div className="absolute left-2 top-2 inline-flex items-center gap-1.5 font-semibold leading-tight">
|
||||
<User.Name className="max-w-[10rem] truncate" />
|
||||
<div className="font-normal text-neutral-700 dark:text-neutral-300">
|
||||
{isRoot ? "posted:" : "replied:"}
|
||||
{isRoot ? t("note.posted") : t("note.replied")}:
|
||||
</div>
|
||||
</div>
|
||||
</User.Root>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { PinIcon } from "@lume/icons";
|
||||
import { COL_TYPES, NOSTR_MENTIONS } from "@lume/utils";
|
||||
import { ReactNode, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import reactStringReplace from "react-string-replace";
|
||||
import { useEvent } from "../../../hooks/useEvent";
|
||||
@ -13,6 +14,7 @@ export function MentionNote({
|
||||
eventId,
|
||||
openable = true,
|
||||
}: { eventId: string; openable?: boolean }) {
|
||||
const { t } = useTranslation();
|
||||
const { addColumn } = useColumnContext();
|
||||
const { isLoading, isError, data } = useEvent(eventId);
|
||||
|
||||
@ -98,7 +100,7 @@ export function MentionNote({
|
||||
contentEditable={false}
|
||||
className="w-full p-3 my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900"
|
||||
>
|
||||
Failed to fetch event.
|
||||
{t("note.error")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -127,7 +129,7 @@ export function MentionNote({
|
||||
to={`/events/${data.id}`}
|
||||
className="text-sm text-blue-500 hover:text-blue-600"
|
||||
>
|
||||
Show more
|
||||
{t("note.showMore")}
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { COL_TYPES } from "@lume/utils";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useArk } from "../../../hooks/useArk";
|
||||
import { useProfile } from "../../../hooks/useProfile";
|
||||
@ -10,6 +11,7 @@ export function MentionUser({ pubkey }: { pubkey: string }) {
|
||||
const cleanPubkey = ark.getCleanPubkey(pubkey);
|
||||
|
||||
const { isLoading, isError, user } = useProfile(pubkey);
|
||||
const { t } = useTranslation();
|
||||
const { addColumn } = useColumnContext();
|
||||
|
||||
return (
|
||||
@ -27,7 +29,7 @@ export function MentionUser({ pubkey }: { pubkey: string }) {
|
||||
to={`/users/${cleanPubkey}`}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
>
|
||||
View profile
|
||||
{t("note.buttons.viewProfile")}
|
||||
</Link>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item asChild>
|
||||
@ -36,13 +38,13 @@ export function MentionUser({ pubkey }: { pubkey: string }) {
|
||||
onClick={async () =>
|
||||
await addColumn({
|
||||
kind: COL_TYPES.user,
|
||||
title: user?.name || user?.displayName || "Profile",
|
||||
title: user?.name || user?.displayName || "User",
|
||||
content: cleanPubkey,
|
||||
})
|
||||
}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
>
|
||||
Pin
|
||||
{t("note.buttons.pin")}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
|
@ -5,6 +5,7 @@ import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { type EventPointer } from "nostr-tools/lib/types/nip19";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import { useColumnContext } from "../column/provider";
|
||||
@ -13,7 +14,10 @@ import { useNoteContext } from "./provider";
|
||||
export function NoteMenu() {
|
||||
const event = useNoteContext();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { addColumn } = useColumnContext();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const copyID = async () => {
|
||||
@ -67,7 +71,7 @@ export function NoteMenu() {
|
||||
onClick={() => copyLink()}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
>
|
||||
View thread
|
||||
{t("note.menu.viewThread")}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item asChild>
|
||||
@ -76,7 +80,7 @@ export function NoteMenu() {
|
||||
onClick={() => navigate(`/events/${event.id}`)}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
>
|
||||
Copy shareable link
|
||||
{t("note.menu.copyLink")}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item asChild>
|
||||
@ -85,7 +89,7 @@ export function NoteMenu() {
|
||||
onClick={() => copyID()}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
>
|
||||
Copy note ID
|
||||
{t("note.menu.copyNoteId")}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item asChild>
|
||||
@ -94,7 +98,7 @@ export function NoteMenu() {
|
||||
onClick={() => copyNpub()}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
>
|
||||
Copy author ID
|
||||
{t("note.menu.copyAuthorId")}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item asChild>
|
||||
@ -102,7 +106,7 @@ export function NoteMenu() {
|
||||
to={`/users/${event.pubkey}`}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
>
|
||||
View author
|
||||
{t("note.menu.viewAuthor")}
|
||||
</Link>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item asChild>
|
||||
@ -117,7 +121,7 @@ export function NoteMenu() {
|
||||
}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
>
|
||||
Pin author
|
||||
{t("note.menu.pinAuthor")}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator className="h-px my-1 bg-black/10 dark:bg-white/10" />
|
||||
@ -127,7 +131,7 @@ export function NoteMenu() {
|
||||
onClick={() => copyRaw()}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||
>
|
||||
Copy raw event
|
||||
{t("note.menu.copyRaw")}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item asChild>
|
||||
@ -136,7 +140,7 @@ export function NoteMenu() {
|
||||
onClick={muteUser}
|
||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium text-red-500 rounded-lg h-9 hover:bg-red-500 hover:text-red-50 focus:outline-none"
|
||||
>
|
||||
Mute
|
||||
{t("note.menu.mute")}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useArk } from "../../hooks/useArk";
|
||||
import { AppHandler } from "./appHandler";
|
||||
import { useNoteContext } from "./provider";
|
||||
@ -7,6 +8,7 @@ export function NIP89({ className }: { className?: string }) {
|
||||
const ark = useArk();
|
||||
const event = useNoteContext();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { isLoading, isError, data } = useQuery({
|
||||
queryKey: ["app-recommend", event.id],
|
||||
queryFn: () => {
|
||||
@ -33,7 +35,7 @@ export function NIP89({ className }: { className?: string }) {
|
||||
<div className="flex flex-col rounded-lg bg-neutral-100 dark:bg-neutral-900">
|
||||
<div className="inline-flex items-center justify-between h-10 px-3 border-b shrink-0 border-neutral-200 dark:border-neutral-800">
|
||||
<p className="text-sm font-medium text-amber-400">
|
||||
Lume isn't support this event
|
||||
{t("nip89.unsupported")}
|
||||
</p>
|
||||
<p className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||
{event.kind}
|
||||
@ -41,10 +43,10 @@ export function NIP89({ className }: { className?: string }) {
|
||||
</div>
|
||||
<div className="flex flex-col flex-1 gap-2 px-3 py-3">
|
||||
<span className="text-sm font-medium uppercase text-neutral-600 dark:text-neutral-400">
|
||||
Open with
|
||||
{t("nip89.openWith")}
|
||||
</span>
|
||||
{data.map((item, index) => (
|
||||
<AppHandler key={item[1] + index} tag={item} />
|
||||
{data.map((item) => (
|
||||
<AppHandler key={item[1]} tag={item} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,6 +3,7 @@ import { NDKEventWithReplies } from "@lume/types";
|
||||
import { cn } from "@lume/utils";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Note } from "..";
|
||||
import { ChildReply } from "./childReply";
|
||||
|
||||
@ -11,6 +12,7 @@ export function Reply({
|
||||
}: {
|
||||
event: NDKEventWithReplies;
|
||||
}) {
|
||||
const [t] = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
@ -30,7 +32,9 @@ export function Reply({
|
||||
className={cn("size-5", open ? "rotate-180 transform" : "")}
|
||||
/>
|
||||
{`${event.replies?.length} ${
|
||||
event.replies?.length === 1 ? "reply" : "replies"
|
||||
event.replies?.length === 1
|
||||
? t("note.reply.single")
|
||||
: t("note.reply.plural")
|
||||
}`}
|
||||
</div>
|
||||
</Collapsible.Trigger>
|
||||
|
@ -2,6 +2,7 @@ import { RepostIcon } from "@lume/icons";
|
||||
import { cn } from "@lume/utils";
|
||||
import { NDKEvent, NostrEvent } from "@nostr-dev-kit/ndk";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Note } from "..";
|
||||
import { useArk } from "../../../hooks/useArk";
|
||||
import { User } from "../../user";
|
||||
@ -12,6 +13,7 @@ export function RepostNote({
|
||||
}: { event: NDKEvent; className?: string }) {
|
||||
const ark = useArk();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
isLoading,
|
||||
isError,
|
||||
@ -51,7 +53,7 @@ export function RepostNote({
|
||||
<User.Avatar className="size-6 shrink-0 rounded object-cover" />
|
||||
<div className="inline-flex items-baseline gap-1">
|
||||
<User.Name className="font-medium text-neutral-900 dark:text-neutral-100" />
|
||||
<span className="text-blue-500">reposted</span>
|
||||
<span className="text-blue-500">{t("note.reposted")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</User.Root>
|
||||
@ -59,10 +61,6 @@ export function RepostNote({
|
||||
<div className="px-3 mb-3 select-text">
|
||||
<div className="flex flex-col items-start justify-start px-3 py-3 bg-red-100 rounded-lg dark:bg-red-900">
|
||||
<p className="text-red-500">Failed to get event</p>
|
||||
<p className="text-sm">
|
||||
You can consider enable Outbox in Settings for better event
|
||||
discovery.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Note.Root>
|
||||
@ -85,7 +83,7 @@ export function RepostNote({
|
||||
<User.Avatar className="size-6 shrink-0 rounded object-cover" />
|
||||
<div className="inline-flex items-baseline gap-1">
|
||||
<User.Name className="font-medium text-neutral-900 dark:text-neutral-100" />
|
||||
<span className="text-blue-500">reposted</span>
|
||||
<span className="text-blue-500">{t("note.reposted")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</User.Root>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { PinIcon } from "@lume/icons";
|
||||
import { COL_TYPES, cn } from "@lume/utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Note } from ".";
|
||||
import { useArk } from "../../hooks/useArk";
|
||||
@ -18,6 +19,7 @@ export function NoteThread({
|
||||
tags: event.tags,
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { addColumn } = useColumnContext();
|
||||
|
||||
if (!thread) return null;
|
||||
@ -36,7 +38,7 @@ export function NoteThread({
|
||||
to={`/events/${thread?.rootEventId || thread?.replyEventId}`}
|
||||
className="self-start text-blue-500 hover:text-blue-600"
|
||||
>
|
||||
Show thread
|
||||
{t("note.showThread")}
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { UnverifiedIcon, VerifiedIcon } from "@lume/icons";
|
||||
import { VerifiedIcon } from "@lume/icons";
|
||||
import { cn, displayNpub } from "@lume/utils";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useArk } from "../../hooks/useArk";
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { NDKUserProfile } from "@nostr-dev-kit/ndk";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useArk } from "./useArk";
|
||||
|
||||
@ -20,7 +21,7 @@ export function useProfile(pubkey: string) {
|
||||
return profile;
|
||||
},
|
||||
initialData: () => {
|
||||
return queryClient.getQueryData(["user", pubkey]);
|
||||
return queryClient.getQueryData(["user", pubkey]) as NDKUserProfile;
|
||||
},
|
||||
refetchOnMount: false,
|
||||
refetchOnWindowFocus: false,
|
||||
|
@ -26,6 +26,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.49.3",
|
||||
"react-hotkeys-hook": "^4.4.4",
|
||||
"react-i18next": "^14.0.1",
|
||||
"react-router-dom": "^6.21.3",
|
||||
"slate": "^0.101.5",
|
||||
"slate-react": "^0.101.6",
|
||||
|
@ -365,6 +365,9 @@ importers:
|
||||
react-currency-input-field:
|
||||
specifier: ^3.6.14
|
||||
version: 3.6.14(react@18.2.0)
|
||||
react-i18next:
|
||||
specifier: ^14.0.1
|
||||
version: 14.0.1(i18next@23.8.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
react-router-dom:
|
||||
specifier: ^6.21.3
|
||||
version: 6.21.3(react-dom@18.2.0)(react@18.2.0)
|
||||
@ -999,6 +1002,9 @@ importers:
|
||||
react-hotkeys-hook:
|
||||
specifier: ^4.4.4
|
||||
version: 4.4.4(react-dom@18.2.0)(react@18.2.0)
|
||||
react-i18next:
|
||||
specifier: ^14.0.1
|
||||
version: 14.0.1(i18next@23.8.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
react-router-dom:
|
||||
specifier: ^6.21.3
|
||||
version: 6.21.3(react-dom@18.2.0)(react@18.2.0)
|
||||
|
@ -3,11 +3,55 @@
|
||||
"relay": "Relay",
|
||||
"continue": "Continue",
|
||||
"loading": "Loading",
|
||||
"error": "Error",
|
||||
"moveLeft": "Move Left",
|
||||
"moveRight": "Move Right",
|
||||
"newColumn": "New Column",
|
||||
"inspect": "Inspect"
|
||||
},
|
||||
"nip89": {
|
||||
"unsupported": "Lume isn't support this event",
|
||||
"openWith": "Open with"
|
||||
},
|
||||
"note": {
|
||||
"showThread": "Show thread",
|
||||
"showMore": "Show more",
|
||||
"error": "Failed to fetch event.",
|
||||
"posted": "posted",
|
||||
"replied": "replied",
|
||||
"reposted": "reposted",
|
||||
"menu": {
|
||||
"viewThread": "View thread",
|
||||
"copyLink": "Copy shareable link",
|
||||
"copyNoteId": "Copy note ID",
|
||||
"copyAuthorId": "Copy author ID",
|
||||
"viewAuthor": "View author",
|
||||
"pinAuthor": "Pin author",
|
||||
"copyRaw": "Copy raw event",
|
||||
"mute": "Mute"
|
||||
},
|
||||
"buttons": {
|
||||
"pin": "Pin",
|
||||
"pinTooltip": "Pin Note",
|
||||
"repost": "Repost",
|
||||
"quote": "Quote",
|
||||
"viewProfile": "View profile"
|
||||
},
|
||||
"zap": {
|
||||
"zap": "Zap",
|
||||
"tooltip": "Send zap",
|
||||
"modalTitle": "Send zap to",
|
||||
"messagePlaceholder": "Enter message (optional)",
|
||||
"buttonFinish": "Zapped",
|
||||
"buttonLoading": "Processing...",
|
||||
"invoiceButton": "Scan to zap",
|
||||
"invoiceFooter": "You must use Bitcoin wallet which support Lightning\nsuch as: Blue Wallet, Bitkit, Phoenix,..."
|
||||
},
|
||||
"reply": {
|
||||
"single": "reply",
|
||||
"plural": "replies"
|
||||
}
|
||||
},
|
||||
"welcome": {
|
||||
"title": "Lume is a magnificent client for Nostr to meet, explore\nand freely share your thoughts with everyone.",
|
||||
"signup": "Join Nostr",
|
||||
|
Loading…
Reference in New Issue
Block a user