feat: migrate note component to i18n

This commit is contained in:
reya 2024-01-29 10:22:55 +07:00
parent b97676dd3e
commit 698bd78684
18 changed files with 125 additions and 47 deletions

View File

@ -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",

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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"

View File

@ -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";

View File

@ -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,

View File

@ -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",

View File

@ -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)

View File

@ -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",