forked from Kieran/snort
1
0
Fork 0

Compare commits

...

33 Commits

Author SHA1 Message Date
kieran 44435db21d
fix: fetch results 2024-06-18 10:09:41 +01:00
kieran 64a34e43f0
fix: build 2024-06-17 14:28:41 +01:00
kieran e26b881297
chore: formatting 2024-06-17 14:26:46 +01:00
kieran 5bd00491f6
chore: cleanup worker relay 2024-06-17 14:26:46 +01:00
kieran 471c10feb0 chore: Update translations 2024-06-17 13:21:50 +00:00
kieran a31c054326
chore: cache compressed filters 2024-06-17 14:20:26 +01:00
kieran 240c3ba1c3 chore: Update translations 2024-06-05 14:39:17 +00:00
kieran ebc45ae9c1
chore: move sqlite files
fix: debug print for deletes
2024-06-05 15:33:11 +01:00
kieran b2b25377cd
chore: bump pkgs 2024-06-05 14:26:13 +01:00
kieran c47d8e7dcd
feat: dev-docs 2024-06-05 13:25:34 +01:00
kieran 57bf51c41c
refactor: move connection sync module 2024-06-05 13:08:55 +01:00
kieran 4185f117cb
chore: adjust sync method 2024-06-04 13:16:27 +01:00
Kieran de1504b4bf chore: Update translations 2024-06-03 08:04:37 +00:00
Kieran 5eb5255ed8
fix: lockfile 2024-06-03 09:03:34 +01:00
Kieran 8feb178ed5
Merge pull request #585 from enjikaka/feature/tidal-encrypted-media
[TIDAL Embeds] Add allow and sandbox attributes
2024-06-03 08:59:36 +01:00
Jeremy Karlsson 0c90f248fd
Add allow and sandbox attributes to TIDAL Embeds. 2024-06-03 09:53:53 +02:00
kieran f2f8b6b225
chore: ToNostrEventTag.equals 2024-05-28 13:09:34 +01:00
kieran bc2169a186
chore: bump packages 2024-05-23 12:06:23 +01:00
kieran b764cc1535
feat: worker-relay delete
fix: worker-relay insert replacable events duplicate
2024-05-23 11:59:48 +01:00
kieran bb5bf34fe9
chore: bump system-wasm 2024-05-22 20:14:40 +01:00
kieran 1c29e3b1c6 chore: Update translations 2024-05-22 17:38:06 +00:00
kieran 6cbc3aeb7f
fix: lazy init nip46, no pubkey 2024-05-22 18:24:51 +01:00
kieran 9e896c5c27 chore: Update translations 2024-05-15 14:54:03 +00:00
kieran 1512f38e7c
chore: bump pkgs 2024-05-15 15:52:55 +01:00
kieran bb2b0901c4 chore: Update translations 2024-05-14 12:17:32 +00:00
kieran 5763d91e8a
feat: NIP-96 server list 2024-05-14 13:16:03 +01:00
kieran 7d0d3030f4
fix: attach file extension to nip96 uploads 2024-05-14 12:16:27 +01:00
kieran f8f54a4e50
fix: service worker bug 2024-05-13 14:54:03 +01:00
kieran 9b99d0e2ea
chore: add cache-control headers for service-worker.js 2024-05-13 14:45:58 +01:00
kieran 8daadb47c2 chore: Update translations 2024-05-13 13:34:11 +00:00
kieran 1060122263
fix: service worker 2024-05-13 14:33:14 +01:00
kieran 9feb98f277 chore: Update translations 2024-05-13 13:20:49 +00:00
kieran b5ca5327db
feat: custom nip96 server 2024-05-13 14:19:28 +01:00
79 changed files with 1314 additions and 496 deletions

30
dev-docs/query.md Normal file
View File

@ -0,0 +1,30 @@
# Reactions
## Problem
When presented with a feed of notes, either a timeline (social) or a live chat log (live stream chat)
how do you fetch the reactions to such notes and maintain realtime updates.
## Current solution
When a list of reactions is requested we use the expensive `buildDiff` operation to compute a
list of new (added) filters and send them to relays.
Usually if `leaveOpen` is specified (as it should be for realtime updates) this new trace will be sent
as a separate subscription causing exhasution.
Another side effect of this this approach is that over time (especially in live chat) the number of filters that get passed to `buildDiff` increases and so the computation time takes longer and causes jank (https://git.v0l.io/Kieran/zap.stream/issues/126).
There is also the question of updating the "root" query, since this is not updated, each independant query trace receives its own set of updates which is a problem of its own.
## Proposed solution (Live chat)
The ideal solution is to update only the "root" query as new filters are detected along with appending the current timestamp as the `since` value.
In this way only 1 subscription is maintained, the "root" query trace.
Each time a new set of filters is created from `buildDiff` we push the same `REQ` again with the new filters which **should** result in no new results from the relays as we expect there to be none `since` the current time is the time of the latest message.
## Proposed solution (Timeline)
TBD

View File

@ -26,7 +26,7 @@
"typescript": "^5.2.2"
},
"devDependencies": {
"@tauri-apps/cli": "^1.5.11",
"@tauri-apps/cli": "^1.5.14",
"typedoc": "^0.25.7"
}
}

View File

@ -4,11 +4,11 @@
"dependencies": {
"@cashu/cashu-ts": "^1.0.0-rc.3",
"@here/maps-api-for-javascript": "^1.50.0",
"@noble/curves": "^1.0.0",
"@noble/hashes": "^1.3.3",
"@scure/base": "^1.1.1",
"@scure/bip32": "^1.3.0",
"@scure/bip39": "^1.1.1",
"@noble/curves": "^1.4.0",
"@noble/hashes": "^1.4.0",
"@scure/base": "^1.1.6",
"@scure/bip32": "^1.4.0",
"@scure/bip39": "^1.3.0",
"@snort/shared": "workspace:*",
"@snort/system": "workspace:*",
"@snort/system-react": "workspace:*",
@ -16,7 +16,7 @@
"@snort/system-web": "workspace:*",
"@snort/wallet": "workspace:*",
"@snort/worker-relay": "workspace:*",
"@szhsin/react-menu": "^3.3.1",
"@szhsin/react-menu": "^3.5.3",
"@uidotdev/usehooks": "^2.4.1",
"@void-cat/api": "^1.0.12",
"classnames": "^2.3.2",
@ -102,7 +102,7 @@
"@typescript-eslint/eslint-plugin": "^6.1.0",
"@typescript-eslint/parser": "^6.1.0",
"@vitejs/plugin-react": "^4.2.0",
"@webbtc/webln-types": "^2.1.0",
"@webbtc/webln-types": "^3.0.0",
"@webscopeio/react-textarea-autocomplete": "^4.9.2",
"@welldone-software/why-did-you-render": "^8.0.1",
"autoprefixer": "^10.4.16",

View File

@ -1,2 +1,4 @@
/*
Content-Security-Policy: default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; frame-src https://youtube.com https://www.youtube.com https://platform.twitter.com https://embed.tidal.com https://w.soundcloud.com https://www.mixcloud.com https://open.spotify.com https://player.twitch.tv https://embed.music.apple.com https://embed.wavlake.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; connect-src *; img-src * data: blob:; font-src 'self'; media-src * blob:; script-src 'self' 'wasm-unsafe-eval' https://platform.twitter.com https://embed.tidal.com https://challenges.cloudflare.com;
Content-Security-Policy: default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; frame-src https://youtube.com https://www.youtube.com https://platform.twitter.com https://embed.tidal.com https://w.soundcloud.com https://www.mixcloud.com https://open.spotify.com https://player.twitch.tv https://embed.music.apple.com https://embed.wavlake.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; connect-src *; img-src * data: blob:; font-src 'self'; media-src * blob:; script-src 'self' 'wasm-unsafe-eval' https://platform.twitter.com https://embed.tidal.com https://challenges.cloudflare.com;
/service-worker.js
Cache-Control: max-age=604800, must-revalidate;

View File

@ -1,2 +1,4 @@
/*
Content-Security-Policy: default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; frame-src https://youtube.com https://www.youtube.com https://platform.twitter.com https://embed.tidal.com https://w.soundcloud.com https://www.mixcloud.com https://open.spotify.com https://player.twitch.tv https://embed.music.apple.com https://embed.wavlake.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; connect-src *; img-src * data: blob:; font-src 'self'; media-src * blob:; script-src 'self' 'wasm-unsafe-eval' https://platform.twitter.com https://embed.tidal.com https://challenges.cloudflare.com;
Content-Security-Policy: default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; frame-src https://youtube.com https://www.youtube.com https://platform.twitter.com https://embed.tidal.com https://w.soundcloud.com https://www.mixcloud.com https://open.spotify.com https://player.twitch.tv https://embed.music.apple.com https://embed.wavlake.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; connect-src *; img-src * data: blob:; font-src 'self'; media-src * blob:; script-src 'self' 'wasm-unsafe-eval' https://platform.twitter.com https://embed.tidal.com https://challenges.cloudflare.com;
/service-worker.js
Cache-Control: max-age=604800, must-revalidate;

View File

@ -13,6 +13,7 @@ export const Relay = new WorkerRelayInterface(
);
export async function initRelayWorker() {
try {
await Relay.debug("");
await Relay.init({
databasePath: "relay.db",
insertBatchSize: 100,

View File

@ -54,8 +54,15 @@ const TidalEmbed = ({ link }: { link: string }) => {
);
}
const iframe = (
// eslint-disable-next-line react/no-unknown-property
<iframe src={source} style={extraStyles} width="100%" title="TIDAL Embed" frameBorder={0} credentialless="" />
<iframe
src={source}
style={extraStyles}
width="100%"
allow="encrypted-media *; clipboard-write *; clipboard-read *"
sandbox="allow-scripts allow-popups allow-forms allow-same-origin"
title="TIDAL Embed"
frameBorder={0}
/>
);
return (
<>

View File

@ -13,7 +13,7 @@ export function useNotificationsView() {
rb.withOptions({
leaveOpen: true,
});
rb.withFilter().kinds(kinds).tag("p", [publicKey]).limit(100);
rb.withFilter().kinds(kinds).tag("p", [publicKey]);
return rb;
}
}, [publicKey]);

View File

@ -0,0 +1,59 @@
import { NostrEvent, TaggedNostrEvent } from "@snort/system";
import { SnortContext } from "@snort/system-react";
import { useContext, useState } from "react";
import AsyncButton from "@/Components/Button/AsyncButton";
export function DebugPage() {
const system = useContext(SnortContext);
const [filter, setFilter] = useState("");
const [event, setEvent] = useState("");
const [results, setResult] = useState<Array<TaggedNostrEvent>>([]);
async function search() {
if (filter && system.cacheRelay) {
const r = await system.cacheRelay.query(["REQ", "test", JSON.parse(filter)]);
setResult(r.map(a => ({ ...a, relays: [] })));
}
}
async function insert() {
if (event && system.cacheRelay) {
const r = await system.cacheRelay.event(JSON.parse(event) as NostrEvent);
setResult([
{
content: JSON.stringify(r),
} as unknown as TaggedNostrEvent,
]);
}
}
async function removeEvents() {
if (filter && system.cacheRelay) {
const r = await system.cacheRelay.delete(["REQ", "delete-events", JSON.parse(filter)]);
setResult(r.map(a => ({ id: a }) as TaggedNostrEvent));
}
}
return (
<div className="flex flex-col gap-2">
<h3>Cache Query</h3>
<textarea value={filter} onChange={e => setFilter(e.target.value)} placeholder="nostr filter" />
<AsyncButton onClick={() => search()}>Query</AsyncButton>
<AsyncButton onClick={() => removeEvents()} className="!bg-red-500">
Delete
</AsyncButton>
<h3>Manual Insert</h3>
<textarea value={event} onChange={e => setEvent(e.target.value)} placeholder="nostr event" />
<AsyncButton onClick={() => insert()}>Insert</AsyncButton>
<div className="p-4 overflow-hidden">
<h4>Results: {results.length}</h4>
{results?.map(a => (
<pre key={a.id} className="text-mono text-xs text-pretty">
{JSON.stringify(a, undefined, 2)}
</pre>
))}
</div>
</div>
);
}

View File

@ -2,7 +2,7 @@ import "./Notifications.css";
import { unwrap } from "@snort/shared";
import { NostrEvent, NostrLink, TaggedNostrEvent } from "@snort/system";
import { lazy, Suspense, useEffect, useMemo } from "react";
import { lazy, Suspense, useEffect, useMemo, useState } from "react";
import { AutoLoadMore } from "@/Components/Event/LoadMore";
import PageSpinner from "@/Components/PageSpinner";
@ -19,6 +19,7 @@ export default function NotificationsPage({ onClick }: { onClick?: (link: NostrL
const login = useLogin();
const { isMuted } = useModeration();
const groupInterval = 3600 * 6;
const [limit, setLimit] = useState(100);
useEffect(() => {
markNotificationsRead(login);
@ -32,8 +33,11 @@ export default function NotificationsPage({ onClick }: { onClick?: (link: NostrL
};
const myNotifications = useMemo(() => {
return notifications.filter(a => !isMuted(a.pubkey) && a.tags.some(b => b[0] === "p" && b[1] === login.publicKey));
}, [notifications, login.publicKey]);
return notifications
.sort((a, b) => (a.created_at > b.created_at ? -1 : 1))
.slice(0, limit)
.filter(a => !isMuted(a.pubkey) && a.tags.some(b => b[0] === "p" && b[1] === login.publicKey));
}, [notifications, login.publicKey, limit]);
const timeGrouped = useMemo(() => {
return myNotifications.reduce((acc, v) => {
@ -54,13 +58,17 @@ export default function NotificationsPage({ onClick }: { onClick?: (link: NostrL
<div className="main-content">
{CONFIG.features.notificationGraph && (
<Suspense fallback={<PageSpinner />}>
<NotificationGraph evs={myNotifications} />
<NotificationGraph evs={notifications} />
</Suspense>
)}
{login.publicKey &&
[...timeGrouped.entries()].map(([k, g]) => <NotificationGroup key={k} evs={g} onClick={onClick} />)}
<AutoLoadMore onClick={() => {}} />
<AutoLoadMore
onClick={() => {
setLimit(l => l + 100);
}}
/>
</div>
</>
);

View File

@ -1,6 +1,7 @@
import { FeedCache } from "@snort/shared";
import { ReactNode, useEffect, useState, useSyncExternalStore } from "react";
import { FormattedMessage, FormattedNumber } from "react-intl";
import { useNavigate } from "react-router-dom";
import { GiftsCache, Relay, RelayMetrics } from "@/Cache";
import AsyncButton from "@/Components/Button/AsyncButton";
@ -32,7 +33,6 @@ function CacheDetails<T>({ cache, name }: { cache: FeedCache<T>; name: ReactNode
<small>
<FormattedMessage
defaultMessage="{count} ({count2} in memory)"
id="geppt8"
values={{
count: <FormattedNumber value={cache.keysOnTable().length} />,
count2: <FormattedNumber value={snapshot.length} />,
@ -53,6 +53,7 @@ function RelayCacheStats() {
const [counts, setCounts] = useState<Record<string, number>>({});
const [myEvents, setMyEvents] = useState<number>(0);
const login = useLogin();
const navigate = useNavigate();
useEffect(() => {
Relay.summary().then(setCounts);
@ -69,7 +70,6 @@ function RelayCacheStats() {
<p>
<FormattedMessage
defaultMessage="My events: {n}"
id="lEnclp"
values={{
n: <FormattedNumber value={myEvents} />,
}}
@ -124,6 +124,9 @@ function RelayCacheStats() {
}}>
<FormattedMessage defaultMessage="Dump" />
</AsyncButton>
<AsyncButton onClick={() => navigate("/cache-debug")}>
<FormattedMessage defaultMessage="Debug" />
</AsyncButton>
</div>
</div>
);

View File

@ -125,6 +125,12 @@ const SettingsIndex = () => {
message: <FormattedMessage defaultMessage="Cache" />,
path: "cache",
},
{
icon: "camera-plus",
iconBg: "bg-lime-500",
message: <FormattedMessage defaultMessage="Media" />,
path: "media",
},
],
},
{

View File

@ -469,6 +469,9 @@ const PreferencesPage = () => {
fileUploader: e.target.value,
} as UserPreferences)
}>
<option value="nip96">
<FormattedMessage defaultMessage="NIP-96" />
</option>
<option value="void.cat">
void.cat <FormattedMessage {...messages.Default} />
</option>

View File

@ -4,6 +4,7 @@ import AccountsPage from "@/Pages/settings/Accounts";
import { CacheSettings } from "@/Pages/settings/Cache";
import { ManageHandleRoutes } from "@/Pages/settings/handle";
import ExportKeys from "@/Pages/settings/Keys";
import MediaSettingsPage from "@/Pages/settings/media-settings";
import Menu from "@/Pages/settings/Menu/Menu";
import ModerationSettings from "@/Pages/settings/Moderation";
import Notifications from "@/Pages/settings/Notifications";
@ -65,6 +66,10 @@ export default [
path: "cache",
element: <CacheSettings />,
},
{
path: "media",
element: <MediaSettingsPage />,
},
{
path: "invite",
element: <ReferralsPage />,

View File

@ -0,0 +1,98 @@
import { unwrap } from "@snort/shared";
import { EventKind, UnknownTag } from "@snort/system";
import { useState } from "react";
import { FormattedMessage } from "react-intl";
import AsyncButton from "@/Components/Button/AsyncButton";
import IconButton from "@/Components/Button/IconButton";
import useEventPublisher from "@/Hooks/useEventPublisher";
import useLogin from "@/Hooks/useLogin";
import { Nip96Uploader } from "@/Utils/Upload/Nip96";
export default function MediaSettingsPage() {
const { state } = useLogin(s => ({ v: s.state.version, state: s.state }));
const { publisher } = useEventPublisher();
const list = state.getList(EventKind.StorageServerList);
const [newServer, setNewServer] = useState("");
const [error, setError] = useState("");
async function validateServer() {
if (!publisher) return;
setError("");
try {
const svc = new Nip96Uploader(newServer, publisher);
await svc.loadInfo();
return true;
} catch (e) {
if (e instanceof Error) {
setError(e.message);
}
return false;
}
}
return (
<div className="flex flex-col gap-3">
<div className="text-xl">
<FormattedMessage defaultMessage="Media Servers" />
</div>
<p>
<FormattedMessage defaultMessage="Media servers store media which you can share in notes as images and videos" />
</p>
<div className="flex flex-col gap-3">
{list.map(a => {
const [, addr] = unwrap(a.toEventTag());
return (
<div key={addr} className="p br bg-ultradark flex justify-between items-center">
{addr}
<IconButton
icon={{
name: "trash",
size: 15,
}}
onClick={async () => {
await state.removeFromList(EventKind.StorageServerList, [new UnknownTag(["server", addr])], true);
}}
/>
</div>
);
})}
{list.length === 0 && (
<small>
<FormattedMessage defaultMessage="You dont have any media servers, try adding some." />
</small>
)}
</div>
<div className="p br bg-ultradark flex flex-col gap-2">
<div className="text-lg">
<FormattedMessage defaultMessage="Add Server" />
</div>
<div className="flex gap-2">
<input
type="text"
className="flex-grow"
placeholder="https://my-files.com/"
value={newServer}
onChange={e => setNewServer(e.target.value)}
/>
<AsyncButton
onClick={async () => {
if (await validateServer()) {
await state.addToList(
EventKind.StorageServerList,
[new UnknownTag(["server", new URL(newServer).toString()])],
true,
);
setNewServer("");
}
}}>
<FormattedMessage defaultMessage="Add" />
</AsyncButton>
</div>
{error && <b className="text-warning">{error}</b>}
</div>
</div>
);
}

View File

@ -20,7 +20,7 @@ export default function SyncAccountTool() {
const relays = Object.entries(myRelays)
.filter(([, v]) => v.write)
.map(([k]) => k);
const sync = new RangeSync(system);
const sync = RangeSync.forSystem(system);
sync.on("event", evs => {
setResults(r => [...r, ...evs]);
});

View File

@ -3,6 +3,7 @@ import * as utils from "@noble/curves/abstract/utils";
import * as secp from "@noble/curves/secp256k1";
import { ExternalStore, unwrap } from "@snort/shared";
import {
EventKind,
EventPublisher,
HexKey,
KeyStorage,
@ -111,6 +112,7 @@ export class MultiAccountStore extends ExternalStore<LoginSession> {
},
stateObj,
);
stateClass.checkIsStandardList(EventKind.StorageServerList); // track nip96 list
stateClass.on("change", () => this.#save());
v.state = stateClass;
@ -197,6 +199,7 @@ export class MultiAccountStore extends ExternalStore<LoginSession> {
stalker: stalker ?? false,
} as LoginSession;
newSession.state.checkIsStandardList(EventKind.StorageServerList); // track nip96 list
newSession.state.on("change", () => this.#save());
const pub = createPublisher(newSession);
if (pub) {
@ -246,6 +249,7 @@ export class MultiAccountStore extends ExternalStore<LoginSession> {
appdataId: "snort",
}),
} as LoginSession;
newSession.state.checkIsStandardList(EventKind.StorageServerList); // track nip96 list
newSession.state.on("change", () => this.#save());
if ("nostr_os" in window && window?.nostr_os) {

View File

@ -46,7 +46,7 @@ export interface UserPreferences {
/**
* File uploading service to upload attachments to
*/
fileUploader: "void.cat" | "nostr.build" | "nostrimg.com" | "void.cat-NIP96" | "nostrcheck.me";
fileUploader: "void.cat" | "nostr.build" | "nostrimg.com" | "void.cat-NIP96" | "nostrcheck.me" | "nip96";
/**
* Use imgproxy to optimize images

View File

@ -2,13 +2,16 @@ import { base64 } from "@scure/base";
import { throwIfOffline } from "@snort/shared";
import { EventKind, EventPublisher } from "@snort/system";
import { FileExtensionRegex } from "../Const";
import { Uploader, UploadResult } from ".";
export class Nip96Uploader implements Uploader {
constructor(
readonly url: string,
readonly publisher: EventPublisher,
) {}
) {
this.url = new URL(this.url).toString();
}
get progress() {
return [];
@ -57,21 +60,46 @@ export class Nip96Uploader implements Uploader {
.find(a => a[0] === "dim")
?.at(1)
?.split("x");
const mime = data.nip94_event.tags.find(a => a[0] === "m")?.at(1) ?? "";
let url = data.nip94_event.tags.find(a => a[0] === "url")?.at(1) ?? "";
if (!url.match(FileExtensionRegex) && mime) {
switch (mime) {
case "image/webp": {
url += ".webp";
break;
}
default: {
url += ".jpg";
break;
}
}
}
return {
url: data.nip94_event.tags.find(a => a[0] === "url")?.at(1),
url,
metadata: {
width: dim?.at(0) ? Number(dim[0]) : undefined,
height: dim?.at(1) ? Number(dim[1]) : undefined,
blurhash: data.nip94_event.tags.find(a => a[0] === "blurhash")?.at(1),
hash: data.nip94_event.tags.find(a => a[0] === "x")?.at(1),
},
};
}
return {
error: data.message,
};
} else {
const text = await rsp.text();
try {
const obj = JSON.parse(text) as Nip96Result;
return {
error: obj.message,
};
} catch {
return {
error: `Upload failed: ${text}`,
};
}
}
return {
error: "Upload failed",
};
}
}

View File

@ -0,0 +1 @@
export class BlossomClient {}

View File

@ -1,10 +1,12 @@
import { NostrEvent } from "@snort/system";
import { removeUndefined } from "@snort/shared";
import { EventKind, NostrEvent } from "@snort/system";
import { useState } from "react";
import { v4 as uuid } from "uuid";
import useEventPublisher from "@/Hooks/useEventPublisher";
import useLogin from "@/Hooks/useLogin";
import usePreferences from "@/Hooks/usePreferences";
import { bech32ToHex, unwrap } from "@/Utils";
import { bech32ToHex, randomSample, unwrap } from "@/Utils";
import { KieranPubKey } from "@/Utils/Const";
import NostrBuild from "@/Utils/Upload/NostrBuild";
import NostrImg from "@/Utils/Upload/NostrImg";
@ -48,6 +50,10 @@ export const UploaderServices = [
name: "nostrimg.com",
owner: bech32ToHex("npub1xv6axulxcx6mce5mfvfzpsy89r4gee3zuknulm45cqqpmyw7680q5pxea6"),
},
{
name: "nostrcheck.me",
owner: bech32ToHex("npub138s5hey76qrnm2pmv7p8nnffhfddsm8sqzm285dyc0wy4f8a6qkqtzx624"),
},
];
export interface Uploader {
@ -66,10 +72,43 @@ export type UploadStage = "starting" | "hashing" | "uploading" | "done" | undefi
export default function useFileUpload(): Uploader {
const fileUploader = usePreferences(s => s.fileUploader);
const { state } = useLogin(s => ({ v: s.state.version, state: s.state }));
const { publisher } = useEventPublisher();
const [progress, setProgress] = useState<Array<UploadProgress>>([]);
const [stage, setStage] = useState<UploadStage>();
const defaultUploader = {
upload: async (f, n) => {
const id = uuid();
setProgress(s => [
...s,
{
id,
file: f,
progress: 0,
stage: undefined,
},
]);
const px = (n: number) => {
setProgress(s =>
s.map(v =>
v.id === id
? {
...v,
progress: n,
}
: v,
),
);
};
const ret = await VoidCat(f, n, publisher, px, s => setStage(s));
setProgress(s => s.filter(a => a.id !== id));
return ret;
},
progress,
stage,
} as Uploader;
switch (fileUploader) {
case "nostr.build": {
return {
@ -89,38 +128,17 @@ export default function useFileUpload(): Uploader {
progress: [],
} as Uploader;
}
case "nip96": {
const servers = removeUndefined(state.getList(EventKind.StorageServerList).map(a => a.toEventTag()?.at(1)));
if (servers.length > 0) {
const random = randomSample(servers, 1)[0];
return new Nip96Uploader(random, unwrap(publisher));
} else {
return defaultUploader;
}
}
default: {
return {
upload: async (f, n) => {
const id = uuid();
setProgress(s => [
...s,
{
id,
file: f,
progress: 0,
stage: undefined,
},
]);
const px = (n: number) => {
setProgress(s =>
s.map(v =>
v.id === id
? {
...v,
progress: n,
}
: v,
),
);
};
const ret = await VoidCat(f, n, publisher, px, s => setStage(s));
setProgress(s => s.filter(a => a.id !== id));
return ret;
},
progress,
stage,
} as Uploader;
return defaultUploader;
}
}
}

View File

@ -2,17 +2,19 @@ import "./index.css";
import "@szhsin/react-menu/dist/index.css";
import "@/assets/fonts/inter.css";
import { unixNow } from "@snort/shared";
import { SnortContext } from "@snort/system-react";
import { StrictMode } from "react";
import * as ReactDOM from "react-dom/client";
import { createBrowserRouter, RouteObject, RouterProvider } from "react-router-dom";
import { initRelayWorker, preload, UserCache } from "@/Cache";
import { initRelayWorker, preload, Relay, UserCache } from "@/Cache";
import { ThreadRoute } from "@/Components/Event/Thread/ThreadRoute";
import { IntlProvider } from "@/Components/IntlProvider/IntlProvider";
import { db } from "@/Db";
import { addCachedMetadataToFuzzySearch } from "@/Db/FuzzySearch";
import { AboutPage } from "@/Pages/About";
import { DebugPage } from "@/Pages/CacheDebug";
import { SnortDeckLayout } from "@/Pages/Deck/DeckLayout";
import DonatePage from "@/Pages/Donate/DonatePage";
import ErrorPage from "@/Pages/ErrorPage";
@ -42,6 +44,7 @@ import { hasWasm, wasmInit, WasmPath } from "@/Utils/wasm";
import { Wallets } from "@/Wallet";
import { setupWebLNWalletConfig } from "@/Wallet";
import { Day } from "./Utils/Const";
import { LoginStore } from "./Utils/Login";
async function initSite() {
@ -68,12 +71,19 @@ async function initSite() {
}
});
// cleanup
Relay.delete(["REQ", "cleanup", { kinds: [1, 7, 9735], until: unixNow() - Day * 30 }]);
return null;
}
let didInit = false;
const mainRoutes = [
...RootRoutes,
{
path: "/cache-debug",
element: <DebugPage />,
},
{
path: "/help",
element: <HelpPage />,

View File

@ -62,6 +62,9 @@
"01iNut": {
"defaultMessage": "Nostr address does not belong to you"
},
"08zn6O": {
"defaultMessage": "Export Keys"
},
"0Azlrb": {
"defaultMessage": "Manage"
},
@ -177,6 +180,9 @@
"3tVy+Z": {
"defaultMessage": "{n} Followers"
},
"3yk8fB": {
"defaultMessage": "Wallet"
},
"450Fty": {
"defaultMessage": "None"
},
@ -508,6 +514,12 @@
"FmXUJg": {
"defaultMessage": "follows you"
},
"FvanT6": {
"defaultMessage": "Accounts"
},
"FzbSGg": {
"defaultMessage": "You dont have any media servers, try adding some."
},
"G/yZLu": {
"defaultMessage": "Remove"
},
@ -572,6 +584,9 @@
"HhcAVH": {
"defaultMessage": "You don't follow this person, click here to load media from <i>{link}</i>, or update <a><i>your preferences</i></a> to always load media from everybody."
},
"HqRNN8": {
"defaultMessage": "Support"
},
"I1AoOu": {
"defaultMessage": "Last post {time}"
},
@ -635,6 +650,9 @@
"JSx7y9": {
"defaultMessage": "Subscribe to {site_name} {plan} for {price} and receive the following rewards"
},
"JTht/T": {
"defaultMessage": "NIP-96"
},
"JeoS4y": {
"defaultMessage": "Repost"
},
@ -819,6 +837,9 @@
"Qxv0B2": {
"defaultMessage": "You currently have {number} sats in your zap pool."
},
"R/6nsx": {
"defaultMessage": "Subscription"
},
"R81upa": {
"defaultMessage": "People you follow"
},
@ -907,6 +928,9 @@
"TvKqBp": {
"defaultMessage": "liked"
},
"TwyMau": {
"defaultMessage": "Account"
},
"U1aPPi": {
"defaultMessage": "Stop listening"
},
@ -928,6 +952,9 @@
"UUPFlt": {
"defaultMessage": "Users must accept the content warning to show the content of your note."
},
"UaCh1c": {
"defaultMessage": "Add Server"
},
"Ub+AGc": {
"defaultMessage": "Sign In"
},
@ -1042,6 +1069,9 @@
"ZlmK/p": {
"defaultMessage": "{name} invited you to {app}"
},
"a1x4gD": {
"defaultMessage": "Media servers store media which you can share in notes as images and videos"
},
"a5UPxh": {
"defaultMessage": "Fund developers and platforms providing NIP-05 verification services"
},
@ -1121,6 +1151,9 @@
"defaultMessage": "URL..",
"description": "Placeholder text for imgproxy url textbox"
},
"cVcgLJ": {
"defaultMessage": "Media Servers"
},
"cWx9t8": {
"defaultMessage": "Mute all"
},
@ -1286,6 +1319,9 @@
"hY4lzx": {
"defaultMessage": "Supports"
},
"hYOE+U": {
"defaultMessage": "Invite"
},
"ha8JKG": {
"defaultMessage": "Show graph"
},
@ -1298,6 +1334,9 @@
"hniz8Z": {
"defaultMessage": "here"
},
"hvFRBo": {
"defaultMessage": "Interaction"
},
"i/dBAR": {
"defaultMessage": "Zap Pool"
},
@ -1739,6 +1778,9 @@
"yCLnBC": {
"defaultMessage": "LNURL or Lightning Address"
},
"z3UjXR": {
"defaultMessage": "Debug"
},
"zCb8fX": {
"defaultMessage": "Weight"
},

View File

@ -1,5 +1,8 @@
/// <reference lib="webworker" />
import { encodeTLVEntries, NostrLink, NostrPrefix, TLVEntryType, tryParseNostrLink } from "@snort/system";
import { hexToBytes } from "@noble/hashes/utils";
import { bech32 } from "@scure/base";
import { encodeTLVEntries, NostrPrefix, TLVEntryType } from "@snort/system/dist/links";
import { NostrLink, tryParseNostrLink } from "@snort/system/dist/nostr-link";
import { CacheableResponsePlugin } from "workbox-cacheable-response";
import { clientsClaim } from "workbox-core";
import { ExpirationPlugin } from "workbox-expiration";
@ -7,9 +10,6 @@ import { precacheAndRoute, PrecacheEntry } from "workbox-precaching";
import { registerRoute } from "workbox-routing";
import { CacheFirst, StaleWhileRevalidate } from "workbox-strategies";
import { defaultAvatar, hexToBech32 } from "@/Utils";
import { formatShort } from "@/Utils/Number";
declare const self: ServiceWorkerGlobalScope & {
__WB_MANIFEST: (string | PrecacheEntry)[];
};
@ -19,7 +19,7 @@ clientsClaim();
// cache everything in current domain /assets because precache doesn't seem to include everything
registerRoute(
({ url }) => url.origin === window.location.origin && url.pathname.startsWith("/assets"),
({ url }) => url.origin === self.location.origin && url.pathname.startsWith("/assets"),
new StaleWhileRevalidate({
cacheName: "assets-cache",
plugins: [
@ -250,7 +250,7 @@ function replaceMentions(content: string, profiles: Array<CompactProfile>) {
.map(i => {
if (MentionNostrEntityRegex.test(i)) {
const link = tryParseNostrLink(i);
if (link?.type === NostrPrefix.PublicKey || link?.type === NostrPrefix.Profile) {
if (link && (link.type === NostrPrefix.PublicKey || link.type === NostrPrefix.Profile)) {
const px = profiles.find(a => a.pubkey === link.id);
return `@${displayNameOrDefault(px ?? { pubkey: link.id })}`;
}
@ -264,7 +264,7 @@ function displayNameOrDefault(p: CompactProfile) {
if ((p.name?.length ?? 0) > 0) {
return p.name;
}
return hexToBech32("npub", p.pubkey).slice(0, 12);
return bech32.encode("npub", bech32.toWords(hexToBytes(p.pubkey))).slice(0, 12);
}
function makeNotification(n: PushNotification) {
@ -286,7 +286,7 @@ function makeNotification(n: PushNotification) {
};
const ret = {
body: body(),
icon: evx.author.avatar ?? defaultAvatar(evx.author.pubkey),
icon: evx.author.avatar ?? `https://nostr.api.v0l.io/api/v1/avatar/robots/${evx.author.pubkey}.webp`,
timestamp: evx.created_at * 1000,
tag: evx.id,
data: JSON.stringify(n),
@ -294,3 +294,11 @@ function makeNotification(n: PushNotification) {
console.debug(ret);
return ret;
}
function formatShort(n: number) {
if (n > 1000) {
return (n / 1000).toFixed(1);
} else {
return n.toFixed(0);
}
}

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} مللي ثانية",
"00LcfG": "تحميل المزيد",
"01iNut": "عنوان Nostr غير مملوك لك",
"08zn6O": "Export Keys",
"0Azlrb": "إدارة",
"0BUTMv": "بحث...",
"0HFX0T": "استخدام الموقع الدقيق",
@ -58,6 +59,7 @@
"3qnJlS": "أنت تصوت بـ {amount} ساتوشي",
"3t3kok": "{n,plural,=1{{n} منشور جديد}other{{n} منشورات جديدة}}",
"3tVy+Z": "المتابِعون {n}",
"3yk8fB": "Wallet",
"450Fty": "لا أحد",
"47FYwb": "الغاء",
"4IPzdn": "المطورون الأساسيون",
@ -168,6 +170,8 @@
"FdhSU2": "احجز الان",
"FfYsOb": "حدث خطأ!",
"FmXUJg": "متابع لك",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "حذف",
"G1BGCg": "اختر محفظة",
"G3A56c": "Subscribed to Push",
@ -189,6 +193,7 @@
"HWbkEK": "مسح ذاكرة التخزين المؤقت وإعادة التحميل",
"HbefNb": "افتح المحفظة",
"HhcAVH": "أنت لا تتبع هذا الشخص، انقر هنا لتحميل الوسائط من <i>{link}</i>، أو قم بتحديث <a><i>تفضيلاتك</i></a> لتحميل الوسائط دائما من الجميع.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "هل أنت متأكد من ازالة تثبيت هذا المنشور؟",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "الرياضة",
"JPFYIM": "لا يوجد عنوان للإضاءة",
"JSx7y9": "اشترك في {site_name} {plan} ل {price} و استلم المكافآت التالية",
"JTht/T": "NIP-96",
"JeoS4y": "إعادة",
"JjGgXI": "البحث عن المستخدمين",
"JkLHGw": "موقع إلكتروني",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "زاب بوول يعمل فقط إذا كنت تستخدم أحد اتصالات المحفظة المدعومة (اتصال WebLN، LNC، LNDHub أو Nostr Wallet)",
"Qxv0B2": "لديك حاليا {number} جلوس في بركة زاب.",
"R/6nsx": "Subscription",
"R81upa": "الأشخاص الذين تتابعهم",
"RDha9y": "Service Worker Not Running",
"RSr2uB": "اسم المستخدم يجب أن يحتوي فقط على أحرف وأرقام صغيرة",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Salt..",
"Tpy00S": "الناس",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "إيقاف الإستماع",
"UDYlxu": "الاشتراكات المعلقة",
"UJTWqI": "إزالة من مرحلتي",
@ -307,6 +315,7 @@
"UNjfWJ": "تحقق من جميع توقيعات الأحداث المتلقاة من الترحيل",
"UT7Nkj": "دردشة جديدة",
"UUPFlt": "سيضطر المستخدمون على قبول التحذير المعروض على منشورك قبل عرضه.",
"UaCh1c": "Add Server",
"Ub+AGc": "تسجيل الدخول",
"Up5U7K": "حظر",
"Ups2/p": "Your application is pending",
@ -345,6 +354,7 @@
"ZS+jRE": "إرسال تقسيم زاف إلى",
"Zff6lu": "اسم المستخدم iris.to/<b>{name}</b> محجوز لك!",
"ZlmK/p": "{name} invited you to {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "ادعم المطورين والمنصات التي تقدم خدمات التحقق من NIP-05",
"a7TDNm": "سيتم بث الملاحظات في الوقت الحقيقي إلى علامة التبويب العالمية والملاحظات",
"aHje0o": "الاسم أو اللاسم",
@ -371,6 +381,7 @@
"cHCwbF": "التصوير",
"cPIKU2": "المتابَعون",
"cQfLWb": "URL ..",
"cVcgLJ": "Media Servers",
"cWx9t8": "كتم الكل",
"cg1VJ2": "ربط المحفظة",
"cuP16y": "دعم الحسابات المتعددة",
@ -426,10 +437,12 @@
"hMzcSq": "رسائل",
"hRTfTR": "PRO",
"hY4lzx": "يدعم",
"hYOE+U": "Invite",
"ha8JKG": "Show graph",
"hicxcO": "عرض الردود",
"hmZ3Bz": "الوسائط",
"hniz8Z": "هنا",
"hvFRBo": "Interaction",
"i/dBAR": "مخزن زابل",
"i5gBFz": "Your sent and received payments will show up here.",
"iCqGww": "التفاعل ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "اللغة",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL أو عنوان برق",
"z3UjXR": "Debug",
"zCb8fX": "الوزن",
"zFegDD": "تواصل",
"zINlao": "مالك",

View File

@ -1,7 +1,7 @@
{
"+D82kt": "Bist du sicher, dass du {id} teilen möchtest?",
"+PzQ9Y": "Jetzt auszahlen",
"+QM0PJ": "Sync all events for your profile into local cache",
"+QM0PJ": "Alle Events für dein Profil in den lokalen Cache synchronisieren",
"+UjDmN": "Eingeloggt mit Schreibzugriff",
"+Vxixo": "Geheimer Gruppenchat",
"+aZY2h": "Zap Typ",
@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "Mehr laden",
"01iNut": "Nostr-Adresse gehört nicht zu dir",
"08zn6O": "Schlüssel exportieren",
"0Azlrb": "Verwalten",
"0BUTMv": "Suche...",
"0HFX0T": "Exakten Standort verwenden",
@ -58,6 +59,7 @@
"3qnJlS": "Du stimmst mit {amount} sats ab",
"3t3kok": "{n,plural,one {}=1{{n} neues Event} other{{n} neue Events}}",
"3tVy+Z": "{n} Follower",
"3yk8fB": "Wallet",
"450Fty": "Keine",
"47FYwb": "Abbrechen",
"4IPzdn": "Primäre Entwickler",
@ -168,6 +170,8 @@
"FdhSU2": "Jetzt abholen",
"FfYsOb": "Ein Fehler ist aufgetreten!",
"FmXUJg": "Folgt dir",
"FvanT6": "Konten",
"FzbSGg": "Du hast keine Medienserver; versuche, welche hinzuzufügen.",
"G/yZLu": "Entfernen",
"G1BGCg": "Wallet auswählen",
"G3A56c": "Push abonniert",
@ -189,9 +193,10 @@
"HWbkEK": "Cache leeren und neu laden",
"HbefNb": "Wallet öffnen",
"HhcAVH": "Du folgst dieser Person nicht, klicke hier, um Medien von <i>{link}</i>zu laden, oder aktualisiere <a><i>deine Einstellungen</i></a>, um Medien immer von allen zu laden.",
"HqRNN8": "Unterstützung",
"I1AoOu": "Letzter Beitrag {time}",
"IEwZvs": "Sind sie sicher, dass sie diese Notiz entpinnen möchten?",
"IIOul1": "Account Data",
"IIOul1": "Kontodaten",
"IKKHqV": "Folgt",
"IOu4Xh": "Du musst ein {tier} Abonnent sein, um auf {app} Deck zugreifen zu können",
"IVbtTS": "Allen {n} sats zappen",
@ -210,6 +215,7 @@
"JIVWWA": "Sport",
"JPFYIM": "Keine Lightning-Adresse",
"JSx7y9": "Abonniere {site_name} {plan} für {price} und erhalte folgende Prämien",
"JTht/T": "NIP-96",
"JeoS4y": "Repost",
"JjGgXI": "Nutzer suchen",
"JkLHGw": "Webseite",
@ -256,8 +262,8 @@
"OQSOJF": "Kostenlose Nostr-Adresse erhalten",
"OQXnew": "Dein Abonnement ist noch aktiv, du kannst es noch nicht erneuern",
"ORGv1Q": "Erstellt",
"OoZgbB": "Failed to update, please try again",
"OxPdQ0": "Scanning {date}",
"OoZgbB": "Update fehlgeschlagen, bitte versuche es erneut",
"OxPdQ0": "Scanne {date}",
"P2o+ZZ": "Ungültige Nostr-Adresse",
"P61BTu": "Event JSON kopieren",
"P7FD0F": "System (Standard)",
@ -268,9 +274,10 @@
"Pe0ogR": "Erscheinungsbild",
"PrsIg7": "Reaktionen werden auf jeder Seite angezeigt, wenn Reaktionen deaktiviert sind, werden sie nicht angezeigt",
"QDFTjG": "{n} Relais",
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QJfhKt": "Der private Schlüssel ist wie ein Passwort, kann aber nicht zurückgesetzt werden. Hüte ihn sorgfältig und zeige ihn niemandem. Sobald jemand deinen privaten Schlüssel hat, hat er für immer Zugang zu deinem Konto.",
"QWhotP": "Zap Pool funktioniert nur, wenn du eine der unterstützten Wallet-Verbindungen verwenden (WebLN, LNC, LNDHub oder Nostr Wallet Connect)",
"Qxv0B2": "Du hast aktuell {number} sats in deinem Zap Pool.",
"R/6nsx": "Abonnement",
"R81upa": "Personen, denen du folgst",
"RDha9y": "Service-Worker läuft nicht",
"RSr2uB": "Benutzername darf nur Kleinbuchstaben und Ziffern enthalten",
@ -278,7 +285,7 @@
"RfhLwC": "Von: {author}",
"RhDAoS": "Sind sie sicher, dass sie {id} löschen möchten?",
"RoOyAh": "Relais",
"RrCui3": "Summary",
"RrCui3": "Übersicht",
"Rs4kCE": "Lesezeichen",
"SLZGPn": "Gib eine PIN ein, um deinen privaten Schlüssel zu verschlüsseln. Du musst diese PIN jedes Mal eingeben, wenn du {site} öffnest.",
"SMO+on": "Zap an {name} senden",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Salt..",
"Tpy00S": "Personen",
"TvKqBp": "gefällt",
"TwyMau": "Konto",
"U1aPPi": "Abspielen beenden",
"UDYlxu": "Ausstehende Abonnements",
"UJTWqI": "Aus meinen Relais entfernen",
@ -307,6 +315,7 @@
"UNjfWJ": "Alle von Relais erhaltenen Event-Signaturen überprüfen",
"UT7Nkj": "Neuer Chat",
"UUPFlt": "Nutzer müssen die Inhaltswarnung akzeptieren, um den Inhalt deiner Note anzuzeigen.",
"UaCh1c": "Server hinzufügen",
"Ub+AGc": "Anmelden",
"Up5U7K": "Blockieren",
"Ups2/p": "Deine Bewerbung ist ausstehend",
@ -330,7 +339,7 @@
"XICsE8": "Datei-Hosts",
"XPB8VV": "Verbindung zur Alby-Wallet",
"XQiFEl": "Folgt-Relais-Gesundheit",
"XSdWHA": "Redeem",
"XSdWHA": "Einlösen",
"XXm7jJ": "Angesagte Hashtags",
"XgWvGA": "Reaktionen",
"Xnimz0": "Von <b>{wallet}</b> senden",
@ -345,6 +354,7 @@
"ZS+jRE": "Sende Zap-Aufteilungen an",
"Zff6lu": "Benutzername iris.to/<b>{name}</b> ist reserviert für dich!",
"ZlmK/p": "{name} hat dich zu {app} eingeladen",
"a1x4gD": "Medienserver speichern Medien, die du in Notes als Bilder und Videos teilen kannst",
"a5UPxh": "Finanziere Entwickler und Plattformen, die NIP-05-Verifizierungsdienste anbieten",
"a7TDNm": "Notes werden in Echtzeit in Global- und Notes-Tab gestreamt",
"aHje0o": "Name oder Nym",
@ -371,6 +381,7 @@
"cHCwbF": "Fotografie",
"cPIKU2": "Folge ich",
"cQfLWb": "URL..",
"cVcgLJ": "Medienserver",
"cWx9t8": "Alle stummschalten",
"cg1VJ2": "Wallet verbinden",
"cuP16y": "Unterstützung für mehrere Konten",
@ -379,7 +390,7 @@
"d+6YsV": "Listen zum Stummschalten:",
"d2ebEu": "Push nicht abonniert",
"d7d0/x": "LN-Adresse",
"dK2CcV": "The public key is like your username, you can share it with anyone.",
"dK2CcV": "Der öffentliche Schlüssel ist wie dein Benutzername, du kannst ihn mit jedem teilen.",
"dOQCL8": "Anzeigename",
"deEeEI": "Registrieren",
"djLctd": "Betrag in Sats",
@ -422,14 +433,16 @@
"h7jvCs": "{site} macht gemeinsam mehr Spaß!",
"h8XMJL": "Auszeichnungen",
"hF6IN2": "Follow-Liste kürzen",
"hMQmIw": "Sync Account",
"hMQmIw": "Konto synchronisieren",
"hMzcSq": "Nachrichten",
"hRTfTR": "PRO",
"hY4lzx": "Unterstützt",
"hYOE+U": "Einladung",
"ha8JKG": "Graph anzeigen",
"hicxcO": "Antworten anzeigen",
"hmZ3Bz": "Medien",
"hniz8Z": "hier",
"hvFRBo": "Interaktion",
"i/dBAR": "Zap Pool",
"i5gBFz": "Deine gesendeten und empfangenen Zahlungen werden hier angezeigt.",
"iCqGww": "Reaktionen ({n})",
@ -438,7 +451,7 @@
"iICVoL": "{x} folgt ({y} Duplikate)",
"iNWbVV": "Handle",
"iXPL0Z": "Anmeldung mit privatem Schlüssel bei einer unsicheren Verbindung nicht möglich, bitte verwenden Sie stattdessen eine Nostr Schlüssel Manager Erweiterung",
"iYc3Ld": "Payments",
"iYc3Ld": "Zahlungen",
"ieGrWo": "Folgen",
"ipHVx5": "Rechnung erstellen",
"itPgxd": "Profil",
@ -464,10 +477,10 @@
"l3H1EK": "Lade deine Freunde ein",
"lCILNz": "Jetzt kaufen",
"lD3+8a": "Bezahlen",
"lEnclp": "My events: {n}",
"lEnclp": "Meine Events: {n}",
"lPWASz": "Snort Nostr-Adresse",
"lTbT3s": "Wallet Passwort",
"lfOesV": "Non-Zap",
"lfOesV": "Nicht-Zap",
"lgg1KN": "Kontoseite",
"ll3xBp": "Bildproxy-Dienst",
"lnaT9F": "Folgt {n}",
@ -477,7 +490,7 @@
"mKAr6h": "Allen folgen",
"mKh2HS": "Datei-Upload-Dienst",
"mKhgP9": "{n,plural,=0{} =1{hat gezappt} other{haben gezappt}}",
"mOFG3K": "Start",
"mOFG3K": "Starten",
"mfe8RW": "Option: {n}",
"n1Whvj": "Wechseln",
"nDejmx": "Freigeben",
@ -485,7 +498,7 @@
"nGGDsi": "Benachrichtigungen erlaubt",
"nIchMQ": "Suche nach Kontoaktivität ({progress})",
"nUT0Lv": "Tools",
"nWQFic": "Renew",
"nWQFic": "Erneuern",
"nihgfo": "Diesen Artikel anhören",
"nwZXeh": "{n} blockiert",
"o/gK53": "Deck",
@ -505,7 +518,7 @@
"qMePPG": "Note",
"qMx1sA": "Standard Zap Betrag",
"qUJTsT": "Blockiert",
"qXCbgZ": "Unlock",
"qXCbgZ": "Entsperren",
"qZsKBR": "{tier} erneuern",
"qcJFEJ": "Benachrichtigungs-API deaktiviert",
"qdGuQo": "Dein privater Schlüssel ist (Teile diesen mit niemanden!)",
@ -539,8 +552,8 @@
"uCk8r+": "Hast du bereits ein Konto?",
"uSV4Ti": "Reposts müssen manuell bestätigt werden",
"uc0din": "Sende Sats-Aufteilungen an",
"ufvXH1": "Found {n} events",
"uhu5aG": "Public",
"ufvXH1": "{n} Events gefunden",
"uhu5aG": "Öffentlich",
"un1nGw": "{n} Notes",
"usAvMr": "Profil anpassen",
"v8lolG": "Chat starten",
@ -577,6 +590,7 @@
"y1Z3or": "Sprache",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL oder Lightning-Adresse",
"z3UjXR": "Debuggen",
"zCb8fX": "Gewichtung",
"zFegDD": "Kontakt",
"zINlao": "Eigentümer",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "Load more",
"01iNut": "Nostr address does not belong to you",
"08zn6O": "Export Keys",
"0Azlrb": "Manage",
"0BUTMv": "Search...",
"0HFX0T": "Use Exact Location",
@ -58,6 +59,7 @@
"3qnJlS": "You are voting with {amount} sats",
"3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}",
"3tVy+Z": "{n} Followers",
"3yk8fB": "Wallet",
"450Fty": "None",
"47FYwb": "Cancel",
"4IPzdn": "Primary Developers",
@ -168,6 +170,8 @@
"FdhSU2": "Claim Now",
"FfYsOb": "An error has occured!",
"FmXUJg": "follows you",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "Remove",
"G1BGCg": "Select Wallet",
"G3A56c": "Subscribed to Push",
@ -189,6 +193,7 @@
"HWbkEK": "Clear cache and reload",
"HbefNb": "Open Wallet",
"HhcAVH": "You don't follow this person, click here to load media from <i>{link}</i>, or update <a><i>your preferences</i></a> to always load media from everybody.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "Are you sure you want to unpin this note?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "Sport",
"JPFYIM": "No lightning address",
"JSx7y9": "Subscribe to {site_name} {plan} for {price} and receive the following rewards",
"JTht/T": "NIP-96",
"JeoS4y": "Repost",
"JjGgXI": "Search users",
"JkLHGw": "Website",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)",
"Qxv0B2": "You currently have {number} sats in your zap pool.",
"R/6nsx": "Subscription",
"R81upa": "People you follow",
"RDha9y": "Service Worker Not Running",
"RSr2uB": "Username must only contain lowercase letters and numbers",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Salt..",
"Tpy00S": "People",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Stop listening",
"UDYlxu": "Pending Subscriptions",
"UJTWqI": "Remove from my relays",
@ -307,6 +315,7 @@
"UNjfWJ": "Check all event signatures received from relays",
"UT7Nkj": "New Chat",
"UUPFlt": "Users must accept the content warning to show the content of your note.",
"UaCh1c": "Add Server",
"Ub+AGc": "Sign In",
"Up5U7K": "Block",
"Ups2/p": "Your application is pending",
@ -345,6 +354,7 @@
"ZS+jRE": "Send zap splits to",
"Zff6lu": "Username iris.to/<b>{name}</b> is reserved for you!",
"ZlmK/p": "{name} invited you to {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "Fund developers and platforms providing NIP-05 verification services",
"a7TDNm": "Notes will stream in real time into global and notes tab",
"aHje0o": "Name or nym",
@ -371,6 +381,7 @@
"cHCwbF": "Photography",
"cPIKU2": "Following",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "Mute all",
"cg1VJ2": "Connect Wallet",
"cuP16y": "Multi account support",
@ -426,10 +437,12 @@
"hMzcSq": "Messages",
"hRTfTR": "PRO",
"hY4lzx": "Supports",
"hYOE+U": "Invite",
"ha8JKG": "Show graph",
"hicxcO": "Show replies",
"hmZ3Bz": "Media",
"hniz8Z": "here",
"hvFRBo": "Interaction",
"i/dBAR": "Zap Pool",
"i5gBFz": "Your sent and received payments will show up here.",
"iCqGww": "Reactions ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "Language",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL or Lightning Address",
"z3UjXR": "Debug",
"zCb8fX": "Weight",
"zFegDD": "Contact",
"zINlao": "Owner",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "Más información",
"01iNut": "Nostr address does not belong to you",
"08zn6O": "Export Keys",
"0Azlrb": "Gestionar",
"0BUTMv": "Buscar...",
"0HFX0T": "Use Exact Location",
@ -58,6 +59,7 @@
"3qnJlS": "Estás votando con {amount} sats",
"3t3kok": "{n,plural,one {}=1{{n} nueva nota} other{{n} nuevas notas}}",
"3tVy+Z": "{n} Seguidores",
"3yk8fB": "Wallet",
"450Fty": "Ninguno",
"47FYwb": "Cancelar",
"4IPzdn": "Desarrolladores principales",
@ -168,6 +170,8 @@
"FdhSU2": "Reclama Ahora",
"FfYsOb": "¡Ha ocurrido un error!",
"FmXUJg": "te sigue",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "Eliminar",
"G1BGCg": "Seleccionar cartera",
"G3A56c": "Subscribed to Push",
@ -189,6 +193,7 @@
"HWbkEK": "Borrar caché y recargar",
"HbefNb": "Abrir Wallet",
"HhcAVH": "Si no sigues a esta persona, haz clic aquí para cargar medios de <i>{link}</i>, o actualiza <a><i>tus preferencias</i></a> para cargar siempre medios de todo el mundo.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "¿Estás seguro de que quieres desmarcar esta nota?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "Deporte",
"JPFYIM": "No hay dirección del rayo",
"JSx7y9": "Suscríbase a {site_name} {plan} para {price} y reciba las siguientes recompensas",
"JTht/T": "NIP-96",
"JeoS4y": "Repost",
"JjGgXI": "Buscar usuarios",
"JkLHGw": "Web",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Pool sólo funciona si utilizas una de las conexiones de monedero compatibles (WebLN, LNC, LNDHub o Nostr Wallet Connect).",
"Qxv0B2": "Actualmente tienes {number} sats en tu reserva de zap.",
"R/6nsx": "Subscription",
"R81upa": "Personas a las que sigues",
"RDha9y": "Service Worker Not Running",
"RSr2uB": "El nombre de usuario sólo debe contener letras minúsculas y números",
@ -300,6 +307,7 @@
"TpgeGw": "sal hexagonal..",
"Tpy00S": "Personas",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Deja de escuchar",
"UDYlxu": "Suscripciones pendientes",
"UJTWqI": "Quitar de mis relés",
@ -307,6 +315,7 @@
"UNjfWJ": "Comprobar todas las firmas de eventos recibidas de los relevos",
"UT7Nkj": "Nuevo chat",
"UUPFlt": "Los usuarios deben aceptar la advertencia de contenido para mostrar el contenido de tu nota.",
"UaCh1c": "Add Server",
"Ub+AGc": "Iniciar sesión",
"Up5U7K": "Bloquear",
"Ups2/p": "Your application is pending",
@ -345,6 +354,7 @@
"ZS+jRE": "Enviar zap splits a",
"Zff6lu": "El nombre de usuario iris.to/<b>{name}</b> está reservado para ti.",
"ZlmK/p": "{name} invited you to {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "Apoya a las plataformas y desarrolladores que proporcionan servicios de verificación",
"a7TDNm": "Las notas se transmitirán en tiempo real a la pestaña global y de notas",
"aHje0o": "Nombre o apodo",
@ -371,6 +381,7 @@
"cHCwbF": "Fotografía",
"cPIKU2": "Siguiendo",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "Silenciar todos",
"cg1VJ2": "Conectar cartera",
"cuP16y": "Soporte multicuenta",
@ -426,10 +437,12 @@
"hMzcSq": "Mensajes",
"hRTfTR": "PRO",
"hY4lzx": "Soporta",
"hYOE+U": "Invite",
"ha8JKG": "Show graph",
"hicxcO": "Mostrar respuestas",
"hmZ3Bz": "Medios de comunicación",
"hniz8Z": "aquí",
"hvFRBo": "Interaction",
"i/dBAR": "Piscina Zap",
"i5gBFz": "Your sent and received payments will show up here.",
"iCqGww": "Reacciones ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "Idioma",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL o dirección lightning",
"z3UjXR": "Debug",
"zCb8fX": "Peso",
"zFegDD": "Contacto",
"zINlao": "Dueño",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} میلی ثانیه",
"00LcfG": "بارگیری بیشتر",
"01iNut": "این آدرس ناستر برای شما نیست",
"08zn6O": "Export Keys",
"0Azlrb": "مدیریت",
"0BUTMv": "جستجو...",
"0HFX0T": "مکان دقیق را استفاده کن",
@ -58,6 +59,7 @@
"3qnJlS": "با {amount} ساتوشی رای می دهید",
"3t3kok": "{n,plural,one {}=1{{n} یادداشت جدید} other{{n} یادداشت جدید}}",
"3tVy+Z": "{n} دنبال کننده",
"3yk8fB": "Wallet",
"450Fty": "هیچ‌کدام",
"47FYwb": "لغو",
"4IPzdn": "توسعه دهندگان اصلی",
@ -168,6 +170,8 @@
"FdhSU2": "حالا برداشت کن",
"FfYsOb": "خطایی رخ داده است!",
"FmXUJg": "شما را دنبال می کند",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "حذف",
"G1BGCg": "انتخاب کیف پول",
"G3A56c": "عضو Push",
@ -189,6 +193,7 @@
"HWbkEK": "پاک کردن حافظه کش و بارگیری مجدد",
"HbefNb": "باز کردن کیف پول",
"HhcAVH": "شما این فرد را دنبال نمی کنید، اینجا کلیک کنید تا فایل های <i>{link}</i> را بارگیری کنید، یا <a><i>ترجیحات خود</i></a> را بروزرسانی کنید تا همیشه رسانه های همه را بارگیری کنید.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "مطمئنید می خواهید سنجاق یادداشت را بردارید؟",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "ورزش",
"JPFYIM": "آدرس لایتنینگی وجود ندارد",
"JSx7y9": "عضویت در {site_name}{plan} به قیمت {price} و دریافت پاداش روبرو",
"JTht/T": "NIP-96",
"JeoS4y": "بازنشر",
"JjGgXI": "جستجوی کاربران",
"JkLHGw": "وب سایت",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "استخر زپ فقط در صورتیکه از یک اتصال کیف پول پشتیبانی شده (WebLN, LNC, LNDHub یا Nostr Wallet Connect) استفاده کنید کار می کند",
"Qxv0B2": "در حال حاضر {number} ساتوشی در استخر زپ خود دارید.",
"R/6nsx": "Subscription",
"R81upa": "کسانی که دنبال می کنید",
"RDha9y": "خدمات دهنده کار نمی کند",
"RSr2uB": "نام کاربری فقط می‌تواند شامل حروف کوچک (a-z) و اعداد باشد.",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Salt..",
"Tpy00S": "افراد",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "توقف شنیدن",
"UDYlxu": "اشتراک معطل",
"UJTWqI": "حذف از رله های من",
@ -307,6 +315,7 @@
"UNjfWJ": "تمام امضاهای رویدادهای دریافت شده توسط رله ها را بررسی کن",
"UT7Nkj": "گپ جدید",
"UUPFlt": "کاربر باید هشدار محتوا را بپذیرد تا محتوای یادداشت نمایش داده شود.",
"UaCh1c": "Add Server",
"Ub+AGc": "ورود",
"Up5U7K": "مسدود کردن",
"Ups2/p": "Your application is pending",
@ -345,6 +354,7 @@
"ZS+jRE": "ارسال تقسیم زپ به",
"Zff6lu": "نام کاربری iris.to/<b>{name}</b>برای شما رزرو شده است!",
"ZlmK/p": "{name} شما را به دعوت می کند {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "توسعه دهندگان و پلتفرم های ارائه دهنده خدمات تایید NIP-05 را پیدا کن",
"a7TDNm": "یادداشت ها در لحظه در سربرگ همگانی و یادداشت ها پخش می شوند",
"aHje0o": "نام یا نام مستعار",
@ -371,6 +381,7 @@
"cHCwbF": "عکاسی",
"cPIKU2": "دنبال کنندگان",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "بیصدا کردن همه",
"cg1VJ2": "اتصال کیف پول",
"cuP16y": "پشتیبانی از چند حساب",
@ -426,10 +437,12 @@
"hMzcSq": "پیام‌ها",
"hRTfTR": "PRO",
"hY4lzx": "پشتیبانی",
"hYOE+U": "Invite",
"ha8JKG": "نمایش نمودار",
"hicxcO": "نمایش پاسخ ها",
"hmZ3Bz": "رسانه",
"hniz8Z": "اینجا",
"hvFRBo": "Interaction",
"i/dBAR": "استخر زپ",
"i5gBFz": "Your sent and received payments will show up here.",
"iCqGww": "({n}) واکنش",
@ -577,6 +590,7 @@
"y1Z3or": "زبان",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL یا آدرس لایتنینگ",
"z3UjXR": "Debug",
"zCb8fX": "وزن",
"zFegDD": "مخاطب",
"zINlao": "مالک",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "Lataa lisää",
"01iNut": "Nostr-osoite ei kuulu sinulle",
"08zn6O": "Export Keys",
"0Azlrb": "Hallitse",
"0BUTMv": "Etsi...",
"0HFX0T": "Käytä tarkkaa sijaintia",
@ -58,6 +59,7 @@
"3qnJlS": "Äänestät {amount} satsilla",
"3t3kok": "{n,plural,=1{{n} uusi viesti} muuta{{n} uutta viestiä}}",
"3tVy+Z": "{n} Seuraajaa",
"3yk8fB": "Wallet",
"450Fty": "Ei mitään",
"47FYwb": "Peruuta",
"4IPzdn": "Pääkehittäjät",
@ -168,6 +170,8 @@
"FdhSU2": "Lunasta nyt",
"FfYsOb": "Tapahtui virhe!",
"FmXUJg": "seuraa sinua",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "Poista",
"G1BGCg": "Valitse lompakko",
"G3A56c": "Tilattu Push-ilmoitukset",
@ -189,6 +193,7 @@
"HWbkEK": "Tyhjennä välimuisti ja lataa uudelleen",
"HbefNb": "Avaa lompakko",
"HhcAVH": "Et seuraa tätä henkilöä, klikkaa tästä ladataksesi mediaa osoitteesta <i>{link}</i>, tai päivitä <a><i>asetuksesi</i></a> ladataksesi mediaa aina kaikilta.",
"HqRNN8": "Support",
"I1AoOu": "Viimeisin viesti {time}",
"IEwZvs": "Haluatko varmasti poistaa tämän viestin kiinnitetyistä?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "Urheilu",
"JPFYIM": "Ei lightning-osoitetta",
"JSx7y9": "Tilaa {site_name} {plan} osoitteeseen {price} ja saat seuraavat palkinnot.",
"JTht/T": "NIP-96",
"JeoS4y": "Jaa uudelleen",
"JjGgXI": "Etsi käyttäjiä",
"JkLHGw": "Verkkosivusto",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap-pool toimii vain tuetuilla lompakkoyhteyksillä (WebLN, LNC, LNDHub tai Nostr Wallet Connect)",
"Qxv0B2": "Sinulla on {number} satsia zap-poolissa.",
"R/6nsx": "Subscription",
"R81upa": "Henkilöt joita seuraat",
"RDha9y": "Palvelutyöntekijä ei ole käynnissä",
"RSr2uB": "Käyttäjätunnus saa sisältää vain pieniä kirjaimia ja numeroita.",
@ -300,6 +307,7 @@
"TpgeGw": "Hex-suola..",
"Tpy00S": "Ihmiset",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Lopeta kuunteleminen",
"UDYlxu": "Odottavat tilaukset",
"UJTWqI": "Poista releistäni",
@ -307,6 +315,7 @@
"UNjfWJ": "Tarkista kaikki releiltä saadut tapahtuman allekirjoitukset",
"UT7Nkj": "Uusi keskustelu",
"UUPFlt": "Käyttäjien täytyy hyväksyä sisältövaroitus nähdäkseen viestisi sisällön.",
"UaCh1c": "Add Server",
"Ub+AGc": "Kirjaudu sisään",
"Up5U7K": "Estä",
"Ups2/p": "Hakemuksesi on vireillä",
@ -345,6 +354,7 @@
"ZS+jRE": "Lähetä zap splitit osoitteeseen",
"Zff6lu": "Käyttäjätunnus iris.to/<b>{name}</b> on varattu sinulle!",
"ZlmK/p": "{name} kutsui sinut {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "Tue kehittäjiä ja alustoja, jotka tarjoavat NIP-05-varmennuspalveluita",
"a7TDNm": "Viestit striimautuvat reaaliajassa yleiseen ja viestit-välilehteen",
"aHje0o": "Nimi tai nimimerkki",
@ -371,6 +381,7 @@
"cHCwbF": "Valokuvaus",
"cPIKU2": "Seuraa",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "Mykistä kaikki",
"cg1VJ2": "Yhdistä lompakko",
"cuP16y": "Usean tilin tuki",
@ -426,10 +437,12 @@
"hMzcSq": "Viestit",
"hRTfTR": "PRO",
"hY4lzx": "Tukee",
"hYOE+U": "Invite",
"ha8JKG": "Näytä kaavio",
"hicxcO": "Näytä vastaukset",
"hmZ3Bz": "Media",
"hniz8Z": "täällä",
"hvFRBo": "Interaction",
"i/dBAR": "Zap-pooli",
"i5gBFz": "Lähetetyt ja vastaanotetut maksut näkyvät täällä.",
"iCqGww": "Reaktioita ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "Kieli",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL tai Lightning-osoite",
"z3UjXR": "Debug",
"zCb8fX": "Paino",
"zFegDD": "Ota yhteyttä",
"zINlao": "Omistaja",

View File

@ -1,7 +1,7 @@
{
"+D82kt": "Êtes-vous sûr que vous voulez republier: {id}",
"+PzQ9Y": "Payer Maintenant",
"+QM0PJ": "Sync all events for your profile into local cache",
"+QM0PJ": "Synchroniser tous les évènements de votre profil dans le cache local",
"+UjDmN": "Connecté avec accès en écriture",
"+Vxixo": "Chat de Groupe Secret",
"+aZY2h": "Type de Zap",
@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "Voir plus",
"01iNut": "L'adresse Nostr ne vous appartient pas",
"08zn6O": "Export Keys",
"0Azlrb": "Gérer",
"0BUTMv": "Chercher...",
"0HFX0T": "Utiliser l'emplacement exact",
@ -29,7 +30,7 @@
"0siT4z": "Politique",
"0uoY11": "Afficher l'état",
"0yO7wF": "{n} secondes",
"0zASjL": "Go",
"0zASjL": "Aller",
"1H4Keq": "{n} utilisateurs",
"1Mo59U": "Êtes-vous sûr de vouloir supprimer cette note de vos favoris ?",
"1R43+L": "Accéder à la configuration de Nostr Wallet Connect",
@ -58,6 +59,7 @@
"3qnJlS": "Vous votez avec {amount} sats",
"3t3kok": "{n,plural,=1{{n} nouvelle note} other{{n} nouvelles notes}}",
"3tVy+Z": "{n} Abonnés",
"3yk8fB": "Wallet",
"450Fty": "Aucun",
"47FYwb": "Annuler",
"4IPzdn": "Développeurs principaux",
@ -168,9 +170,11 @@
"FdhSU2": "Obtenir maintenant",
"FfYsOb": "Une erreur est survenue!",
"FmXUJg": "vous suit",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "Retirer",
"G1BGCg": "Sélectionnez un portefeuille",
"G3A56c": "Subscribed to Push",
"G3A56c": "Abonné aux pushs",
"GFOoEE": "Sel",
"GL8aXW": "Favoris ({n})",
"GSye7T": "Adresse Lightning",
@ -189,9 +193,10 @@
"HWbkEK": "Vider le cache et recharger",
"HbefNb": "Ouvrir le Wallet",
"HhcAVH": "Vous ne suivez pas cette personne, cliquez ici pour charger les médias de <i>{link}</i>, ou mettez à jour <a><i>vos préférences</i></a> pour toujours charger les médias de tout le monde.",
"HqRNN8": "Support",
"I1AoOu": "Dernier message {time}",
"IEwZvs": "Êtes-vous sûr de vouloir désépingler cette note?",
"IIOul1": "Account Data",
"IIOul1": "Données du compte",
"IKKHqV": "Abonnements",
"IOu4Xh": "Vous devez être abonné à {tier} pour accéder à {app} deck",
"IVbtTS": "Zapper tous les {n} sats",
@ -203,13 +208,14 @@
"J+dIsA": "Abonnements",
"J1iLmb": "Notifications non autorisées",
"J2HeQ+": "Utiliser des virgules pour séparer les mots, par exemple mot1, mot2, mot3",
"JA+tz3": "Looking up thread...",
"JA+tz3": "Recherche du fil de discussion...",
"JCIgkj": "Nom dutilisateur",
"JGrt9q": "Envoyer des sats à {name}",
"JHEHCk": "Zaps ({n})",
"JIVWWA": "Le sport",
"JPFYIM": "Aucune adresse Lightning",
"JSx7y9": "Abonnez-vous à {site_name} {plan} pour {price} et recevez les récompenses suivantes",
"JTht/T": "NIP-96",
"JeoS4y": "Reposter",
"JjGgXI": "Rechercher des utilisateurs",
"JkLHGw": "Site Internet",
@ -256,8 +262,8 @@
"OQSOJF": "Obtenir une adresse nostr gratuite",
"OQXnew": "Votre abonnement est toujours actif, vous ne pouvez pas le renouveler pour le moment",
"ORGv1Q": "Créé",
"OoZgbB": "Failed to update, please try again",
"OxPdQ0": "Scanning {date}",
"OoZgbB": "Échec de la mise à jour, veuillez réessayer.",
"OxPdQ0": "Analyse du {date}",
"P2o+ZZ": "Adresse Nostr invalide",
"P61BTu": "Copier l'événement JSON",
"P7FD0F": "Système (Défaut)",
@ -268,9 +274,10 @@
"Pe0ogR": "Thème",
"PrsIg7": "Les réactions seront affichées sur chaque page, si désactivées aucune réaction ne sera affichée",
"QDFTjG": "{n} Relais",
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QJfhKt": "La clé privée est comme un mot de passe, mais elle ne peut pas être réinitialisée. Gardez-la précieusement et ne la montrez jamais à personne. Une fois que quelqu'un possède votre clé privée, il a accès à votre compte pour toujours.",
"QWhotP": "Zap Pool ne fonctionne que si vous utilisez l'une des connexions de portefeuille supportées (WebLN, LNC, LNDHub ou Nostr Wallet Connect)",
"Qxv0B2": "Vous avez actuellement {number} sats dans votre réserve de zap.",
"R/6nsx": "Subscription",
"R81upa": "Personnes que vous suivez",
"RDha9y": "Le Service Worker ne fonctionne pas",
"RSr2uB": "Le nom d'utilisateur ne doit contenir que des lettres minuscules et des chiffres.",
@ -278,7 +285,7 @@
"RfhLwC": "Par : {author}",
"RhDAoS": "Êtes-vous sûr que vous voulez supprimer {id}",
"RoOyAh": "Relais",
"RrCui3": "Summary",
"RrCui3": "Résumé",
"Rs4kCE": "Favori",
"SLZGPn": "Saisissez un code PIN pour crypter votre clé privée. Vous devez saisir ce code PIN à chaque fois que vous ouvrez le site {site}.",
"SMO+on": "Envoyer zap à {name}",
@ -299,7 +306,8 @@
"TdtZQ5": "Crypto",
"TpgeGw": "Sel Hex..",
"Tpy00S": "Personnes",
"TvKqBp": "liked",
"TvKqBp": "aimé",
"TwyMau": "Account",
"U1aPPi": "Cessez d'écouter",
"UDYlxu": "Demandes en attente",
"UJTWqI": "Retirer de mes relais",
@ -307,6 +315,7 @@
"UNjfWJ": "Vérifier toutes les signatures d'événement reçues des relais",
"UT7Nkj": "Nouvelle Discussion",
"UUPFlt": "Les utilisateur-trice-s doivent accepter l'avertissement de contenu pour afficher le contenu de votre note.",
"UaCh1c": "Add Server",
"Ub+AGc": "Se connecter",
"Up5U7K": "Bloquer",
"Ups2/p": "Votre demande est en attente",
@ -345,6 +354,7 @@
"ZS+jRE": "Envoyer les zap splits à",
"Zff6lu": "Le nom d'utilisateur iris.to/<b>{name}</b> vous est réservé !",
"ZlmK/p": "{name} vous a invité à rejoindre {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "Financer les développeurs et plateformes fournissant des services de vérification NIP-05",
"a7TDNm": "Les notes seront diffusées en temps réel dans l'onglet global et dans l'onglet notes",
"aHje0o": "Nom ou pseudonyme",
@ -354,7 +364,7 @@
"aWpBzj": "Montrer plus",
"b12Goz": "Mnémonique",
"b5vAk0": "Votre identifiant agira comme une adresse Lightning et redirigera vers l'adresse LNURL ou Lightning de votre choix",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bF1MYT": "Vous êtes un leader de la communauté et vous gagnez <b>{percent}</b> des abonnements des utilisateurs parrainés !",
"bG00/W": "Service Worker en fonctionnement",
"bJ+wrA": "Compute prune list",
"bLZL5a": "Obtenir l'adresse",
@ -367,19 +377,20 @@
"c35bj2": "Si vous avez une question concernant votre commande NIP-05, veuillez contacter en DM {link}",
"c3g2hL": "Republier",
"cFbU1B": "Vous utilisez Alby ? Allez sur {link} pour obtenir votre configuration NWC !",
"cG/bKQ": "Native nostr wallet connection",
"cG/bKQ": "Connexion au portefeuille nostr natif",
"cHCwbF": "Photographie",
"cPIKU2": "Abonnements",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "Tout mettre en sourdine",
"cg1VJ2": "Connecter un portefeuille",
"cuP16y": "Support multi-comptes",
"cuV2gK": "le nom est enregistré",
"cyR7Kh": "Retourner",
"d+6YsV": "Listes pour la mise en sourdine :",
"d2ebEu": "Not Subscribed to Push",
"d2ebEu": "Non abonné aux pushs",
"d7d0/x": "Adresse LN",
"dK2CcV": "The public key is like your username, you can share it with anyone.",
"dK2CcV": "La clé publique est comme votre nom d'utilisateur, vous pouvez la partager avec n'importe qui.",
"dOQCL8": "Nom à afficher",
"deEeEI": "Registre",
"djLctd": "Quantité en sats",
@ -396,7 +407,7 @@
"eXT2QQ": "Discussion de groupe",
"egib+2": "{n,plural,=1{& {n} autre} other{& {n} autres}}",
"ejEGdx": "Accueil",
"f1OxTe": "Community leaders are individuals who grow the nostr ecosystem by being active in their local communities and helping onboard new users. Anyone can become a community leader, but few hold the current honorary title.",
"f1OxTe": "Les leaders de la communauté sont des personnes qui développent l'écosystème de nostr en étant actifs dans leurs communautés locales et en aidant à l'intégration de nouveaux utilisateurs. Tout le monde peut devenir un leader de la communauté, mais peu de personnes détiennent ce titre honorifique.",
"f2CAxA": "Dump",
"fBI91o": "Zap",
"fBlba3": "Merci d'utiliser {site}. Veuillez envisager de faire un don si vous le pouvez.",
@ -410,7 +421,7 @@
"flnGvv": "Qu'avez-vous en tête?",
"fqwcJ1": "Don On-Chain",
"fsB/4p": "Enregistré",
"furjvW": "Watch Stream",
"furjvW": "Regarder la diffusion",
"g5pX+a": "À propos",
"g985Wp": "Échec de l'envoi du vote",
"gDzDRs": "Emoji à envoyer en cas de réaction à une note",
@ -422,14 +433,16 @@
"h7jvCs": "{site} est plus amusant ensemble !",
"h8XMJL": "Badges",
"hF6IN2": "Prune Follow List",
"hMQmIw": "Sync Account",
"hMQmIw": "Synchroniser le compte",
"hMzcSq": "Messages",
"hRTfTR": "PRO",
"hY4lzx": "Supporte",
"hYOE+U": "Invite",
"ha8JKG": "Afficher le graphique",
"hicxcO": "Afficher les réponses",
"hmZ3Bz": "Les médias",
"hniz8Z": "ici",
"hvFRBo": "Interaction",
"i/dBAR": "Zap Pool",
"i5gBFz": "Vos paiements envoyés et reçus apparaîtront ici.",
"iCqGww": "Réactions ({n})",
@ -438,7 +451,7 @@
"iICVoL": "{x} suit ({y} doublons)",
"iNWbVV": "Gérer",
"iXPL0Z": "Impossible de se connecter avec une clé privée via une connexion non sécurisée, veuillez utiliser une extension de gestionnaire de clés Nostr à la place",
"iYc3Ld": "Payments",
"iYc3Ld": "Paiements",
"ieGrWo": "Suivre",
"ipHVx5": "Générer la facture",
"itPgxd": "Profil",
@ -458,13 +471,13 @@
"kJYo0u": "{n,plural,=0{{name} a reposté} other{{name} & {n} autres ont reposté}}",
"kaaf1E": "maintenant",
"kc79d3": "Sujets",
"kqPQJD": "Configure zap pool",
"kqPQJD": "Configurer le pool de zaps",
"kuPHYE": "{n,plural,=0{{name} a liké} other{{name} & {n} autres ont liké}}",
"l+ikU1": "Tout dans {plan}",
"l3H1EK": "Invitez vos amis",
"lCILNz": "Acheter Maintenant",
"lD3+8a": "Payer",
"lEnclp": "My events: {n}",
"lEnclp": "Mes évènements : {n}",
"lPWASz": "Adresse nostr Snort",
"lTbT3s": "Mot de passe du portefeuille",
"lfOesV": "Non-Zap",
@ -477,7 +490,7 @@
"mKAr6h": "Suivre tout",
"mKh2HS": "Service d'hébergement de fichiers",
"mKhgP9": "{n,plural,=0{} =1{zappé} other{zappé}}",
"mOFG3K": "Start",
"mOFG3K": "Démarrer",
"mfe8RW": "Option : {n}",
"n1Whvj": "Changer",
"nDejmx": "Débloquer",
@ -485,7 +498,7 @@
"nGGDsi": "Notifications autorisées",
"nIchMQ": "Recherche de l'activité du compte ({progress})",
"nUT0Lv": "Outils",
"nWQFic": "Renew",
"nWQFic": "Renouveler",
"nihgfo": "Écouter cet article",
"nwZXeh": "{n} bloqué",
"o/gK53": "Deck",
@ -505,7 +518,7 @@
"qMePPG": "Note",
"qMx1sA": "Montant des Zaps par défaut",
"qUJTsT": "Bloqué",
"qXCbgZ": "Unlock",
"qXCbgZ": "Déverrouiller",
"qZsKBR": "Renouveler {tier}",
"qcJFEJ": "API de notifications désactivée",
"qdGuQo": "Votre Clé Privée Est (ne la partagez avec personne)",
@ -522,7 +535,7 @@
"rT14Ow": "Ajouter Relais",
"rfuMjE": "(Défaut)",
"rmdsT4": "{n} jours",
"rn52n9": "Public Chat Channels",
"rn52n9": "Chaînes de discussion publiques",
"rx1i0i": "Lien court",
"sKDn4e": "Badges d'exposition",
"sUNhQE": "utilisateur",
@ -539,8 +552,8 @@
"uCk8r+": "Vous avez déjà un compte ?",
"uSV4Ti": "Les republications seront automatiquement confirmées",
"uc0din": "Envoyer les splits de sats à",
"ufvXH1": "Found {n} events",
"uhu5aG": "Public",
"ufvXH1": "{n} évènements trouvés",
"uhu5aG": "Publique",
"un1nGw": "{n} notes",
"usAvMr": "Modifier le Profil",
"v8lolG": "Démarrer la discussion",
@ -563,7 +576,7 @@
"wtLjP6": "Copier Identifiant",
"x/Fx2P": "Financez les services que vous utilisez en fractionnant une partie de tous vos zaps en un ensemble de fonds !",
"x82IOl": "Mode Sourdine",
"xEjBS7": "For you",
"xEjBS7": "Pour vous",
"xIcAOU": "Votes par {type}",
"xIoGG9": "Aller à",
"xSoIUU": "Worker Relay",
@ -577,6 +590,7 @@
"y1Z3or": "Langue",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL ou adresse Lightning",
"z3UjXR": "Debug",
"zCb8fX": "Poids",
"zFegDD": "Contacted",
"zINlao": "Propriétaire",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} mikrosekundi",
"00LcfG": "Load more",
"01iNut": "Nostr address does not belong to you",
"08zn6O": "Export Keys",
"0Azlrb": "Upravljaj",
"0BUTMv": "Pretraga...",
"0HFX0T": "Use Exact Location",
@ -58,6 +59,7 @@
"3qnJlS": "Glasate sa {amount} sats-a",
"3t3kok": "{n,plural,=1{{n} nova bilješka} other{{n} novih bilješki}}",
"3tVy+Z": "{n} Pratitelji",
"3yk8fB": "Wallet",
"450Fty": "Nijedan",
"47FYwb": "Otkaži",
"4IPzdn": "Primarni Developeri",
@ -168,6 +170,8 @@
"FdhSU2": "Preuzmi Sad",
"FfYsOb": "Došlo je do pogreške!",
"FmXUJg": "vas prati",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "Ukloni",
"G1BGCg": "Odaberite novčanik",
"G3A56c": "Subscribed to Push",
@ -189,6 +193,7 @@
"HWbkEK": "Clear cache and reload",
"HbefNb": "Otvori novčanik",
"HhcAVH": "You don't follow this person, click here to load media from <i>{link}</i>, or update <a><i>your preferences</i></a> to always load media from everybody.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "Jeste sigurni da želite otkačiti bilješku?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "Sport",
"JPFYIM": "No lightning address",
"JSx7y9": "Subscribe to {site_name} {plan} for {price} and receive the following rewards",
"JTht/T": "NIP-96",
"JeoS4y": "Repost",
"JjGgXI": "Search users",
"JkLHGw": "Web stranica",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)",
"Qxv0B2": "You currently have {number} sats in your zap pool.",
"R/6nsx": "Subscription",
"R81upa": "People you follow",
"RDha9y": "Service Worker Not Running",
"RSr2uB": "Username must only contain lowercase letters and numbers",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Salt..",
"Tpy00S": "People",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Stop listening",
"UDYlxu": "Pretplate na čekanju",
"UJTWqI": "Remove from my relays",
@ -307,6 +315,7 @@
"UNjfWJ": "Check all event signatures received from relays",
"UT7Nkj": "New Chat",
"UUPFlt": "Korisnici moraju prihvatiti upozorenje o sadržaju kako bi prikazali sadržaj Vaše bilješke.",
"UaCh1c": "Add Server",
"Ub+AGc": "Sign In",
"Up5U7K": "Blokiraj",
"Ups2/p": "Your application is pending",
@ -345,6 +354,7 @@
"ZS+jRE": "Send zap splits to",
"Zff6lu": "Username iris.to/<b>{name}</b> is reserved for you!",
"ZlmK/p": "{name} invited you to {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "Financirajte developere i platforme koje pružaju NIP-05 verifikacijske usluge",
"a7TDNm": "Notes will stream in real time into global and notes tab",
"aHje0o": "Name or nym",
@ -371,6 +381,7 @@
"cHCwbF": "Photography",
"cPIKU2": "Praćenja",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "Utišaj sve",
"cg1VJ2": "Spojiti Novčanik",
"cuP16y": "Podrška za više računa",
@ -426,10 +437,12 @@
"hMzcSq": "Poruke",
"hRTfTR": "PRO",
"hY4lzx": "Podržava",
"hYOE+U": "Invite",
"ha8JKG": "Show graph",
"hicxcO": "Pokaži odgovore",
"hmZ3Bz": "Media",
"hniz8Z": "ovdje",
"hvFRBo": "Interaction",
"i/dBAR": "Zap Pool",
"i5gBFz": "Your sent and received payments will show up here.",
"iCqGww": "Reakcije ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "Jezik",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL ili Lightning Adresa",
"z3UjXR": "Debug",
"zCb8fX": "Weight",
"zFegDD": "Kontakt",
"zINlao": "Vlasnik",

View File

@ -1,7 +1,7 @@
{
"+D82kt": "Biztos hogy ezt meg akarod osztani: {id}",
"+PzQ9Y": "Fizetés Most",
"+QM0PJ": "Sync all events for your profile into local cache",
"+QM0PJ": "Szinkronizálja a profil összes eseményét a helyi gyorsítótárba",
"+UjDmN": "Bejelentkezve írási hozzáféréssel",
"+Vxixo": "Titkos Csoportos Chat",
"+aZY2h": "Zap típusa",
@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "Továbbiak betöltése",
"01iNut": "A Nostr cím nem tiéd",
"08zn6O": "Kulcsok exportálása",
"0Azlrb": "Menedzselés",
"0BUTMv": "Keresés...",
"0HFX0T": "Pontos hely használata",
@ -58,6 +59,7 @@
"3qnJlS": "{amount} sats-al szavazol",
"3t3kok": "{n,plural,one {}=1{{n} új bejegyzés} other{{n} új bejegyzések}}",
"3tVy+Z": "{n} Követő",
"3yk8fB": "Tárca",
"450Fty": "Nincs",
"47FYwb": "Törlés",
"4IPzdn": "Elsődleges Fejlesztők",
@ -168,6 +170,8 @@
"FdhSU2": "Igényeld most",
"FfYsOb": "Hiba történt!",
"FmXUJg": "követ téged",
"FvanT6": "Fiókok",
"FzbSGg": "Nincs médiakiszolgálója, próbáljon meg néhányat hozzáadni.",
"G/yZLu": "Eltávolítás",
"G1BGCg": "Tárca kiválasztása",
"G3A56c": "Feliratkoztál a Push szolgáltatásra",
@ -189,9 +193,10 @@
"HWbkEK": "Törölje a gyorsítótárat és töltse be újra",
"HbefNb": "Pénztárca megnyitása",
"HhcAVH": "Ön nem követi ezt a személyt, kattintson ide a média betöltéséhez a(z) <i>{link}</i> webhelyről, vagy frissítse <a><i>beállításait</i></a>, hogy a médiát mindig mindenkitől betöltse.",
"HqRNN8": "Segítség",
"I1AoOu": "Utolsó hozzászólás {time}",
"IEwZvs": "Biztos hogy a bejegyzés kiemelését visszavonod?",
"IIOul1": "Account Data",
"IIOul1": "Fiók adatok",
"IKKHqV": "Követőim",
"IOu4Xh": "A {tier} előfizetőjének kell lenned ahhoz, hogy a {app} fedélzetéhez hozzáférj",
"IVbtTS": "Zap-elni mind a {n} sats-ot",
@ -210,6 +215,7 @@
"JIVWWA": "Sport",
"JPFYIM": "Nincs Lightning cím",
"JSx7y9": "Iratkozz fel a {site_name} {plan} webhelyére {price} áron, és a következő jutalmakat kapod",
"JTht/T": "NIP-96",
"JeoS4y": "Megosztás",
"JjGgXI": "Felhasználók keresése",
"JkLHGw": "Weboldal",
@ -256,8 +262,8 @@
"OQSOJF": "Szerezz egy Ingyenes Nostr címet",
"OQXnew": "Az előfizetésed még aktív, így nem tudod megújítani",
"ORGv1Q": "Létrehozva",
"OoZgbB": "Failed to update, please try again",
"OxPdQ0": "Scanning {date}",
"OoZgbB": "Nem sikerült frissíteni, kérjük próbáld meg újra",
"OxPdQ0": "Szkennelés {date}",
"P2o+ZZ": "Érvénytelen Nostr cím",
"P61BTu": "JSON eset",
"P7FD0F": "Rendszer (Alapértelmezett)",
@ -268,9 +274,10 @@
"Pe0ogR": "Téma",
"PrsIg7": "A reakciók minden oldalon megjelennek, ha letiltod őket nem jelennek meg",
"QDFTjG": "{n} Csomópont",
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QJfhKt": "A privát kulcs olyan, mint egy jelszó, de nem lehet visszaállítani. Vigyázz rá gondosan, és soha ne mutasd meg senkinek. Ha valaki a privát kulcsodat egyszer megszerzi, örökre hozzáférhet a fiókodhoz.",
"QWhotP": "A Zap Pool csak akkor működik, ha a támogatott pénztárcakapcsolatok egyikét használja (WebLN, LNC, LNDHub vagy Nostr Wallet Connect)",
"Qxv0B2": "Jelenleg a zap-medencében {number} sats van.",
"R/6nsx": "Előfizetés",
"R81upa": "Általad követett személyek",
"RDha9y": "A szervizmunkás nem fut",
"RSr2uB": "A felhasználónév csak kisbetűket és számokat tartalmazhat",
@ -278,7 +285,7 @@
"RfhLwC": "Írta: {author}",
"RhDAoS": "Biztos hogy törölni akarod a {id}",
"RoOyAh": "Csomópontok",
"RrCui3": "Summary",
"RrCui3": "Összegzés",
"Rs4kCE": "Könyvjelző",
"SLZGPn": "A privát kulcs titkosításához adjon meg egy PIN-kódot. Ezt a PIN-kódot minden alkalommal meg kell adnod, amikor a(z) {site}-ot megnyitod.",
"SMO+on": "Sat küldése {name}",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Salt..",
"Tpy00S": "Személyek",
"TvKqBp": "kedvelted",
"TwyMau": "Fiók",
"U1aPPi": "Hallgatás leállítása",
"UDYlxu": "Függőben lévő előfizetések",
"UJTWqI": "Eltávolítás a csomópontjaim közül",
@ -307,6 +315,7 @@
"UNjfWJ": "Ellenőrizze a csomópontokról kapott összes esemény aláírást",
"UT7Nkj": "Új Beszélgetés",
"UUPFlt": "Ahhoz hogy a bejegyzés tartalma megjelenjen, a felhasználóknak a tartalomra vonatkozó figyelmeztetést el kell fogadniuk.",
"UaCh1c": "Szerver hozzáadása",
"Ub+AGc": "Bejelentkezés",
"Up5U7K": "Tiltás",
"Ups2/p": "Jelentkezésed folyamatban van",
@ -330,7 +339,7 @@
"XICsE8": "Fájl tárhelyszolgáltatók",
"XPB8VV": "Alby tárca kapcsolat",
"XQiFEl": "Követői csomópontok minősége",
"XSdWHA": "Redeem",
"XSdWHA": "Beváltás",
"XXm7jJ": "Trendi hashtagek",
"XgWvGA": "Reakciók",
"Xnimz0": "Küldés a <b>{wallet}</b>",
@ -345,6 +354,7 @@
"ZS+jRE": "Zap felosztások küldése ide",
"Zff6lu": "Az iris.to/<b>{name}</b> számodra fenntartva van!",
"ZlmK/p": "{name} meghívtt a {app}",
"a1x4gD": "A médiaszerverek olyan médiát tárolnak, amelyet a bejegyzésekben megoszthatsz képként és videóként.",
"a5UPxh": "Támogasd a fejlesztőket és a platform szolgáltatókat akik NIP-05 azonosító szolgáltatásokat biztosítanak",
"a7TDNm": "A bejegyzések a globális és a bejegyzések fülek alatt valós időben jelennek meg",
"aHje0o": "Név vagy név",
@ -371,6 +381,7 @@
"cHCwbF": "Fényképezés",
"cPIKU2": "Követek",
"cQfLWb": "Cím..",
"cVcgLJ": "Médiakiszolgálók",
"cWx9t8": "Mind némítása",
"cg1VJ2": "Pénztárca csatlakoztatása",
"cuP16y": "Több fiók támogatása",
@ -379,7 +390,7 @@
"d+6YsV": "Listák a némításhoz:",
"d2ebEu": "A Push-ra nem vagy feliratkozva",
"d7d0/x": "LN cím",
"dK2CcV": "The public key is like your username, you can share it with anyone.",
"dK2CcV": "A nyilvános kulcs olyan, mint a felhasználóneved, bárkivel megoszthatod.",
"dOQCL8": "Megjelenítendő név",
"deEeEI": "Regisztráció",
"djLctd": "Mennyiség sat-ban",
@ -422,14 +433,16 @@
"h7jvCs": "{site} együtt sokkal szórakoztatóbb!",
"h8XMJL": "Kitüntetések",
"hF6IN2": "Követői lista metszése",
"hMQmIw": "Sync Account",
"hMQmIw": "Fiók szinkronizálása",
"hMzcSq": "Üzenetek",
"hRTfTR": "PRO",
"hY4lzx": "Támogatás",
"hYOE+U": "Meghívás",
"ha8JKG": "Diagram megjelenítése",
"hicxcO": "Válaszok megjelenítése",
"hmZ3Bz": "Média",
"hniz8Z": "itt",
"hvFRBo": "Interakció",
"i/dBAR": "Zap Medence",
"i5gBFz": "Itt jelennek meg az általad küldött és fogadott fizetések.",
"iCqGww": "({n}) reakciók",
@ -438,7 +451,7 @@
"iICVoL": "{x} követők ({y} másolatok)",
"iNWbVV": "Azonosító",
"iXPL0Z": "Nem biztonságos kapcsolaton a privát kulccsoddal NEM (!) tudsz bejelentkezni, használj helyette egy Nostr kulcskezelő bővítményt",
"iYc3Ld": "Payments",
"iYc3Ld": "Fizetések",
"ieGrWo": "Követem",
"ipHVx5": "Díjbekérő létrehozása",
"itPgxd": "Profil",
@ -464,10 +477,10 @@
"l3H1EK": "Ismerősök meghívása",
"lCILNz": "Vásárlás",
"lD3+8a": "Fizetem",
"lEnclp": "My events: {n}",
"lEnclp": "Saját eseményeim: {n}",
"lPWASz": "Snort nostr cím",
"lTbT3s": "Pénztárca jelszava",
"lfOesV": "Non-Zap",
"lfOesV": "Nem Zap",
"lgg1KN": "Felhasználói felület",
"ll3xBp": "Képmegosztó szolgáltató",
"lnaT9F": "Követek {n}",
@ -477,7 +490,7 @@
"mKAr6h": "Követem mindet",
"mKh2HS": "Fájl feltöltő szolgáltatás",
"mKhgP9": "{n, plural, =0 {} =1 {zapp-olva} other {zapp-olva}}",
"mOFG3K": "Start",
"mOFG3K": "Indítás",
"mfe8RW": "Lehetőség: {n}",
"n1Whvj": "Váltás",
"nDejmx": "Tiltás visszavonása",
@ -485,7 +498,7 @@
"nGGDsi": "Értesítések engedélyezve",
"nIchMQ": "Fióktevékenység keresése ({progress})",
"nUT0Lv": "Eszközök",
"nWQFic": "Renew",
"nWQFic": "Megújítás",
"nihgfo": "Hallgassa meg ezt a cikket",
"nwZXeh": "{n} tiltott",
"o/gK53": "Fedélzet",
@ -505,7 +518,7 @@
"qMePPG": "Bejegyzés",
"qMx1sA": "Alapértelmezett Zap összeg",
"qUJTsT": "Tiltva",
"qXCbgZ": "Unlock",
"qXCbgZ": "Feloldás",
"qZsKBR": "{tier} megújítása",
"qcJFEJ": "Értesítési API letiltva",
"qdGuQo": "A te privát kulcsod (senkivel se oszd meg)",
@ -539,8 +552,8 @@
"uCk8r+": "Már van fiókja?",
"uSV4Ti": "A megosztásokhoz manuális megerősítés szükséges",
"uc0din": "Zap felosztások küldése ide",
"ufvXH1": "Found {n} events",
"uhu5aG": "Public",
"ufvXH1": "Talált {n} eseményt",
"uhu5aG": "Nyilvános",
"un1nGw": "{n} bejegyzés",
"usAvMr": "Profil módosítása",
"v8lolG": "Csevegés indítása",
@ -577,6 +590,7 @@
"y1Z3or": "Nyelv",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL vagy Lightning cím",
"z3UjXR": "Debug",
"zCb8fX": "Súly",
"zFegDD": "Kapcsolat",
"zINlao": "Tulajdonos",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "Memuat lebih banyak",
"01iNut": "Nostr address does not belong to you",
"08zn6O": "Export Keys",
"0Azlrb": "Mengelola",
"0BUTMv": "Cari...",
"0HFX0T": "Use Exact Location",
@ -58,6 +59,7 @@
"3qnJlS": "Anda memberikan suara dengan {amount} sats",
"3t3kok": "{n,plural,=1{{n} catatan baru} other{{n} catatan-catatan baru}}",
"3tVy+Z": "{n} Pengikut",
"3yk8fB": "Wallet",
"450Fty": "Tidak ada",
"47FYwb": "Membatalkan",
"4IPzdn": "Pengembang Utama",
@ -168,6 +170,8 @@
"FdhSU2": "Klaim Sekarang",
"FfYsOb": "Terjadi kesalahan!",
"FmXUJg": "mengikutimu",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "Hapus",
"G1BGCg": "Pilih Dompet",
"G3A56c": "Subscribed to Push",
@ -189,6 +193,7 @@
"HWbkEK": "Hapus cache dan muat ulang",
"HbefNb": "Buka Dompet",
"HhcAVH": "Anda tidak mengikuti orang ini, klik di sini untuk memuat media dari <i>{link}</i>, atau perbarui <a><i>preferensi Anda</i></a> untuk selalu memuat media dari semua orang.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "Apakah Anda yakin Anda ingin menghilangkan sematan catatan ini?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "Olahraga",
"JPFYIM": "Tidak ada alamat petir",
"JSx7y9": "Berlangganan ke {site_name} {plan} untuk mendapatkan {price} dan dapatkan hadiah berikut",
"JTht/T": "NIP-96",
"JeoS4y": "Repost",
"JjGgXI": "Cari pengguna",
"JkLHGw": "Situs web",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Pool hanya berfungsi jika Anda menggunakan salah satu koneksi dompet yang didukung (WebLN, LNC, LNDHub, atau Nostr Wallet Connect)",
"Qxv0B2": "Saat ini Anda memiliki {number} sat di zap pool Anda.",
"R/6nsx": "Subscription",
"R81upa": "Orang yang Anda ikuti",
"RDha9y": "Service Worker Not Running",
"RSr2uB": "Nama pengguna hanya boleh terdiri dari huruf kecil dan angka",
@ -300,6 +307,7 @@
"TpgeGw": "Garam Hex..",
"Tpy00S": "Orang",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Berhenti mendengarkan",
"UDYlxu": "Langganan Tertunda",
"UJTWqI": "Hapus dari relai saya",
@ -307,6 +315,7 @@
"UNjfWJ": "Periksa semua tanda tangan peristiwa yang diterima dari relay",
"UT7Nkj": "Obrolan Baru",
"UUPFlt": "Pengguna harus menerima peringatan konten untuk menampilkan konten catatan Anda.",
"UaCh1c": "Add Server",
"Ub+AGc": "Masuk",
"Up5U7K": "Blokir",
"Ups2/p": "Your application is pending",
@ -345,6 +354,7 @@
"ZS+jRE": "Kirim zap split ke",
"Zff6lu": "Nama pengguna iris.to/<b>{name}</b> disediakan untuk Anda!",
"ZlmK/p": "{name} invited you to {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "Danai pengembang-pengembang dan platform-platform yang menyediakan layanan verifikasi NIP-05",
"a7TDNm": "Catatan akan mengalir secara real time ke tab global dan catatan",
"aHje0o": "Nama atau nym",
@ -371,6 +381,7 @@
"cHCwbF": "Fotografi",
"cPIKU2": "Yang diikuti",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "Bisukan semua",
"cg1VJ2": "Hubungkan Dompet",
"cuP16y": "Dukungan multi akun",
@ -426,10 +437,12 @@
"hMzcSq": "Pesan-pesan",
"hRTfTR": "PRO",
"hY4lzx": "Dukungan",
"hYOE+U": "Invite",
"ha8JKG": "Show graph",
"hicxcO": "Tampilkan balasan-balasan",
"hmZ3Bz": "Media",
"hniz8Z": "di sini",
"hvFRBo": "Interaction",
"i/dBAR": "Zap Pool",
"i5gBFz": "Your sent and received payments will show up here.",
"iCqGww": "Reaksi-reaksi ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "Bahasa",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL atau Alamat Kilat",
"z3UjXR": "Debug",
"zCb8fX": "Berat",
"zFegDD": "Kontak",
"zINlao": "Pemilik",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "Per saperne di più",
"01iNut": "Nostr address does not belong to you",
"08zn6O": "Export Keys",
"0Azlrb": "Gestisci",
"0BUTMv": "Cerca...",
"0HFX0T": "Use Exact Location",
@ -58,6 +59,7 @@
"3qnJlS": "Stai votando con {amount} sats",
"3t3kok": "{n,plural,one {}=1{{n} nuova nota} other{{n} nuove note}}",
"3tVy+Z": "{n} Seguaci",
"3yk8fB": "Wallet",
"450Fty": "Nessuno",
"47FYwb": "Annulla",
"4IPzdn": "Sviluppatori principali",
@ -168,6 +170,8 @@
"FdhSU2": "Rivendica Ora",
"FfYsOb": "Si è verificato un errore!",
"FmXUJg": "ti segue",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "Rimuovi",
"G1BGCg": "Seleziona portafoglio",
"G3A56c": "Subscribed to Push",
@ -189,6 +193,7 @@
"HWbkEK": "Cancellare la cache e ricaricare",
"HbefNb": "Apri portafoglio",
"HhcAVH": "Se non segui questa persona, clicca qui per caricare i media da <i>{link}</i>, oppure aggiorna <a><i>le tue preferenze</i></a> per caricare sempre i media da tutti.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "Sei sicuro di voler staccare questa nota?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "Lo sport",
"JPFYIM": "Nessun indirizzo per i fulmini",
"JSx7y9": "Abbonatevi a {site_name} {plan} per {price} e riceverete i seguenti premi",
"JTht/T": "NIP-96",
"JeoS4y": "Riposta",
"JjGgXI": "Ricerca utenti",
"JkLHGw": "Sito web",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Pool funziona solo se si utilizza una delle connessioni di portafoglio supportate (WebLN, LNC, LNDHub o Nostr Wallet Connect).",
"Qxv0B2": "Attualmente hai {number} sats nel tuo pool di zap.",
"R/6nsx": "Subscription",
"R81upa": "Persone che segui",
"RDha9y": "Service Worker Not Running",
"RSr2uB": "Il nome utente deve contenere solo lettere minuscole e numeri",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Salt..",
"Tpy00S": "Persone",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Smettere di ascoltare",
"UDYlxu": "Registrazione in sospeso",
"UJTWqI": "Rimuovere dai miei relè",
@ -307,6 +315,7 @@
"UNjfWJ": "Controllare tutte le firme degli eventi ricevute dalle staffette",
"UT7Nkj": "Nuova chat",
"UUPFlt": "Gli utenti devono accettare l'avviso di contenuto per mostrare il contenuto della nota.",
"UaCh1c": "Add Server",
"Ub+AGc": "Accedi",
"Up5U7K": "Blocca",
"Ups2/p": "Your application is pending",
@ -345,6 +354,7 @@
"ZS+jRE": "Inviare gli split zap a",
"Zff6lu": "Il nome utente iris.to/<b>{name}</b> è riservato a voi!",
"ZlmK/p": "{name} invited you to {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "Sviluppatori di fondi e piattaforme che forniscono servizi di verifica NIP-05",
"a7TDNm": "Le note verranno trasmesse in tempo reale nella scheda globale e in quella delle note.",
"aHje0o": "Nome o nomenclatura",
@ -371,6 +381,7 @@
"cHCwbF": "Fotografia",
"cPIKU2": "Seguiti",
"cQfLWb": "URL...",
"cVcgLJ": "Media Servers",
"cWx9t8": "Silenzia tutti",
"cg1VJ2": "Collega il portafoglio",
"cuP16y": "Supporto per più account",
@ -426,10 +437,12 @@
"hMzcSq": "Messaggi",
"hRTfTR": "PRO",
"hY4lzx": "Supporto",
"hYOE+U": "Invite",
"ha8JKG": "Show graph",
"hicxcO": "Mostra le risposte",
"hmZ3Bz": "Media",
"hniz8Z": "qui",
"hvFRBo": "Interaction",
"i/dBAR": "Piscina Zap",
"i5gBFz": "Your sent and received payments will show up here.",
"iCqGww": "Reazioni ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "Lingua",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL o indirizzo Lightning",
"z3UjXR": "Debug",
"zCb8fX": "Peso",
"zFegDD": "Contatta",
"zINlao": "Proprietario",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n}ミリ秒",
"00LcfG": "さらに読み込む",
"01iNut": "Nostrアドレスはあなたのものではありません",
"08zn6O": "Export Keys",
"0Azlrb": "管理",
"0BUTMv": "検索する",
"0HFX0T": "正確な位置情報を使用する",
@ -58,6 +59,7 @@
"3qnJlS": "{amount}satsで投票します",
"3t3kok": "{n,plural,=1{{n}件の新しい投稿} other{{n}件の新しい投稿}}",
"3tVy+Z": "{n} フォロワー",
"3yk8fB": "Wallet",
"450Fty": "なし",
"47FYwb": "キャンセル",
"4IPzdn": "主要な開発者",
@ -168,6 +170,8 @@
"FdhSU2": "今すぐ要求する",
"FfYsOb": "エラーが発生しました!",
"FmXUJg": "フォローされています",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "削除",
"G1BGCg": "ウォレットを選択する",
"G3A56c": "プッシュを受け取る",
@ -189,6 +193,7 @@
"HWbkEK": "キャッシュをクリアして再読み込み",
"HbefNb": "ウォレットを開く",
"HhcAVH": "この人物をフォローしていない場合は、ここをクリックして <i>{link}</i>からメディアを読み込むか、または <a><i>設定を更新して</i></a> から常に全員からメディアを読み込むようにしてください。",
"HqRNN8": "Support",
"I1AoOu": "最終投稿 {time}",
"IEwZvs": "本当にこの投稿のピン留めを解除しますか?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "スポーツ",
"JPFYIM": "ライトニングアドレスがありません",
"JSx7y9": "{site_name} {plan} for {price} に登録すると、以下の特典が受けられる。",
"JTht/T": "NIP-96",
"JeoS4y": "リポスト",
"JjGgXI": "ユーザーを検索",
"JkLHGw": "ウェブサイト",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Poolは、サポートされているウォレットコネクション (WebLN, LNC, LNDHub, Nostr Wallet Connect) のいずれかを使用する場合にのみ機能します。",
"Qxv0B2": "現在、Zap Poolには{number} satsあります。",
"R/6nsx": "Subscription",
"R81upa": "あなたがフォローしている人",
"RDha9y": "サービスワーカーが実行されていません",
"RSr2uB": "ユーザー名には小文字と数字のみを使用すること",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Saltを入力",
"Tpy00S": "ユーザー",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "聞くのをやめる",
"UDYlxu": "保留中のサブスクリプション",
"UJTWqI": "リレーから外す",
@ -307,6 +315,7 @@
"UNjfWJ": "リレーから受け取ったすべてのイベント署名をチェックする",
"UT7Nkj": "新規チャット",
"UUPFlt": "ユーザーは、投稿内容の表示のためにコンテンツ警告へ同意する必要があります。",
"UaCh1c": "Add Server",
"Ub+AGc": "サインイン",
"Up5U7K": "ブロック",
"Ups2/p": "申請は保留中です",
@ -345,6 +354,7 @@
"ZS+jRE": "ザップ・スプリットの送信先",
"Zff6lu": "ユーザー名 iris.to/<b>{name}</b> はあなたのために予約されています!",
"ZlmK/p": "{name} から {app} に招待されました",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "NIP-05認証サービスを提供するプラットフォームや開発者に資金援助する",
"a7TDNm": "グローバルタブと投稿タブで投稿がリアルタイムに流れるようになります",
"aHje0o": "名前またはニックネーム",
@ -371,6 +381,7 @@
"cHCwbF": "写真撮影",
"cPIKU2": "フォロー中",
"cQfLWb": "URLを入力",
"cVcgLJ": "Media Servers",
"cWx9t8": "全てミュート",
"cg1VJ2": "ウォレットを接続",
"cuP16y": "マルチアカウント対応",
@ -426,10 +437,12 @@
"hMzcSq": "メッセージ",
"hRTfTR": "プロ",
"hY4lzx": "対応",
"hYOE+U": "Invite",
"ha8JKG": "グラフを表示",
"hicxcO": "返信を見る",
"hmZ3Bz": "メディア",
"hniz8Z": "こちら",
"hvFRBo": "Interaction",
"i/dBAR": "Zap Pool",
"i5gBFz": "送受した支払いはここに表示されます。",
"iCqGww": "リアクション ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "言語",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURLまたはライトニングアドレス",
"z3UjXR": "Debug",
"zCb8fX": "重量",
"zFegDD": "連絡先",
"zINlao": "運営者",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "더 보기",
"01iNut": "노스트르 주소는 당신의 소유가 아닙니다.",
"08zn6O": "Export Keys",
"0Azlrb": "관리",
"0BUTMv": "검색...",
"0HFX0T": "정확한 위치 사용",
@ -58,6 +59,7 @@
"3qnJlS": "{amount} sats로 투표 중입니다.",
"3t3kok": "{n,plural,=1{{n} 새 노트} other{{n} 새 노트}}",
"3tVy+Z": "{n} 팔로워",
"3yk8fB": "Wallet",
"450Fty": "없음",
"47FYwb": "취소",
"4IPzdn": "기본 개발자",
@ -168,6 +170,8 @@
"FdhSU2": "지금 신청하기",
"FfYsOb": "오류가 발생했습니다!",
"FmXUJg": "당신을 팔로우함",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "제거",
"G1BGCg": "지갑 선택",
"G3A56c": "푸시 구독",
@ -189,6 +193,7 @@
"HWbkEK": "캐시 지우기 및 리로드",
"HbefNb": "지갑 열기",
"HhcAVH": "이 사용자를 팔로우하지 않는 경우 여기를 클릭하여 <i>{link}</i>에서 미디어를 로드하거나 <a><i>환경설정</i></a> 을 업데이트하여 모든 사용자의 미디어를 항상 로드하세요.",
"HqRNN8": "Support",
"I1AoOu": "최근 게시물 {time}",
"IEwZvs": "이 노트를 고정 해제하시겠습니까?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "스포츠",
"JPFYIM": "라이트닝 주소 없음",
"JSx7y9": "{site_name} {plan} ( {price} )을 구독하고 다음과 같은 보상을 받으세요.",
"JTht/T": "NIP-96",
"JeoS4y": "리포스트",
"JjGgXI": "사용자 검색",
"JkLHGw": "웹사이트",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Pool은 지원되는 지갑 연결 중 하나를 사용하는 경우에만 작동합니다(WebLN, LNC, LNDHub 또는 Nostr Wallet Connect).",
"Qxv0B2": "현재 잽 풀에 {number} sats가 있습니다.",
"R/6nsx": "Subscription",
"R81upa": "내가 팔로우하는 사람들",
"RDha9y": "서비스 작업자가 실행되지 않음",
"RSr2uB": "사용자 아이디는 소문자와 숫자만 포함해야 합니다.",
@ -300,6 +307,7 @@
"TpgeGw": "헥스 솔트...",
"Tpy00S": "사람",
"TvKqBp": "좋아요",
"TwyMau": "Account",
"U1aPPi": "듣기 중단",
"UDYlxu": "보류 중인 구독",
"UJTWqI": "내 릴레이에서 제거",
@ -307,6 +315,7 @@
"UNjfWJ": "릴레이에서 받은 모든 이벤트 서명 확인",
"UT7Nkj": "새 채팅",
"UUPFlt": "노트의 콘텐츠를 표시하려면 사용자가 콘텐츠 경고를 수락해야 합니다.",
"UaCh1c": "Add Server",
"Ub+AGc": "로그인",
"Up5U7K": "차단",
"Ups2/p": "신청이 보류 중입니다.",
@ -345,6 +354,7 @@
"ZS+jRE": "다음 주소로 Zap을 분할하여 보냅니다.",
"Zff6lu": "사용자 이름 iris.to/<b>{name}</b> 은 귀하를 위해 예약되었습니다!",
"ZlmK/p": "{name} {app}에 초대되었습니다.",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "NIP-05 검증 서비스를 제공하는 펀드 개발자 및 플랫폼",
"a7TDNm": "노트가 글로벌 및 노트 탭으로 실시간 스트리밍됩니다.",
"aHje0o": "이름 또는 님",
@ -371,6 +381,7 @@
"cHCwbF": "사진",
"cPIKU2": "팔로잉",
"cQfLWb": "URL...",
"cVcgLJ": "Media Servers",
"cWx9t8": "모두 뮤트",
"cg1VJ2": "지갑 연결",
"cuP16y": "다중 계정 지원",
@ -426,10 +437,12 @@
"hMzcSq": "메시지",
"hRTfTR": "PRO",
"hY4lzx": "지원",
"hYOE+U": "Invite",
"ha8JKG": "그래프 표시",
"hicxcO": "답글 표시",
"hmZ3Bz": "미디어",
"hniz8Z": "여기",
"hvFRBo": "Interaction",
"i/dBAR": "Zap Pool",
"i5gBFz": "송금 및 수취한 결제 금액이 여기에 표시됩니다.",
"iCqGww": "리액션 ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "언어",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL 또는 라이트닝 주소",
"z3UjXR": "Debug",
"zCb8fX": "무게",
"zFegDD": "연락처",
"zINlao": "소유자",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "Minta Lebih",
"01iNut": "Nostr address does not belong to you",
"08zn6O": "Export Keys",
"0Azlrb": "Manage",
"0BUTMv": "Cari...",
"0HFX0T": "Use Exact Location",
@ -58,6 +59,7 @@
"3qnJlS": "You are voting with {amount} sats",
"3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}",
"3tVy+Z": "{n} Followers",
"3yk8fB": "Wallet",
"450Fty": "Kosong",
"47FYwb": "Tolak",
"4IPzdn": "Pelooper Utama",
@ -168,6 +170,8 @@
"FdhSU2": "Claim Now",
"FfYsOb": "Terjumpa error!",
"FmXUJg": "mengintip kamu",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "Hapus",
"G1BGCg": "Select Wallet",
"G3A56c": "Subscribed to Push",
@ -189,6 +193,7 @@
"HWbkEK": "Hapus cache dan reload",
"HbefNb": "Open Wallet",
"HhcAVH": "You don't follow this person, click here to load media from <i>{link}</i>, or update <a><i>your preferences</i></a> to always load media from everybody.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "Are you sure you want to unpin this note?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "Sport",
"JPFYIM": "Tidak ada alamat Lightning",
"JSx7y9": "Subscribe to {site_name} {plan} for {price} and receive the following rewards",
"JTht/T": "NIP-96",
"JeoS4y": "Repost",
"JjGgXI": "Search ulama",
"JkLHGw": "Situs web",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)",
"Qxv0B2": "Saat ini Anda memiliki {number} sat di zap pool Anda.",
"R/6nsx": "Subscription",
"R81upa": "Orang yang Anda mengintip",
"RDha9y": "Service Worker Not Running",
"RSr2uB": "Nama hanya boleh diterima jika ia huruf kecilan dan ber-angka",
@ -300,6 +307,7 @@
"TpgeGw": "Garam Hex..",
"Tpy00S": "Ummah",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Stop listening",
"UDYlxu": "Langganan Tertunda",
"UJTWqI": "Hapus dari `relay` saya",
@ -307,6 +315,7 @@
"UNjfWJ": "Check all event signatures received from relays",
"UT7Nkj": "Chat Baru",
"UUPFlt": "Users must accept the content warning to show the content of your note.",
"UaCh1c": "Add Server",
"Ub+AGc": "Masuk",
"Up5U7K": "Blok",
"Ups2/p": "Your application is pending",
@ -345,6 +354,7 @@
"ZS+jRE": "Kirim zap split ke",
"Zff6lu": "Nama iris.to/<b>{name}</b> telah disediakan untuk Anda!",
"ZlmK/p": "{name} invited you to {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "Fund developers and platforms providing NIP-05 verification services",
"a7TDNm": "Notes will stream in real time into global and notes tab",
"aHje0o": "Nama atau nym",
@ -371,6 +381,7 @@
"cHCwbF": "Photography",
"cPIKU2": "Mengintipi",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "Bisukan semua",
"cg1VJ2": "Connect Wallet",
"cuP16y": "Multi account support",
@ -426,10 +437,12 @@
"hMzcSq": "Msg2",
"hRTfTR": "PRO",
"hY4lzx": "Supports",
"hYOE+U": "Invite",
"ha8JKG": "Show graph",
"hicxcO": "Tampilkan balasan",
"hmZ3Bz": "Media",
"hniz8Z": "di sini",
"hvFRBo": "Interaction",
"i/dBAR": "Zap Pool",
"i5gBFz": "Your sent and received payments will show up here.",
"iCqGww": "Reactions ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "Language",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL or Lightning Address",
"z3UjXR": "Debug",
"zCb8fX": "Weight",
"zFegDD": "Contact",
"zINlao": "Owner",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "Meer laden",
"01iNut": "Nostr address does not belong to you",
"08zn6O": "Export Keys",
"0Azlrb": "Beheren",
"0BUTMv": "Zoeken naar...",
"0HFX0T": "Use Exact Location",
@ -58,6 +59,7 @@
"3qnJlS": "Je stemt met {amount} sats",
"3t3kok": "{n,plural,one {}=1{{n} nieuwe note} other{{n} nieuwe notes}}",
"3tVy+Z": "{n} Volgers",
"3yk8fB": "Wallet",
"450Fty": "Geen",
"47FYwb": "Annuleer",
"4IPzdn": "Primaire ontwikkelaars",
@ -168,6 +170,8 @@
"FdhSU2": "Claim nu",
"FfYsOb": "Er is een fout opgetreden!",
"FmXUJg": "volgt u",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "Verwijderen",
"G1BGCg": "Selecteer Wallet",
"G3A56c": "Subscribed to Push",
@ -189,6 +193,7 @@
"HWbkEK": "Cache wissen en herladen",
"HbefNb": "Open wallet",
"HhcAVH": "Je volgt deze persoon niet, klik hier om media te laden van <i>{link}</i>, of update <a><i>je voorkeuren</i></a> om altijd media van iedereen te laden.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "Weet u zeker dat u deze note wilt losmaken?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "Sport",
"JPFYIM": "Geen lightning adres",
"JSx7y9": "Abonneer je op {site_name} {plan} voor {price} en ontvang de volgende beloningen",
"JTht/T": "NIP-96",
"JeoS4y": "Repost",
"JjGgXI": "Zoek Account",
"JkLHGw": "Website",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Pool werkt alleen als u een van de ondersteunde wallet connecties gebruikt (WebLN, LNDHub of Nostr Wallet Connect)",
"Qxv0B2": "Op dit moment heb je {number} sats in je Zap-Pool.",
"R/6nsx": "Subscription",
"R81upa": "Mensen die je volgt",
"RDha9y": "Service Worker Not Running",
"RSr2uB": "Gebruikersnaam mag alleen kleine letters en cijfers bevatten",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Salt..",
"Tpy00S": "Personen",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Stop met luisteren",
"UDYlxu": "Abonnementen in afwachting",
"UJTWqI": "Verwijderen uit mijn relais",
@ -307,6 +315,7 @@
"UNjfWJ": "Controleer alle ontvangen evenementhandtekeningen van estafettes",
"UT7Nkj": "Nieuwe Chat",
"UUPFlt": "Gebruikers moeten akkoord gaan met de waarschuwing van de inhoud om de inhoud van uw notitie te tonen.",
"UaCh1c": "Add Server",
"Ub+AGc": "Aanmelden",
"Up5U7K": "Blokkeren",
"Ups2/p": "Your application is pending",
@ -345,6 +354,7 @@
"ZS+jRE": "Zap-splitsingen verzenden naar",
"Zff6lu": "Gebruikersnaam iris.to/<b>{name}</b> is voor jou gereserveerd!",
"ZlmK/p": "{name} invited you to {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "Support ontwikkelaars en platforms die NIP-05 identificatie-diensten aanbieden",
"a7TDNm": "Notes zullen in real-time worden gestreamt naar het Global en Notes tabblad",
"aHje0o": "Naam of nym",
@ -371,6 +381,7 @@
"cHCwbF": "Fotografie",
"cPIKU2": "Volgt",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "Demp allen",
"cg1VJ2": "Wallet verbinden",
"cuP16y": "Ondersteuning voor meerdere accounts",
@ -426,10 +437,12 @@
"hMzcSq": "Berichten",
"hRTfTR": "PRO",
"hY4lzx": "Ondersteuningen",
"hYOE+U": "Invite",
"ha8JKG": "Show graph",
"hicxcO": "Reacties tonen",
"hmZ3Bz": "Media",
"hniz8Z": "hier",
"hvFRBo": "Interaction",
"i/dBAR": "Zap Pool",
"i5gBFz": "Your sent and received payments will show up here.",
"iCqGww": "Reacties ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "Taal",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL of Lightning Address",
"z3UjXR": "Debug",
"zCb8fX": "Gewicht",
"zFegDD": "Contact",
"zINlao": "Eigenaar",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "Carregar mais",
"01iNut": "Nostr address does not belong to you",
"08zn6O": "Export Keys",
"0Azlrb": "Gerenciar",
"0BUTMv": "Pesquisar...",
"0HFX0T": "Use Exact Location",
@ -58,6 +59,7 @@
"3qnJlS": "Você está votando com {amount} sats",
"3t3kok": "{n,plural,=1{{n} post novo} other{{n} posts novos}}",
"3tVy+Z": "{n} Seguidores",
"3yk8fB": "Wallet",
"450Fty": "Nenhum",
"47FYwb": "Cancelar",
"4IPzdn": "Desenvolvedores principais",
@ -168,6 +170,8 @@
"FdhSU2": "Solicitar agora",
"FfYsOb": "Um erro aconteceu!",
"FmXUJg": "segue você",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "Remover",
"G1BGCg": "Selecionar carteira",
"G3A56c": "Subscribed to Push",
@ -189,6 +193,7 @@
"HWbkEK": "Apagar cache e recarregar",
"HbefNb": "Abrir Carteira",
"HhcAVH": "Se você não segue essa pessoa, clique aqui para carregar mídia de <i>{link}</i>ou atualize <a><i>suas preferências</i></a> para sempre carregar mídia de todos.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "Tem certeza de que deseja desafixar essa nota?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "Esporte",
"JPFYIM": "Nenhum endereço lightning",
"JSx7y9": "Inscreva-se em {site_name} {plan} para {price} e receba os seguintes prêmios",
"JTht/T": "NIP-96",
"JeoS4y": "Repostar",
"JjGgXI": "Procurar usuários",
"JkLHGw": "Website",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "O Zap Pool só funciona se você usar uma das conexões de carteira suportadas (WebLN, LNC, LNDHub ou Nostr Wallet Connect)",
"Qxv0B2": "Você tem atualmente {number} sats no seu grupo de zap.",
"R/6nsx": "Subscription",
"R81upa": "Pessoas que você segue",
"RDha9y": "Service Worker Not Running",
"RSr2uB": "O nome de usuário deve conter apenas letras minúsculas e números",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Salt..",
"Tpy00S": "Pessoas",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Pare de ouvir",
"UDYlxu": "Assinaturas pendentes",
"UJTWqI": "Remover de meus relés",
@ -307,6 +315,7 @@
"UNjfWJ": "Verifique todas as assinaturas de eventos recebidas dos revezamentos",
"UT7Nkj": "Novo chat",
"UUPFlt": "Os usuários devem aceitar o aviso de conteúdo para mostrar o conteúdo da sua nota.",
"UaCh1c": "Add Server",
"Ub+AGc": "Entrar",
"Up5U7K": "Bloquear",
"Ups2/p": "Your application is pending",
@ -345,6 +354,7 @@
"ZS+jRE": "Enviar divisões de zap para",
"Zff6lu": "O nome de usuário iris.to/<b>{name}</b> está reservado para você!",
"ZlmK/p": "{name} invited you to {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "Financie desenvolvedores e plataformas que fornecem serviços de verificação NIP-05",
"a7TDNm": "As notas serão transmitidas em tempo real na guia global e de notas",
"aHje0o": "Nome ou nym",
@ -371,6 +381,7 @@
"cHCwbF": "Fotografia",
"cPIKU2": "Seguindo",
"cQfLWb": "URL...",
"cVcgLJ": "Media Servers",
"cWx9t8": "Silenciar todos",
"cg1VJ2": "Conectar carteira",
"cuP16y": "Suporte a múltiplas contas",
@ -426,10 +437,12 @@
"hMzcSq": "Mensagens",
"hRTfTR": "PRO",
"hY4lzx": "Suporta",
"hYOE+U": "Invite",
"ha8JKG": "Show graph",
"hicxcO": "Exibir respostas",
"hmZ3Bz": "Mídia",
"hniz8Z": "aqui",
"hvFRBo": "Interaction",
"i/dBAR": "Piscina Zap",
"i5gBFz": "Your sent and received payments will show up here.",
"iCqGww": "Reações ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "Idioma",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL ou endereço Lightning",
"z3UjXR": "Debug",
"zCb8fX": "Peso",
"zFegDD": "Contato",
"zINlao": "Dono",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} мс",
"00LcfG": "Загрузить больше",
"01iNut": "Nostr address does not belong to you",
"08zn6O": "Export Keys",
"0Azlrb": "Управление",
"0BUTMv": "Поиск...",
"0HFX0T": "Use Exact Location",
@ -58,6 +59,7 @@
"3qnJlS": "Вы голосуете {amount} сатами",
"3t3kok": "{n,plural,=1{{n} новая заметка} other{{n} новые заметки}}",
"3tVy+Z": "{n} Подписчиков",
"3yk8fB": "Wallet",
"450Fty": "Ничьи",
"47FYwb": "Отменить",
"4IPzdn": "Основные разработчики",
@ -168,6 +170,8 @@
"FdhSU2": "Получить сейчас",
"FfYsOb": "Произошла ошибка!",
"FmXUJg": "подписан(а) на Вас",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "Удалить",
"G1BGCg": "Выберите кошелек",
"G3A56c": "Subscribed to Push",
@ -189,6 +193,7 @@
"HWbkEK": "Очистить кэш и перезагрузить",
"HbefNb": "Открыть кошелек",
"HhcAVH": "Вы не подписаны на этого человека, нажмите здесь, чтобы загрузить медиа из <i>{link}</i>, или обновите <a><i>ваши предпочтения</i></a> чтобы всегда загружать медиа от всех.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "Вы уверены, что хотите открепить эту заметку?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "Спорт",
"JPFYIM": "Нет адреса молнии",
"JSx7y9": "Подпишитесь на {site_name} {plan} для {price} и получите следующие вознаграждения",
"JTht/T": "NIP-96",
"JeoS4y": "Repost",
"JjGgXI": "Поиск пользователей",
"JkLHGw": "Сайт",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Pool работает только в том случае, если вы используете одно из поддерживаемых соединений кошельков (WebLN, LNC, LNDHub или Nostr Wallet Connect)",
"Qxv0B2": "В настоящее время в вашем пуле zap имеется {number} сатов.",
"R/6nsx": "Subscription",
"R81upa": "Люди, за которыми вы следите",
"RDha9y": "Сервисный работник не запущен",
"RSr2uB": "Имя пользователя должно содержать только строчные буквы и цифры",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Salt..",
"Tpy00S": "Пользователи",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Перестаньте слушать",
"UDYlxu": "Подписки в ожидании",
"UJTWqI": "Удалить из моих реле",
@ -307,6 +315,7 @@
"UNjfWJ": "Проверьте все подписи, полученные от ретрансляторов",
"UT7Nkj": "Новый чат",
"UUPFlt": "Пользователи должны принять предупреждение, чтобы увидеть содержание вашей заметки.",
"UaCh1c": "Add Server",
"Ub+AGc": "Войти",
"Up5U7K": "Блок",
"Ups2/p": "Your application is pending",
@ -345,6 +354,7 @@
"ZS+jRE": "Отправляйте сплиты zap в",
"Zff6lu": "Имя пользователя iris.to/<b>{name}</b> зарезервировано для Вас!",
"ZlmK/p": "{name} invited you to {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "Поддержать разработчиков и платформы, предоставляющие услуги NIP-05",
"a7TDNm": "Заметки в режиме реального времени будут поступать на вкладку \"Глобальные и заметки\".",
"aHje0o": "Имя или название",
@ -371,6 +381,7 @@
"cHCwbF": "Фотография",
"cPIKU2": "Подписки",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "Заглушить всех",
"cg1VJ2": "Подключить кошелек",
"cuP16y": "Поддержка нескольких аккаунтов",
@ -426,10 +437,12 @@
"hMzcSq": "Сообщения",
"hRTfTR": "PRO",
"hY4lzx": "Поддержка",
"hYOE+U": "Invite",
"ha8JKG": "Show graph",
"hicxcO": "Показать ответы",
"hmZ3Bz": "СМИ",
"hniz8Z": "здесь",
"hvFRBo": "Interaction",
"i/dBAR": "Zap Pool",
"i5gBFz": "Здесь отображаются отправленные и полученные платежи.",
"iCqGww": "Реакции ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "Язык",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL или Лайтнинг-адрес",
"z3UjXR": "Debug",
"zCb8fX": "Вес",
"zFegDD": "Связь",
"zINlao": "Владелец",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "Ladda fler",
"01iNut": "Nostr-adressen tillhör inte dig",
"08zn6O": "Export Keys",
"0Azlrb": "Hantera",
"0BUTMv": "Sök...",
"0HFX0T": "Använd exakt plats",
@ -58,6 +59,7 @@
"3qnJlS": "Du röstar med {amount} sats",
"3t3kok": "{n,plural,=1{{n} ny anteckning} other{{n} nya anteckningar}}",
"3tVy+Z": "{n} Följare",
"3yk8fB": "Wallet",
"450Fty": "Ingen",
"47FYwb": "Avbryt",
"4IPzdn": "Primära Utvecklare",
@ -168,6 +170,8 @@
"FdhSU2": "Hämta nu",
"FfYsOb": "Ett fel har uppstått!",
"FmXUJg": "följer dig",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "Ta bort",
"G1BGCg": "Välj plånbok",
"G3A56c": "Prenumererar på Push",
@ -189,6 +193,7 @@
"HWbkEK": "Rensa cache och ladda om",
"HbefNb": "Öppna plånbok",
"HhcAVH": "Du följer inte denna person, klicka här för att ladda media från <i>{link}</i>, eller uppdatera <a><i>dina inställningar</i></a> för att alltid ladda media från alla.",
"HqRNN8": "Support",
"I1AoOu": "Senaste inlägg {time}",
"IEwZvs": "Är du säker på att du vill ta bort denna anteckningen?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "Sport",
"JPFYIM": "Ingen lightning adress",
"JSx7y9": "Prenumerera på {site_name} {plan} för {price} och få följande belöningar",
"JTht/T": "NIP-96",
"JeoS4y": "Dela",
"JjGgXI": "Sök användare",
"JkLHGw": "Webbsida",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Pool fungerar endast om du använder en av plånboksanslutningarna (WebLN, LNC, LNDHub eller Nostr Wallet Connect)",
"Qxv0B2": "Du har för närvarande {number} sats i din zap pool.",
"R/6nsx": "Subscription",
"R81upa": "Personer du följer",
"RDha9y": "Service Worker körs inte",
"RSr2uB": "Användarnamnet får bara innehålla gemener och siffror",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Salt..",
"Tpy00S": "Personer",
"TvKqBp": "gillade",
"TwyMau": "Account",
"U1aPPi": "Sluta lyssna",
"UDYlxu": "Väntande prenumerationer",
"UJTWqI": "Ta bort från mina reläer",
@ -307,6 +315,7 @@
"UNjfWJ": "Kontrollera alla signaturer mottagna från reläer",
"UT7Nkj": "Ny chatt",
"UUPFlt": "Användare måste acceptera innehållsvarningen för att visa innehållet i din anteckning.",
"UaCh1c": "Add Server",
"Ub+AGc": "Logga in",
"Up5U7K": "Blockera",
"Ups2/p": "Din ansökan är under behandling",
@ -345,6 +354,7 @@
"ZS+jRE": "Skicka zap-delning till",
"Zff6lu": "Användarnamnet iris.to/<b>{name}</b> är reserverat för dig!",
"ZlmK/p": "{name} bjöd in dig till {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "Finansiera utvecklare och plattformar som tillhandahåller NIP-05 verifieringstjänster",
"a7TDNm": "Anteckningar kommer att strömmas i realtid in i global och antecknings fliken",
"aHje0o": "Namn eller nym",
@ -371,6 +381,7 @@
"cHCwbF": "Fotografi",
"cPIKU2": "Följer",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "Tysta alla",
"cg1VJ2": "Anslut plånbok",
"cuP16y": "Stöd för flera konton",
@ -426,10 +437,12 @@
"hMzcSq": "Meddelanden",
"hRTfTR": "PRO",
"hY4lzx": "Support",
"hYOE+U": "Invite",
"ha8JKG": "Visa graf",
"hicxcO": "Visa svar",
"hmZ3Bz": "Media",
"hniz8Z": "här",
"hvFRBo": "Interaction",
"i/dBAR": "Zap Pool",
"i5gBFz": "Dina skickade och mottagna betalningar kommer att visas här.",
"iCqGww": "Reaktioner ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "Språk",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL or Lightning Adress",
"z3UjXR": "Debug",
"zCb8fX": "Vikt",
"zFegDD": "Kontakt",
"zINlao": "Ägare",

View File

@ -1,7 +1,7 @@
{
"+D82kt": "Je, una uhakika unataka kuchapisha upya: {id}",
"+PzQ9Y": "Malipo sasa",
"+QM0PJ": "Sync all events for your profile into local cache",
"+QM0PJ": "Sawazisha matukio yote ya wasifu wako kwenye akiba ya ndani",
"+UjDmN": "Umeingia na ufikiaji wa kuandika",
"+Vxixo": "Gumzo la Siri la Kikundi",
"+aZY2h": "Aina ya Zap",
@ -20,16 +20,17 @@
"/n5KSF": "{n} dakika",
"00LcfG": "Pakia zaidi",
"01iNut": "Anwani ya Nostr sio yako",
"08zn6O": "Export Keys",
"0Azlrb": "Dhibiti",
"0BUTMv": "Tafuta...",
"0HFX0T": "Tumia Mahali Halisi",
"0MndVW": "Generic LNDHub wallet (BTCPayServer / Alby / LNBits)",
"0MndVW": "Mkoba wa kawaida wa LNDHub (BTCPayServer / Alby / LNBits)",
"0jOEtS": "LNURL isiyo halali",
"0mch2Y": "herufi katika jina imezuiliwa",
"0siT4z": "Siasa",
"0uoY11": "Onyesha Hali",
"0yO7wF": "{n} sekunde",
"0zASjL": "Go",
"0zASjL": "Enda",
"1H4Keq": "{n} watumiaji",
"1Mo59U": "Je, una uhakika unataka kuondoa kidokezo hiki kutoka kwa alamisho?",
"1R43+L": "Ingiza usanidi wa Nostr Wallet Connect",
@ -48,7 +49,7 @@
"2a2YiP": "{n} Alamisho",
"2k0Cv+": "Haipendwi ({n})",
"2mcwT8": "Nakala Mpya",
"2oCF7O": "Followed by friends of friends",
"2oCF7O": "Ikifuatiwa na marafiki wa marafiki",
"2ukA4d": "{n} masaa",
"39AHJm": "Jisajili",
"3KNMbJ": "Makala",
@ -58,6 +59,7 @@
"3qnJlS": "Unapiga kura kwa kutumia {amount} sats",
"3t3kok": "{n,plural,=1{{n} kidokezo kipya} other{{n} kidokezo kipya}}",
"3tVy+Z": "{n} Wafuasi",
"3yk8fB": "Wallet",
"450Fty": "Hakuna",
"47FYwb": "Ghairi",
"4IPzdn": "Wasanidi wa Msingi",
@ -103,7 +105,7 @@
"9HU8vw": "Jibu",
"9SvQep": "Anafuata {n}",
"9WRlF4": "Tuma",
"9kO0VQ": "Hide muted notes",
"9kO0VQ": "Ficha madokezo yaliyonyamazishwa",
"9kSari": "Jaribu tena kuchapisha",
"9pMqYs": "Anwani ya Nostr",
"9wO4wJ": "Ankara ya Umeme",
@ -114,7 +116,7 @@
"Ai8VHU": "Hifadhi tukio bila kikomo kwenye reli ya Snort",
"AkCxS/": "Sababu",
"Am8glJ": "Mchezo",
"Aujn2T": "Count",
"Aujn2T": "Hesabu",
"Awq32I": "Taarifa za Push",
"AxDOiG": "Miezi",
"AyGauy": "Ingia",
@ -148,8 +150,8 @@
"DrZqav": "Kuhusu inapaswa kuwa chini ya {limit} herufi",
"DtYelJ": "Hamisha",
"Dx4ey3": "Badilisha yote",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EHqHsu": "Invoice / Lightning Address",
"E5ZIPD": "<big>{amount}</big> <small>sati</small>",
"EHqHsu": "Ankara / Anwani ya umeme",
"EJbFi7": "Tafuta vidokezo",
"ELbg9p": "Watoa Taarifa",
"EQKRE4": "Onyesha bendera kwenye kurasa za wasifu",
@ -168,6 +170,8 @@
"FdhSU2": "Dai sasa",
"FfYsOb": "Hitilafu imetokea!",
"FmXUJg": "anafuata",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "Ondoa",
"G1BGCg": "Chagua Pochi",
"G3A56c": "Imejiunga na Push",
@ -189,9 +193,10 @@
"HWbkEK": "Futa akiba na upakie upya",
"HbefNb": "Fungua Pochi",
"HhcAVH": "Humfuati mtu huyu, bofya hapa ili kupakia midia kutoka <i>{link}</i>, au usasishe <a><i>mapendeleo yako</i></a> ili kupakia kila mara midia kutoka kwa kila mtu.",
"HqRNN8": "Support",
"I1AoOu": "Chapisho la mwisho {time}",
"IEwZvs": "Je, una uhakika unataka kubandua kidokezo hiki?",
"IIOul1": "Account Data",
"IIOul1": "Data ya Akaunti",
"IKKHqV": "Anafuata",
"IOu4Xh": "Lazima uwe mnunuzi wa {tier} ili ufikie kisanduku cha {app}",
"IVbtTS": "Zap kila {n} ameketi",
@ -203,13 +208,14 @@
"J+dIsA": "Usajili",
"J1iLmb": "Arifa Haziruhusiwi",
"J2HeQ+": "Tumia koma kutenganisha maneno k.m. neno1, neno2, neno3",
"JA+tz3": "Looking up thread...",
"JA+tz3": "Inatafuta thread...",
"JCIgkj": "Jina la mtumiaji",
"JGrt9q": "Tuma sats kwa {name}",
"JHEHCk": "Zaps ({n})",
"JIVWWA": "Michezo",
"JPFYIM": "Hakuna anwani ya umeme",
"JSx7y9": "Jiunge na {site_name} {plan} kwa {price} na upokee tuzo zifuatazo",
"JTht/T": "NIP-96",
"JeoS4y": "Reposti",
"JjGgXI": "Tafuta watumiaji",
"JkLHGw": "Tovuti",
@ -220,13 +226,13 @@
"KQvWvD": "Imefutwa",
"KahimY": "Aina ya tukio lisilojulikana: {kind}",
"KtsyO0": "Weka Pini",
"LBAnc7": "View as user?",
"LBAnc7": "Je, unaona kama mtumiaji?",
"LF5kYT": "Viunganisho Vingine",
"LKw/ue": "Check out the code {link}",
"LKw/ue": "Angalia msimbo {link}",
"LR1XjT": "Pini fupi sana",
"LXxsbk": "Asiyejulikana",
"LgbKvU": "Toa maoni",
"LhLvRx": "Name must be between 8 and 15 characters",
"LhLvRx": "Jina lazima liwe kati ya vibambo 8 na 15",
"LmdPXO": "Haiwezi kuthibitisha Anwani ya Nostr",
"Lu5/Bj": "Fungua kwenye Zapstr",
"Lw+I+J": "{n,plural,one {}=0{{name} zapped} other{{name} & {n} wengine walizap}}",
@ -256,21 +262,22 @@
"OQSOJF": "Pata anwani ya nostr bure",
"OQXnew": "Usajili wako bado unaendelea, bado huwezi kusasisha",
"ORGv1Q": "Imeundwa",
"OoZgbB": "Failed to update, please try again",
"OxPdQ0": "Scanning {date}",
"OoZgbB": "Imeshindwa kusasisha, tafadhali jaribu tena",
"OxPdQ0": "Inachanganua {date}",
"P2o+ZZ": "Anwani Isiyofaa ya Nostr",
"P61BTu": "Nakili Tukio JSON",
"P7FD0F": "Mfumo (Chaguo-msingi)",
"P7nJT9": "Jumla ya leo (UTC): {amount} sats",
"PCSt5T": "Mapendeleo",
"PXQ0z0": "Receiving to <b>{wallet}</b>",
"PXQ0z0": "Inapokea kwa <b>{wallet}</b>",
"PamNxw": "Kijajuu cha faili kisichojulikana: {name}",
"Pe0ogR": "Mandhari",
"PrsIg7": "Maoni yataonyeshwa kwenye kila ukurasa, ikiwa imezimwa hakuna maoni yataonyeshwa",
"QDFTjG": "{n} Relays",
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QJfhKt": "Ufunguo wa faragha ni kama nenosiri, lakini hauwezi kuwekwa upya. Ilinde kwa uangalifu na usiwahi kuionyesha kwa mtu yeyote. Mtu akishapata ufunguo wako wa faragha, ataweza kufikia akaunti yako milele.",
"QWhotP": "Zap Pool hufanya kazi tu ikiwa unatumia mojawapo ya miunganisho ya pochi inayotumika (WebLN, LNC, LNDHub au Nostr Wallet Connect)",
"Qxv0B2": "Kwa sasa una viti {number} kwenye zap pool yako.",
"R/6nsx": "Subscription",
"R81upa": "Watu unaowafuata",
"RDha9y": "Service Worker Haujatekelezwa",
"RSr2uB": "Jina la mtumiaji linapaswa kuwa na herufi ndogo na nambari pekee",
@ -278,7 +285,7 @@
"RfhLwC": "Na: {author}",
"RhDAoS": "Je, una uhakika unataka kufuta {id}",
"RoOyAh": "Relays",
"RrCui3": "Summary",
"RrCui3": "Muhtasari",
"Rs4kCE": "Alamisho",
"SLZGPn": "Ingiza pin kuweka funguo yako binafsi, unapaswa kuingiza pin hii kila wakati unapofungua {site}.",
"SMO+on": "Tuma zap kwa {name}",
@ -299,14 +306,16 @@
"TdtZQ5": "Crypto",
"TpgeGw": "Hex chumvi..",
"Tpy00S": "Watu",
"TvKqBp": "liked",
"TvKqBp": "alipenda",
"TwyMau": "Account",
"U1aPPi": "Acha kusikiliza",
"UDYlxu": "Usajili Unaosubiri",
"UJTWqI": "Ondoa kwenye relays yangu",
"ULXFfP": "Receive",
"ULXFfP": "Pokea",
"UNjfWJ": "Angalia saini zote za tukio zilizopokelewa kutoka kwenye relays",
"UT7Nkj": "Gumzo Mpya",
"UUPFlt": "Watumiaji lazima wakubali onyo la maudhui ili kuonyesha maudhui ya dokezo lako.",
"UaCh1c": "Add Server",
"Ub+AGc": "Ingia",
"Up5U7K": "Zuia",
"Ups2/p": "Maombi yako yanangojea",
@ -328,29 +337,30 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemoniki",
"XECMfW": "Tuma takwimu za matumizi",
"XICsE8": "Wapangaji wa Faili",
"XPB8VV": "Alby wallet connection",
"XPB8VV": "Uunganisho wa mkoba wa Alby",
"XQiFEl": "Afya za Ufuatiliaji",
"XSdWHA": "Redeem",
"XSdWHA": "Komboa",
"XXm7jJ": "Vishazi Vya Kupanda",
"XgWvGA": "Miitikio",
"Xnimz0": "Sending from <b>{wallet}</b>",
"Xnimz0": "Inatuma kutoka <b>{wallet}</b>",
"Xopqkl": "Kiasi chako chaguo-msingi cha zap ni {number} sats, thamani zilizopo zinakadiriwa kutoka hapo.",
"YDURw6": "URL ya Huduma",
"YR2I9M": "Hakuna ufunguo, hakuna {app}, Hakuna njia ya kuzirejesha ikiwa haujafanya nakala rudufu. Inachukua dakika tu.",
"YXA3AH": "Washa Mijibu",
"Yf3DwC": "Connect a wallet to send instant payments",
"Yf3DwC": "Unganisha pochi ili kutuma malipo ya papo hapo",
"Z4BMCZ": "Weka maneno ya kuoanisha",
"ZKORll": "Washa Sasa",
"ZLmyG9": "Wachangiaji",
"ZS+jRE": "Tuma mgawanyo wa zap kwa",
"Zff6lu": "Jina la mtumiaji iris.to/<b>{name}</b> limetengwa kwa ajili yako!",
"ZlmK/p": "{name} amekualika kwenye {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "Watengenezaji wa mfuko na mifumo inayotoa huduma za uthibitishaji wa NIP-05",
"a7TDNm": "Vidokezo vitatiririka kwa wakati halisi kwenye kichupo cha kimataifa cha madokezo",
"aHje0o": "Jina au kifupisho",
"aMaLBK": "Vifaa Vinavyoungwa mkono",
"aRex7h": "Paid {amount} sats, fee {fee} sats",
"aSGz4J": "Connect to your own LND node with Lightning Node Connect",
"aRex7h": "Imelipwa {amount} sats, ada {fee} sats",
"aSGz4J": "Unganisha kwa nodi yako ya LND na Uunganisho wa Njia ya Umeme",
"aWpBzj": "Onyesha Zaidi",
"b12Goz": "Mnemoniki",
"b5vAk0": "Nchi yako itafanya kama anwani ya umeme na itaelekeza kwenye LNURL uliyochagua au anwani ya Umeme",
@ -367,10 +377,11 @@
"c35bj2": "Ikiwa una swali kuhusu agizo lako la NIP-05 tafadhali DM {link}",
"c3g2hL": "Tangaza Tena",
"cFbU1B": "Je, unatumia Alby? Nenda kwenye {link} kupata usanidi wako wa NWC!",
"cG/bKQ": "Native nostr wallet connection",
"cG/bKQ": "Muunganisho wa mkoba wa asili wa nostr",
"cHCwbF": "Ufotografia",
"cPIKU2": "Ninafuata",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "Funga Mijibu Yote",
"cg1VJ2": "Unganisha Pochi",
"cuP16y": "Msaada wa Akaunti Nyingi",
@ -379,13 +390,13 @@
"d+6YsV": "Orodha za kufuta sauti:",
"d2ebEu": "Hajajiandikisha kwa Piga",
"d7d0/x": "Anwani ya LN",
"dK2CcV": "The public key is like your username, you can share it with anyone.",
"dK2CcV": "Ufunguo wa umma ni kama jina lako la mtumiaji, unaweza kuushiriki na mtu yeyote.",
"dOQCL8": "Jina la kuonyesha",
"deEeEI": "Jisajili",
"djLctd": "Amount in sats",
"djLctd": "Kiasi katika sati",
"djNL6D": "Soma pekee",
"dmsiLv": "Mgawo wa default wa Zap Pool wa {n} umewekwa kwa watengenezaji wa {site}, unaweza kuzima wakati wowote kwenye {link}",
"e5x8FT": "Kind",
"e5x8FT": "Aina",
"e61Jf3": "Inakuja hivi karibuni",
"e7VmYP": "Ingiza pin kufuli funguo yako binafsi",
"e7qqly": "Weka Alama Yote Yamesomwa",
@ -395,12 +406,12 @@
"eSzf2G": "Zap moja ya {nIn} sats itatenga {nOut} sats kwenye bwawa la zap.",
"eXT2QQ": "Mazungumzo ya Kikundi",
"egib+2": "{n,plural,=1{& {n} mwingine} other{& {n} wengine}}",
"ejEGdx": "Home",
"ejEGdx": "Nyumbani",
"f1OxTe": "Viongozi wa jamii ni watu binafsi wanaoendeleza mfumo wa nostr kwa kuwa na shughuli katika jamii zao za kienyeji na kusaidia kuwajumuisha watumiaji wapya. Kila mtu anaweza kuwa kiongozi wa jamii, lakini wachache wanashikilia cheo cha heshima cha sasa.",
"f2CAxA": "Dump",
"f2CAxA": "Tupa",
"fBI91o": "Zap",
"fBlba3": "Asante kwa kutumia {site}, tafadhali fikiria kuchangia ikiwa unaweza.",
"fLIvbC": "Snort is an open source project built by passionate people in their free time, your donations are greatly appreciated",
"fLIvbC": "Snort ni mradi wa chanzo huria uliojengwa na watu wenye shauku katika wakati wao wa bure, michango yako inathaminiwa sana",
"fOksnD": "Haiwezi kupiga kura kwa sababu huduma ya LNURL haitumii zaps",
"fQN+tq": "Onyesha machapisho yenye lebo ya onyo la yaliyomo",
"fWZYP5": "Imebandikwa",
@ -410,7 +421,7 @@
"flnGvv": "Unafikiria nini?",
"fqwcJ1": "Michango kwenye Mnyororo",
"fsB/4p": "Imehifadhiwa",
"furjvW": "Watch Stream",
"furjvW": "Tazama Tiririsha",
"g5pX+a": "Kuhusu",
"g985Wp": "Haiwezi kutuma kura",
"gDzDRs": "Emoji za kutuma unapojibu ujumbe kwa dokezo",
@ -422,25 +433,27 @@
"h7jvCs": "{site} ina furaha zaidi pamoja!",
"h8XMJL": "Beji",
"hF6IN2": "Pogoa Orodha ya Ufuatao",
"hMQmIw": "Sync Account",
"hMQmIw": "Sawazisha Akaunti",
"hMzcSq": "Ujumbe",
"hRTfTR": "PRO",
"hY4lzx": "Inasaidia",
"hYOE+U": "Invite",
"ha8JKG": "Onyesha grafu",
"hicxcO": "Onyesha majibu",
"hmZ3Bz": "Media",
"hniz8Z": "hapa",
"hvFRBo": "Interaction",
"i/dBAR": "Zap Pool",
"i5gBFz": "Your sent and received payments will show up here.",
"i5gBFz": "Malipo uliyotuma na kupokea yataonekana hapa.",
"iCqGww": "Maoni ({n})",
"iEoXYx": "Tafsiri za DeepL",
"iGT1eE": "Afya ya Kufuatilia Ufuatao ({x})",
"iICVoL": "{x} follows ({y} duplicates)",
"iICVoL": "{x} inafuata ({y} nakala)",
"iNWbVV": "Mpini",
"iXPL0Z": "Haiwezi kuingia kwa kutumia ufunguo wa faragha kwenye muunganisho usio salama, tafadhali tumia kiendelezi cha kidhibiti cha ufunguo wa Nostr badala yake",
"iYc3Ld": "Payments",
"iYc3Ld": "Malipo",
"ieGrWo": "Fuata",
"ipHVx5": "Generate Invoice",
"ipHVx5": "Tengeneza ankara",
"itPgxd": "Wasifu",
"izWS4J": "Acha kufuata",
"j9xbzF": "Tayari umefanya nakala rudufu",
@ -464,10 +477,10 @@
"l3H1EK": "Waalika marafiki zako",
"lCILNz": "Nunua Sasa hivi",
"lD3+8a": "Lipa",
"lEnclp": "My events: {n}",
"lEnclp": "Matukio yangu: {n}",
"lPWASz": "Anwani ya nostr ya Snort",
"lTbT3s": "Nenosiri la Wallet",
"lfOesV": "Non-Zap",
"lfOesV": "Isiyo ya Zap",
"lgg1KN": "ukurasa wa akaunti",
"ll3xBp": "Huduma ya wakala wa picha",
"lnaT9F": "Unafuata {n}",
@ -477,7 +490,7 @@
"mKAr6h": "Fuata wote",
"mKh2HS": "Huduma ya kupakia faili",
"mKhgP9": "{n,plural,one {}=0{} =1{zapped} other{zapped}}",
"mOFG3K": "Start",
"mOFG3K": "Anza",
"mfe8RW": "Chaguo: {n}",
"n1Whvj": "Badili",
"nDejmx": "Ondoa kizuizi",
@ -485,10 +498,10 @@
"nGGDsi": "Ruhusu Arifa",
"nIchMQ": "Inatafuta shughuli za akaunti ({progress})",
"nUT0Lv": "Vyombo",
"nWQFic": "Renew",
"nWQFic": "Upya",
"nihgfo": "Sikiliza makala hii",
"nwZXeh": "{n} imezuiliwa",
"o/gK53": "Deck",
"o/gK53": "Sitaha",
"o7e+nJ": "wafuasi {n}",
"oJ+JJN": "Hakuna kilichopatikana:/",
"odFwjL": "Inafuata pekee",
@ -505,7 +518,7 @@
"qMePPG": "Taarifa",
"qMx1sA": "Chaguo-msingi cha Zap",
"qUJTsT": "Imezuiwa",
"qXCbgZ": "Unlock",
"qXCbgZ": "Fungua",
"qZsKBR": "Jiandikishie upya {tier}",
"qcJFEJ": "API za Arifa zimelemazwa",
"qdGuQo": "Ufunguo wako wa Faragha Ni (usishiriki hii na mtu yeyote)",
@ -522,16 +535,16 @@
"rT14Ow": "Ongeza Relay",
"rfuMjE": "(Chaguo-msingi)",
"rmdsT4": "siku {n}",
"rn52n9": "Public Chat Channels",
"rn52n9": "Vituo vya Gumzo la Umma",
"rx1i0i": "Kiungo fupi",
"sKDn4e": "Onyesha Vialamisho",
"sUNhQE": "mtumiaji",
"sZQzjQ": "Imeshindwa kuchambua mgawanyo wa zap: {input}",
"sfL/O+": "Muted notes will not be shown",
"sfL/O+": "Vidokezo vilivyonyamazishwa havitaonyeshwa",
"tOdNiY": "Giza",
"th5lxp": "Tuma dokezo kwa kikundi kidogo cha relay zako za uandishi",
"thnRpU": "Kuthibitishwa kwa NIP-05 kunaweza kusaidia:",
"tj6kdX": "{sign} {amount} sats",
"tj6kdX": "{sign} {amount} sati",
"tjpYlr": "Takwimu za Relay",
"ttxS0b": "Beji ya Msaidizi",
"u/vOPu": "Imelipwa",
@ -539,8 +552,8 @@
"uCk8r+": "Je, tayari una akaunti?",
"uSV4Ti": "Machapisho mapya yanahitaji kuthibitishwa mwenyewe",
"uc0din": "Tuma sats zilizogawanywa kwa",
"ufvXH1": "Found {n} events",
"uhu5aG": "Public",
"ufvXH1": "Imepata matukio {n}",
"uhu5aG": "Umma",
"un1nGw": "{n} taarifa",
"usAvMr": "Badilisha Wasifu",
"v8lolG": "Anzisha gumzo",
@ -556,27 +569,28 @@
"w6qrwX": "NSFW",
"wEQDC6": "Hariri",
"wSZR47": "Tuma",
"whSrs+": "Nostr Public Chat",
"whSrs+": "Nostr Gumzo la Umma",
"wih7iJ": "jina limezuiwa",
"wofVHy": "Usimamizi",
"wqyN/i": "Pata maelezo zaidi kuhusu {service} kwenye {link}",
"wtLjP6": "Nakili ID",
"x/Fx2P": "Fundisha huduma unazotumia kwa kugawanya sehemu ya zap zako zote kwenye kundi la fedha!",
"x82IOl": "Nyamazisha",
"xEjBS7": "For you",
"xEjBS7": "Kwa ajili yako",
"xIcAOU": "Kura za {type}",
"xIoGG9": "Nenda kwa",
"xSoIUU": "Worker Relay",
"xSoIUU": "Relay ya Wafanyikazi",
"xaj9Ba": "Mtoa huduma",
"xbVgIm": "Pakia midia kiotomatiki",
"xhQMeQ": "Muda wake unaisha",
"xl4s/X": "Vigezo vingine:",
"xmcVZ0": "Tafuta",
"xybOUv": "FAN",
"xybOUv": "SHABIKI",
"y/bmsG": "Ruhusu",
"y1Z3or": "Lugha",
"yAztTU": "{n} eSats",
"yAztTU": "{n} eSatI",
"yCLnBC": "LNURL au Anwani ya Umeme",
"z3UjXR": "Debug",
"zCb8fX": "Uzito",
"zFegDD": "Wasiliana",
"zINlao": "Mmiliki",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} மில்லி வினாடிகள்",
"00LcfG": "மேலும் காண்க",
"01iNut": "Nostr address does not belong to you",
"08zn6O": "Export Keys",
"0Azlrb": "நிர்வகி",
"0BUTMv": "தேடு...",
"0HFX0T": "Use Exact Location",
@ -58,6 +59,7 @@
"3qnJlS": "{amount} சேட்களைக் கொண்டு நீங்கள் வாக்களிக்கிறீர்கள்",
"3t3kok": "{n,plural,=1{{n} புதிய குறிப்பு} other{{n} புதிய குறிப்புகள்}}",
"3tVy+Z": "{n} பின்தொடர்வோர்",
"3yk8fB": "Wallet",
"450Fty": "எதுவுமில்லை",
"47FYwb": "ரத்துசெய்",
"4IPzdn": "முதன்மை உருவாக்கி",
@ -168,6 +170,8 @@
"FdhSU2": "இப்போது உரிமை கோரவும்",
"FfYsOb": "ஓரு பிழை நேர்ந்துவிட்டது!",
"FmXUJg": "உங்களைப் பின்தொடர்கிறார்",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "நீக்கு",
"G1BGCg": "பணப்பை தேர்வு",
"G3A56c": "Subscribed to Push",
@ -189,6 +193,7 @@
"HWbkEK": "தற்காலிக சேமிப்பை அழித்து மீண்டும் ஏற்றவும்",
"HbefNb": "திறந்த பணப்பை",
"HhcAVH": "நீங்கள் இவரைப் பின்தொடரவில்லை, மீடியாவை ஏற்ற இங்கே கிளிக் செய்யவும் <i>{link}</i>, அல்லது புதுப்பிக்கவும் <a><i>உங்கள் விருப்பங்களை</i></a> எல்லாரிடமிருந்தும் எப்போதும் மீடியாவை ஏற்றுவதற்கு.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "இந்தக் குறிப்பின் நிலையான பொறுத்தத்தை நிச்சயமாக நீக்க விரும்புகிறீர்களா?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "விளையாட்டு",
"JPFYIM": "லைட்னிங் முகவரி இல்லை",
"JSx7y9": "{price} க்கு {site_name} {plan} இல் குழுசேர்ந்து பின்வரும் வெகுமதிகளைப் பெறுங்கள்",
"JTht/T": "NIP-96",
"JeoS4y": "மறுபதிவு",
"JjGgXI": "பயனர்களைத் தேடுக",
"JkLHGw": "வலைத்தளம்",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)",
"Qxv0B2": "You currently have {number} sats in your zap pool.",
"R/6nsx": "Subscription",
"R81upa": "நீங்கள் பின்தொடர்வோர்",
"RDha9y": "Service Worker Not Running",
"RSr2uB": "Username must only contain lowercase letters and numbers",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Salt..",
"Tpy00S": "People",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Stop listening",
"UDYlxu": "நிலுவையிலுள்ள சந்தாக்கள்",
"UJTWqI": "Remove from my relays",
@ -307,6 +315,7 @@
"UNjfWJ": "Check all event signatures received from relays",
"UT7Nkj": "New Chat",
"UUPFlt": "உங்கள் குறிப்பின் உள்ளடக்கத்தைக் காட்ட, உள்ளடக்க எச்சரிக்கையைப் பயனர்கள் ஏற்க வேண்டும்.",
"UaCh1c": "Add Server",
"Ub+AGc": "Sign In",
"Up5U7K": "முடக்கு",
"Ups2/p": "Your application is pending",
@ -345,6 +354,7 @@
"ZS+jRE": "Send zap splits to",
"Zff6lu": "Username iris.to/<b>{name}</b> is reserved for you!",
"ZlmK/p": "{name} invited you to {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "NIP-05 சரிபார்ப்பு சேவைகளை வழங்கும் டெவலப்பர்கள் மற்றும் தளங்களுக்கு நிதி உதவி செய்யுங்கள்",
"a7TDNm": "Notes will stream in real time into global and notes tab",
"aHje0o": "Name or nym",
@ -371,6 +381,7 @@
"cHCwbF": "Photography",
"cPIKU2": "பின்தொடரப் படுவோர்",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "அனைத்தையும் ஒலிநிறுத்து",
"cg1VJ2": "பணப்பையை இணைக்கவும்",
"cuP16y": "பல கணக்கு ஆதரவு",
@ -426,10 +437,12 @@
"hMzcSq": "அஞ்சல்கள்",
"hRTfTR": "PRO",
"hY4lzx": "ஆதரவு",
"hYOE+U": "Invite",
"ha8JKG": "Show graph",
"hicxcO": "பதில்களைக் காட்டு",
"hmZ3Bz": "Media",
"hniz8Z": "இங்கே",
"hvFRBo": "Interaction",
"i/dBAR": "Zap Pool",
"i5gBFz": "Your sent and received payments will show up here.",
"iCqGww": "எதிர்வினைகள் ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "மொழி",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL அல்லது லைட்னிங் முகவரி",
"z3UjXR": "Debug",
"zCb8fX": "Weight",
"zFegDD": "தொடர்பு",
"zINlao": "உரிமையாளர்",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} ms",
"00LcfG": "Load more",
"01iNut": "Nostr address does not belong to you",
"08zn6O": "Export Keys",
"0Azlrb": "จัดการ",
"0BUTMv": "ค้นหา...",
"0HFX0T": "Use Exact Location",
@ -58,6 +59,7 @@
"3qnJlS": "คุณกำลังลงคะแนนด้วย {amount} sats",
"3t3kok": "{n,plural,=1{{n} โน้ตใหม่} other{{n} โน้ตใหม่}}",
"3tVy+Z": "ผู้ติดตาม {n}",
"3yk8fB": "Wallet",
"450Fty": "ไม่มี",
"47FYwb": "ยกเลิก",
"4IPzdn": "นักพัฒนาหลัก",
@ -168,6 +170,8 @@
"FdhSU2": "รับทันที",
"FfYsOb": "พบข้อผิดพลาด!",
"FmXUJg": "ได้ติดตามคุณ",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "ลบ",
"G1BGCg": "เลือก wallet",
"G3A56c": "Subscribed to Push",
@ -189,6 +193,7 @@
"HWbkEK": "ล้างแคชและดาวน์โหลดใหม่อีกครั้ง",
"HbefNb": "เปิด Wallet",
"HhcAVH": "You don't follow this person, click here to load media from <i>{link}</i>, or update <a><i>your preferences</i></a> to always load media from everybody.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "คุณแน่ใจแล้วใช่มั้ยว่าต้องการลบโน้ตนี้?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "Sport",
"JPFYIM": "ไม่มี lightning address นี้",
"JSx7y9": "Subscribe to {site_name} {plan} for {price} and receive the following rewards",
"JTht/T": "NIP-96",
"JeoS4y": "Repost",
"JjGgXI": "Search users",
"JkLHGw": "เว็บไซต์",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "Zap Pool ใช้งานได้เฉพาะเมื่อคุณใช้การเชื่อมต่อ wallet ที่รองรับ (WebLN, LNC, LNDHub หรือ Nostr Wallet Connect)",
"Qxv0B2": "ขณะนี้มี {number} sats ใน zappool ของคุณ",
"R/6nsx": "Subscription",
"R81upa": "People you follow",
"RDha9y": "Service Worker Not Running",
"RSr2uB": "Username must only contain lowercase letters and numbers",
@ -300,6 +307,7 @@
"TpgeGw": "Hex Salt..",
"Tpy00S": "ผู้คน",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Stop listening",
"UDYlxu": "รอการสมัครสมาชิก",
"UJTWqI": "Remove from my relays",
@ -307,6 +315,7 @@
"UNjfWJ": "Check all event signatures received from relays",
"UT7Nkj": "New Chat",
"UUPFlt": "ผู้ใช้จะต้องยอมรับคำเตือนเพื่อแสดงเนื้อหาในโน้ตของคุณ",
"UaCh1c": "Add Server",
"Ub+AGc": "Sign In",
"Up5U7K": "บล็อก",
"Ups2/p": "Your application is pending",
@ -345,6 +354,7 @@
"ZS+jRE": "Send zap splits to",
"Zff6lu": "Username iris.to/<b>{name}</b> is reserved for you!",
"ZlmK/p": "{name} invited you to {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "สนับสนุนนักพัฒนาและแฟลตฟอร์มที่ให้บริการ NIP-05",
"a7TDNm": "โน้ตจะแสดงแบบเรียลไทม์บนหน้า global และ โน้ต",
"aHje0o": "Name or nym",
@ -371,6 +381,7 @@
"cHCwbF": "Photography",
"cPIKU2": "กำลังติดตาม",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "ปิดการโต้ตอบทั้งหมด",
"cg1VJ2": "เชื่อมต่อ Wallet",
"cuP16y": "รับรองการใช้งานหลายบัญชี",
@ -426,10 +437,12 @@
"hMzcSq": "ข้อความ",
"hRTfTR": "PRO",
"hY4lzx": "สนับสนุน",
"hYOE+U": "Invite",
"ha8JKG": "Show graph",
"hicxcO": "แสดงการตอบกลับ",
"hmZ3Bz": "Media",
"hniz8Z": "ที่นี่",
"hvFRBo": "Interaction",
"i/dBAR": "Zap Pool",
"i5gBFz": "Your sent and received payments will show up here.",
"iCqGww": "Reactions ({n})",
@ -577,6 +590,7 @@
"y1Z3or": "ภาษา",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL หรือ Lightning Address",
"z3UjXR": "Debug",
"zCb8fX": "Weight",
"zFegDD": "รายชื่อผู้ติดต่อ",
"zINlao": "เจ้าของ",

View File

@ -1,7 +1,7 @@
{
"+D82kt": "是否确定要转发:{id}",
"+PzQ9Y": "立即支出",
"+QM0PJ": "Sync all events for your profile into local cache",
"+QM0PJ": "将您个人资料的所有事件同步到本地缓存",
"+UjDmN": "已使用写入权限登录",
"+Vxixo": "秘密群聊",
"+aZY2h": "打闪种类",
@ -20,6 +20,7 @@
"/n5KSF": "{n} 毫秒",
"00LcfG": "加载更多",
"01iNut": "Nostr 地址不属于你",
"08zn6O": "导出密钥",
"0Azlrb": "管理",
"0BUTMv": "搜索...",
"0HFX0T": "使用精确位置",
@ -58,6 +59,7 @@
"3qnJlS": "你正在用 {amount} 聪投票",
"3t3kok": "{n,plural,=1{{n}条新笔记} other{{n}条新笔记}}",
"3tVy+Z": "{n} 个粉丝",
"3yk8fB": "钱包",
"450Fty": "无",
"47FYwb": "取消",
"4IPzdn": "主要开发人员",
@ -130,7 +132,7 @@
"C8HhVE": "推荐关注",
"CHTbO3": "加载发票失败",
"CM+Cfj": "关注列表",
"CM0k0d": "修剪关注列表",
"CM0k0d": "清理关注列表",
"CVWeJ6": "热门用户",
"CYkOCI": "和 {count} 个你关注的其他人",
"CbM2hK": "热门话题标签",
@ -145,7 +147,7 @@
"DZzCem": "显示最新的 {n} 条笔记",
"Dh3hbq": "自动打闪",
"Dn82AL": "直播",
"DrZqav": "关于必须少于 {limit} 个字符",
"DrZqav": "介绍必须少于 {limit} 个字符",
"DtYelJ": "转移",
"Dx4ey3": "全部切换",
"E5ZIPD": "<big>{amount}</big> <small>聪</small>",
@ -168,6 +170,8 @@
"FdhSU2": "立即领取",
"FfYsOb": "发生错误!",
"FmXUJg": "正在关注你",
"FvanT6": "帐户",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "移除",
"G1BGCg": "选择钱包",
"G3A56c": "已订阅推送通知",
@ -189,9 +193,10 @@
"HWbkEK": "清除缓存并重新加载",
"HbefNb": "打开钱包",
"HhcAVH": "你不关注这个用户,点击此处从<i>{link}</i>加载多媒体,或更新<a><i>你的选项</i></a>来自动加载来自任何人的多媒体。",
"HqRNN8": "Support",
"I1AoOu": "最近发布 {time}",
"IEwZvs": "是否确定要取消置顶此条笔记?",
"IIOul1": "Account Data",
"IIOul1": "账户数据",
"IKKHqV": "关注",
"IOu4Xh": "你必须是 {tier} 订阅者,才能访问 {app} 面板",
"IVbtTS": "打闪所有 {n} 聪",
@ -210,6 +215,7 @@
"JIVWWA": "体育",
"JPFYIM": "没有闪电地址",
"JSx7y9": "以 {price} 订阅 {site_name} {plan} 并获得以下奖励",
"JTht/T": "NIP-96",
"JeoS4y": "转发",
"JjGgXI": "搜索用户",
"JkLHGw": "网站",
@ -256,7 +262,7 @@
"OQSOJF": "获取一个免费的 nostr 地址",
"OQXnew": "你的订阅仍然活跃,你还不能续订",
"ORGv1Q": "已创建",
"OoZgbB": "Failed to update, please try again",
"OoZgbB": "更新失败,请重试",
"OxPdQ0": "Scanning {date}",
"P2o+ZZ": "Nostr 地址无效",
"P61BTu": "复制事件 JSON",
@ -268,9 +274,10 @@
"Pe0ogR": "主题",
"PrsIg7": "回应将在每个页面上显示,如果禁用则不会显示任何回应",
"QDFTjG": "{n} 个中继器",
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QJfhKt": "私钥就像密码,但无法重置。请小心保管,千万不要给任何人看。一旦有人拿到了你的私钥,他们就可以永远访问你的账户。",
"QWhotP": "打闪池只有当你使用支持的钱包连接WebLN、LNC、LNDHub 或 Nostr Wallet Connect时才能启用",
"Qxv0B2": "目前你的打闪池中有 {number} 聪。",
"R/6nsx": "订阅",
"R81upa": "你关注的用户",
"RDha9y": "服务工作程序未运行",
"RSr2uB": "用户名必须只包含小写字母和数字",
@ -300,6 +307,7 @@
"TpgeGw": "十六进制盐..",
"Tpy00S": "用户",
"TvKqBp": "已点赞",
"TwyMau": "Account",
"U1aPPi": "停止收听",
"UDYlxu": "待定订阅",
"UJTWqI": "从我的中继器中移除",
@ -307,6 +315,7 @@
"UNjfWJ": "检查从中继站收到的所有赛事签名",
"UT7Nkj": "新聊天",
"UUPFlt": "用户必须接受内容警告才能显示你的笔记的内容。",
"UaCh1c": "添加服务器",
"Ub+AGc": "登录",
"Up5U7K": "屏蔽",
"Ups2/p": "你的申请正在处理中",
@ -345,6 +354,7 @@
"ZS+jRE": "将打闪拆分发送到",
"Zff6lu": "用户名 iris.to/<b>{name}</b> 已为你保留!",
"ZlmK/p": "{name} 邀请你访问 {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "资助提供 NIP-05 验证服务的开发人员和平台",
"a7TDNm": "笔记将实时流式传输到全球和帖子选项卡",
"aHje0o": "姓名",
@ -371,6 +381,7 @@
"cHCwbF": "摄影",
"cPIKU2": "关注",
"cQfLWb": "网址..",
"cVcgLJ": "Media Servers",
"cWx9t8": "全部静音",
"cg1VJ2": "连接钱包",
"cuP16y": "多帐户支持",
@ -379,7 +390,7 @@
"d+6YsV": "应静音的列表:",
"d2ebEu": "未订阅推送",
"d7d0/x": "闪电地址",
"dK2CcV": "The public key is like your username, you can share it with anyone.",
"dK2CcV": "公钥就像你的用户名,你可以分享给任何人。",
"dOQCL8": "显示名称",
"deEeEI": "注册",
"djLctd": "聪金额",
@ -411,7 +422,7 @@
"fqwcJ1": "链上捐款",
"fsB/4p": "已保存",
"furjvW": "观看直播",
"g5pX+a": "关于",
"g5pX+a": "介绍",
"g985Wp": "发送投票失败",
"gDzDRs": "回应笔记时发送的表情符号",
"gXgY3+": "并非所有客户端都支持此功能",
@ -426,10 +437,12 @@
"hMzcSq": "消息",
"hRTfTR": "专业",
"hY4lzx": "支持",
"hYOE+U": "邀请",
"ha8JKG": "显示图表",
"hicxcO": "显示回复",
"hmZ3Bz": "多媒体",
"hniz8Z": "这里",
"hvFRBo": "Interaction",
"i/dBAR": "打闪池",
"i5gBFz": "你已发送和收到的付款将显示在此处。",
"iCqGww": "回应({n}",
@ -539,7 +552,7 @@
"uCk8r+": "已有账户?",
"uSV4Ti": "转发需要人工确认",
"uc0din": "将聪拆分发送到",
"ufvXH1": "Found {n} events",
"ufvXH1": "发现 {n} 个事件",
"uhu5aG": "Public",
"un1nGw": "{n} 条笔记",
"usAvMr": "编辑个人档案",
@ -577,6 +590,7 @@
"y1Z3or": "语言",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL 或闪电地址",
"z3UjXR": "Debug",
"zCb8fX": "权重",
"zFegDD": "联系人",
"zINlao": "所有者",

View File

@ -20,6 +20,7 @@
"/n5KSF": "{n} 毫秒",
"00LcfG": "加載更多",
"01iNut": "Nostr 地址不屬於你",
"08zn6O": "Export Keys",
"0Azlrb": "管理",
"0BUTMv": "搜索...",
"0HFX0T": "使用精確位置",
@ -58,6 +59,7 @@
"3qnJlS": "你正在用{amount}聰投票",
"3t3kok": "{n,plural,=1{{n}條新筆記} other{{n}條新筆記}}",
"3tVy+Z": "{n} 個粉絲",
"3yk8fB": "Wallet",
"450Fty": "無",
"47FYwb": "取消",
"4IPzdn": "主要開發人員",
@ -168,6 +170,8 @@
"FdhSU2": "立即領取",
"FfYsOb": "發生錯誤!",
"FmXUJg": "正在關注你",
"FvanT6": "Accounts",
"FzbSGg": "You dont have any media servers, try adding some.",
"G/yZLu": "移除",
"G1BGCg": "選擇錢包",
"G3A56c": "已訂閱推送通知",
@ -189,6 +193,7 @@
"HWbkEK": "清除緩存並重新加載",
"HbefNb": "打開錢包",
"HhcAVH": "你不關注此用戶,點擊此處從<i>{link}</i>加載多媒體,或更新<a><i>你的選項</i></a>來自動加載來自任何人的多媒體。",
"HqRNN8": "Support",
"I1AoOu": "最近發佈 {time}",
"IEwZvs": "是否確定要取消置頂此條筆記?",
"IIOul1": "Account Data",
@ -210,6 +215,7 @@
"JIVWWA": "體育",
"JPFYIM": "沒有閃電地址",
"JSx7y9": "以 {price} 訂閱 {site_name} {plan} 並獲得以下獎勵",
"JTht/T": "NIP-96",
"JeoS4y": "轉發",
"JjGgXI": "搜索用戶",
"JkLHGw": "網站",
@ -271,6 +277,7 @@
"QJfhKt": "The private key is like a password, but it cannot be reset. Guard it carefully and never show it to anyone. Once someone has your private key, they will have access to your account forever.",
"QWhotP": "打閃池只有當你使用支持的錢包WebLN、LNC、LNDHub 或 Nostr Wallet Connect時才能啟用",
"Qxv0B2": "目前你的打閃池中有 {number} 聰。",
"R/6nsx": "Subscription",
"R81upa": "你關注的用戶",
"RDha9y": "服務工作程式未運行",
"RSr2uB": "用戶名只能含有小寫字母和數字",
@ -300,6 +307,7 @@
"TpgeGw": "十六進制鹽..",
"Tpy00S": "用戶",
"TvKqBp": "已點贊",
"TwyMau": "Account",
"U1aPPi": "停止收聽",
"UDYlxu": "待定訂閱",
"UJTWqI": "從我的中繼器中移除",
@ -307,6 +315,7 @@
"UNjfWJ": "检查从中继收到的所有事件签名",
"UT7Nkj": "新聊天",
"UUPFlt": "用戶必須接受內容警告才能顯示你的筆記的內容。",
"UaCh1c": "Add Server",
"Ub+AGc": "登錄",
"Up5U7K": "屏蔽",
"Ups2/p": "你的申請正在處理中",
@ -345,6 +354,7 @@
"ZS+jRE": "將打閃拆分發送到",
"Zff6lu": "用戶名 iris.to/<b>{name}</b> 已為你保留!",
"ZlmK/p": "{name} 邀請你訪問 {app}",
"a1x4gD": "Media servers store media which you can share in notes as images and videos",
"a5UPxh": "資助提供 NIP-05 驗證服務的開發人員和平台",
"a7TDNm": "筆記將實時流式傳輸到全球和帖子選項卡",
"aHje0o": "名稱",
@ -371,6 +381,7 @@
"cHCwbF": "攝影",
"cPIKU2": "關注",
"cQfLWb": "URL..",
"cVcgLJ": "Media Servers",
"cWx9t8": "全部靜音",
"cg1VJ2": "連接錢包",
"cuP16y": "多帳戶支持",
@ -426,10 +437,12 @@
"hMzcSq": "消息",
"hRTfTR": "PRO",
"hY4lzx": "支持",
"hYOE+U": "Invite",
"ha8JKG": "顯示圖表",
"hicxcO": "顯示回覆",
"hmZ3Bz": "多媒體",
"hniz8Z": "這裡",
"hvFRBo": "Interaction",
"i/dBAR": "打閃池",
"i5gBFz": "你已發送和收到的付款將顯示在此處。",
"iCqGww": "回應({n}",
@ -577,6 +590,7 @@
"y1Z3or": "語言",
"yAztTU": "{n} eSats",
"yCLnBC": "LNURL 或閃電地址",
"z3UjXR": "Debug",
"zCb8fX": "權重",
"zFegDD": "聯絡",
"zINlao": "所有者",

View File

@ -13,9 +13,9 @@
"build": "rm -rf dist && tsc"
},
"dependencies": {
"@noble/curves": "^1.2.0",
"@noble/hashes": "^1.3.2",
"@scure/base": "^1.1.2",
"@noble/curves": "^1.4.0",
"@noble/hashes": "^1.4.0",
"@scure/base": "^1.1.6",
"debug": "^4.3.4",
"eventemitter3": "^5.0.1",
"light-bolt11-decoder": "^3.0.0"

View File

@ -1,6 +1,6 @@
{
"name": "@snort/system-react",
"version": "1.3.1",
"version": "1.3.6",
"description": "React hooks for @snort/system",
"main": "dist/index.js",
"module": "src/index.ts",
@ -17,7 +17,7 @@
],
"dependencies": {
"@snort/shared": "^1.0.15",
"@snort/system": "^1.3.1",
"@snort/system": "^1.3.6",
"react": "^18.2.0"
},
"devDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@snort/system-wasm",
"version": "1.0.3",
"version": "1.0.4",
"packageManager": "yarn@3.6.3",
"author": "Kieran",
"license": "MIT",

View File

@ -1,6 +1,6 @@
{
"name": "@snort/system",
"version": "1.3.1",
"version": "1.3.6",
"description": "Snort nostr system package",
"type": "module",
"main": "dist/index.js",
@ -19,7 +19,7 @@
],
"devDependencies": {
"@jest/globals": "^29.5.0",
"@peculiar/webcrypto": "^1.4.3",
"@peculiar/webcrypto": "^1.4.6",
"@types/debug": "^4.1.8",
"@types/jest": "^29.5.11",
"@types/lokijs": "^1.5.14",
@ -33,10 +33,10 @@
"typescript": "^5.2.2"
},
"dependencies": {
"@noble/curves": "^1.2.0",
"@noble/hashes": "^1.3.2",
"@nostr-dev-kit/ndk": "^2.7.1",
"@scure/base": "^1.1.2",
"@noble/curves": "^1.4.0",
"@noble/hashes": "^1.4.0",
"@nostr-dev-kit/ndk": "^2.8.2",
"@scure/base": "^1.1.6",
"@snort/shared": "^1.0.15",
"@stablelib/xchacha20": "^1.0.1",
"debug": "^4.3.4",

View File

@ -14,4 +14,9 @@ export interface CacheRelay {
* Read event from cache relay
*/
query(req: ReqCommand): Promise<Array<NostrEvent>>;
/**
* Delete events by filter
*/
delete(req: ReqCommand): Promise<Array<string>>;
}

View File

@ -5,6 +5,7 @@ import { EventEmitter } from "eventemitter3";
import { Connection, RelaySettings, SyncCommand } from "./connection";
import { NostrEvent, OkResponse, ReqCommand, TaggedNostrEvent } from "./nostr";
import { RelayInfo, SystemInterface } from ".";
import { ConnectionSyncModule, DefaultSyncModule } from "./sync/connection";
/**
* Events which the ConnectionType must emit
@ -93,6 +94,7 @@ export type ConnectionBuilder<T extends ConnectionType> = (
address: string,
options: RelaySettings,
ephemeral: boolean,
syncModule?: ConnectionSyncModule,
) => Promise<T> | T;
/**
@ -105,6 +107,11 @@ export class DefaultConnectionPool<T extends ConnectionType = Connection>
#system: SystemInterface;
#log = debug("ConnectionPool");
/**
* Track if a connection request has started
*/
#connectStarted = new Set<string>();
/**
* All currently connected websockets
*/
@ -122,7 +129,8 @@ export class DefaultConnectionPool<T extends ConnectionType = Connection>
this.#connectionBuilder = builder;
} else {
this.#connectionBuilder = (addr, options, ephemeral) => {
return new Connection(addr, options, ephemeral) as unknown as T;
const sync = new DefaultSyncModule(this.#system.config.fallbackSync);
return new Connection(addr, options, ephemeral, sync) as unknown as T;
};
}
}
@ -140,12 +148,14 @@ export class DefaultConnectionPool<T extends ConnectionType = Connection>
*/
async connect(address: string, options: RelaySettings, ephemeral: boolean) {
const addr = unwrap(sanitizeRelayUrl(address));
if (this.#connectStarted.has(addr)) return;
this.#connectStarted.add(addr);
try {
const existing = this.#sockets.get(addr);
if (!existing) {
const c = await this.#connectionBuilder(addr, options, ephemeral);
this.#sockets.set(addr, c);
c.on("event", (s, e) => {
if (this.#system.checkSigs && !this.#system.optimizer.schnorrVerify(e)) {
this.#log("Reject invalid event %o", e);
@ -177,6 +187,8 @@ export class DefaultConnectionPool<T extends ConnectionType = Connection>
this.#log("%O", e);
this.emit("connectFailed", addr);
this.#sockets.delete(addr);
} finally {
this.#connectStarted.delete(addr);
}
}

View File

@ -1,7 +1,7 @@
import { v4 as uuid } from "uuid";
import debug from "debug";
import WebSocket from "isomorphic-ws";
import { unixNowMs, dedupe } from "@snort/shared";
import { unixNowMs } from "@snort/shared";
import { EventEmitter } from "eventemitter3";
import { DefaultConnectTimeout } from "./const";
@ -9,8 +9,8 @@ import { NostrEvent, OkResponse, ReqCommand, ReqFilter, TaggedNostrEvent, u256 }
import { RelayInfo } from "./relay-info";
import EventKind from "./event-kind";
import { EventExt } from "./event-ext";
import { NegentropyFlow } from "./negentropy/negentropy-flow";
import { ConnectionType, ConnectionTypeEvents } from "./connection-pool";
import { ConnectionSyncModule } from "./sync/connection";
/**
* Relay settings
@ -44,6 +44,7 @@ export class Connection extends EventEmitter<ConnectionTypeEvents> implements Co
#downCount = 0;
#activeRequests = new Set<string>();
#connectStarted = false;
#syncModule?: ConnectionSyncModule;
id: string;
readonly address: string;
@ -62,7 +63,7 @@ export class Connection extends EventEmitter<ConnectionTypeEvents> implements Co
AwaitingAuth: Map<string, boolean>;
Authed = false;
constructor(addr: string, options: RelaySettings, ephemeral: boolean = false) {
constructor(addr: string, options: RelaySettings, ephemeral: boolean = false, syncModule?: ConnectionSyncModule) {
super();
this.id = uuid();
this.address = addr;
@ -70,6 +71,7 @@ export class Connection extends EventEmitter<ConnectionTypeEvents> implements Co
this.EventsCallback = new Map();
this.AwaitingAuth = new Map();
this.#ephemeral = ephemeral;
this.#syncModule = syncModule;
this.#log = debug("Connection").extend(addr);
}
@ -393,37 +395,10 @@ export class Connection extends EventEmitter<ConnectionTypeEvents> implements Co
this.#activeRequests.add(cmd[1]);
this.#send(cmd);
} else if (cmd[0] === "SYNC") {
const [_, id, eventSet, ...filters] = cmd;
const lastResortSync = () => {
if (filters.some(a => a.since || a.until || a.ids)) {
this.request(["REQ", id, ...filters], item.cb);
} else {
const latest = eventSet.reduce((acc, v) => (acc = v.created_at > acc ? v.created_at : acc), 0);
const newFilters = filters.map(a => ({
...a,
since: latest + 1,
}));
this.request(["REQ", id, ...newFilters], item.cb);
}
};
if (this.info?.negentropy === "v1") {
const newFilters = filters;
const neg = new NegentropyFlow(id, this, eventSet, newFilters);
neg.once("finish", filters => {
if (filters.length > 0) {
this.request(["REQ", cmd[1], ...filters], item.cb);
} else {
// no results to query, emulate closed
this.emit("closed", id, "Nothing to sync");
}
});
neg.once("error", () => {
lastResortSync();
});
neg.start();
} else {
lastResortSync();
if (!this.#syncModule) {
throw new Error("no sync module");
}
this.#syncModule.sync(this, cmd, item.cb);
}
} catch (e) {
console.error(e);

View File

@ -34,6 +34,7 @@ const enum EventKind {
SearchRelaysList = 10_007, // NIP-51
InterestsList = 10_015, // NIP-51
EmojisList = 10_030, // NIP-51
StorageServerList = 10_096, // NIP-96 server list
FollowSet = 30_000, // NIP-51
RelaySet = 30_002, // NIP-51

View File

@ -76,6 +76,10 @@ export class Nip46Signer extends EventEmitter<Nip46Events> implements EventSigne
this.#relay = unwrap(u.searchParams.get("relay"));
this.#insideSigner = insideSigner ?? new PrivateKeySigner(secp256k1.utils.randomPrivateKey());
if (this.isBunker) {
this.#remotePubkey = this.#localPubkey;
}
}
get supports(): string[] {
@ -92,17 +96,17 @@ export class Nip46Signer extends EventEmitter<Nip46Events> implements EventSigne
}
}
get isBunker() {
return this.#proto === "bunker:";
}
/**
* Connect to the bunker relay
* @param autoConnect Start connect flow for pubkey
* @returns
*/
async init(autoConnect = true) {
const isBunker = this.#proto === "bunker:";
if (isBunker) {
this.#remotePubkey = this.#localPubkey;
this.#localPubkey = await this.#insideSigner.getPubKey();
}
this.#localPubkey = await this.#insideSigner.getPubKey();
return await new Promise<void>((resolve, reject) => {
this.#conn = new Connection(this.#relay, { read: true, write: true });
this.#conn.on("event", async (sub, e) => {
@ -121,7 +125,7 @@ export class Nip46Signer extends EventEmitter<Nip46Events> implements EventSigne
]);
if (autoConnect) {
if (isBunker) {
if (this.isBunker) {
const rsp = await this.#connect(unwrap(this.#remotePubkey));
if (rsp.result === "ack") {
resolve();

View File

@ -2,7 +2,7 @@ import * as utils from "@noble/curves/abstract/utils";
import { bech32 } from "@scure/base";
import { HexKey } from "./nostr";
export const enum NostrPrefix {
export enum NostrPrefix {
PublicKey = "npub",
PrivateKey = "nsec",
Note = "note",

View File

@ -17,11 +17,17 @@ import { findTag } from "./utils";
*/
export interface ToNostrEventTag {
toEventTag(): Array<string> | undefined;
equals(other: ToNostrEventTag): boolean;
}
export class NostrHashtagLink implements ToNostrEventTag {
constructor(readonly tag: string) {}
equals(other: ToNostrEventTag): boolean {
const otherTag = other.toEventTag();
return otherTag?.at(0) === "t" && otherTag?.at(1) === this.tag;
}
toEventTag() {
return ["t", this.tag];
}
@ -29,6 +35,12 @@ export class NostrHashtagLink implements ToNostrEventTag {
export class UnknownTag implements ToNostrEventTag {
constructor(readonly value: Array<string>) {}
equals(other: ToNostrEventTag): boolean {
const otherTag = other.toEventTag();
return otherTag?.at(0) === this.value.at(0) && otherTag?.at(1) === this.value.at(1);
}
toEventTag(): string[] | undefined {
return this.value;
}

View File

@ -1,97 +1,33 @@
import debug from "debug";
import { EventEmitter } from "eventemitter3";
import { CachedTable, unixNowMs } from "@snort/shared";
import { unixNowMs } from "@snort/shared";
import { NostrEvent, TaggedNostrEvent, OkResponse } from "./nostr";
import { RelaySettings } from "./connection";
import { BuiltRawReqFilter, RequestBuilder } from "./request-builder";
import { RelayMetricHandler } from "./relay-metric-handler";
import {
CachedMetadata,
ProfileLoaderService,
RelayMetrics,
SystemInterface,
SystemSnapshot,
UserProfileCache,
UserRelaysCache,
RelayMetricCache,
UsersRelays,
QueryLike,
OutboxModel,
socialGraphInstance,
EventKind,
UsersFollows,
ID,
NostrSystemEvents,
SystemConfig,
} from ".";
import { EventsCache } from "./cache/events";
import { RelayMetadataLoader } from "./outbox";
import { Optimizer, DefaultOptimizer } from "./query-optimizer";
import { ConnectionPool, DefaultConnectionPool } from "./connection-pool";
import { QueryManager } from "./query-manager";
import { CacheRelay } from "./cache-relay";
import { RequestRouter } from "./request-router";
import { UserFollowsCache } from "./cache/user-follows-lists";
import { SystemBase } from "./system-base";
/**
* Manages nostr content retrieval system
*/
export class NostrSystem extends EventEmitter<NostrSystemEvents> implements SystemInterface {
export class NostrSystem extends SystemBase implements SystemInterface {
#log = debug("System");
#queryManager: QueryManager;
#config: SystemConfig;
/**
* Storage class for user relay lists
*/
get relayCache(): CachedTable<UsersRelays> {
return this.#config.relays;
}
/**
* Storage class for user profiles
*/
get profileCache(): CachedTable<CachedMetadata> {
return this.#config.profiles;
}
/**
* Storage class for relay metrics (connects/disconnects)
*/
get relayMetricsCache(): CachedTable<RelayMetrics> {
return this.#config.relayMetrics;
}
/**
* Optimizer instance, contains optimized functions for processing data
*/
get optimizer(): Optimizer {
return this.#config.optimizer;
}
get eventsCache(): CachedTable<NostrEvent> {
return this.#config.events;
}
get userFollowsCache(): CachedTable<UsersFollows> {
return this.#config.contactLists;
}
get cacheRelay(): CacheRelay | undefined {
return this.#config.cachingRelay;
}
/**
* Check event signatures (recommended)
*/
get checkSigs(): boolean {
return this.#config.checkSigs;
}
set checkSigs(v: boolean) {
this.#config.checkSigs = v;
}
readonly profileLoader: ProfileLoaderService;
readonly relayMetricsHandler: RelayMetricHandler;
@ -100,39 +36,26 @@ export class NostrSystem extends EventEmitter<NostrSystemEvents> implements Syst
readonly requestRouter: RequestRouter | undefined;
constructor(props: Partial<SystemConfig>) {
super();
this.#config = {
relays: props.relays ?? new UserRelaysCache(props.db?.userRelays),
profiles: props.profiles ?? new UserProfileCache(props.db?.users),
relayMetrics: props.relayMetrics ?? new RelayMetricCache(props.db?.relayMetrics),
events: props.events ?? new EventsCache(props.db?.events),
contactLists: props.contactLists ?? new UserFollowsCache(props.db?.contacts),
optimizer: props.optimizer ?? DefaultOptimizer,
checkSigs: props.checkSigs ?? false,
cachingRelay: props.cachingRelay,
db: props.db,
automaticOutboxModel: props.automaticOutboxModel ?? true,
buildFollowGraph: props.buildFollowGraph ?? false,
};
super(props);
this.profileLoader = new ProfileLoaderService(this, this.profileCache);
this.relayMetricsHandler = new RelayMetricHandler(this.relayMetricsCache);
this.relayLoader = new RelayMetadataLoader(this, this.relayCache);
// if automatic outbox model, setup request router as OutboxModel
if (this.#config.automaticOutboxModel) {
if (this.config.automaticOutboxModel) {
this.requestRouter = OutboxModel.fromSystem(this);
}
// Cache everything
if (this.#config.cachingRelay) {
if (this.config.cachingRelay) {
this.on("event", async (_, ev) => {
await this.#config.cachingRelay?.event(ev);
await this.config.cachingRelay?.event(ev);
});
}
// Hook on-event when building follow graph
if (this.#config.buildFollowGraph) {
if (this.config.buildFollowGraph) {
let evBuf: Array<TaggedNostrEvent> = [];
let t: ReturnType<typeof setTimeout> | undefined;
this.on("event", (_, ev) => {
@ -213,7 +136,7 @@ export class NostrSystem extends EventEmitter<NostrSystemEvents> implements Syst
async PreloadSocialGraph() {
// Insert data to socialGraph from cache
if (this.#config.buildFollowGraph) {
if (this.config.buildFollowGraph) {
for (const list of this.userFollowsCache.snapshot()) {
const user = ID(list.pubkey);
for (const fx of list.follows) {

View File

@ -76,7 +76,7 @@ export class QueryManager extends EventEmitter<QueryManagerEvents> {
* Async fetch results
*/
async fetch(req: RequestBuilder, cb?: (evs: Array<TaggedNostrEvent>) => void) {
const filters = req.buildRaw();
const filters = req.buildRaw(this.#system);
const q = this.query(req);
if (cb) {
q.on("event", cb);

View File

@ -76,7 +76,7 @@ export class QueryTrace extends EventEmitter<QueryTraceEvents> {
* Total time spent waiting for relay to respond
*/
get responseTime() {
return this.finished ? unwrap(this.eose) - unwrap(this.sent) : 0;
return this.finished ? unwrap(this.eose) - (this.sent ?? unixNowMs()) : 0;
}
/**
@ -168,6 +168,11 @@ export class Query extends EventEmitter<QueryEvents> {
#log = debug("Query");
/**
* Compressed cached trace filters
*/
#cachedFilters?: Array<ReqFilter>;
constructor(system: SystemInterface, req: RequestBuilder) {
super();
this.request = req;
@ -193,7 +198,6 @@ export class Query extends EventEmitter<QueryEvents> {
addRequest(req: RequestBuilder) {
if (req.instance === this.request.instance) {
// same requst, do nothing
this.#log("Same query %O === %O", req, this.request);
return;
}
this.#log("Add query %O to %s", req, this.id);
@ -214,7 +218,10 @@ export class Query extends EventEmitter<QueryEvents> {
* Recompute the complete set of compressed filters from all query traces
*/
get filters() {
return this.#tracing.flatMap(a => a.filters);
if (this.#system && !this.#cachedFilters) {
this.#cachedFilters = this.#system.optimizer.compress(this.#tracing.flatMap(a => a.filters));
}
return this.#cachedFilters ?? this.#tracing.flatMap(a => a.filters);
}
get feed() {
@ -412,8 +419,12 @@ export class Query extends EventEmitter<QueryEvents> {
}
});
const eventHandler = (sub: string, ev: TaggedNostrEvent) => {
if (this.request.options?.fillStore ?? true) {
this.handleEvent(sub, ev);
if ((this.request.options?.fillStore ?? true) && qt.id === sub) {
if (qt.filters.some(v => eventMatchesFilter(ev, v))) {
this.feed.add(ev);
} else {
this.#log("Event did not match filter, rejecting %O %O", ev, qt);
}
}
};
const eoseHandler = (sub: string) => {
@ -428,6 +439,7 @@ export class Query extends EventEmitter<QueryEvents> {
c.off("closed", eoseHandler);
});
this.#tracing.push(qt);
this.#cachedFilters = undefined;
if (q.syncFrom !== undefined) {
c.request(["SYNC", qt.id, q.syncFrom, ...qt.filters], () => qt.sentToRelay());

View File

@ -59,6 +59,7 @@ export class RequestBuilder {
#builders: Array<RequestFilterBuilder>;
#options?: RequestBuilderOptions;
#log = debug("RequestBuilder");
#rawCached?: Array<ReqFilter>;
constructor(id: string) {
this.instance = uuid();
@ -83,17 +84,20 @@ export class RequestBuilder {
*/
add(other: RequestBuilder) {
this.#builders.push(...other.#builders);
this.#rawCached = undefined;
}
withFilter() {
const ret = new RequestFilterBuilder();
this.#builders.push(ret);
this.#rawCached = undefined;
return ret;
}
withBareFilter(f: ReqFilter) {
const ret = new RequestFilterBuilder(f);
this.#builders.push(ret);
this.#rawCached = undefined;
return ret;
}
@ -105,12 +109,15 @@ export class RequestBuilder {
return this;
}
buildRaw(): Array<ReqFilter> {
return this.#builders.map(f => f.filter);
buildRaw(system?: SystemInterface): Array<ReqFilter> {
if (!this.#rawCached && system) {
this.#rawCached = system.optimizer.compress(this.#builders.map(f => f.filter));
}
return this.#rawCached ?? this.#builders.map(f => f.filter);
}
build(system: SystemInterface): Array<BuiltRawReqFilter> {
let rawFilters = this.buildRaw();
let rawFilters = this.buildRaw(system);
if (system.requestRouter) {
rawFilters = system.requestRouter.forAllRequest(rawFilters);
}
@ -124,7 +131,7 @@ export class RequestBuilder {
buildDiff(system: SystemInterface, prev: Array<ReqFilter>): Array<BuiltRawReqFilter> {
const start = unixNowMs();
let rawFilters = this.buildRaw();
let rawFilters = this.buildRaw(system);
if (system.requestRouter) {
rawFilters = system.requestRouter.forAllRequest(rawFilters);
}
@ -133,7 +140,7 @@ export class RequestBuilder {
const ret = this.#groupFlatByRelay(system, diff);
const ts = unixNowMs() - start;
if (ts >= 100) {
this.#log("slow diff %s %d ms, consider separate query ids, or use skipDiff: %O", this.id, ts, ret);
this.#log("slow diff %s %d ms, consider separate query ids, or use skipDiff: %O", this.id, ts, prev);
}
return ret;
}

View File

@ -0,0 +1,111 @@
import { Connection, SyncCommand } from "../connection";
import { EventExt, EventType } from "../event-ext";
import { NoteCollection } from "../note-collection";
import { RangeSync } from "./range-sync";
import { NegentropyFlow } from "../negentropy/negentropy-flow";
import { SystemConfig } from "../system";
export interface ConnectionSyncModule {
sync: (c: Connection, item: SyncCommand, cb?: () => void) => void;
}
export class DefaultSyncModule implements ConnectionSyncModule {
constructor(readonly method: SystemConfig["fallbackSync"]) {}
sync(c: Connection, item: SyncCommand, cb?: () => void) {
const [_, id, eventSet, ...filters] = item;
if (c.info?.negentropy === "v1") {
const newFilters = filters;
const neg = new NegentropyFlow(id, c, eventSet, newFilters);
neg.once("finish", filters => {
if (filters.length > 0) {
c.request(["REQ", id, ...filters], cb);
} else {
// no results to query, emulate closed
c.emit("closed", id, "Nothing to sync");
}
});
neg.once("error", () => {
this.#fallbackSync(c, item, cb);
});
neg.start();
} else {
this.#fallbackSync(c, item, cb);
}
}
#fallbackSync(c: Connection, item: SyncCommand, cb?: () => void) {
const [type, id, eventSet, ...filters] = item;
if (type !== "SYNC") throw new Error("Must be a SYNC command");
// if the event is replaceable there is no need to use any special sync query,
// just send the filters directly
const isReplaceableSync = filters.every(
a =>
a.kinds?.every(
b =>
EventExt.getType(b) === EventType.Replaceable || EventExt.getType(b) === EventType.ParameterizedReplaceable,
) ?? false,
);
if (filters.some(a => a.since || a.until || a.ids || a.limit) || isReplaceableSync) {
c.request(["REQ", id, ...filters], cb);
} else if (this.method === "since") {
this.#syncSince(c, item, cb);
} else if (this.method === "range-sync") {
this.#syncRangeSync(c, item, cb);
} else {
throw new Error("No fallback sync method");
}
}
/**
* Using the latest data, fetch only newer items
*
* The downfall of this method is when the dataset is truncated by the relay (ie. limit results to 1000 items)
*/
#syncSince(c: Connection, item: SyncCommand, cb?: () => void) {
const [type, id, eventSet, ...filters] = item;
if (type !== "SYNC") throw new Error("Must be a SYNC command");
const latest = eventSet.reduce((acc, v) => (acc = v.created_at > acc ? v.created_at : acc), 0);
const newFilters = filters.map(a => ({
...a,
since: latest + 1,
}));
c.request(["REQ", id, ...newFilters], cb);
}
/**
* Using the RangeSync class, sync data using fixed window size
*/
#syncRangeSync(c: Connection, item: SyncCommand, cb?: () => void) {
const [type, id, eventSet, ...filters] = item;
if (type !== "SYNC") throw new Error("Must be a SYNC command");
const rs = RangeSync.forFetcher(async (rb, cb) => {
return await new Promise((resolve, reject) => {
const results = new NoteCollection();
const f = rb.buildRaw();
c.on("event", (c, e) => {
if (rb.id === c) {
cb?.([e]);
results.add(e);
}
});
c.on("eose", s => {
if (s === rb.id) {
resolve(results.takeSnapshot());
}
});
c.request(["REQ", rb.id, ...f], undefined);
});
});
const latest = eventSet.reduce((acc, v) => (acc = v.created_at > acc ? v.created_at : acc), 0);
rs.setStartPoint(latest + 1);
rs.on("event", ev => {
ev.forEach(e => c.emit("event", id, e));
});
for (const f of filters) {
rs.sync(f);
}
}
}

View File

@ -160,7 +160,7 @@ export class DiffSyncTags extends EventEmitter<SafeSyncEvents> {
? (change.tag as Array<Array<string>>)
: [change.tag as Array<string>];
for (const changeTag of changeTags) {
const existing = tags.findIndex(a => change.tag[0] === a[0] && change.tag[1] === a[1]);
const existing = tags.findIndex(a => changeTag[0] === a[0] && changeTag[1] === a[1]);
if (existing === -1) {
tags.push(changeTag);
} else {
@ -174,7 +174,7 @@ export class DiffSyncTags extends EventEmitter<SafeSyncEvents> {
? (change.tag as Array<Array<string>>)
: [change.tag as Array<string>];
for (const changeTag of changeTags) {
const existing = tags.findIndex(a => change.tag[0] === a[0] && change.tag[1] === a[1]);
const existing = tags.findIndex(a => changeTag[0] === a[0] && changeTag[1] === a[1]);
if (existing !== -1) {
tags.splice(existing, 1);
} else {
@ -188,7 +188,7 @@ export class DiffSyncTags extends EventEmitter<SafeSyncEvents> {
? (change.tag as Array<Array<string>>)
: [change.tag as Array<string>];
for (const changeTag of changeTags) {
const existing = tags.findIndex(a => change.tag[0] === a[0] && change.tag[1] === a[1]);
const existing = tags.findIndex(a => changeTag[0] === a[0] && changeTag[1] === a[1]);
if (existing !== -1) {
tags[existing] = changeTag;
} else {

View File

@ -1,6 +1,7 @@
import { unixNow } from "@snort/shared";
import EventEmitter from "eventemitter3";
import { ReqFilter, RequestBuilder, SystemInterface, TaggedNostrEvent } from "..";
import { v4 as uuid } from "uuid";
/**
* When nostr was created
@ -16,13 +17,27 @@ interface RangeSyncEvents {
* A simple time based sync for pulling lots of data from nostr
*/
export class RangeSync extends EventEmitter<RangeSyncEvents> {
#id = uuid();
#start: number = NostrBirthday;
#windowSize: number = 60 * 60 * 12;
#fetcher!: SystemInterface["Fetch"];
constructor(readonly system: SystemInterface) {
private constructor() {
super();
}
static forSystem(system: SystemInterface) {
const rs = new RangeSync();
rs.#fetcher = (r, c) => system.Fetch(r, c);
return rs;
}
static forFetcher(fn: SystemInterface["Fetch"]) {
const rs = new RangeSync();
rs.#fetcher = fn;
return rs;
}
/**
* Set window size in seconds
*/
@ -52,18 +67,15 @@ export class RangeSync extends EventEmitter<RangeSyncEvents> {
throw new Error("Filter must not contain since/until/limit");
}
if (!this.system.requestRouter) {
throw new Error("RangeSync cannot work without request router!");
}
const now = unixNow();
let ctr = 1;
for (let end = now; end > this.#start; end -= this.#windowSize) {
const rb = new RequestBuilder(`range-query:${end}`);
const rb = new RequestBuilder(`${this.#id}+${ctr++}`);
rb.withBareFilter(filter)
.since(end - this.#windowSize)
.until(end);
this.emit("scan", end);
const results = await this.system.Fetch(rb);
const results = await this.#fetcher(rb);
this.emit("event", results);
}
}

View File

@ -11,8 +11,13 @@ import { EventEmitter } from "eventemitter3";
export abstract class SystemBase extends EventEmitter<NostrSystemEvents> {
#config: SystemConfig;
get config() {
return this.#config;
}
constructor(props: Partial<SystemConfig>) {
super();
this.#config = {
relays: props.relays ?? new UserRelaysCache(props.db?.userRelays),
profiles: props.profiles ?? new UserProfileCache(props.db?.users),
@ -25,6 +30,7 @@ export abstract class SystemBase extends EventEmitter<NostrSystemEvents> {
db: props.db,
automaticOutboxModel: props.automaticOutboxModel ?? true,
buildFollowGraph: props.buildFollowGraph ?? false,
fallbackSync: props.fallbackSync ?? "since",
};
}

View File

@ -91,6 +91,11 @@ export interface SystemConfig {
* for users when fetching by author.
*/
buildFollowGraph: boolean;
/**
* Pick a fallback sync method when negentropy is not available
*/
fallbackSync: "since" | "range-sync";
}
export interface SystemInterface {
@ -200,6 +205,8 @@ export interface SystemInterface {
* Request router instance
*/
get requestRouter(): RequestRouter | undefined;
get config(): SystemConfig;
}
export interface SystemSnapshot {

View File

@ -88,7 +88,7 @@ export class UserState<TAppData> extends EventEmitter<UserStateEvents> {
}
// always track mute list
this.#checkIsStandardList(EventKind.MuteList);
this.checkIsStandardList(EventKind.MuteList);
this.#profile.on("change", () => this.emit("change", UserStateChangeType.Profile));
this.#contacts.on("change", () => this.emit("change", UserStateChangeType.Contacts));
@ -338,7 +338,7 @@ export class UserState<TAppData> extends EventEmitter<UserStateEvents> {
autoCommit = false,
encrypted = false,
) {
this.#checkIsStandardList(kind);
this.checkIsStandardList(kind);
this.#checkInit();
const list = this.#standardLists.get(kind);
const tags = removeUndefined(Array.isArray(links) ? links.map(a => a.toEventTag()) : [links.toEventTag()]);
@ -363,7 +363,7 @@ export class UserState<TAppData> extends EventEmitter<UserStateEvents> {
autoCommit = false,
encrypted = false,
) {
this.#checkIsStandardList(kind);
this.checkIsStandardList(kind);
this.#checkInit();
const list = this.#standardLists.get(kind);
const tags = removeUndefined(Array.isArray(links) ? links.map(a => a.toEventTag()) : [links.toEventTag()]);
@ -416,7 +416,7 @@ export class UserState<TAppData> extends EventEmitter<UserStateEvents> {
};
}
#checkIsStandardList(kind: EventKind) {
checkIsStandardList(kind: EventKind) {
if (!(kind >= 10_000 && kind < 20_000)) {
throw new Error("Not a standar list");
}

View File

@ -1,6 +1,6 @@
{
"name": "@snort/wallet",
"version": "0.1.2",
"version": "0.1.4",
"description": "Snort wallet system package",
"type": "module",
"main": "dist/index.js",
@ -22,8 +22,8 @@
"@cashu/cashu-ts": "^1.0.0-rc.3",
"@lightninglabs/lnc-web": "^0.3.1-alpha",
"@scure/base": "^1.1.6",
"@snort/shared": "^1.0.14",
"@snort/system": "^1.2.12",
"@snort/shared": "^1.0.15",
"@snort/system": "^1.3.6",
"debug": "^4.3.4",
"eventemitter3": "^5.0.1"
},

View File

@ -1,6 +1,6 @@
{
"name": "@snort/worker-relay",
"version": "1.0.10",
"version": "1.1.0",
"description": "A nostr relay in a service worker",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@ -18,7 +18,7 @@
"dist"
],
"dependencies": {
"@sqlite.org/sqlite-wasm": "^3.45.1-build1",
"@sqlite.org/sqlite-wasm": "^3.45.3-build3",
"eventemitter3": "^5.0.1",
"uuid": "^9.0.1"
},

View File

@ -63,6 +63,11 @@ export class WorkerRelayInterface {
return await this.#workerRpc<ReqCommand, number>("count", req);
}
async delete(req: ReqCommand) {
console.debug("DELETE", req);
return await this.#workerRpc<ReqCommand, Array<string>>("delete", req);
}
async summary() {
return await this.#workerRpc<void, Record<string, number>>("summary");
}

View File

@ -80,6 +80,13 @@ export class InMemoryRelay extends EventEmitter<RelayHandlerEvents> implements R
return ret;
}
delete(filter: ReqFilter) {
const forDelete = this.req("ids-for-delete", { ...filter, ids_only: true }) as Array<string>;
forDelete.forEach(a => this.#events.delete(a));
return forDelete;
}
setEventMetadata(_id: string, _meta: EventMetadata) {
return;
}

View File

@ -0,0 +1,3 @@
import { SqliteRelay } from "./sqlite-relay";
export async function runFixers(relay: SqliteRelay) {}

View File

@ -1,6 +1,6 @@
import { NostrEvent } from "./types";
import { NostrEvent } from "../types";
import { SqliteRelay } from "./sqlite-relay";
import { debugLog } from "./debug";
import { debugLog } from "../debug";
const log = (msg: string, ...args: Array<any>) => debugLog("SqliteRelay:migrations", msg, ...args);

View File

@ -1,11 +1,12 @@
import sqlite3InitModule, { Database, Sqlite3Static } from "@sqlite.org/sqlite-wasm";
import { EventEmitter } from "eventemitter3";
import { EventMetadata, NostrEvent, RelayHandler, RelayHandlerEvents, ReqFilter, unixNowMs } from "./types";
import { EventMetadata, NostrEvent, RelayHandler, RelayHandlerEvents, ReqFilter, unixNowMs } from "../types";
import migrate from "./migrations";
import { debugLog } from "./debug";
import { debugLog } from "../debug";
// import wasm file directly, this needs to be copied from https://sqlite.org/download.html
import SqlitePath from "./sqlite3.wasm?url";
import { runFixers } from "./fixers";
export class SqliteRelay extends EventEmitter<RelayHandlerEvents> implements RelayHandler {
#sqlite?: Sqlite3Static;
@ -30,7 +31,11 @@ export class SqliteRelay extends EventEmitter<RelayHandlerEvents> implements Rel
});
this.#log(`Got SQLite version: ${this.#sqlite.version.libVersion}`);
await this.#open(path);
this.db && migrate(this);
if (this.db) {
await migrate(this);
// dont await to avoid timeout
runFixers(this);
}
}
/**
@ -99,13 +104,15 @@ export class SqliteRelay extends EventEmitter<RelayHandlerEvents> implements Rel
}
#deleteById(db: Database, ids: Array<string>) {
if (ids.length === 0) return;
db.exec(`delete from events where id in (${this.#repeatParams(ids.length)})`, {
bind: ids,
});
const deleted = db.changes();
db.exec(`delete from search_content where id in (${this.#repeatParams(ids.length)})`, {
bind: ids,
});
this.#log("Deleted", ids, db.changes());
this.#log("Deleted", ids, deleted);
}
#insertEvent(db: Database, ev: NostrEvent) {
@ -126,6 +133,9 @@ export class SqliteRelay extends EventEmitter<RelayHandlerEvents> implements Rel
this.#deleteById(db, toDelete);
}
return false;
} else {
// delete older versions
this.#deleteById(db, oldEvents);
}
}
if (ev.kind >= 30_000 && ev.kind < 40_000) {
@ -142,22 +152,27 @@ export class SqliteRelay extends EventEmitter<RelayHandlerEvents> implements Rel
this.#deleteById(db, toDelete);
}
return false;
} else {
// delete older versions
this.#deleteById(db, oldEvents);
}
}
db.exec("insert or ignore into events(id, pubkey, created, kind, json) values(?,?,?,?,?)", {
bind: [ev.id, ev.pubkey, ev.created_at, ev.kind, JSON.stringify(ev)],
});
let eventInserted = (this.db?.changes() as number) > 0;
if (eventInserted) {
const insertedEvents = db.changes();
if (insertedEvents > 0) {
for (const t of ev.tags.filter(a => a[0].length === 1)) {
db.exec("insert into tags(event_id, key, value) values(?, ?, ?)", {
bind: [ev.id, t[0], t[1]],
});
}
this.insertIntoSearchIndex(db, ev);
} else {
return 0;
}
this.#seenInserts.add(ev.id);
return eventInserted;
return insertedEvents;
}
/**
@ -196,6 +211,32 @@ export class SqliteRelay extends EventEmitter<RelayHandlerEvents> implements Rel
return results;
}
/**
* Delete events by nostr filter
*/
delete(req: ReqFilter) {
this.#log(`Starting delete of ${JSON.stringify(req)}`);
const start = unixNowMs();
const for_delete = this.req("ids-for-delete", { ...req, ids_only: true }) as Array<string>;
const grouped = for_delete.reduce(
(acc, v, i) => {
const batch = (i / 1000).toFixed(0);
acc[batch] ??= [];
acc[batch].push(v);
return acc;
},
{} as Record<string, Array<string>>,
);
this.#log(`Starting delete of ${Object.keys(grouped).length} batches`);
Object.entries(grouped).forEach(([batch, ids]) => {
this.#deleteById(this.db!, ids);
});
const time = unixNowMs() - start;
this.#log(`Delete ${for_delete.length} events took ${time.toLocaleString()}ms`);
return for_delete;
}
/**
* Get a summary about events table
*/
@ -231,7 +272,7 @@ export class SqliteRelay extends EventEmitter<RelayHandlerEvents> implements Rel
return new Uint8Array();
}
#buildQuery(req: ReqFilter, count = false): [string, Array<any>] {
#buildQuery(req: ReqFilter, count = false, remove = false): [string, Array<any>] {
const conditions: Array<string> = [];
const params: Array<any> = [];
@ -241,7 +282,11 @@ export class SqliteRelay extends EventEmitter<RelayHandlerEvents> implements Rel
} else if (req.ids_only === true) {
resultType = "id";
}
let sql = `select ${resultType} from events`;
let operation = `select ${resultType}`;
if (remove) {
operation = "delete";
}
let sql = `${operation} from events`;
const tags = Object.entries(req).filter(([k]) => k.startsWith("#"));
let tx = 0;
for (const [key, values] of tags) {
@ -342,4 +387,6 @@ export class SqliteRelay extends EventEmitter<RelayHandlerEvents> implements Rel
});
}
}
#fixMissingTags(db: Database) {}
}

View File

@ -12,7 +12,8 @@ export type WorkerMessageCommand =
| "emit-event"
| "forYouFeed"
| "setEventMetadata"
| "debug";
| "debug"
| "delete";
export interface WorkerMessage<T> {
id: string;
@ -70,6 +71,7 @@ export interface RelayHandler extends EventEmitter<RelayHandlerEvents> {
count(req: ReqFilter): number;
summary(): Record<string, number>;
dump(): Promise<Uint8Array>;
delete(req: ReqFilter): Array<string>;
setEventMetadata(id: string, meta: EventMetadata): void;
}

View File

@ -1,10 +1,19 @@
/// <reference lib="webworker" />
import { SqliteRelay } from "./sqlite-relay";
import { SqliteRelay } from "./sqlite/sqlite-relay";
import { InMemoryRelay } from "./memory-relay";
import { setLogging } from "./debug";
import { WorkQueueItem, barrierQueue, processWorkQueue } from "./queue";
import { NostrEvent, RelayHandler, ReqCommand, ReqFilter, WorkerMessage, unixNowMs, EventMetadata } from "./types";
import {
NostrEvent,
RelayHandler,
ReqCommand,
ReqFilter,
WorkerMessage,
unixNowMs,
EventMetadata,
OkResponse,
} from "./types";
import { getForYouFeed } from "./forYouFeed";
let relay: RelayHandler | undefined;
@ -86,8 +95,13 @@ const handleMsg = async (port: MessagePort | DedicatedWorkerGlobalScope, ev: Mes
break;
}
case "event": {
eventWriteQueue.push(msg.args as NostrEvent);
reply(msg.id, true);
const ev = msg.args as NostrEvent;
eventWriteQueue.push(ev);
reply(msg.id, {
ok: true,
id: ev.id,
relay: "",
} as OkResponse);
break;
}
case "close": {
@ -130,6 +144,20 @@ const handleMsg = async (port: MessagePort | DedicatedWorkerGlobalScope, ev: Mes
});
break;
}
case "delete": {
console.debug("DELETE", msg.args);
await barrierQueue(cmdQueue, async () => {
const req = msg.args as ReqCommand;
let results = [];
const filters = req.slice(2) as Array<ReqFilter>;
for (const r of filters) {
const c = relay!.delete(r);
results.push(...c);
}
reply(msg.id, results);
});
break;
}
case "summary": {
await barrierQueue(cmdQueue, async () => {
const res = relay!.summary();

214
yarn.lock
View File

@ -4060,7 +4060,7 @@ __metadata:
languageName: node
linkType: hard
"@noble/curves@npm:1.2.0, @noble/curves@npm:^1.0.0, @noble/curves@npm:^1.2.0, @noble/curves@npm:~1.2.0":
"@noble/curves@npm:1.2.0":
version: 1.2.0
resolution: "@noble/curves@npm:1.2.0"
dependencies:
@ -4085,14 +4085,14 @@ __metadata:
languageName: node
linkType: hard
"@noble/hashes@npm:1.3.2, @noble/hashes@npm:^1.3.2, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.2":
"@noble/hashes@npm:1.3.2, @noble/hashes@npm:~1.3.0":
version: 1.3.2
resolution: "@noble/hashes@npm:1.3.2"
checksum: 10/685f59d2d44d88e738114b71011d343a9f7dce9dfb0a121f1489132f9247baa60bc985e5ec6f3213d114fbd1e1168e7294644e46cbd0ce2eba37994f28eeb51b
languageName: node
linkType: hard
"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.4.0":
"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:~1.4.0":
version: 1.4.0
resolution: "@noble/hashes@npm:1.4.0"
checksum: 10/e156e65794c473794c52fa9d06baf1eb20903d0d96719530f523cc4450f6c721a957c544796e6efd0197b2296e7cd70efeb312f861465e17940a3e3c7e0febc6
@ -4140,9 +4140,9 @@ __metadata:
languageName: node
linkType: hard
"@nostr-dev-kit/ndk@npm:^2.7.1":
version: 2.7.1
resolution: "@nostr-dev-kit/ndk@npm:2.7.1"
"@nostr-dev-kit/ndk@npm:^2.8.2":
version: 2.8.2
resolution: "@nostr-dev-kit/ndk@npm:2.8.2"
dependencies:
"@noble/curves": "npm:^1.4.0"
"@noble/hashes": "npm:^1.3.1"
@ -4156,7 +4156,7 @@ __metadata:
typescript-lru-cache: "npm:^2.0.0"
utf8-buffer: "npm:^1.0.0"
websocket-polyfill: "npm:^0.0.3"
checksum: 10/8553167dbc8e93952f5deb15dea443272827039a8e03410f851ec898c68811f6d3d21b36becc7194009ff5994cf073c8997355b43d29a55ad98d1ce95176f1f7
checksum: 10/4ee2513259800e4cedf88c772cfdf2e2c9652b9ecd432e25582363158635909c293ba7748d1cd314b612ca5344f1da820df456cdd6d908f6533856c0b038b648
languageName: node
linkType: hard
@ -4182,7 +4182,7 @@ __metadata:
languageName: node
linkType: hard
"@peculiar/asn1-schema@npm:^2.3.6":
"@peculiar/asn1-schema@npm:^2.3.8":
version: 2.3.8
resolution: "@peculiar/asn1-schema@npm:2.3.8"
dependencies:
@ -4202,16 +4202,16 @@ __metadata:
languageName: node
linkType: hard
"@peculiar/webcrypto@npm:^1.4.3":
version: 1.4.3
resolution: "@peculiar/webcrypto@npm:1.4.3"
"@peculiar/webcrypto@npm:^1.4.6":
version: 1.4.6
resolution: "@peculiar/webcrypto@npm:1.4.6"
dependencies:
"@peculiar/asn1-schema": "npm:^2.3.6"
"@peculiar/asn1-schema": "npm:^2.3.8"
"@peculiar/json-schema": "npm:^1.1.12"
pvtsutils: "npm:^1.3.2"
tslib: "npm:^2.5.0"
webcrypto-core: "npm:^1.7.7"
checksum: 10/548f5e32badcfdb02c903ca240daccac5d87ba841e436bd6d30e5455ced22917146130dab21afb718568ea935d6b04dc66fb33a4b6ab652dd868abff81e74a81
pvtsutils: "npm:^1.3.5"
tslib: "npm:^2.6.2"
webcrypto-core: "npm:^1.7.9"
checksum: 10/c1700b585cac0a161539f0060c97d52dc0d99c6e05a40b01ec714a83bfdb54708e615cd5a1fe4c9f22c617fdf05936de2f552dd33e856a2d1c5b0f39ec25ccab
languageName: node
linkType: hard
@ -4500,7 +4500,7 @@ __metadata:
languageName: node
linkType: hard
"@scure/base@npm:^1.1.1, @scure/base@npm:^1.1.2, @scure/base@npm:~1.1.0, @scure/base@npm:~1.1.2":
"@scure/base@npm:^1.1.1, @scure/base@npm:~1.1.0":
version: 1.1.3
resolution: "@scure/base@npm:1.1.3"
checksum: 10/cb715fa8cdb043c4d96b6ba0666791d4eb4d033f7b5285a853aba25e0ba94914f05ff5d956029ad060005f9bdd02dab0caef9a0a63f07ed096a2c2a0c0cf9c36
@ -4525,18 +4525,7 @@ __metadata:
languageName: node
linkType: hard
"@scure/bip32@npm:^1.3.0":
version: 1.3.2
resolution: "@scure/bip32@npm:1.3.2"
dependencies:
"@noble/curves": "npm:~1.2.0"
"@noble/hashes": "npm:~1.3.2"
"@scure/base": "npm:~1.1.2"
checksum: 10/b90da28dfe75519496a85c97e77c9443734873910f32b8557762910a5c4e642290a462b0ed14fa42e0efed6acb9a7f6155ad5cb5d38d4ff87eb2de4760eb32a4
languageName: node
linkType: hard
"@scure/bip32@npm:^1.3.3":
"@scure/bip32@npm:^1.3.3, @scure/bip32@npm:^1.4.0":
version: 1.4.0
resolution: "@scure/bip32@npm:1.4.0"
dependencies:
@ -4547,7 +4536,7 @@ __metadata:
languageName: node
linkType: hard
"@scure/bip39@npm:1.2.1, @scure/bip39@npm:^1.1.1":
"@scure/bip39@npm:1.2.1":
version: 1.2.1
resolution: "@scure/bip39@npm:1.2.1"
dependencies:
@ -4557,7 +4546,7 @@ __metadata:
languageName: node
linkType: hard
"@scure/bip39@npm:^1.2.2":
"@scure/bip39@npm:^1.2.2, @scure/bip39@npm:^1.3.0":
version: 1.3.0
resolution: "@scure/bip39@npm:1.3.0"
dependencies:
@ -4599,11 +4588,11 @@ __metadata:
"@cashu/cashu-ts": "npm:^1.0.0-rc.3"
"@formatjs/cli": "npm:^6.1.3"
"@here/maps-api-for-javascript": "npm:^1.50.0"
"@noble/curves": "npm:^1.0.0"
"@noble/hashes": "npm:^1.3.3"
"@scure/base": "npm:^1.1.1"
"@scure/bip32": "npm:^1.3.0"
"@scure/bip39": "npm:^1.1.1"
"@noble/curves": "npm:^1.4.0"
"@noble/hashes": "npm:^1.4.0"
"@scure/base": "npm:^1.1.6"
"@scure/bip32": "npm:^1.4.0"
"@scure/bip39": "npm:^1.3.0"
"@snort/shared": "workspace:*"
"@snort/system": "workspace:*"
"@snort/system-react": "workspace:*"
@ -4611,7 +4600,7 @@ __metadata:
"@snort/system-web": "workspace:*"
"@snort/wallet": "workspace:*"
"@snort/worker-relay": "workspace:*"
"@szhsin/react-menu": "npm:^3.3.1"
"@szhsin/react-menu": "npm:^3.5.3"
"@types/config": "npm:^3.3.3"
"@types/debug": "npm:^4.1.8"
"@types/latlon-geohash": "npm:^2.0.3"
@ -4628,7 +4617,7 @@ __metadata:
"@uidotdev/usehooks": "npm:^2.4.1"
"@vitejs/plugin-react": "npm:^4.2.0"
"@void-cat/api": "npm:^1.0.12"
"@webbtc/webln-types": "npm:^2.1.0"
"@webbtc/webln-types": "npm:^3.0.0"
"@webscopeio/react-textarea-autocomplete": "npm:^4.9.2"
"@welldone-software/why-did-you-render": "npm:^8.0.1"
autoprefixer: "npm:^10.4.16"
@ -4696,9 +4685,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "@snort/shared@workspace:packages/shared"
dependencies:
"@noble/curves": "npm:^1.2.0"
"@noble/hashes": "npm:^1.3.2"
"@scure/base": "npm:^1.1.2"
"@noble/curves": "npm:^1.4.0"
"@noble/hashes": "npm:^1.4.0"
"@scure/base": "npm:^1.1.6"
"@types/debug": "npm:^4.1.8"
debug: "npm:^4.3.4"
eventemitter3: "npm:^5.0.1"
@ -4712,7 +4701,7 @@ __metadata:
resolution: "@snort/system-react@workspace:packages/system-react"
dependencies:
"@snort/shared": "npm:^1.0.15"
"@snort/system": "npm:^1.3.1"
"@snort/system": "npm:^1.3.6"
"@types/react": "npm:^18.2.14"
react: "npm:^18.2.0"
typescript: "npm:^5.2.2"
@ -4747,16 +4736,16 @@ __metadata:
languageName: unknown
linkType: soft
"@snort/system@npm:^1.0.21, @snort/system@npm:^1.2.11, @snort/system@npm:^1.2.12, @snort/system@npm:^1.3.1, @snort/system@workspace:*, @snort/system@workspace:packages/system":
"@snort/system@npm:^1.0.21, @snort/system@npm:^1.2.11, @snort/system@npm:^1.3.6, @snort/system@workspace:*, @snort/system@workspace:packages/system":
version: 0.0.0-use.local
resolution: "@snort/system@workspace:packages/system"
dependencies:
"@jest/globals": "npm:^29.5.0"
"@noble/curves": "npm:^1.2.0"
"@noble/hashes": "npm:^1.3.2"
"@nostr-dev-kit/ndk": "npm:^2.7.1"
"@peculiar/webcrypto": "npm:^1.4.3"
"@scure/base": "npm:^1.1.2"
"@noble/curves": "npm:^1.4.0"
"@noble/hashes": "npm:^1.4.0"
"@nostr-dev-kit/ndk": "npm:^2.8.2"
"@peculiar/webcrypto": "npm:^1.4.6"
"@scure/base": "npm:^1.1.6"
"@snort/shared": "npm:^1.0.15"
"@stablelib/xchacha20": "npm:^1.0.1"
"@types/debug": "npm:^4.1.8"
@ -4787,8 +4776,8 @@ __metadata:
"@cashu/cashu-ts": "npm:^1.0.0-rc.3"
"@lightninglabs/lnc-web": "npm:^0.3.1-alpha"
"@scure/base": "npm:^1.1.6"
"@snort/shared": "npm:^1.0.14"
"@snort/system": "npm:^1.2.12"
"@snort/shared": "npm:^1.0.15"
"@snort/system": "npm:^1.3.6"
"@types/debug": "npm:^4.1.12"
"@webbtc/webln-types": "npm:^3.0.0"
debug: "npm:^4.3.4"
@ -4801,7 +4790,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@snort/worker-relay@workspace:packages/worker-relay"
dependencies:
"@sqlite.org/sqlite-wasm": "npm:^3.45.1-build1"
"@sqlite.org/sqlite-wasm": "npm:^3.45.3-build3"
"@types/debug": "npm:^4.1.12"
"@types/sharedworker": "npm:^0.0.112"
"@types/uuid": "npm:^9.0.7"
@ -4819,12 +4808,12 @@ __metadata:
languageName: node
linkType: hard
"@sqlite.org/sqlite-wasm@npm:^3.45.1-build1":
version: 3.45.1-build1
resolution: "@sqlite.org/sqlite-wasm@npm:3.45.1-build1"
"@sqlite.org/sqlite-wasm@npm:^3.45.3-build3":
version: 3.45.3-build3
resolution: "@sqlite.org/sqlite-wasm@npm:3.45.3-build3"
bin:
sqlite-wasm: bin/index.js
checksum: 10/ae86fedbccfc56f115195dc8f2afefa99b1f63969eb4571ed48bf4bd846773c0bfb9daa84703b95d5f1393954d30042f3aeafc903077ba042aed89dec9c3366f
checksum: 10/58dd96936973c8bb3989a7d23d6d6021b05e4b35ae997d617d28b5faf5f370196aaf71d1a060eb1822fed3636acb4b5e0d444807d86687bff2a6efa637f9f1dc
languageName: node
linkType: hard
@ -4884,7 +4873,7 @@ __metadata:
languageName: node
linkType: hard
"@szhsin/react-menu@npm:^3.3.1":
"@szhsin/react-menu@npm:^3.5.3":
version: 3.5.3
resolution: "@szhsin/react-menu@npm:3.5.3"
dependencies:
@ -4897,90 +4886,90 @@ __metadata:
languageName: node
linkType: hard
"@tauri-apps/cli-darwin-arm64@npm:1.5.11":
version: 1.5.11
resolution: "@tauri-apps/cli-darwin-arm64@npm:1.5.11"
"@tauri-apps/cli-darwin-arm64@npm:1.5.14":
version: 1.5.14
resolution: "@tauri-apps/cli-darwin-arm64@npm:1.5.14"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@tauri-apps/cli-darwin-x64@npm:1.5.11":
version: 1.5.11
resolution: "@tauri-apps/cli-darwin-x64@npm:1.5.11"
"@tauri-apps/cli-darwin-x64@npm:1.5.14":
version: 1.5.14
resolution: "@tauri-apps/cli-darwin-x64@npm:1.5.14"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@tauri-apps/cli-linux-arm-gnueabihf@npm:1.5.11":
version: 1.5.11
resolution: "@tauri-apps/cli-linux-arm-gnueabihf@npm:1.5.11"
"@tauri-apps/cli-linux-arm-gnueabihf@npm:1.5.14":
version: 1.5.14
resolution: "@tauri-apps/cli-linux-arm-gnueabihf@npm:1.5.14"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@tauri-apps/cli-linux-arm64-gnu@npm:1.5.11":
version: 1.5.11
resolution: "@tauri-apps/cli-linux-arm64-gnu@npm:1.5.11"
"@tauri-apps/cli-linux-arm64-gnu@npm:1.5.14":
version: 1.5.14
resolution: "@tauri-apps/cli-linux-arm64-gnu@npm:1.5.14"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
"@tauri-apps/cli-linux-arm64-musl@npm:1.5.11":
version: 1.5.11
resolution: "@tauri-apps/cli-linux-arm64-musl@npm:1.5.11"
"@tauri-apps/cli-linux-arm64-musl@npm:1.5.14":
version: 1.5.14
resolution: "@tauri-apps/cli-linux-arm64-musl@npm:1.5.14"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
"@tauri-apps/cli-linux-x64-gnu@npm:1.5.11":
version: 1.5.11
resolution: "@tauri-apps/cli-linux-x64-gnu@npm:1.5.11"
"@tauri-apps/cli-linux-x64-gnu@npm:1.5.14":
version: 1.5.14
resolution: "@tauri-apps/cli-linux-x64-gnu@npm:1.5.14"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
"@tauri-apps/cli-linux-x64-musl@npm:1.5.11":
version: 1.5.11
resolution: "@tauri-apps/cli-linux-x64-musl@npm:1.5.11"
"@tauri-apps/cli-linux-x64-musl@npm:1.5.14":
version: 1.5.14
resolution: "@tauri-apps/cli-linux-x64-musl@npm:1.5.14"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
"@tauri-apps/cli-win32-arm64-msvc@npm:1.5.11":
version: 1.5.11
resolution: "@tauri-apps/cli-win32-arm64-msvc@npm:1.5.11"
"@tauri-apps/cli-win32-arm64-msvc@npm:1.5.14":
version: 1.5.14
resolution: "@tauri-apps/cli-win32-arm64-msvc@npm:1.5.14"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@tauri-apps/cli-win32-ia32-msvc@npm:1.5.11":
version: 1.5.11
resolution: "@tauri-apps/cli-win32-ia32-msvc@npm:1.5.11"
"@tauri-apps/cli-win32-ia32-msvc@npm:1.5.14":
version: 1.5.14
resolution: "@tauri-apps/cli-win32-ia32-msvc@npm:1.5.14"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@tauri-apps/cli-win32-x64-msvc@npm:1.5.11":
version: 1.5.11
resolution: "@tauri-apps/cli-win32-x64-msvc@npm:1.5.11"
"@tauri-apps/cli-win32-x64-msvc@npm:1.5.14":
version: 1.5.14
resolution: "@tauri-apps/cli-win32-x64-msvc@npm:1.5.14"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@tauri-apps/cli@npm:^1.5.11":
version: 1.5.11
resolution: "@tauri-apps/cli@npm:1.5.11"
"@tauri-apps/cli@npm:^1.5.14":
version: 1.5.14
resolution: "@tauri-apps/cli@npm:1.5.14"
dependencies:
"@tauri-apps/cli-darwin-arm64": "npm:1.5.11"
"@tauri-apps/cli-darwin-x64": "npm:1.5.11"
"@tauri-apps/cli-linux-arm-gnueabihf": "npm:1.5.11"
"@tauri-apps/cli-linux-arm64-gnu": "npm:1.5.11"
"@tauri-apps/cli-linux-arm64-musl": "npm:1.5.11"
"@tauri-apps/cli-linux-x64-gnu": "npm:1.5.11"
"@tauri-apps/cli-linux-x64-musl": "npm:1.5.11"
"@tauri-apps/cli-win32-arm64-msvc": "npm:1.5.11"
"@tauri-apps/cli-win32-ia32-msvc": "npm:1.5.11"
"@tauri-apps/cli-win32-x64-msvc": "npm:1.5.11"
"@tauri-apps/cli-darwin-arm64": "npm:1.5.14"
"@tauri-apps/cli-darwin-x64": "npm:1.5.14"
"@tauri-apps/cli-linux-arm-gnueabihf": "npm:1.5.14"
"@tauri-apps/cli-linux-arm64-gnu": "npm:1.5.14"
"@tauri-apps/cli-linux-arm64-musl": "npm:1.5.14"
"@tauri-apps/cli-linux-x64-gnu": "npm:1.5.14"
"@tauri-apps/cli-linux-x64-musl": "npm:1.5.14"
"@tauri-apps/cli-win32-arm64-msvc": "npm:1.5.14"
"@tauri-apps/cli-win32-ia32-msvc": "npm:1.5.14"
"@tauri-apps/cli-win32-x64-msvc": "npm:1.5.14"
dependenciesMeta:
"@tauri-apps/cli-darwin-arm64":
optional: true
@ -5004,7 +4993,7 @@ __metadata:
optional: true
bin:
tauri: tauri.js
checksum: 10/18376915830153f4670f3c68e8acfdb75a33f8f011ba7d366ad8c0babd8d28381d231a8924f0163d48680d72e15345ff6ffa02cc057cb9f936a852426cc38861
checksum: 10/000fd57f22997a90555f344c64efa1535b8dec853937763b41abc0fb1735e96b3a64115cd6a9a3269477aeaac21f4954ed806d74c92e47372e76498f388519b7
languageName: node
linkType: hard
@ -5985,13 +5974,6 @@ __metadata:
languageName: node
linkType: hard
"@webbtc/webln-types@npm:^2.1.0":
version: 2.1.0
resolution: "@webbtc/webln-types@npm:2.1.0"
checksum: 10/18bb0e33e7961f9de8040332448ee6d4b02fbd2b63c92c05b3bc19dfd955379e419a0c102753ed7af9dac03c09029e5f7a10a30ec1a20a8856173a41cba3ab6c
languageName: node
linkType: hard
"@webbtc/webln-types@npm:^3.0.0":
version: 3.0.0
resolution: "@webbtc/webln-types@npm:3.0.0"
@ -13466,7 +13448,7 @@ __metadata:
resolution: "root-workspace-0b6124@workspace:."
dependencies:
"@cloudflare/workers-types": "npm:^4.20230307.0"
"@tauri-apps/cli": "npm:^1.5.11"
"@tauri-apps/cli": "npm:^1.5.14"
eslint: "npm:^8.48.0"
prettier: "npm:^3.0.3"
typedoc: "npm:^0.25.7"
@ -14569,7 +14551,7 @@ __metadata:
languageName: node
linkType: hard
"tslib@npm:2.6.2, tslib@npm:^2.0.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2":
"tslib@npm:2.6.2, tslib@npm:^2.0.0, tslib@npm:^2.4.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2":
version: 2.6.2
resolution: "tslib@npm:2.6.2"
checksum: 10/bd26c22d36736513980091a1e356378e8b662ded04204453d353a7f34a4c21ed0afc59b5f90719d4ba756e581a162ecbf93118dc9c6be5acf70aa309188166ca
@ -15333,16 +15315,16 @@ __metadata:
languageName: node
linkType: hard
"webcrypto-core@npm:^1.7.7":
version: 1.7.7
resolution: "webcrypto-core@npm:1.7.7"
"webcrypto-core@npm:^1.7.9":
version: 1.7.9
resolution: "webcrypto-core@npm:1.7.9"
dependencies:
"@peculiar/asn1-schema": "npm:^2.3.6"
"@peculiar/asn1-schema": "npm:^2.3.8"
"@peculiar/json-schema": "npm:^1.1.12"
asn1js: "npm:^3.0.1"
pvtsutils: "npm:^1.3.2"
tslib: "npm:^2.4.0"
checksum: 10/e87ac59d7d05c2aa96117c8f589e99ec9556dfc9ff3cd7fe9464de32e60ed6ff237cdfd35ed53c93546dd0d548bab67b244be381e97b162fe87b6d826e8765ae
pvtsutils: "npm:^1.3.5"
tslib: "npm:^2.6.2"
checksum: 10/515140c6330024f49142a8dd7d84cdb5adddfc09827b6d3aad5fdec398038465fe8f2b48a3a2d9f67a34ab2ac5324c150ec68d552c9313b65a3130d23629da16
languageName: node
linkType: hard