Compare commits

...

65 Commits

Author SHA1 Message Date
2e4471cee6 hide reposts/reactions/zaps when the lowdatamode pref is on 2024-01-04 07:20:47 +00:00
e2fc71502a low data mode preferences added, images loaded based as per the low data mode settings 2024-01-04 07:20:47 +00:00
0043b7e8bd
feat: full event verify in wasm 2024-01-03 23:20:37 +00:00
267c09a946 dont save p tags for now 2024-01-04 00:03:17 +02:00
2fa75e8e3d fuzzysearch username -> display_name 2024-01-03 23:57:51 +02:00
2a2144b59b increase idb throttle time, query by pubkey+kind index 2024-01-03 23:56:17 +02:00
7ac5bb6d41 LRUSet for seenEvents 2024-01-03 23:33:35 +02:00
38093fdf3b load all profiles from indexeddb 2024-01-03 21:29:17 +02:00
98e1be883b trending posts into system, check sigs 2024-01-03 20:22:58 +02:00
e700c97c71 chore: Update translations 2024-01-03 16:49:44 +00:00
4d9226b3b6
feat: wallet connection flow 2024-01-03 16:46:34 +00:00
0cc0a47501 fix: rm eventMatchesFilter 2024-01-03 18:34:28 +02:00
d187fbc6e5 save only followDistance <= 2 evts to idb 2024-01-03 17:39:55 +02:00
6582b4c7d5 chore: Update translations 2024-01-03 15:34:43 +00:00
acbd3a9004 package.json 2024-01-03 17:24:15 +02:00
1199418d0e check filter match on the worker side 2024-01-03 17:19:22 +02:00
395848fd8c add optional idb "relay" worker 2024-01-03 16:53:33 +02:00
8571ea0aa7 chore: Update translations 2024-01-03 10:34:35 +00:00
d840f2b952 NostrSystemEvent "event" id -> subId 2024-01-03 12:27:05 +02:00
43591c4ce6 chore: Update translations 2024-01-02 18:20:53 +00:00
bc7ec4d77f
feat: new wallet design 2024-01-02 18:11:44 +00:00
0d10122394
fix: event hook 2024-01-02 16:06:43 +00:00
8bbbd11f7a
fix: nwc history 2024-01-02 16:05:21 +00:00
7c8136b503
fix: check sig exists 2024-01-02 15:51:19 +00:00
6ad99e7e95 ProxyImg fix 2024-01-02 10:24:34 +02:00
f7d8d1de16 chore: Update translations 2024-01-02 07:38:33 +00:00
2c14d64a95 NoteInner prop waitUntilInView = false 2024-01-02 09:33:22 +02:00
81c9285d46 proxyimg fix 2024-01-02 01:30:10 +02:00
6928ad04d7 chore: Update translations 2024-01-01 22:59:18 +00:00
19eeb890ac broken img placeholder fix 2024-01-02 00:54:25 +02:00
3d98532e40 chore: Update translations 2024-01-01 22:32:53 +00:00
4bbad0563b useMemo in Avatar 2024-01-02 00:25:44 +02:00
13fc3bb843 notetime init value 2024-01-02 00:21:30 +02:00
7b72f9f775
chore: formatting 2023-12-28 17:40:53 +00:00
c2e1215667
refactor: extract connection pool
wip: setup system-worker
2023-12-28 17:40:26 +00:00
e7e7fdc14d chore: Update translations 2023-12-27 20:42:52 +00:00
3e52bb755e show trending error only if no cached content 2023-12-27 22:35:51 +02:00
789476c677 NostrLink initialState 2023-12-27 15:07:20 +02:00
f47994b3ee faster nostrlinkhandler 2023-12-27 14:45:00 +02:00
9d2b867552 sw caching for nostr.json and images 2023-12-26 21:33:22 +02:00
d82c7957be if suggested follows is empty, show trending users 2023-12-26 20:50:34 +02:00
3af04a79cc rm dataProcessor from useCachedFetch hook deps 2023-12-26 20:40:57 +02:00
9fc0b676f5
feat: add dimensions from void.cat uploads 2023-12-26 18:35:54 +00:00
f70d752fae useCachedFetch hook for trending api calls 2023-12-26 20:15:26 +02:00
6b88df96ab rm webrtc client for now 2023-12-26 19:20:12 +02:00
1f03a5ee5a NostrBand cache fix 2023-12-26 19:20:11 +02:00
9c94e84b9d chore: Update translations 2023-12-26 08:17:40 +00:00
8c1bbe58f6 cache NostrBand requests in localstorage 2023-12-26 10:07:11 +02:00
118ada989e chore: Update translations 2023-12-23 07:33:28 +00:00
1b9dc3f480 add missing login 2023-12-23 09:26:52 +02:00
82bb71136e chore: Update translations 2023-12-22 08:14:56 +00:00
74591a6adb add telegram link to donate page 2023-12-22 10:11:29 +02:00
87d3bbe1a1 move chat files to same dir, webrtc, chat 2023-12-21 23:40:29 +02:00
1639937d8c webrtc works in dev, add some missing list keys 2023-12-21 22:40:08 +02:00
e10a11b707 chore: Update translations 2023-12-21 20:04:03 +00:00
9e6971423e webrtc 2023-12-21 21:57:08 +02:00
782a2217b4 chore: Update translations 2023-12-21 17:59:56 +00:00
1309937869 wip webrtc 2023-12-21 19:51:37 +02:00
0c2ed147b0
fix: match file extensions with lower case 2023-12-20 16:07:04 +00:00
9ed5757875 chore: Update translations 2023-12-20 14:15:35 +00:00
06b7dcad11
feat: tools pages
Various other fixes:
- Better handeling of limit/since/before merging
- Expose timeout through request builder
- Expose PickN through request builder
- Fix tests
2023-12-20 14:08:05 +00:00
96368d4a2b fix build 2023-12-20 13:41:24 +02:00
bf822aae5b chore: Update translations 2023-12-20 10:34:53 +00:00
80690df15a fix spotlight modal for replies 2023-12-20 12:31:24 +02:00
df66a861f7 make grid media modal closable when not loaded 2023-12-20 12:06:52 +02:00
127 changed files with 3611 additions and 1462 deletions

View File

@ -39,5 +39,6 @@
"wss://relay.snort.social/": { "read": true, "write": true },
"wss://nostr.wine/": { "read": true, "write": false },
"wss://eden.nostr.land/": { "read": true, "write": false }
}
},
"useIndexedDBEvents": false
}

View File

@ -37,5 +37,6 @@
"wss://eden.nostr.land/": { "read": true, "write": false },
"wss://relay.nostr.band/": { "read": true, "write": true },
"wss://relay.damus.io/": { "read": true, "write": true }
}
},
"useIndexedDBEvents": true
}

View File

@ -84,6 +84,7 @@ declare const CONFIG: {
eventLinkPrefix: NostrPrefix;
profileLinkPrefix: NostrPrefix;
defaultRelays: Record<string, RelaySettings>;
useIndexedDBEvents: boolean;
};
/**

View File

@ -16,8 +16,9 @@
"@snort/system-web": "workspace:*",
"@szhsin/react-menu": "^3.3.1",
"@uidotdev/usehooks": "^2.4.1",
"@void-cat/api": "^1.0.10",
"@void-cat/api": "^1.0.12",
"classnames": "^2.3.2",
"comlink": "^4.4.1",
"debug": "^4.3.4",
"dexie": "^3.2.4",
"emojilib": "^3.0.10",
@ -43,6 +44,7 @@
"use-sync-external-store": "^1.2.0",
"uuid": "^9.0.0",
"workbox-core": "^6.4.2",
"workbox-expiration": "^7.0.0",
"workbox-precaching": "^7.0.0",
"workbox-routing": "^6.4.2",
"workbox-strategies": "^6.4.2"

View File

@ -26,7 +26,7 @@ export class FollowListCache extends RefreshFeedCache<TaggedNostrEvent> {
loaded: unixNowMs(),
});
if (update !== "no_change") {
socialGraphInstance.handleFollowEvent(e);
socialGraphInstance.handleEvent(e);
}
}),
);
@ -42,6 +42,6 @@ export class FollowListCache extends RefreshFeedCache<TaggedNostrEvent> {
override async preload() {
await super.preload();
this.snapshot().forEach(e => socialGraphInstance.handleFollowEvent(e));
this.snapshot().forEach(e => socialGraphInstance.handleEvent(e));
}
}

View File

@ -27,8 +27,10 @@ export class FollowsFeedCache extends RefreshFeedCache<TaggedNostrEvent> {
}
buildSub(session: LoginSession, rb: RequestBuilder): void {
const authors = session.follows.item;
authors.push(session.publicKey);
const authors = [...session.follows.item];
if (session.publicKey) {
authors.push(session.publicKey);
}
const since = this.newest();
rb.withFilter()
.kinds(this.#kinds)
@ -69,8 +71,10 @@ export class FollowsFeedCache extends RefreshFeedCache<TaggedNostrEvent> {
async loadMore(system: SystemInterface, session: LoginSession, before: number) {
if (this.#oldest && before <= this.#oldest) {
const rb = new RequestBuilder(`${this.name}-loadmore`);
const authors = session.follows.item;
authors.push(session.publicKey);
const authors = [...session.follows.item];
if (session.publicKey) {
authors.push(session.publicKey);
}
rb.withFilter()
.kinds(this.#kinds)
.authors(authors)

View File

@ -0,0 +1,222 @@
import Dexie, { Table } from "dexie";
import { TaggedNostrEvent, ReqFilter as Filter } from "@snort/system";
import * as Comlink from "comlink";
import LRUSet from "@/Cache/LRUSet";
type Tag = {
id: string;
eventId: string;
type: string;
value: string;
};
type SaveQueueEntry = { event: TaggedNostrEvent; tags: Tag[] };
class IndexedDB extends Dexie {
events!: Table<TaggedNostrEvent>;
tags!: Table<Tag>;
private saveQueue: SaveQueueEntry[] = [];
private seenEvents = new LRUSet<string>(1000);
private subscribedEventIds = new Set<string>();
private subscribedAuthors = new Set<string>();
private subscribedTags = new Set<string>();
private subscribedAuthorsAndKinds = new Set<string>();
constructor() {
super("EventDB");
this.version(5).stores({
events: "id, pubkey, kind, created_at, [pubkey+kind]",
tags: "id, eventId, [type+value]",
});
this.startInterval();
}
private startInterval() {
const processQueue = async () => {
if (this.saveQueue.length > 0) {
try {
const eventsToSave: TaggedNostrEvent[] = [];
const tagsToSave: Tag[] = [];
for (const item of this.saveQueue) {
eventsToSave.push(item.event);
tagsToSave.push(...item.tags);
}
await this.events.bulkPut(eventsToSave);
await this.tags.bulkPut(tagsToSave);
} catch (e) {
console.error(e);
} finally {
this.saveQueue = [];
}
}
setTimeout(() => processQueue(), 3000);
};
setTimeout(() => processQueue(), 3000);
}
handleEvent(event: TaggedNostrEvent) {
if (this.seenEvents.has(event.id)) {
return;
}
this.seenEvents.add(event.id);
// maybe we don't want event.kind 3 tags
const tags =
event.kind === 3
? []
: event.tags
?.filter(tag => {
if (tag[0] === "d") {
return true;
}
if (tag[0] === "e") {
return true;
}
// we're only interested in p tags where we are mentioned
/*
if (tag[0] === "p") {
Key.isMine(tag[1])) { // TODO
return true;
}*/
return false;
})
.map(tag => ({
id: event.id.slice(0, 16) + "-" + tag[0].slice(0, 16) + "-" + tag[1].slice(0, 16),
eventId: event.id,
type: tag[0],
value: tag[1],
})) || [];
this.saveQueue.push({ event, tags });
}
_throttle(func, limit) {
let inThrottle;
return function (...args) {
if (!inThrottle) {
inThrottle = true;
setTimeout(() => {
inThrottle = false;
func.apply(this, args);
}, limit);
}
};
}
subscribeToAuthors = this._throttle(async function (callback: (event: TaggedNostrEvent) => void, limit?: number) {
const authors = [...this.subscribedAuthors];
this.subscribedAuthors.clear();
await this.events
.where("pubkey")
.anyOf(authors)
.limit(limit || 1000)
.each(callback);
}, 200);
subscribeToEventIds = this._throttle(async function (callback: (event: TaggedNostrEvent) => void) {
const ids = [...this.subscribedEventIds];
this.subscribedEventIds.clear();
await this.events.where("id").anyOf(ids).each(callback);
}, 200);
subscribeToTags = this._throttle(async function (callback: (event: TaggedNostrEvent) => void) {
const tagPairs = [...this.subscribedTags].map(tag => tag.split("|"));
this.subscribedTags.clear();
await this.tags
.where("[type+value]")
.anyOf(tagPairs)
.each(tag => this.subscribedEventIds.add(tag.eventId));
await this.subscribeToEventIds(callback);
}, 200);
subscribeToAuthorsAndKinds = this._throttle(async function (callback: (event: TaggedNostrEvent) => void) {
const authorsAndKinds = [...this.subscribedAuthorsAndKinds];
this.subscribedAuthorsAndKinds.clear();
// parse pair[1] as int
const pairs = authorsAndKinds.map(pair => {
const [author, kind] = pair.split("|");
return [author, parseInt(kind)];
});
await this.events.where("[pubkey+kind]").anyOf(pairs).each(callback);
}, 200);
async find(filter: Filter, callback: (event: TaggedNostrEvent) => void): Promise<void> {
if (!filter) return;
// make sure only 1 argument is passed
const cb = e => {
this.seenEvents.add(e.id);
callback(e);
};
if (filter["#p"] && Array.isArray(filter["#p"])) {
for (const eventId of filter["#p"]) {
this.subscribedTags.add("p|" + eventId);
}
await this.subscribeToTags(cb);
return;
}
if (filter["#e"] && Array.isArray(filter["#e"])) {
for (const eventId of filter["#e"]) {
this.subscribedTags.add("e|" + eventId);
}
await this.subscribeToTags(cb);
return;
}
if (filter["#d"] && Array.isArray(filter["#d"])) {
for (const eventId of filter["#d"]) {
this.subscribedTags.add("d|" + eventId);
}
await this.subscribeToTags(cb);
return;
}
if (filter.ids?.length) {
filter.ids.forEach(id => this.subscribedEventIds.add(id));
await this.subscribeToEventIds(cb);
return;
}
if (filter.authors?.length && filter.kinds?.length) {
const permutations = filter.authors.flatMap(author => filter.kinds!.map(kind => author + "|" + kind));
permutations.forEach(permutation => this.subscribedAuthorsAndKinds.add(permutation));
await this.subscribeToAuthorsAndKinds(cb);
return;
}
if (filter.authors?.length) {
filter.authors.forEach(author => this.subscribedAuthors.add(author));
await this.subscribeToAuthors(cb);
return;
}
let query = this.events;
if (filter.kinds) {
query = query.where("kind").anyOf(filter.kinds);
}
if (filter.search) {
const regexp = new RegExp(filter.search, "i");
query = query.filter((event: Event) => event.content?.match(regexp));
}
if (filter.limit) {
query = query.limit(filter.limit);
}
// TODO test that the sort is actually working
await query.each(e => {
cb(e);
});
}
}
const db = new IndexedDB();
Comlink.expose(db);

View File

@ -0,0 +1,23 @@
export default class LRUSet<T> {
private set = new Set<T>();
private limit: number;
constructor(limit: number) {
this.limit = limit;
}
add(item: T) {
if (this.set.size >= this.limit) {
this.set.delete(this.set.values().next().value);
}
this.set.add(item);
}
has(item: T) {
return this.set.has(item);
}
values() {
return this.set.values();
}
}

View File

@ -408,7 +408,7 @@ export function NoteCreator() {
<div className="flex flex-col g8">
{[...(note.zapSplits ?? [])].map((v, i, arr) => (
<div className="flex items-center g8">
<div className="flex flex-col f-4 g4">
<div className="flex flex-col flex-4 g4">
<h4>
<FormattedMessage defaultMessage="Recipient" id="8Rkoyb" />
</h4>
@ -423,7 +423,7 @@ export function NoteCreator() {
placeholder={formatMessage({ defaultMessage: "npub / nprofile / nostr address", id: "WvGmZT" })}
/>
</div>
<div className="flex flex-col f-1 g4">
<div className="flex flex-col flex-1 g4">
<h4>
<FormattedMessage defaultMessage="Weight" id="zCb8fX" />
</h4>

View File

@ -1,23 +1,21 @@
import { useEffect, useState } from "react";
import { useLocale } from "@/IntlProvider";
import NostrBandApi from "@/External/NostrBand";
import { FormattedMessage } from "react-intl";
import useCachedFetch from "@/Hooks/useCachedFetch";
import { ErrorOrOffline } from "@/Element/ErrorOrOffline";
export function TrendingHashTagsLine(props: { onClick: (tag: string) => void }) {
const [hashtags, setHashtags] = useState<Array<{ hashtag: string; posts: number }>>();
const { lang } = useLocale();
const api = new NostrBandApi();
const trendingHashtagsUrl = api.trendingHashtagsUrl(lang);
const storageKey = `nostr-band-${trendingHashtagsUrl}`;
async function loadTrendingHashtags() {
const api = new NostrBandApi();
const rsp = await api.trendingHashtags(lang);
setHashtags(rsp.hashtags);
}
const { data: hashtags, isLoading, error } = useCachedFetch(trendingHashtagsUrl, storageKey, data => data.hashtags);
useEffect(() => {
loadTrendingHashtags().catch(console.error);
}, []);
if (error && !hashtags) return <ErrorOrOffline error={error} className="p" />;
if (isLoading || hashtags.length === 0) return null;
if (!hashtags || hashtags.length === 0) return;
return (
<div className="flex flex-col g4">
<small>
@ -25,7 +23,10 @@ export function TrendingHashTagsLine(props: { onClick: (tag: string) => void })
</small>
<div className="flex g4 flex-wrap">
{hashtags.slice(0, 5).map(a => (
<span className="px-2 py-1 bg-dark rounded-full pointer nowrap" onClick={() => props.onClick(a.hashtag)}>
<span
key={a.hashtag}
className="px-2 py-1 bg-dark rounded-full pointer nowrap"
onClick={() => props.onClick(a.hashtag)}>
#{a.hashtag}
</span>
))}

View File

@ -7,8 +7,8 @@ export async function sendEventToRelays(
customRelays?: Array<string>,
setResults?: (x: Array<OkResponse>) => void,
) {
console.log("sendEventToRelays", ev, customRelays);
if (customRelays) {
system.HandleEvent({ ...ev, relays: [] });
return removeUndefined(
await Promise.all(
customRelays.map(async r => {

View File

@ -41,6 +41,7 @@ export interface NoteProps {
longFormPreview?: boolean;
truncate?: boolean;
};
waitUntilInView?: boolean;
}
export default function Note(props: NoteProps) {

View File

@ -197,6 +197,9 @@ export default function NoteFooter(props: NoteFooterProps) {
}
function tipButton() {
if(prefs.lowDataMode){
return null;
}
const targets = getZapTarget();
if (targets) {
return (
@ -214,7 +217,12 @@ export default function NoteFooter(props: NoteFooterProps) {
}
function repostIcon() {
if (readonly) return;
if (readonly) {
return;
}
else if(prefs.lowDataMode){
return null;
}
return (
<Menu
menuButton={
@ -254,7 +262,7 @@ export default function NoteFooter(props: NoteFooterProps) {
}
function reactionIcon() {
if (!prefs.enableReactions) {
if (!prefs.enableReactions || prefs.lowDataMode){
return null;
}
const reacted = hasReacted("+");

View File

@ -31,7 +31,7 @@ import DisplayName from "@/Element/User/DisplayName";
const TEXT_TRUNCATE_LENGTH = 400;
export function NoteInner(props: NoteProps) {
const { data: ev, related, highlight, options: opt, ignoreModeration = false, className } = props;
const { data: ev, related, highlight, options: opt, ignoreModeration = false, className, waitUntilInView } = props;
const baseClassName = classNames("note min-h-[110px] flex flex-col gap-4 card", className);
const navigate = useNavigate();
@ -326,7 +326,7 @@ export function NoteInner(props: NoteProps) {
}
function content() {
if (!inView) return undefined;
if (waitUntilInView && !inView) return undefined;
return (
<>
{options.showHeader && (

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { FormattedMessage } from "react-intl";
export interface NoteTimeProps {
@ -11,13 +11,17 @@ const secondsInAnHour = secondsInAMinute * 60;
const secondsInADay = secondsInAnHour * 24;
export default function NoteTime(props: NoteTimeProps) {
const [time, setTime] = useState<string | JSX.Element>();
const { from, fallback } = props;
const [time, setTime] = useState<string | JSX.Element>(calcTime());
const absoluteTime = new Intl.DateTimeFormat(undefined, {
dateStyle: "medium",
timeStyle: "long",
}).format(from);
const absoluteTime = useMemo(
() =>
new Intl.DateTimeFormat(undefined, {
dateStyle: "medium",
timeStyle: "long",
}).format(from),
[from],
);
const isoDate = new Date(from).toISOString();

View File

@ -25,7 +25,7 @@ export default function RevealMedia(props: RevealMediaProps) {
const isMine = props.creator === publicKey;
const hideMedia = preferences.autoLoadMedia === "none" || (!isMine && hideNonFollows);
const hostname = new URL(props.link).hostname;
const lowDataMode = preferences.lowDataMode;
const url = new URL(props.link);
const extension = FileExtensionRegex.test(url.pathname.toLowerCase()) && RegExp.$1;
const type = (() => {
@ -76,7 +76,29 @@ export default function RevealMedia(props: RevealMediaProps) {
/>
</Reveal>
);
} else {
} else if(lowDataMode) {
return (
<Reveal
message={
<FormattedMessage
defaultMessage="Images will not be loaded automatically in low data mode, update <a><i>your preference</i></a> to show media as per your media preference."
id="VfHIx1"
values={{
i: i => <i>{i}</i>,
a: a => <Link to="/settings/preferences">{a}</Link>,
}}
/>
}>
<MediaElement
mime={`${type}/${extension}`}
url={url.toString()}
onMediaClick={props.onMediaClick}
meta={props.meta}
/>
</Reveal>
);
}
else{
return (
<MediaElement
mime={`${type}/${extension}`}

View File

@ -13,6 +13,7 @@ export interface TimelineFragment {
export interface TimelineFragProps {
frag: TimelineFragment;
related: Array<TaggedNostrEvent>;
index: number;
noteRenderer?: (ev: TaggedNostrEvent) => ReactNode;
noteOnClick?: (ev: TaggedNostrEvent) => void;
noteContext?: (ev: TaggedNostrEvent) => ReactNode;
@ -41,6 +42,7 @@ export function TimelineFragment(props: TimelineFragProps) {
options={{
truncate: true,
}}
waitUntilInView={props.index > 10}
/>
),
)}

View File

@ -2,7 +2,7 @@ import { useInView } from "react-intersection-observer";
import ProfileImage from "@/Element/User/ProfileImage";
import { FormattedMessage } from "react-intl";
import Icon from "@/Icons/Icon";
import { NostrLink, TaggedNostrEvent } from "@snort/system";
import { TaggedNostrEvent } from "@snort/system";
import { ReactNode, useEffect, useMemo, useRef, useState } from "react";
import { TimelineFragment } from "@/Element/Feed/TimelineFragment";
import { DisplayAs } from "@/Element/Feed/DisplayAsSelector";
@ -27,7 +27,7 @@ export interface TimelineRendererProps {
// filter frags[0].events that have media
function Grid({ frags }: { frags: Array<TimelineFragment> }) {
const [modalThreadIndex, setModalThreadIndex] = useState<number | undefined>(undefined);
const [modalEventIndex, setModalEventIndex] = useState<number | undefined>(undefined);
const allEvents = useMemo(() => {
return frags.flatMap(frag => frag.events);
}, [frags]);
@ -35,40 +35,32 @@ function Grid({ frags }: { frags: Array<TimelineFragment> }) {
return allEvents.filter(event => getEventMedia(event).length > 0);
}, [allEvents]);
const modalThread = modalThreadIndex !== undefined ? mediaEvents[modalThreadIndex] : undefined;
const nextModalThread = modalThreadIndex !== undefined ? mediaEvents[modalThreadIndex + 1] : undefined;
const prevModalThread = modalThreadIndex !== undefined ? mediaEvents[modalThreadIndex - 1] : undefined;
const modalEvent = modalEventIndex !== undefined ? mediaEvents[modalEventIndex] : undefined;
const nextModalEvent = modalEventIndex !== undefined ? mediaEvents[modalEventIndex + 1] : undefined;
const prevModalEvent = modalEventIndex !== undefined ? mediaEvents[modalEventIndex - 1] : undefined;
return (
<>
<div className="grid grid-cols-3 gap-px md:gap-1">
{mediaEvents.map((event, index) => (
<ImageGridItem key={event.id} event={event} onClick={() => setModalThreadIndex(index)} />
<ImageGridItem key={event.id} event={event} onClick={() => setModalEventIndex(index)} />
))}
</div>
{modalThread && (
{modalEvent && (
<SpotlightThreadModal
key={modalThread.id}
thread={NostrLink.fromEvent(modalThread)}
onClose={() => setModalThreadIndex(undefined)}
onBack={() => setModalThreadIndex(undefined)}
onNext={() => setModalThreadIndex(Math.min(modalThreadIndex + 1, mediaEvents.length - 1))}
onPrev={() => setModalThreadIndex(Math.max(modalThreadIndex - 1, 0))}
key={modalEvent.id}
event={modalEvent}
onClose={() => setModalEventIndex(undefined)}
onBack={() => setModalEventIndex(undefined)}
onNext={() => setModalEventIndex(Math.min((modalEventIndex ?? 0) + 1, mediaEvents.length - 1))}
onPrev={() => setModalEventIndex(Math.max((modalEventIndex ?? 0) - 1, 0))}
/>
)}
{nextModalThread && ( // preload next
<SpotlightThreadModal
className="hidden"
key={`${nextModalThread.id}-next`}
thread={NostrLink.fromEvent(nextModalThread)}
/>
{nextModalEvent && ( // preload next
<SpotlightThreadModal className="hidden" key={`${nextModalEvent.id}-next`} event={nextModalEvent} />
)}
{prevModalThread && ( // preload previous
<SpotlightThreadModal
className="hidden"
key={`${prevModalThread.id}-prev`}
thread={NostrLink.fromEvent(prevModalThread)}
/>
{prevModalEvent && ( // preload previous
<SpotlightThreadModal className="hidden" key={`${prevModalEvent.id}-prev`} event={prevModalEvent} />
)}
</>
);
@ -99,14 +91,15 @@ export function TimelineRenderer(props: TimelineRendererProps) {
}, [inView, props.latest]);
const renderNotes = () => {
return props.frags.map(frag => (
<ErrorBoundary>
return props.frags.map((frag, index) => (
<ErrorBoundary key={frag.events[0]?.id + index}>
<TimelineFragment
frag={frag}
related={props.related}
noteRenderer={props.noteRenderer}
noteOnClick={props.noteOnClick}
noteContext={props.noteContext}
index={index}
/>
</ErrorBoundary>
));

View File

@ -1,5 +1,5 @@
import useImgProxy from "@/Hooks/useImgProxy";
import React, { HTMLProps, ReactNode, forwardRef, useState } from "react";
import React, { HTMLProps, ReactNode, forwardRef, useState, useMemo, useEffect } from "react";
import { FormattedMessage } from "react-intl";
import { getUrlHostname } from "@/SnortUtils";
@ -16,6 +16,13 @@ export const ProxyImg = forwardRef<HTMLImageElement, ProxyImgProps>(
const { proxy } = useImgProxy();
const [loadFailed, setLoadFailed] = useState(false);
const [bypass, setBypass] = useState(CONFIG.media.bypassImgProxyError);
const proxiedSrc = useMemo(() => proxy(props.src ?? "", size, sha256), [props.src, size, sha256]);
const [src, setSrc] = useState(proxiedSrc);
useEffect(() => {
setLoadFailed(false);
setSrc(proxy(props.src, size, sha256));
}, [props.src, size, sha256]);
if (loadFailed && !bypass && (promptToLoadDirectly ?? true)) {
return (
@ -35,25 +42,24 @@ export const ProxyImg = forwardRef<HTMLImageElement, ProxyImgProps>(
</div>
);
}
const src = loadFailed && bypass ? props.src : proxy(props.src ?? "", size, sha256);
if (!src || (loadFailed && !bypass)) return missingImageElement;
const handleImageError = e => {
if (props.onError) {
props.onError(e);
} else {
console.error("Failed to load image: ", props.src, e);
if (bypass && src === proxiedSrc) {
setSrc(props.src ?? "");
} else {
setLoadFailed(true);
}
}
};
if (!src || loadFailed) return missingImageElement ?? <div>Image not available</div>;
return (
<img
{...props}
ref={ref}
src={src}
width={size}
height={size}
className={className}
onError={e => {
if (props.onError) {
props.onError(e);
} else {
console.error("Failed to proxy image: ", props.src, e);
setLoadFailed(true);
}
}}
/>
<img {...props} ref={ref} src={src} width={size} height={size} className={className} onError={handleImageError} />
);
},
);

View File

@ -8,9 +8,9 @@ import { NostrLink, tryParseNostrLink } from "@snort/system";
import { useLocation, useNavigate } from "react-router-dom";
import { unixNow } from "@snort/shared";
import useTimelineFeed, { TimelineFeedOptions, TimelineSubject } from "../Feed/TimelineFeed";
import { fuzzySearch, FuzzySearchResult } from "@/index";
import ProfileImage from "@/Element/User/ProfileImage";
import { socialGraphInstance } from "@snort/system";
import fuzzySearch, { FuzzySearchResult } from "@/FuzzySearch";
const MAX_RESULTS = 3;

View File

@ -1,13 +1,13 @@
import Modal from "@/Element/Modal";
import { ThreadContext, ThreadContextWrapper } from "@/Hooks/useThreadContext";
import { ThreadContextWrapper } from "@/Hooks/useThreadContext";
import { Thread } from "@/Element/Event/Thread";
import { useContext } from "react";
import { SpotlightMedia } from "@/Element/Spotlight/SpotlightMedia";
import { NostrLink } from "@snort/system";
import { NostrLink, TaggedNostrEvent } from "@snort/system";
import getEventMedia from "@/Element/Event/getEventMedia";
interface SpotlightThreadModalProps {
thread: NostrLink;
thread?: NostrLink;
event?: TaggedNostrEvent;
className?: string;
onClose?: () => void;
onBack?: () => void;
@ -24,12 +24,23 @@ export function SpotlightThreadModal(props: SpotlightThreadModalProps) {
}
};
if (!props.thread && !props.event) {
throw new Error("SpotlightThreadModal requires either thread or event");
}
const link = props.event ? NostrLink.fromEvent(props.event) : props.thread;
return (
<Modal className={props.className} onClose={onClose} bodyClassName={"flex flex-1"}>
<ThreadContextWrapper link={props.thread}>
<ThreadContextWrapper link={link!}>
<div className="flex flex-row h-screen w-screen">
<div className="flex w-full md:w-2/3 items-center justify-center overflow-hidden" onClick={onClickBg}>
<SpotlightFromThread onClose={onClose} onNext={props.onNext} onPrev={props.onPrev} />
<SpotlightFromEvent
event={props.event || thread.root}
onClose={onClose}
onNext={props.onNext}
onPrev={props.onPrev}
/>
</div>
<div className="hidden md:flex w-1/3 min-w-[400px] flex-shrink-0 overflow-y-auto bg-bg-color">
<Thread onBack={onBack} disableSpotlight={true} />
@ -40,18 +51,15 @@ export function SpotlightThreadModal(props: SpotlightThreadModalProps) {
);
}
interface SpotlightFromThreadProps {
interface SpotlightFromEventProps {
event: TaggedNostrEvent;
onClose: () => void;
onNext?: () => void;
onPrev?: () => void;
}
function SpotlightFromThread({ onClose, onNext, onPrev }: SpotlightFromThreadProps) {
const thread = useContext(ThreadContext);
if (!thread?.root) return null;
const media = getEventMedia(thread.root);
if (media.length === 0) return;
function SpotlightFromEvent({ event, onClose, onNext, onPrev }: SpotlightFromEventProps) {
const media = getEventMedia(event);
return (
<SpotlightMedia
className="w-full"

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useState } from "react";
import { HexKey, NostrPrefix } from "@snort/system";
import { FormattedMessage } from "react-intl";
@ -9,6 +9,8 @@ import SemisolDevApi from "@/External/SemisolDev";
import useLogin from "@/Hooks/useLogin";
import { hexToBech32 } from "@/SnortUtils";
import { ErrorOrOffline } from "./ErrorOrOffline";
import useCachedFetch from "@/Hooks/useCachedFetch";
import TrendingUsers from "@/Element/Trending/TrendingUsers";
enum Provider {
NostrBand = 1,
@ -17,42 +19,45 @@ enum Provider {
export default function SuggestedProfiles() {
const login = useLogin(s => ({ publicKey: s.publicKey, follows: s.follows.item }));
const [userList, setUserList] = useState<HexKey[]>();
const [provider, setProvider] = useState(Provider.NostrBand);
const [error, setError] = useState<Error>();
async function loadSuggestedProfiles() {
if (!login.publicKey) return;
setUserList(undefined);
setError(undefined);
try {
switch (provider) {
case Provider.NostrBand: {
const api = new NostrBandApi();
const users = await api.sugguestedFollows(hexToBech32(NostrPrefix.PublicKey, login.publicKey));
const keys = users.profiles.map(a => a.pubkey);
setUserList(keys);
break;
}
case Provider.SemisolDev: {
const api = new SemisolDevApi();
const users = await api.sugguestedFollows(login.publicKey, login.follows);
const keys = users.recommendations.sort(a => a[1]).map(a => a[0]);
setUserList(keys);
break;
}
const getUrlAndKey = () => {
if (!login.publicKey) return { url: null, key: null };
switch (provider) {
case Provider.NostrBand: {
const api = new NostrBandApi();
const url = api.suggestedFollowsUrl(hexToBech32(NostrPrefix.PublicKey, login.publicKey));
return { url, key: `nostr-band-${url}` };
}
} catch (e) {
if (e instanceof Error) {
setError(e);
case Provider.SemisolDev: {
const api = new SemisolDevApi();
const url = api.suggestedFollowsUrl(login.publicKey, login.follows);
return { url, key: `semisol-dev-${url}` };
}
default:
return { url: null, key: null };
}
}
};
useEffect(() => {
loadSuggestedProfiles();
}, [login.publicKey, login.follows, provider]);
const { url, key } = getUrlAndKey();
const {
data: userList,
error,
isLoading,
} = useCachedFetch(url, key, data => {
switch (provider) {
case Provider.NostrBand:
return data.profiles.map(a => a.pubkey);
case Provider.SemisolDev:
return data.recommendations.sort(a => a[1]).map(a => a[0]);
default:
return [];
}
});
if (error) return <ErrorOrOffline error={error} onRetry={() => {}} />;
if (isLoading) return <PageSpinner />;
if (userList.length === 0) return <TrendingUsers title={""} />;
return (
<>
@ -63,9 +68,7 @@ export default function SuggestedProfiles() {
{/*<option value={Provider.SemisolDev}>semisol.dev</option>*/}
</select>
</div>
{error && <ErrorOrOffline error={error} onRetry={loadSuggestedProfiles} />}
{userList && <FollowListBase pubkeys={userList} showAbout={true} />}
{!userList && !error && <PageSpinner />}
<FollowListBase pubkeys={userList as HexKey[]} showAbout={true} />
</>
);
}

View File

@ -120,12 +120,12 @@ export default function Text({
return (
<>
{fragments.map(f => {
{fragments.map((f, index) => {
if (typeof f === "string") {
return f;
}
return <HighlightedText content={f.content} />;
return <HighlightedText key={index} content={f.content} />;
})}
</>
);

View File

@ -1,12 +1,12 @@
import { ReactNode, useEffect, useState } from "react";
import PageSpinner from "@/Element/PageSpinner";
import { ReactNode } from "react";
import NostrBandApi from "@/External/NostrBand";
import { ErrorOrOffline } from "../ErrorOrOffline";
import { HashTagHeader } from "@/Pages/HashTagsPage";
import { useLocale } from "@/IntlProvider";
import classNames from "classnames";
import { Link } from "react-router-dom";
import useCachedFetch from "@/Hooks/useCachedFetch";
import PageSpinner from "@/Element/PageSpinner";
export default function TrendingHashtags({
title,
@ -17,38 +17,28 @@ export default function TrendingHashtags({
count?: number;
short?: boolean;
}) {
const [hashtags, setHashtags] = useState<Array<{ hashtag: string; posts: number }>>();
const [error, setError] = useState<Error>();
const { lang } = useLocale();
const api = new NostrBandApi();
const trendingHashtagsUrl = api.trendingHashtagsUrl(lang);
const storageKey = `nostr-band-${trendingHashtagsUrl}`;
async function loadTrendingHashtags() {
const api = new NostrBandApi();
const rsp = await api.trendingHashtags(lang);
setHashtags(rsp.hashtags.slice(0, count)); // Limit the number of hashtags to the count
}
const {
data: hashtags,
error,
isLoading,
} = useCachedFetch(trendingHashtagsUrl, storageKey, data => data.hashtags.slice(0, count));
useEffect(() => {
loadTrendingHashtags().catch(e => {
if (e instanceof Error) {
setError(e);
}
});
}, []);
if (error) return <ErrorOrOffline error={error} onRetry={loadTrendingHashtags} className="p" />;
if (!hashtags) return <PageSpinner />;
if (error && !hashtags) return <ErrorOrOffline error={error} onRetry={() => {}} className="p" />;
if (isLoading) return <PageSpinner />;
return (
<>
{title}
{hashtags.map(a => {
if (short) {
// return just the hashtag (not HashTagHeader) and post count
return (
<div className="my-1 font-bold" key={a.hashtag}>
<Link to={`/t/${a.hashtag}`} key={a.hashtag}>
#{a.hashtag}
</Link>
<Link to={`/t/${a.hashtag}`}>#{a.hashtag}</Link>
</div>
);
} else {

View File

@ -1,5 +1,5 @@
import { useEffect, useState } from "react";
import { NostrEvent, NostrLink, TaggedNostrEvent } from "@snort/system";
import { useState } from "react";
import { EventExt, NostrLink, TaggedNostrEvent } from "@snort/system";
import { useReactions } from "@snort/system-react";
import PageSpinner from "@/Element/PageSpinner";
@ -14,66 +14,79 @@ import { DisplayAs, DisplayAsSelector } from "@/Element/Feed/DisplayAsSelector";
import ImageGridItem from "@/Element/Feed/ImageGridItem";
import { SpotlightThreadModal } from "@/Element/Spotlight/SpotlightThreadModal";
import useLogin from "@/Hooks/useLogin";
import useCachedFetch from "@/Hooks/useCachedFetch";
import { System } from "@/index";
export default function TrendingNotes({ count = Infinity, small = false }) {
const api = new NostrBandApi();
const { lang } = useLocale();
const trendingNotesUrl = api.trendingNotesUrl(lang);
const storageKey = `nostr-band-${trendingNotesUrl}`;
const {
data: trendingNotesData,
isLoading,
error,
} = useCachedFetch(trendingNotesUrl, storageKey, data => {
return data.notes.map(a => {
const ev = a.event;
const id = EventExt.createId(ev);
if (!System.QueryOptimizer.schnorrVerify(id, ev.sig, ev.pubkey)) {
console.error(`Event with invalid sig\n\n${ev}\n\nfrom ${trendingNotesUrl}`);
return;
}
System.HandleEvent(ev);
return ev;
});
});
const login = useLogin();
const displayAsInitial = small ? "list" : login.feedDisplayAs ?? "list";
// Added count prop with a default value
const [posts, setPosts] = useState<Array<NostrEvent>>();
const [error, setError] = useState<Error>();
const { lang } = useLocale();
const { isEventMuted } = useModeration();
const [displayAs, setDisplayAs] = useState<DisplayAs>(displayAsInitial);
const related = useReactions("trending", posts?.map(a => NostrLink.fromEvent(a)) ?? [], undefined, true);
const { isEventMuted } = useModeration();
const related = useReactions("trending", trendingNotesData?.map(a => NostrLink.fromEvent(a)) ?? [], undefined, true);
const [modalThread, setModalThread] = useState<NostrLink | undefined>(undefined);
async function loadTrendingNotes() {
const api = new NostrBandApi();
const trending = await api.trendingNotes(lang);
setPosts(trending.notes.map(a => a.event));
}
if (error && !trendingNotesData) return <ErrorOrOffline error={error} className="p" />;
if (isLoading) return <PageSpinner />;
useEffect(() => {
loadTrendingNotes().catch(e => {
if (e instanceof Error) {
setError(e);
}
});
}, []);
if (error) return <ErrorOrOffline error={error} onRetry={loadTrendingNotes} className="p" />;
if (!posts) return <PageSpinner />;
// if small, render less stuff
const options = {
showFooter: !small,
showReactionsLink: !small,
showMedia: !small,
longFormPreview: !small,
truncate: small,
showContextMenu: !small,
};
const filteredAndLimitedPosts = () => {
return posts.filter(a => !isEventMuted(a)).slice(0, count);
};
const filteredAndLimitedPosts = trendingNotesData
? trendingNotesData.filter(a => !isEventMuted(a)).slice(0, count)
: [];
const renderGrid = () => {
return (
<div className="grid grid-cols-3 gap-px md:gap-1">
{filteredAndLimitedPosts().map(e => (
<ImageGridItem event={e as TaggedNostrEvent} onClick={() => setModalThread(NostrLink.fromEvent(e))} />
{filteredAndLimitedPosts.map(e => (
<ImageGridItem
key={e.id}
event={e as TaggedNostrEvent}
onClick={() => setModalThread(NostrLink.fromEvent(e))}
/>
))}
</div>
);
};
const renderList = () => {
return filteredAndLimitedPosts().map(e =>
return filteredAndLimitedPosts.map(e =>
small ? (
<ShortNote event={e as TaggedNostrEvent} />
<ShortNote key={e.id} event={e as TaggedNostrEvent} />
) : (
<Note data={e as TaggedNostrEvent} related={related?.data ?? []} depth={0} options={options} />
<Note
key={e.id}
data={e as TaggedNostrEvent}
related={related?.data ?? []}
depth={0}
options={{
showFooter: !small,
showReactionsLink: !small,
showMedia: !small,
longFormPreview: !small,
truncate: small,
showContextMenu: !small,
}}
/>
),
);
};

View File

@ -1,32 +1,29 @@
import { ReactNode, useEffect, useState } from "react";
import { ReactNode } from "react";
import { HexKey } from "@snort/system";
import FollowListBase from "@/Element/User/FollowListBase";
import PageSpinner from "@/Element/PageSpinner";
import NostrBandApi from "@/External/NostrBand";
import { ErrorOrOffline } from "../ErrorOrOffline";
import useCachedFetch from "@/Hooks/useCachedFetch";
export default function TrendingUsers({ title, count = Infinity }: { title?: ReactNode; count?: number }) {
const [userList, setUserList] = useState<HexKey[]>();
const [error, setError] = useState<Error>();
const api = new NostrBandApi();
const trendingProfilesUrl = api.trendingProfilesUrl();
const storageKey = `nostr-band-${trendingProfilesUrl}`;
async function loadTrendingUsers() {
const api = new NostrBandApi();
const users = await api.trendingProfiles();
const keys = users.profiles.map(a => a.pubkey).slice(0, count); // Limit the user list to the count
setUserList(keys);
const {
data: trendingUsersData,
isLoading,
error,
} = useCachedFetch(trendingProfilesUrl, storageKey, data => data.profiles.map(a => a.pubkey));
if (error && !trendingUsersData) {
return <ErrorOrOffline error={error} onRetry={() => {}} className="p" />;
}
useEffect(() => {
loadTrendingUsers().catch(e => {
if (e instanceof Error) {
setError(e);
}
});
}, []);
if (isLoading) {
return <PageSpinner />;
}
if (error) return <ErrorOrOffline error={error} onRetry={loadTrendingUsers} className="p" />;
if (!userList) return <PageSpinner />;
return <FollowListBase pubkeys={userList} showAbout={true} title={title} />;
return <FollowListBase pubkeys={trendingUsersData.slice(0, count) as HexKey[]} showAbout={true} title={title} />;
}

View File

@ -1,6 +1,6 @@
import "./Avatar.css";
import { ReactNode, useEffect, useState } from "react";
import { ReactNode, useMemo } from "react";
import type { UserMetadata } from "@snort/system";
import classNames from "classnames";
@ -31,10 +31,8 @@ const Avatar = ({
className,
showTitle = true,
}: AvatarProps) => {
const [url, setUrl] = useState("");
useEffect(() => {
setUrl(image ?? user?.picture ?? defaultAvatar(pubkey));
const url = useMemo(() => {
return image ?? user?.picture ?? defaultAvatar(pubkey);
}, [user, image, pubkey]);
const s = size ?? 120;

View File

@ -32,13 +32,14 @@ export default function FollowListBase({
profileActions,
}: FollowListBaseProps) {
const { publisher, system } = useEventPublisher();
const { id, follows } = useLogin(s => ({ id: s.id, follows: s.follows }));
const login = useLogin();
async function followAll() {
if (publisher) {
const newFollows = dedupe([...pubkeys, ...login.follows.item]);
const newFollows = dedupe([...pubkeys, ...follows.item]);
const ev = await publisher.contactList(newFollows.map(a => ["p", a]));
setFollows(login, newFollows, ev.created_at);
setFollows(id, newFollows, ev.created_at);
await system.BroadcastEvent(ev);
await FollowsFeed.backFill(system, pubkeys);
}

View File

@ -1,83 +1,20 @@
import { throwIfOffline } from "@snort/shared";
import { NostrEvent } from "@snort/system";
export interface TrendingUser {
pubkey: string;
}
export interface TrendingUserResponse {
profiles: Array<TrendingUser>;
}
export interface TrendingNote {
event: NostrEvent;
author: NostrEvent; // kind0 event
}
export interface TrendingNoteResponse {
notes: Array<TrendingNote>;
}
export interface TrendingHashtagsResponse {
hashtags: Array<{
hashtag: string;
posts: number;
}>;
}
export interface SuggestedFollow {
pubkey: string;
}
export interface SuggestedFollowsResponse {
profiles: Array<SuggestedFollow>;
}
export class NostrBandError extends Error {
body: string;
statusCode: number;
constructor(message: string, body: string, status: number) {
super(message);
this.body = body;
this.statusCode = status;
}
}
export default class NostrBandApi {
readonly #url = "https://api.nostr.band";
readonly #supportedLangs = ["en", "de", "ja", "zh", "th", "pt", "es", "fr"];
async trendingProfiles() {
return await this.#json<TrendingUserResponse>("GET", "/v0/trending/profiles");
trendingProfilesUrl() {
return `${this.#url}/v0/trending/profiles`;
}
async trendingNotes(lang?: string) {
if (lang && this.#supportedLangs.includes(lang)) {
return await this.#json<TrendingNoteResponse>("GET", `/v0/trending/notes?lang=${lang}`);
}
return await this.#json<TrendingNoteResponse>("GET", "/v0/trending/notes");
trendingNotesUrl(lang?: string) {
return `${this.#url}/v0/trending/notes${lang && this.#supportedLangs.includes(lang) ? `?lang=${lang}` : ""}`;
}
async sugguestedFollows(pubkey: string) {
return await this.#json<SuggestedFollowsResponse>("GET", `/v0/suggested/profiles/${pubkey}`);
suggestedFollowsUrl(pubkey: string) {
return `${this.#url}/v0/suggested/profiles/${pubkey}`;
}
async trendingHashtags(lang?: string) {
if (lang && this.#supportedLangs.includes(lang)) {
return await this.#json<TrendingHashtagsResponse>("GET", `/v0/trending/hashtags?lang=${lang}`);
}
return await this.#json<TrendingHashtagsResponse>("GET", "/v0/trending/hashtags");
}
async #json<T>(method: string, path: string) {
throwIfOffline();
const res = await fetch(`${this.#url}${path}`, {
method: method ?? "GET",
});
if (res.ok) {
return (await res.json()) as T;
} else {
throw new NostrBandError("Failed to load content from nostr.band", await res.text(), res.status);
}
trendingHashtagsUrl(lang?: string) {
return `${this.#url}/v0/trending/hashtags${lang && this.#supportedLangs.includes(lang) ? `?lang=${lang}` : ""}`;
}
}

View File

@ -1,46 +1,8 @@
import { throwIfOffline } from "@snort/shared";
export interface RecommendedProfilesResponse {
quality: number;
recommendations: Array<[pubkey: string, score: number]>;
}
export class SemisolDevApiError extends Error {
body: string;
statusCode: number;
constructor(message: string, body: string, status: number) {
super(message);
this.body = body;
this.statusCode = status;
}
}
export default class SemisolDevApi {
readonly #url = "https://api.semisol.dev";
async sugguestedFollows(pubkey: string, follows: Array<string>) {
return await this.#json<RecommendedProfilesResponse>("POST", "/nosgraph/v1/recommend", {
pubkey,
exclude: [],
following: follows,
});
}
async #json<T>(method: string, path: string, body?: unknown) {
throwIfOffline();
const url = `${this.#url}${path}`;
const res = await fetch(url, {
method: method ?? "GET",
body: body ? JSON.stringify(body) : undefined,
headers: {
...(body ? { "content-type": "application/json" } : {}),
},
});
if (res.ok) {
return (await res.json()) as T;
} else {
throw new SemisolDevApiError(`Failed to load content from ${url}`, await res.text(), res.status);
}
suggestedFollowsUrl(pubkey: string, follows: Array<string>) {
const query = new URLSearchParams({ pubkey, follows: JSON.stringify(follows) });
return `${this.#url}/nosgraph/v1/recommend?${query.toString()}`;
}
}

View File

@ -104,7 +104,7 @@ export default function useLoginFeed() {
const contactList = getNewest(loginFeed.data.filter(a => a.kind === EventKind.ContactList));
if (contactList) {
const pTags = contactList.tags.filter(a => a[0] === "p").map(a => a[1]);
setFollows(login, pTags, contactList.created_at * 1000);
setFollows(login.id, pTags, contactList.created_at * 1000);
FollowsFeed.backFillIfMissing(system, pTags);
}

View File

@ -1,43 +1,41 @@
import Fuse from "fuse.js";
import { socialGraphInstance } from "@snort/system";
import { System } from ".";
export type FuzzySearchResult = {
pubkey: string;
name?: string;
username?: string;
display_name?: string;
nip05?: string;
};
export const fuzzySearch = new Fuse<FuzzySearchResult>([], {
keys: ["name", "username", { name: "nip05", weight: 0.5 }],
const fuzzySearch = new Fuse<FuzzySearchResult>([], {
keys: ["name", "display_name", { name: "nip05", weight: 0.5 }],
threshold: 0.3,
// sortFn here?
});
const profileTimestamps = new Map<string, number>(); // is this somewhere in cache?
System.on("event", ev => {
if (ev.kind === 0) {
const existing = profileTimestamps.get(ev.pubkey);
if (existing) {
if (existing > ev.created_at) {
return;
}
fuzzySearch.remove(doc => doc.pubkey === ev.pubkey);
}
profileTimestamps.set(ev.pubkey, ev.created_at);
try {
const data = JSON.parse(ev.content);
if (ev.pubkey && (data.name || data.username || data.nip05)) {
data.pubkey = ev.pubkey;
fuzzySearch.add(data);
}
} catch (e) {
console.error(e);
}
export const addEventToFuzzySearch = ev => {
if (ev.kind !== 0) {
return;
}
if (ev.kind === 3) {
socialGraphInstance.handleFollowEvent(ev);
const existing = profileTimestamps.get(ev.pubkey);
if (existing) {
if (existing > ev.created_at) {
return;
}
fuzzySearch.remove(doc => doc.pubkey === ev.pubkey);
}
});
profileTimestamps.set(ev.pubkey, ev.created_at);
try {
const data = JSON.parse(ev.content);
if (ev.pubkey && (data.name || data.display_name || data.nip05)) {
data.pubkey = ev.pubkey;
fuzzySearch.add(data);
}
} catch (e) {
console.error(e);
}
};
export default fuzzySearch;

View File

@ -0,0 +1,46 @@
import { useEffect, useMemo, useState } from "react";
const useCachedFetch = (url, storageKey, dataProcessor = data => data) => {
const cachedData = useMemo(() => {
const cached = localStorage.getItem(storageKey);
return cached ? JSON.parse(cached) : null;
}, [storageKey]);
const initialData = cachedData ? cachedData.data : null;
const [data, setData] = useState(initialData);
const [isLoading, setIsLoading] = useState(!cachedData);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
setError(null);
try {
const res = await fetch(url);
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
const fetchedData = await res.json();
const processedData = dataProcessor(fetchedData);
setData(processedData);
localStorage.setItem(storageKey, JSON.stringify({ data: processedData, timestamp: new Date().getTime() }));
} catch (e) {
setError(e);
if (cachedData?.data) {
setData(cachedData.data);
}
} finally {
setIsLoading(false);
}
};
if (!cachedData || (new Date().getTime() - cachedData.timestamp) / 1000 / 60 >= 15) {
fetchData();
}
}, [url, storageKey]);
return { data, isLoading, error };
};
export default useCachedFetch;

View File

@ -0,0 +1,34 @@
import { bech32ToHex } from "@snort/shared";
import { EventKind, ReplaceableNoteStore, RequestBuilder } from "@snort/system";
import { useRequestBuilder } from "@snort/system-react";
import { useMemo } from "react";
// Snort backend publishes rates
const SnortPubkey = "npub1sn0rtcjcf543gj4wsg7fa59s700d5ztys5ctj0g69g2x6802npjqhjjtws";
export function useRates(symbol: string, leaveOpen = true) {
const sub = useMemo(() => {
const rb = new RequestBuilder(`rates:${symbol}`);
rb.withOptions({
leaveOpen,
});
rb.withFilter()
.kinds([1009 as EventKind])
.authors([bech32ToHex(SnortPubkey)])
.tag("d", [symbol])
.limit(1);
return rb;
}, [symbol]);
const data = useRequestBuilder(ReplaceableNoteStore, sub);
const tag = data?.data?.tags.find(a => a[0] === "d" && a[1] === symbol);
if (!tag) return undefined;
return {
time: data.data?.created_at,
ask: Number(tag[2]),
bid: Number(tag[3]),
low: Number(tag[4]),
hight: Number(tag[5]),
};
}

View File

@ -0,0 +1,75 @@
export default function AlbyIcon(props: { size?: number }) {
return (
<svg width={props.size ?? 400} height={props.size ?? 578} viewBox="0 0 400 578" fill="none">
<path
opacity="0.1"
d="M201.283 577.511C255.405 577.511 299.281 569.411 299.281 559.419C299.281 549.427 255.405 541.327 201.283 541.327C147.16 541.327 103.285 549.427 103.285 559.419C103.285 569.411 147.16 577.511 201.283 577.511Z"
fill="black"
/>
<path
d="M295.75 471.344C346.377 471.344 369.42 359.242 369.42 316.736C369.42 283.606 346.56 263.528 316.507 263.528C286.641 263.528 262.394 276.371 262.093 292.275C262.092 334.246 254.705 471.344 295.75 471.344Z"
fill="white"
stroke="black"
stroke-width="15.0766"
/>
<path
d="M110.837 471.344C60.2098 471.344 37.1665 359.242 37.1665 316.736C37.1665 283.606 60.0269 263.528 90.0803 263.528C119.946 263.528 144.193 276.371 144.494 292.275C144.495 334.246 151.882 471.344 110.837 471.344Z"
fill="white"
stroke="black"
stroke-width="15.0766"
/>
<path
d="M68.8309 303.262L68.8307 303.26C68.7764 302.741 68.8817 302.44 68.9894 302.244C69.1165 302.012 69.3578 301.736 69.7632 301.506C70.6022 301.029 71.7772 300.943 72.8713 301.582C110.474 323.624 153.847 336.26 201.001 336.26C248.164 336.26 292.34 323.379 330.185 300.953C331.272 300.308 332.445 300.388 333.287 300.862C333.694 301.091 333.937 301.366 334.066 301.599C334.175 301.796 334.282 302.098 334.229 302.618C328.375 360.632 296.907 408.595 254.611 430.672C240.642 437.965 231.035 450.634 222.598 461.761C222.447 461.961 222.296 462.16 222.146 462.358L222.144 462.36C215.287 471.406 209.081 479.507 201.496 485.476C193.912 479.507 187.705 471.406 180.848 462.36L180.847 462.358C180.697 462.16 180.546 461.961 180.395 461.761C171.958 450.634 162.352 437.965 148.382 430.672C106.247 408.68 74.8589 360.995 68.8309 303.262Z"
fill="#FFDF6F"
stroke="black"
stroke-width="15"
/>
<path
d="M201.786 346.338C275.06 346.338 334.46 326.538 334.46 302.113C334.46 277.688 275.06 257.888 201.786 257.888C128.512 257.888 69.1118 277.688 69.1118 302.113C69.1118 326.538 128.512 346.338 201.786 346.338Z"
fill="black"
stroke="black"
stroke-width="15.0766"
/>
<path
d="M95.2446 376.491C95.2446 376.491 160.685 398.603 202.791 398.603C244.896 398.603 310.337 376.491 310.337 376.491"
stroke="black"
stroke-width="15.0766"
stroke-linecap="round"
/>
<path
d="M77 143C60.4315 143 47 129.569 47 113C47 96.4315 60.4315 83 77 83C93.5685 83 107 96.4315 107 113C107 129.569 93.5685 143 77 143Z"
fill="black"
/>
<path d="M72 108.5L128 164.5" stroke="black" stroke-width="15" />
<path
d="M322 143C338.569 143 352 129.569 352 113C352 96.4315 338.569 83 322 83C305.431 83 292 96.4315 292 113C292 129.569 305.431 143 322 143Z"
fill="black"
/>
<path d="M327.5 108.5L271.5 164.5" stroke="black" stroke-width="15" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M85.5155 292.019C69.3466 284.321 59.9364 267.036 63.0886 249.407C76.6177 173.747 133 117 200.5 117C268.163 117 324.655 174.023 338.009 249.958C341.115 267.618 331.628 284.895 315.404 292.53C280.687 308.868 241.91 318 201 318C159.665 318 120.507 308.677 85.5155 292.019Z"
fill="#FFDF6F"
/>
<path
d="M70.4715 250.728C83.5443 177.62 137.582 124.5 200.5 124.5V109.5C128.418 109.5 69.6912 169.875 55.7057 248.087L70.4715 250.728ZM200.5 124.5C263.569 124.5 317.718 177.879 330.622 251.257L345.396 248.659C331.592 170.166 272.758 109.5 200.5 109.5V124.5ZM312.21 285.744C278.472 301.621 240.783 310.5 201 310.5V325.5C243.037 325.5 282.902 316.114 318.597 299.317L312.21 285.744ZM201 310.5C160.804 310.5 122.745 301.436 88.7393 285.247L82.2918 298.791C118.269 315.918 158.526 325.5 201 325.5V310.5ZM330.622 251.257C333.112 265.416 325.531 279.476 312.21 285.744L318.597 299.317C337.725 290.315 349.117 269.82 345.396 248.659L330.622 251.257ZM55.7057 248.087C51.9285 269.211 63.2298 289.716 82.2918 298.791L88.7393 285.247C75.4633 278.927 67.9443 264.86 70.4715 250.728L55.7057 248.087Z"
fill="black"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M114.365 273.209C101.35 267.908 93.6293 254.06 98.1392 240.75C112.047 199.704 152.618 170 200.5 170C248.382 170 288.953 199.704 302.861 240.75C307.371 254.06 299.65 267.908 286.635 273.209C260.053 284.035 230.973 290 200.5 290C170.027 290 140.947 284.035 114.365 273.209Z"
fill="black"
/>
<path
d="M235 254C248.807 254 260 245.046 260 234C260 222.954 248.807 214 235 214C221.193 214 210 222.954 210 234C210 245.046 221.193 254 235 254Z"
fill="white"
/>
<path
d="M163.432 254.012C177.239 254.012 188.432 245.058 188.432 234.012C188.432 222.966 177.239 214.012 163.432 214.012C149.625 214.012 138.432 222.966 138.432 234.012C138.432 245.058 149.625 254.012 163.432 254.012Z"
fill="white"
/>
</svg>
);
}

View File

@ -0,0 +1,48 @@
export default function CashuIcon(props: { size?: number }) {
return (
<svg width={props.size ?? 135} height={props.size ?? 153} viewBox="0 0 135 153">
<path
d="m 18,0 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 V 8 7 6 5 4 3 2 1 0 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 z m 0,9 H 17 16 15 14 13 12 11 10 9 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 V 17 16 15 14 13 12 11 10 Z M 9,18 H 8 7 6 5 4 3 2 1 0 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 H 1 2 3 4 5 6 7 8 9 V 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 Z M 0,53 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 H 8 7 6 V 60 59 58 57 H 5 4 3 V 56 55 54 53 H 2 1 Z m 9,55 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z m 9,18 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z m 9,9 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z m 9,9 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 z m 81,0 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v 1 1 1 1 1 1 1 1 z"
style={{
fill: "#b89563",
}}
/>
<path
d="m 36,0 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 V 8 7 6 5 4 3 2 1 0 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 z m 45,9 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 V 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 h -1 -1 -1 -1 -1 -1 -1 -1 z m 0,27 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 V 44 43 42 41 40 39 38 37 Z M 63,64 v 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z m 9,8 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z m 9,18 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 z m 36,9 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z m 9,9 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 z"
style={{
fill: "#e2d2b3",
}}
/>
<path
d="m 18,9 v 1 1 1 1 1 1 1 1 1 H 17 16 15 14 13 12 11 10 9 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h 1 1 1 1 1 1 1 1 1 V 17 16 15 14 13 12 11 10 9 H 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 Z M 9,61 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 z"
style={{
fill: "#c5a77f",
}}
/>
<path
d="m 36,9 v 1 1 1 1 1 1 1 1 1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h 1 1 1 1 1 1 1 1 1 V 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 z m 9,40 v 1 1 1 1 1 1 h -1 -1 v 1 1 1 h -1 -1 -1 v 1 1 1 h -1 -1 -1 -1 v 1 1 1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 v -1 -1 -1 h -1 -1 -1 v -1 -1 -1 -1 h -1 -1 -1 -1 v -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 z"
style={{
fill: "#dbbf98",
}}
/>
<path
d="m 0,45 v 1 1 1 1 1 1 1 1 h 1 1 1 v 1 1 1 1 h 1 1 1 v 1 1 1 1 h 1 1 1 1 v 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 h 1 1 1 1 v -1 -1 -1 h 1 1 1 v -1 -1 -1 h 1 1 v -1 -1 -1 -1 -1 -1 h 1 1 1 1 1 1 v 1 1 1 1 1 1 1 1 h 1 1 1 1 v 1 1 1 1 h 1 1 1 v 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 h 1 1 1 1 v -1 -1 -1 -1 h 1 1 1 1 v -1 -1 -1 -1 h 1 1 1 1 V 52 51 50 49 48 47 46 45 H 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 80 79 78 77 76 75 74 73 72 71 70 69 68 67 66 65 64 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 Z m 6,4 h 1 1 1 1 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h 1 1 1 1 v 1 1 1 1 h 1 1 1 1 v 1 1 1 1 h 1 1 1 1 v 1 1 1 1 h -1 -1 -1 -1 v -1 -1 -1 -1 h -1 -1 -1 -1 v 1 1 1 1 H 17 16 15 14 V 60 59 58 57 H 13 12 11 10 V 56 55 54 53 H 9 8 7 6 v -1 -1 -1 z m 8,8 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 -1 v 1 1 1 z m 44,-8 h 1 1 1 1 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h 1 1 1 1 v 1 1 1 1 h 1 1 1 1 v 1 1 1 1 h 1 1 1 1 v 1 1 1 1 h -1 -1 -1 -1 v -1 -1 -1 -1 h -1 -1 -1 -1 v 1 1 1 1 h -1 -1 -1 -1 v -1 -1 -1 -1 h -1 -1 -1 -1 v -1 -1 -1 -1 h -1 -1 -1 -1 v -1 -1 -1 z m 8,8 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 -1 v 1 1 1 z"
style={{
fill: "#000000",
}}
/>
<path
d="m 6,49 v 1 1 1 1 h 1 1 1 1 V 52 51 50 49 H 9 8 7 Z m 4,4 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m 4,0 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 -1 v 1 1 1 z m 4,0 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m 4,4 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m -4,0 h -1 -1 -1 -1 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 z m 40,-8 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m 4,4 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m 4,0 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 -1 v 1 1 1 z m 4,0 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m 4,4 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 -1 h -1 -1 -1 z m -4,0 h -1 -1 -1 -1 v 1 1 1 1 h 1 1 1 1 v -1 -1 -1 z"
style={{
fill: "#ffffff",
}}
/>
<path
d="m 99,99 v 1 1 1 1 1 1 1 1 1 h 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 v -1 -1 -1 -1 -1 -1 -1 -1 -1 h -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 z"
style={{
fill: "#f7f8f3",
}}
/>
</svg>
);
}

View File

@ -177,13 +177,15 @@ export function setBlocked(state: LoginSession, blocked: Array<string>, ts: numb
LoginStore.updateSession(state);
}
export function setFollows(state: LoginSession, follows: Array<string>, ts: number) {
if (state.follows.timestamp >= ts) {
return;
export function setFollows(id: string, follows: Array<string>, ts: number) {
const session = LoginStore.get(id);
if (session) {
if (ts > session.follows.timestamp) {
session.follows.item = follows;
session.follows.timestamp = ts;
LoginStore.updateSession(session);
}
}
state.follows.item = follows;
state.follows.timestamp = ts;
LoginStore.updateSession(state);
}
export function setPinned(state: LoginSession, pinned: Array<string>, ts: number) {

View File

@ -96,6 +96,11 @@ export interface UserPreferences {
* Auto-translate when available
*/
autoTranslate?: boolean;
/**
* Low Data mode - images/reactions wont be loaded
*/
lowDataMode: boolean;
}
export const DefaultPreferences = {
@ -116,4 +121,5 @@ export const DefaultPreferences = {
showStatus: true,
checkSigs: true,
autoTranslate: true,
lowDataMode: false
} as UserPreferences;

View File

@ -71,7 +71,7 @@ export function SnortDeckLayout() {
id="IOu4Xh"
values={{
app: CONFIG.appNameCapitalized,
tier: mapPlanName(CONFIG.deckSubKind),
tier: mapPlanName(CONFIG.deckSubKind ?? -1),
}}
/>
</div>
@ -122,7 +122,11 @@ export function SnortDeckLayout() {
)}
{deckState.article && (
<>
<Modal onClose={() => setDeckState({})} className="long-form" onClick={() => setDeckState({})}>
<Modal
id="deck-article"
onClose={() => setDeckState({})}
className="long-form"
onClick={() => setDeckState({})}>
<div onClick={e => e.stopPropagation()}>
<LongFormText ev={deckState.article} isPreview={false} related={[]} />
</div>
@ -140,11 +144,11 @@ function NotesCol() {
return (
<div>
<div className="deck-col-header flex">
<div className="flex f-1 g8">
<div className="flex flex-1 g8">
<Icon name="rows-01" size={24} />
<FormattedMessage defaultMessage="Notes" id="7+Domh" />
</div>
<div className="f-1">
<div className="flex-1">
<RootTabs base="/deck" />
</div>
</div>

View File

@ -132,6 +132,11 @@ const DonatePage = () => {
}}
/>
</p>
<p>
<a href="https://t.me/irismessenger" target="_blank" rel="noreferrer" className="underline">
Telegram
</a>
</p>
<div className="flex flex-col g12">
<div className="b br p">
<div className="flex items-center justify-between">

View File

@ -49,8 +49,8 @@ const Footer = () => {
return (
<footer className="md:hidden fixed bottom-0 z-10 w-full bg-base-200 pb-safe-area bg-bg-color">
<div className="flex">
{MENU_ITEMS.map(item => (
<FooterNavItem item={item} readonly={readonly} />
{MENU_ITEMS.map((item, index) => (
<FooterNavItem key={index} item={item} readonly={readonly} />
))}
{publicKey && (
<ProfileLink

View File

@ -1,5 +1,5 @@
import { useLocation, useNavigate } from "react-router-dom";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import React, { useCallback, useMemo } from "react";
import classNames from "classnames";
import { LogoHeader } from "@/Pages/Layout/LogoHeader";
import { rootTabItems, RootTabs } from "@/Element/Feed/RootTabs";
@ -16,7 +16,15 @@ export function Header() {
const navigate = useNavigate();
const location = useLocation();
const pageName = decodeURIComponent(location.pathname.split("/")[1]);
const [nostrLink, setNostrLink] = useState<NostrLink | undefined>();
const nostrLink = useMemo(() => {
try {
return parseNostrLink(pageName);
} catch (e) {
return undefined;
}
}, [pageName]);
const { publicKey, tags } = useLogin();
const isRootTab = useMemo(() => {
@ -27,14 +35,6 @@ export function Header() {
window.scrollTo({ top: 0, behavior: "instant" });
}, []);
useEffect(() => {
try {
setNostrLink(parseNostrLink(pageName));
} catch (e) {
setNostrLink(undefined);
}
}, [pageName]);
const handleBackButtonClick = () => {
const idx = window.history.state?.idx;
if (idx === undefined || idx > 0) {

View File

@ -1,18 +1,21 @@
import { LogoHeader } from "./LogoHeader";
import { useNavigate } from "react-router-dom";
import { Link, useNavigate } from "react-router-dom";
import Icon from "@/Icons/Icon";
import { ProfileLink } from "../../Element/User/ProfileLink";
import Avatar from "../../Element/User/Avatar";
import useLogin from "../../Hooks/useLogin";
import { useUserProfile } from "@snort/system-react";
import { NoteCreatorButton } from "../../Element/Event/Create/NoteCreatorButton";
import { FormattedMessage, useIntl } from "react-intl";
import { FormattedMessage, FormattedNumber, useIntl } from "react-intl";
import classNames from "classnames";
import { getCurrentSubscription } from "@/Subscription";
import { HasNotificationsMarker } from "@/Pages/Layout/HasNotificationsMarker";
import NavLink from "@/Element/Button/NavLink";
import { subscribeToNotifications } from "@/Notifications";
import useEventPublisher from "@/Hooks/useEventPublisher";
import { Sats, useWallet } from "@/Wallet";
import { useEffect, useState } from "react";
import { useRates } from "@/Hooks/useRates";
const MENU_ITEMS = [
{
@ -72,6 +75,44 @@ const getNavLinkClass = (isActive: boolean, narrow: boolean) => {
});
};
const WalletBalance = () => {
const [balance, setBalance] = useState<Sats>();
const wallet = useWallet();
const rates = useRates("BTCUSD");
useEffect(() => {
setBalance(undefined);
if (wallet.wallet && wallet.wallet.canGetBalance()) {
wallet.wallet.getBalance().then(setBalance);
}
}, [wallet]);
return (
<div className="w-max flex flex-col max-xl:hidden">
<div className="grow flex items-center justify-between">
<div className="flex gap-1 items-center">
<Icon name="sats" size={24} />
<FormattedNumber value={balance ?? 0} />
</div>
<Link to="/wallet">
<Icon name="dots" className="text-secondary" />
</Link>
</div>
<div className="text-secondary text-sm">
<FormattedMessage
defaultMessage="~{amount}"
id="3QwfJR"
values={{
amount: (
<FormattedNumber style="currency" currency="USD" value={(rates?.ask ?? 0) * (balance ?? 0) * 1e-8} />
),
}}
/>
</div>
</div>
);
};
export default function NavSidebar({ narrow = false }) {
const { publicKey, subscriptions, readonly } = useLogin(s => ({
publicKey: s.publicKey,
@ -106,6 +147,7 @@ export default function NavSidebar({ narrow = false }) {
{ "xl:items-start": !narrow, "xl:gap-2": !narrow },
"gap-1 flex flex-col items-center text-lg font-bold",
)}>
<WalletBalance narrow={narrow} />
{MENU_ITEMS.filter(a => {
if ((CONFIG.hideFromNavbar ?? []).includes(a.link)) {
return false;
@ -122,7 +164,7 @@ export default function NavSidebar({ narrow = false }) {
return "";
}
const onClick = () => {
if (item.label === "Notifications") {
if (item.label === "Notifications" && publisher) {
subscribeToNotifications(publisher);
}
};

View File

@ -1,8 +1,8 @@
import { MetadataCache } from "@snort/system";
import { ChatParticipant } from "@/chat";
import NoteToSelf from "../User/NoteToSelf";
import ProfileImage from "../User/ProfileImage";
import NoteToSelf from "../../Element/User/NoteToSelf";
import ProfileImage from "../../Element/User/ProfileImage";
import useLogin from "@/Hooks/useLogin";
export function ChatParticipantProfile({ participant }: { participant: ChatParticipant }) {

View File

@ -9,9 +9,9 @@ import NoteTime from "@/Element/Event/NoteTime";
import Text from "@/Element/Text";
import useLogin from "@/Hooks/useLogin";
import { Chat, ChatMessage, ChatType, setLastReadIn } from "@/chat";
import ProfileImage from "../User/ProfileImage";
import ProfileImage from "../../Element/User/ProfileImage";
import messages from "../messages";
import messages from "../../Element/messages";
export interface DMProps {
chat: Chat;

View File

@ -1,8 +1,8 @@
import { useEffect, useMemo, useRef } from "react";
import ProfileImage from "@/Element/User/ProfileImage";
import DM from "@/Element/Chat/DM";
import DM from "@/Pages/Messages/DM";
import useLogin from "@/Hooks/useLogin";
import WriteMessage from "@/Element/Chat/WriteMessage";
import WriteMessage from "@/Pages/Messages/WriteMessage";
import { Chat, createEmptyChatObject, useChatSystem } from "@/chat";
import { FormattedMessage } from "react-intl";
import { ChatParticipantProfile } from "./ChatParticipant";

View File

@ -9,9 +9,9 @@ import NoteToSelf from "@/Element/User/NoteToSelf";
import useLogin from "@/Hooks/useLogin";
import usePageWidth from "@/Hooks/usePageWidth";
import NoteTime from "@/Element/Event/NoteTime";
import DmWindow from "@/Element/Chat/DmWindow";
import DmWindow from "@/Pages/Messages/DmWindow";
import { Chat, ChatType, useChatSystem } from "@/chat";
import { ChatParticipantProfile } from "@/Element/Chat/ChatParticipant";
import { ChatParticipantProfile } from "@/Pages/Messages/ChatParticipant";
import classNames from "classnames";
import NewChatWindow from "@/Pages/Messages/NewChatWindow";

View File

@ -1,6 +1,6 @@
import { useState } from "react";
import useEventPublisher from "@/Hooks/useEventPublisher";
import Textarea from "../Textarea";
import Textarea from "../../Element/Textarea";
import { Chat } from "@/chat";
import { AsyncIcon } from "@/Element/Button/AsyncIcon";

View File

@ -1,55 +1,60 @@
import { NostrPrefix, tryParseNostrLink } from "@snort/system";
import { useEffect, useState } from "react";
import { FormattedMessage } from "react-intl";
import { useLocation, useParams } from "react-router-dom";
import React, { useEffect, useState } from "react";
import { useParams, useLocation } from "react-router-dom";
import { fetchNip05Pubkey } from "@snort/shared";
import Spinner from "@/Icons/Spinner";
import ProfilePage from "@/Pages/Profile/ProfilePage";
import { ThreadRoute } from "@/Element/Event/Thread";
import { GenericFeed } from "@/Element/Feed/Generic";
import { NostrPrefix, tryParseNostrLink } from "@snort/system";
import { FormattedMessage } from "react-intl";
export default function NostrLinkHandler() {
const params = useParams();
const { state } = useLocation();
const [loading, setLoading] = useState(true);
const [renderComponent, setRenderComponent] = useState<React.ReactNode>(null);
const { link } = useParams();
const link = decodeURIComponent(params["*"] ?? "").toLowerCase();
async function handleLink(link: string) {
const determineInitialComponent = link => {
const nav = tryParseNostrLink(link);
if (nav) {
if (nav.type === NostrPrefix.Event || nav.type === NostrPrefix.Note || nav.type === NostrPrefix.Address) {
setRenderComponent(<ThreadRoute key={link} id={nav.encode()} />); // Directly render ThreadRoute
} else if (nav.type === NostrPrefix.PublicKey || nav.type === NostrPrefix.Profile) {
const id = nav.encode();
setRenderComponent(<ProfilePage key={id} id={id} state={state} />); // Directly render ProfilePage
} else if (nav.type === NostrPrefix.Req) {
setRenderComponent(<GenericFeed key={link} link={nav} />);
switch (nav.type) {
case NostrPrefix.Event:
case NostrPrefix.Note:
case NostrPrefix.Address:
return <ThreadRoute key={link} id={nav.encode()} />;
case NostrPrefix.PublicKey:
case NostrPrefix.Profile:
return <ProfilePage key={link} id={nav.encode()} state={state} />;
case NostrPrefix.Req:
return <GenericFeed key={link} link={nav} />;
default:
return null;
}
} else {
if (state) {
setRenderComponent(<ProfilePage key={link} state={state} />); // Directly render ProfilePage from route state
} else {
try {
const pubkey = await fetchNip05Pubkey(link, CONFIG.nip05Domain);
if (pubkey) {
setRenderComponent(<ProfilePage key={link} id={pubkey} state={state} />); // Directly render ProfilePage
}
} catch {
//ignored
}
}
return state ? <ProfilePage key={link} state={state} /> : null;
}
};
const initialRenderComponent = determineInitialComponent(link);
const [loading, setLoading] = useState(initialRenderComponent ? false : true);
const [renderComponent, setRenderComponent] = useState(initialRenderComponent);
async function handleLink(link) {
if (!tryParseNostrLink(link)) {
try {
const pubkey = await fetchNip05Pubkey(link, CONFIG.nip05Domain);
if (pubkey) {
setRenderComponent(<ProfilePage key={link} id={pubkey} state={state} />);
}
} catch {
// Ignored
}
setLoading(false);
}
setLoading(false);
}
useEffect(() => {
if (link.length > 0) {
handleLink(link).catch(console.error);
}
}, [link]);
setRenderComponent(determineInitialComponent(link));
handleLink(link);
}, [link]); // Depend only on 'link'
if (renderComponent) {
return renderComponent;

View File

@ -5,31 +5,35 @@ import { useNavigate } from "react-router-dom";
import { FormattedMessage, FormattedNumber, useIntl } from "react-intl";
import NoteTime from "@/Element/Event/NoteTime";
import { WalletInvoice, Sats, WalletInfo, WalletInvoiceState, useWallet, LNWallet, Wallets } from "@/Wallet";
import { WalletInvoice, Sats, useWallet, LNWallet, Wallets } from "@/Wallet";
import AsyncButton from "@/Element/Button/AsyncButton";
import { unwrap } from "@/SnortUtils";
import Icon from "@/Icons/Icon";
import { useRates } from "@/Hooks/useRates";
import { AsyncIcon } from "@/Element/Button/AsyncIcon";
import classNames from "classnames";
export default function WalletPage() {
export default function WalletPage(props: { showHistory: boolean }) {
const navigate = useNavigate();
const { formatMessage } = useIntl();
const [info, setInfo] = useState<WalletInfo>();
const [balance, setBalance] = useState<Sats>();
const [history, setHistory] = useState<WalletInvoice[]>();
const [walletPassword, setWalletPassword] = useState<string>();
const [error, setError] = useState<string>();
const walletState = useWallet();
const wallet = walletState.wallet;
const rates = useRates("BTCUSD");
async function loadWallet(wallet: LNWallet) {
try {
const i = await wallet.getInfo();
setInfo(i);
setError(undefined);
setBalance(0);
setHistory(undefined);
if (wallet.canGetBalance()) {
const b = await wallet.getBalance();
setBalance(b as Sats);
}
if (wallet.canGetInvoices()) {
if (wallet.canGetInvoices() && (props.showHistory ?? true)) {
const h = await wallet.getInvoices();
setHistory((h as WalletInvoice[]).sort((a, b) => b.timestamp - a.timestamp));
}
@ -43,29 +47,11 @@ export default function WalletPage() {
}
useEffect(() => {
if (wallet) {
if (wallet.isReady()) {
loadWallet(wallet).catch(console.warn);
} else if (wallet.canAutoLogin()) {
wallet
.login()
.then(async () => await loadWallet(wallet))
.catch(console.warn);
}
if (wallet && wallet.isReady()) {
loadWallet(wallet).catch(console.warn);
}
}, [wallet]);
function stateIcon(s: WalletInvoiceState) {
switch (s) {
case WalletInvoiceState.Pending:
return <Icon name="clock" size={15} />;
case WalletInvoiceState.Paid:
return <Icon name="check" size={15} />;
case WalletInvoiceState.Expired:
return <Icon name="close" size={15} />;
}
}
async function loginWallet(pw: string) {
if (wallet) {
await wallet.login(pw);
@ -112,11 +98,11 @@ export default function WalletPage() {
);
}
return (
<div className="flex w-max">
<h4 className="f-1">
<div className="flex items-center">
<h4 className="grow">
<FormattedMessage defaultMessage="Select Wallet" id="G1BGCg" />
</h4>
<div className="f-1">
<div>
<select className="w-max" onChange={e => Wallets.switch(e.target.value)} value={walletState.config?.id}>
{Wallets.list().map(a => {
return <option value={a.id}>{a.info.alias}</option>;
@ -128,79 +114,108 @@ export default function WalletPage() {
}
function walletHistory() {
if (!wallet?.canGetInvoices()) return;
if (!wallet?.canGetInvoices() || !(props.showHistory ?? true)) return;
return (
<>
<div className="flex flex-col gap-1">
<h3>
<FormattedMessage defaultMessage="History" id="d6CyG5" description="Wallet transation history" />
<FormattedMessage defaultMessage="Payments" id="pukxg/" description="Wallet transation history" />
</h3>
{history?.map(a => (
<div className="card flex wallet-history-item" key={a.timestamp}>
<div className="grow">
<NoteTime from={a.timestamp * 1000} fallback={formatMessage({ defaultMessage: "now", id: "kaaf1E" })} />
<div>{(a.memo ?? "").length === 0 ? <>&nbsp;</> : a.memo}</div>
</div>
<div
className={`flex gap-2 items-center ${(() => {
switch (a.state) {
case WalletInvoiceState.Paid:
return "success";
case WalletInvoiceState.Expired:
return "expired";
case WalletInvoiceState.Failed:
return "failed";
default:
return "pending";
}
})()}`}>
<div>{stateIcon(a.state)}</div>
{history?.map(a => {
const dirClassname = {
"text-[--success]": a.direction === "in",
"text-[--error]": a.direction === "out",
};
return (
<div className="flex gap-4 p-2 hover:bg-[--gray-superdark] rounded-xl items-center" key={a.timestamp}>
<div>
<FormattedMessage
defaultMessage="{amount} sats"
id="vrTOHJ"
values={{
amount: <FormattedNumber value={a.amount / 1e3} />,
}}
/>
<div className="rounded-full aspect-square p-2 bg-[--gray-dark]">
<Icon
name="arrow-up-right"
className={classNames(dirClassname, {
"rotate-180": a.direction === "in",
})}
/>
</div>
</div>
<div className="grow flex justify-between">
<div className="flex flex-col gap-1">
<div>{a.memo?.length === 0 ? CONFIG.appNameCapitalized : a.memo}</div>
<div className="text-secondary text-sm">
<NoteTime
from={a.timestamp * 1000}
fallback={formatMessage({ defaultMessage: "now", id: "kaaf1E" })}
/>
</div>
</div>
<div className="flex flex-col gap-1 text-right">
<div className={classNames(dirClassname)}>
<FormattedMessage
defaultMessage="{sign} {amount} sats"
id="tj6kdX"
values={{
sign: a.direction === "in" ? "+" : "-",
amount: <FormattedNumber value={a.amount / 1e3} />,
}}
/>
</div>
<div className="text-secondary text-sm">
<FormattedMessage
defaultMessage="~{amount}"
id="3QwfJR"
values={{
amount: (
<FormattedNumber
style="currency"
currency="USD"
value={(rates?.ask ?? 0) * a.amount * 1e-11}
/>
),
}}
/>
</div>
</div>
</div>
</div>
</div>
))}
</>
);
})}
</div>
);
}
function walletBalance() {
if (!wallet?.canGetBalance()) return;
return (
<small>
<div className="flex items-center gap-2">
<FormattedMessage
defaultMessage="Balance: {amount} sats"
id="VN0+Fz"
defaultMessage="<big>{amount}</big> <small>sats</small>"
id="E5ZIPD"
values={{
big: c => <span className="text-5xl font-bold">{c}</span>,
small: c => <span className="text-secondary text-sm">{c}</span>,
amount: <FormattedNumber value={balance ?? 0} />,
}}
/>
</small>
<AsyncIcon size={20} className="text-secondary cursor-pointer" iconName="closedeye" />
</div>
);
}
function walletInfo() {
if (!wallet?.isReady()) return;
return (
<>
<div className="p br b flex justify-between">
<div>
<div>{info?.alias}</div>
{walletBalance()}
</div>
<div>
{walletState.config?.id && (
<AsyncButton onClick={() => Wallets.remove(unwrap(walletState.config?.id))}>
<FormattedMessage defaultMessage="Remove" id="G/yZLu" />
</AsyncButton>
)}
<div className="flex flex-col items-center px-6 py-4 bg-[--gray-superdark] rounded-2xl gap-1">
{walletBalance()}
<div className="text-secondary">
<FormattedMessage
defaultMessage="~{amount}"
id="3QwfJR"
values={{
amount: (
<FormattedNumber style="currency" currency="USD" value={(rates?.ask ?? 0) * (balance ?? 0) * 1e-8} />
),
}}
/>
</div>
</div>
{walletHistory()}
@ -209,7 +224,7 @@ export default function WalletPage() {
}
return (
<div className="main-content p">
<div className="main-content">
{walletList()}
{error && <b className="warning">{error}</b>}
{unlockWallet()}

View File

@ -22,7 +22,7 @@ export default function AccountsPage() {
about: false,
}}
actions={
<div className="f-1">
<div className="flex-1">
<button className="mb10" onClick={() => LoginStore.switchAccount(a.id)}>
<FormattedMessage defaultMessage="Switch" id="n1Whvj" />
</button>

View File

@ -1,4 +1,4 @@
import { useCallback } from "react";
import { ReactNode, useCallback } from "react";
import { FormattedMessage } from "react-intl";
import { Link, useNavigate } from "react-router-dom";
import Icon from "@/Icons/Icon";
@ -7,6 +7,17 @@ import useLogin from "@/Hooks/useLogin";
import classNames from "classnames";
import { getCurrentSubscription } from "@/Subscription";
export type SettingsMenuItems = Array<{
title: ReactNode;
items: Array<{
icon: string;
iconBg: string;
message: ReactNode;
path?: string;
action?: () => void;
}>;
}>;
const SettingsIndex = () => {
const login = useLogin();
const navigate = useNavigate();
@ -61,6 +72,12 @@ const SettingsIndex = () => {
},
]
: []),
{
icon: "tool",
iconBg: "bg-slate-800",
message: <FormattedMessage defaultMessage="Tools" id="nUT0Lv" />,
path: "tools",
},
],
},
{
@ -140,11 +157,15 @@ const SettingsIndex = () => {
},
],
},
];
] as SettingsMenuItems;
return <SettingsMenuComponent menu={settingsGroups} />;
};
export function SettingsMenuComponent({ menu }: { menu: SettingsMenuItems }) {
return (
<div className="flex flex-col">
{settingsGroups.map((group, groupIndex) => (
{menu.map((group, groupIndex) => (
<div key={groupIndex} className="mb-4">
<div className="p-2 font-bold uppercase text-secondary text-xs tracking-wide">{group.title}</div>
{group.items.map(({ icon, iconBg, message, path, action }, index) => (
@ -152,7 +173,6 @@ const SettingsIndex = () => {
to={path || "#"}
onClick={action}
key={path || index}
end
className={classNames("px-2.5 py-1.5 flex justify-between items-center border border-border-color", {
"rounded-t-xl": index === 0,
"rounded-b-xl": index === group.items.length - 1,
@ -160,7 +180,7 @@ const SettingsIndex = () => {
})}>
<div className="flex items-center gap-3">
<div className={`p-1 ${iconBg} rounded-lg flex justify-center items-center text-white`}>
<Icon name={icon} size={16} className="w-4 h-4 relative" />
<Icon name={icon} size={18} className="relative" />
</div>
<span className="text-base font-semibold flex-grow">{message}</span>
</div>
@ -171,6 +191,6 @@ const SettingsIndex = () => {
))}
</div>
);
};
}
export default SettingsIndex;

View File

@ -497,6 +497,23 @@ const PreferencesPage = () => {
/>
</div>
</div>
<div className="flex justify-between">
<div className="flex flex-col g8">
<h4>
<FormattedMessage {...messages.LowDataMode} />
</h4>
<small>
<FormattedMessage {...messages.LowDataModeHelp} />
</small>
</div>
<div>
<input
type="checkbox"
checked={perf.lowDataMode}
onChange={e => updatePreferences(id, { ...perf, lowDataMode: e.target.checked })}
/>
</div>
</div>
</div>
);
};

View File

@ -5,13 +5,14 @@ import Preferences from "@/Pages/settings/Preferences";
import Notifications from "@/Pages/settings/Notifications";
import RelayInfo from "@/Pages/settings/RelayInfo";
import AccountsPage from "@/Pages/settings/Accounts";
import { WalletSettingsRoutes } from "@/Pages/settings/WalletSettings";
import { ManageHandleRoutes } from "@/Pages/settings/handle";
import ExportKeys from "@/Pages/settings/Keys";
import ModerationSettings from "@/Pages/settings/Moderation";
import { CacheSettings } from "@/Pages/settings/Cache";
import { ReferralsPage } from "@/Pages/settings/Referrals";
import { Outlet } from "react-router-dom";
import { ToolsPage, ToolsPages } from "./tools";
import { WalletSettingsRoutes } from "./wallet";
const SettingsPage = () => {
return (
@ -70,6 +71,11 @@ export default [
path: "invite",
element: <ReferralsPage />,
},
{
path: "tools",
element: <ToolsPage />,
children: ToolsPages,
},
...ManageHandleRoutes,
...WalletSettingsRoutes,
],

View File

@ -1,14 +0,0 @@
.wallet-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
text-align: center;
grid-gap: 10px;
}
.wallet-grid > div {
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
}

View File

@ -1,64 +1,82 @@
import "./WalletSettings.css";
import LndLogo from "@/lnd-logo.png";
import { ReactNode } from "react";
import { FormattedMessage } from "react-intl";
import { RouteObject, useNavigate } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import BlueWallet from "@/Icons/BlueWallet";
import ConnectLNC from "@/Pages/settings/wallet/LNC";
import ConnectLNDHub from "@/Pages/settings/wallet/LNDHub";
import ConnectNostrWallet from "@/Pages/settings/wallet/NWC";
import ConnectCashu from "@/Pages/settings/wallet/Cashu";
import NostrIcon from "@/Icons/Nostrich";
import WalletPage from "../WalletPage";
import CashuIcon from "@/Icons/Cashu";
import AlbyIcon from "@/Icons/Alby";
import Icon from "@/Icons/Icon";
import { getAlbyOAuth } from "./wallet/Alby";
const WalletSettings = () => {
const WalletRow = (props: { logo: ReactNode; name: ReactNode; url: string; desc?: ReactNode }) => {
const navigate = useNavigate();
return (
<div
className="flex items-center gap-4 px-4 py-2 bg-[--gray-superdark] rounded-xl hover:bg-[--gray-ultradark]"
onClick={() => {
if (props.url.startsWith("http")) {
window.location.href = props.url;
} else {
navigate(props.url);
}
}}>
<div className="rounded-xl aspect-square h-[4rem] bg-[--gray-dark] p-3 flex items-center justify-center">
{props.logo}
</div>
<div className="flex flex-col gap-1 grow justify-center">
<div className="text-xl font-bold">{props.name}</div>
<div className="text-sm text-secondary">{props.desc}</div>
</div>
<Icon name="arrowFront" />
</div>
);
};
const WalletSettings = () => {
const alby = getAlbyOAuth();
return (
<>
<WalletPage />
<h3>
<FormattedMessage defaultMessage="Connect Wallet" id="cg1VJ2" />
</h3>
<div className="wallet-grid">
<div onClick={() => navigate("/settings/wallet/lnc")}>
<img src={LndLogo} width={100} />
<h3>LND with LNC</h3>
</div>
<div onClick={() => navigate("/settings/wallet/lndhub")}>
<BlueWallet width={100} height={100} />
<h3>LNDHub</h3>
</div>
<div onClick={() => navigate("/settings/wallet/nwc")}>
<NostrIcon width={100} height={100} />
<h3>Nostr Wallet Connect</h3>
</div>
<div className="flex flex-col gap-3 cursor-pointer">
<WalletRow
logo={<NostrIcon width={64} height={64} />}
name="Nostr Wallet Connect"
url="/settings/wallet/nwc"
desc={<FormattedMessage defaultMessage="Native nostr wallet connection" id="cG/bKQ" />}
/>
<WalletRow
logo={<img src={LndLogo} />}
name="LND via LNC"
url="/settings/wallet/lnc"
desc={
<FormattedMessage defaultMessage="Connect to your own LND node with Lightning Node Connect" id="aSGz4J" />
}
/>
<WalletRow
logo={<BlueWallet width={64} height={64} />}
name="LNDHub"
url="/settings/wallet/lndhub"
desc={<FormattedMessage defaultMessage="Generic LNDHub wallet (BTCPayServer / Alby / LNBits)" id="0MndVW" />}
/>
<WalletRow
logo={<CashuIcon size={64} />}
name="Cashu"
url="/settings/wallet/cashu"
desc={<FormattedMessage defaultMessage="Cashu mint wallet" id="3natuV" />}
/>
<WalletRow
logo={<AlbyIcon size={64} />}
name="Alby"
url={alby.authUrl}
desc={<FormattedMessage defaultMessage="Alby wallet connection" id="XPB8VV" />}
/>
</div>
</>
);
};
export default WalletSettings;
export const WalletSettingsRoutes = [
{
path: "/settings/wallet",
element: <WalletSettings />,
},
{
path: "/settings/wallet/lnc",
element: <ConnectLNC />,
},
{
path: "/settings/wallet/lndhub",
element: <ConnectLNDHub />,
},
{
path: "/settings/wallet/nwc",
element: <ConnectNostrWallet />,
},
{
path: "/settings/wallet/cashu",
element: <ConnectCashu />,
},
] as Array<RouteObject>;

View File

@ -76,4 +76,9 @@ export default defineMessages({
ServiceWorkerNotRunning: { defaultMessage: "Service Worker Not Running", id: "RDha9y" },
SubscribedToPush: { defaultMessage: "Subscribed to Push", id: "G3A56c" },
NotSubscribedToPush: { defaultMessage: "Not Subscribed to Push", id: "d2ebEu" },
LowDataMode: { defaultMessage: "Low Data Mode", id: '87tg/I' },
LowDataModeHelp: {
defaultMessage: "Reactions will not be shown on timeline and images would not load automatically",
id: 'Qie1Ef',
},
});

View File

@ -0,0 +1,94 @@
import { CollapsedSection } from "@/Element/Collapsed";
import ProfilePreview from "@/Element/User/ProfilePreview";
import useLogin from "@/Hooks/useLogin";
import { getRelayName } from "@/SnortUtils";
import { dedupe } from "@snort/shared";
import { pickTopRelays } from "@snort/system";
import { SnortContext } from "@snort/system-react";
import { ReactNode, useContext, useMemo } from "react";
import { FormattedMessage, FormattedNumber } from "react-intl";
export function FollowsRelayHealth({
withTitle,
popularRelays,
missingRelaysActions,
}: {
withTitle?: boolean;
popularRelays?: boolean;
missingRelaysActions?: (k: string) => ReactNode;
}) {
const system = useContext(SnortContext);
const follows = useLogin(s => s.follows);
const uniqueFollows = dedupe(follows.item);
const hasRelays = useMemo(() => {
return uniqueFollows.filter(a => (system.RelayCache.getFromCache(a)?.relays.length ?? 0) > 0);
}, [uniqueFollows]);
const missingRelays = useMemo(() => {
return uniqueFollows.filter(a => !hasRelays.includes(a));
}, [hasRelays]);
const topWriteRelays = useMemo(() => {
return pickTopRelays(system.RelayCache, uniqueFollows, 1e31, "write");
}, [uniqueFollows]);
return (
<div className="flex flex-col gap-4">
{(withTitle ?? true) && (
<div className="text-2xl font-semibold">
<FormattedMessage defaultMessage="Follows Relay Health" id="XQiFEl" />
</div>
)}
<div>
<FormattedMessage
defaultMessage="{x}/{y} have relays ({percent})"
id="p9Ps2l"
values={{
x: hasRelays.length,
y: uniqueFollows.length,
percent: <FormattedNumber style="percent" value={hasRelays.length / uniqueFollows.length} />,
}}
/>
</div>
{missingRelays.length > 0 && (
<CollapsedSection
className="rounded-xl border border-border-color px-3 py-4"
title={
<div className="text-lg">
<FormattedMessage defaultMessage="Missing Relays" id="4emo2p" />
</div>
}>
<div>
{missingRelays.map(a => (
<ProfilePreview
pubkey={a}
options={{
about: false,
}}
actions={missingRelaysActions?.(a)}
/>
))}
</div>
</CollapsedSection>
)}
{(popularRelays ?? true) && (
<div>
<div className="text-xl font-medium">Popular Relays</div>
{dedupe(topWriteRelays.flatMap(a => a.relays))
.map(a => ({ relay: a, count: topWriteRelays.filter(b => b.relays.includes(a)).length }))
.sort((a, b) => (a.count > b.count ? -1 : 1))
.slice(0, 10)
.map(a => (
<div className="flex justify-between">
<div>{getRelayName(a.relay)}</div>
<div>
{a.count} (<FormattedNumber style="percent" value={a.count / uniqueFollows.length} />)
</div>
</div>
))}
</div>
)}
</div>
);
}

View File

@ -0,0 +1,51 @@
import { FormattedMessage } from "react-intl";
import { Outlet, RouteObject } from "react-router-dom";
import { SettingsMenuComponent, SettingsMenuItems } from "../Menu";
import { PruneFollowList } from "./prune-follows";
import { FollowsRelayHealth } from "./follows-relay-health";
const ToolMenuItems = [
{
title: <FormattedMessage defaultMessage="Follow List" id="CM+Cfj" />,
items: [
{
icon: "trash",
iconBg: "bg-red-500",
message: <FormattedMessage defaultMessage="Prune Follow List" id="hF6IN2" />,
path: "prune-follows",
},
{
icon: "medical-cross",
iconBg: "bg-green-800",
message: <FormattedMessage defaultMessage="Follows Relay Health" id="XQiFEl" />,
path: "follows-relay-health",
},
],
},
] as SettingsMenuItems;
export const ToolsPages = [
{
path: "",
element: (
<>
<h2>
<FormattedMessage defaultMessage="Tools" id="nUT0Lv" />
</h2>
<SettingsMenuComponent menu={ToolMenuItems} />
</>
),
},
{
path: "prune-follows",
element: <PruneFollowList />,
},
{
path: "follows-relay-health",
element: <FollowsRelayHealth />,
},
] as Array<RouteObject>;
export function ToolsPage() {
return <Outlet />;
}

View File

@ -0,0 +1,174 @@
import { Day } from "@/Const";
import AsyncButton from "@/Element/Button/AsyncButton";
import useLogin from "@/Hooks/useLogin";
import { dedupe, unixNow } from "@snort/shared";
import { RequestBuilder } from "@snort/system";
import { useMemo, useState } from "react";
import { FormattedMessage, FormattedNumber } from "react-intl";
import { FollowsRelayHealth } from "./follows-relay-health";
import ProfileImage from "@/Element/User/ProfileImage";
import useEventPublisher from "@/Hooks/useEventPublisher";
import { setFollows } from "@/Login";
const enum PruneStage {
FetchLastPostTimestamp,
Done,
}
export function PruneFollowList() {
const { id, follows } = useLogin(s => ({ id: s.id, follows: s.follows }));
const { publisher, system } = useEventPublisher();
const uniqueFollows = dedupe(follows.item);
const [status, setStatus] = useState<PruneStage>();
const [progress, setProgress] = useState(0);
const [lastPost, setLastPosts] = useState<Record<string, number>>();
const [unfollow, setUnfollow] = useState<Array<string>>([]);
async function fetchLastPosts() {
setStatus(PruneStage.FetchLastPostTimestamp);
setProgress(0);
setLastPosts(undefined);
const BatchSize = 10;
const chunks = uniqueFollows.reduce(
(acc, v, i) => {
const batch = Math.floor(i / BatchSize).toString();
acc[batch] ??= [];
acc[batch].push(v);
return acc;
},
{} as Record<string, Array<string>>,
);
const result = {} as Record<string, number>;
const batches = Math.ceil(uniqueFollows.length / BatchSize);
for (const [batch, pubkeys] of Object.entries(chunks)) {
console.debug(batch, pubkeys);
const req = new RequestBuilder(`prune-${batch}`);
req.withOptions({
outboxPickN: 10,
timeout: 10_000,
});
pubkeys.forEach(p => req.withFilter().limit(1).kinds([0, 1, 3, 5, 6, 7, 10002]).authors([p]));
const results = await system.Fetch(req);
console.debug(results);
for (const rx of results) {
if ((result[rx.pubkey] ?? 0) < rx.created_at) {
result[rx.pubkey] = rx.created_at;
}
}
setProgress(Number(batch) / batches);
}
for (const pk of uniqueFollows) {
result[pk] ??= 0;
}
setLastPosts(result);
setStatus(PruneStage.Done);
}
const newFollowList = useMemo(() => {
return uniqueFollows.filter(a => !unfollow.includes(a) && a.length === 64);
}, [uniqueFollows, unfollow]);
async function publishFollowList() {
const newFollows = newFollowList.map(a => ["p", a]) as Array<[string, string]>;
if (publisher) {
const ev = await publisher.contactList(newFollows);
await system.BroadcastEvent(ev);
setFollows(id, newFollowList, ev.created_at * 1000);
}
}
function getStatus() {
switch (status) {
case PruneStage.FetchLastPostTimestamp:
return (
<FormattedMessage
defaultMessage="Searching for account activity ({progress})"
id="nIchMQ"
values={{
progress: <FormattedNumber style="percent" value={progress} />,
}}
/>
);
}
}
function personToggle(k: string) {
return (
<div className="flex gap-1">
<input
type="checkbox"
onChange={e => setUnfollow(v => (e.target.checked ? dedupe([...v, k]) : v.filter(a => a !== k)))}
checked={unfollow.includes(k)}
/>
<FormattedMessage defaultMessage="Unfollow" id="izWS4J" />
</div>
);
}
return (
<div className="flex flex-col gap-4">
<div className="text-2xl font-semibold">
<FormattedMessage defaultMessage="Prune follow list" id="CM0k0d" />
</div>
<p>
<FormattedMessage
defaultMessage="This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months"
id="vU/Q5i"
/>
</p>
<div>
<FormattedMessage
defaultMessage="{x} follows ({y} duplicates)"
id="iICVoL"
values={{
x: follows.item.length,
y: follows.item.length - uniqueFollows.length,
}}
/>
</div>
<FollowsRelayHealth withTitle={false} popularRelays={false} missingRelaysActions={k => personToggle(k)} />
<AsyncButton onClick={fetchLastPosts}>
<FormattedMessage defaultMessage="Compute prune list" id="bJ+wrA" />
</AsyncButton>
{getStatus()}
<div className="flex flex-col gap-1">
{lastPost &&
Object.entries(lastPost)
.filter(([, v]) => v <= unixNow() - 90 * Day)
.sort(([, a], [, b]) => (a > b ? -1 : 1))
.map(([k, v]) => {
return (
<div className="flex justify-between">
<ProfileImage pubkey={k} />
<div className="flex flex-col gap-1">
<FormattedMessage
defaultMessage="Last post {time}"
id="I1AoOu"
values={{
time: new Date(v * 1000).toLocaleDateString(),
}}
/>
{personToggle(k)}
</div>
</div>
);
})}
</div>
<div className="px-4 pb-5 pt-2 rounded-2xl bg-bg-secondary">
<p>
<FormattedMessage
defaultMessage="New follow list length {length}"
id="6559gb"
values={{ length: newFollowList.length }}
/>
</p>
<AsyncButton onClick={publishFollowList}>
<FormattedMessage defaultMessage="Save" id="jvo0vs" />
</AsyncButton>
</div>
</div>
);
}

View File

@ -0,0 +1,94 @@
import PageSpinner from "@/Element/PageSpinner";
import { sha256 } from "@noble/hashes/sha256";
import { randomBytes } from "@noble/hashes/utils";
import { base64, base64urlnopad, hex } from "@scure/base";
import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
export default function AlbyOAuth() {
const location = useLocation();
const alby = getAlbyOAuth();
const [error, setError] = useState("");
async function setupWallet(token: string) {
const auth = await alby.getToken(token);
console.debug(auth);
}
useEffect(() => {
if (location.search) {
const params = new URLSearchParams(location.search);
const token = params.get("code");
if (token) {
setupWallet(token).catch(e => {
setError((e as Error).message);
});
}
}
}, [location]);
if (!location.search) return;
return (
<>
<h1>Alby Wallet Connection</h1>
{!error && <PageSpinner />}
{error && <b className="warning">{error}</b>}
</>
);
}
export function getAlbyOAuth() {
const clientId = "35EQp6crss";
const clientSecret = "DTUPIqOjsjwxZXcJwF5C";
const redirectUrl = `${window.location.protocol}//${window.location.host}/settings/wallet/alby`;
const scopes = ["invoices:create", "invoices:read", "transactions:read", "balance:read", "payments:send"];
const ec = new TextEncoder();
const code_verifier = hex.encode(randomBytes(64));
window.sessionStorage.setItem("alby-code", code_verifier);
const params = new URLSearchParams();
params.set("client_id", clientId);
params.set("response_type", "code");
params.set("code_challenge", base64urlnopad.encode(sha256(code_verifier)));
params.set("code_challenge_method", "S256");
params.set("redirect_uri", redirectUrl);
params.set("scope", scopes.join(" "));
const tokenUrl = "https://api.getalby.com/oauth/token";
const authUrl = `https://getalby.com/oauth?${params}`;
return {
tokenUrl,
authUrl,
getToken: async (token: string) => {
const code = window.sessionStorage.getItem("alby-code");
if (!code) throw new Error("Alby code is missing!");
window.sessionStorage.removeItem("alby-code");
const form = new URLSearchParams();
form.set("client_id", clientId);
form.set("code_verifier", code);
form.set("grant_type", "authorization_code");
form.set("redirect_uri", redirectUrl);
form.set("code", token);
const req = await fetch(tokenUrl, {
method: "POST",
headers: {
accept: "application/json",
"content-type": "application/x-www-form-urlencoded",
authorization: `Basic ${base64.encode(ec.encode(`${clientId}:${clientSecret}`))}`,
},
body: form,
});
const data = await req.json();
if (req.ok) {
return data.access_token as string;
} else {
throw new Error(data.error_description as string);
}
},
};
}

View File

@ -0,0 +1,34 @@
import { RouteObject } from "react-router-dom";
import WalletSettings from "../WalletSettings";
import ConnectCashu from "./Cashu";
import ConnectLNC from "./LNC";
import ConnectLNDHub from "./LNDHub";
import ConnectNostrWallet from "./NWC";
import AlbyOAuth from "./Alby";
export const WalletSettingsRoutes = [
{
path: "/settings/wallet",
element: <WalletSettings />,
},
{
path: "/settings/wallet/lnc",
element: <ConnectLNC />,
},
{
path: "/settings/wallet/lndhub",
element: <ConnectLNDHub />,
},
{
path: "/settings/wallet/nwc",
element: <ConnectNostrWallet />,
},
{
path: "/settings/wallet/cashu",
element: <ConnectCashu />,
},
{
path: "/settings/wallet/alby",
element: <AlbyOAuth />,
},
] as Array<RouteObject>;

View File

@ -47,7 +47,7 @@ export default function SubscriptionCard({ sub }: { sub: Subscription }) {
{mapPlanName(sub.type)}
</div>
<div className="flex">
<p className="f-1">
<p className="flex-1">
<FormattedMessage defaultMessage="Created" id="ORGv1Q" />
:&nbsp;
<time dateTime={created.toISOString()}>
@ -55,7 +55,7 @@ export default function SubscriptionCard({ sub }: { sub: Subscription }) {
</time>
</p>
{daysToExpire >= 1 && (
<p className="f-1">
<p className="flex-1">
<FormattedMessage defaultMessage="Expires" id="xhQMeQ" />
:&nbsp;
<time dateTime={expires.toISOString()}>
@ -70,7 +70,7 @@ export default function SubscriptionCard({ sub }: { sub: Subscription }) {
</p>
)}
{daysToExpire >= 0 && daysToExpire < 1 && (
<p className="f-1">
<p className="flex-1">
<FormattedMessage defaultMessage="Expires" id="xhQMeQ" />
:&nbsp;
<time dateTime={expires.toISOString()}>
@ -85,12 +85,12 @@ export default function SubscriptionCard({ sub }: { sub: Subscription }) {
</p>
)}
{isExpired && (
<p className="f-1 error">
<p className="flex-1 error">
<FormattedMessage defaultMessage="Expired" id="RahCRH" />
</p>
)}
{isNew && (
<p className="f-1">
<p className="flex-1">
<FormattedMessage defaultMessage="Unpaid" id="6uMqL1" />
</p>
)}

View File

@ -1,5 +1,5 @@
import "./TaskList.css";
import { useSyncExternalStore } from "react";
import { Fragment, useSyncExternalStore } from "react";
import { useUserProfile } from "@snort/system-react";
import useLogin from "@/Hooks/useLogin";
@ -54,7 +54,7 @@ export const TaskList = () => {
.filter(a => (user ? a.check(user, session) : false))
.map(a => {
if (a.noBaseStyle) {
return a.render();
return <Fragment key={a.id}>{a.render()}</Fragment>;
} else {
return (
<div key={a.id} className="card">

View File

@ -60,10 +60,13 @@ export default async function VoidCatUpload(
}
const resultUrl = rsp.file?.metadata?.url ?? `https://void.cat/d/${rsp.file?.id}${ext ? `.${ext[1]}` : ""}`;
const dim = rsp.file?.metadata?.mediaDimensions ? rsp.file.metadata.mediaDimensions.split("x") : undefined;
const ret = {
url: resultUrl,
metadata: {
hash: rsp.file?.metadata?.digest,
width: dim ? Number(dim[0]) : undefined,
height: dim ? Number(dim[1]) : undefined,
},
} as UploadResult;

View File

@ -23,7 +23,10 @@ export default class LNDHubWallet implements LNWallet {
password: string;
auth?: AuthResponse;
constructor(url: string) {
constructor(
url: string,
readonly changed: () => void,
) {
if (url.startsWith("lndhub://")) {
const regex = /^lndhub:\/\/([\S-]+):([\S-]+)@(.*)$/i;
const parsedUrl = url.match(regex);
@ -60,25 +63,31 @@ export default class LNDHubWallet implements LNWallet {
}
async getInfo() {
await this.login();
return await this.getJson<WalletInfo>("GET", "/getinfo");
}
async login() {
if (this.auth) return true;
const rsp = await this.getJson<AuthResponse>("POST", "/auth?type=auth", {
login: this.user,
password: this.password,
});
this.auth = rsp as AuthResponse;
this.changed();
return true;
}
async getBalance(): Promise<Sats> {
await this.login();
const rsp = await this.getJson<GetBalanceResponse>("GET", "/balance");
const bal = Math.floor((rsp as GetBalanceResponse).BTC.AvailableBalance);
return bal as Sats;
}
async createInvoice(req: InvoiceRequest) {
await this.login();
const rsp = await this.getJson<UserInvoicesResponse>("POST", "/addinvoice", {
amt: req.amount,
memo: req.memo,
@ -95,6 +104,7 @@ export default class LNDHubWallet implements LNWallet {
}
async payInvoice(pr: string) {
await this.login();
const rsp = await this.getJson<PayInvoiceResponse>("POST", "/payinvoice", {
invoice: pr,
});
@ -113,6 +123,7 @@ export default class LNDHubWallet implements LNWallet {
}
async getInvoices(): Promise<WalletInvoice[]> {
await this.login();
const rsp = await this.getJson<UserInvoicesResponse[]>("GET", "/getuserinvoices");
return (rsp as UserInvoicesResponse[])
.sort((a, b) => (a.timestamp > b.timestamp ? -1 : 1))

View File

@ -60,6 +60,8 @@ interface ListTransactionsResponse {
amount: number;
feed_paid: number;
settled_at?: number;
created_at: number;
expires_at: number;
metadata?: object;
}>;
}
@ -79,7 +81,10 @@ export class NostrConnectWallet implements LNWallet {
#info?: WalletInfo;
#supported_methods: Array<string> = DefaultSupported;
constructor(cfg: string) {
constructor(
cfg: string,
readonly changed: () => void,
) {
this.#config = NostrConnectWallet.parseConfigUrl(cfg);
this.#commandQueue = new Map();
}
@ -145,9 +150,9 @@ export class NostrConnectWallet implements LNWallet {
async login() {
if (this.#conn) return true;
return await new Promise<boolean>(resolve => {
await new Promise<void>(resolve => {
this.#conn = new Connection(this.#config.relayUrl, { read: true, write: true });
this.#conn.on("connected", () => resolve(true));
this.#conn.on("connected", () => resolve());
this.#conn.on("auth", async (c, r, cb) => {
const eb = new EventBuilder();
eb.kind(EventKind.Auth).tag(["relay", r]).tag(["challenge", c]);
@ -159,6 +164,9 @@ export class NostrConnectWallet implements LNWallet {
});
this.#conn.Connect();
});
await this.getInfo();
this.changed();
return true;
}
async close() {
@ -227,9 +235,10 @@ export class NostrConnectWallet implements LNWallet {
memo: a.description,
amount: a.amount,
fees: a.feed_paid,
timestamp: a.settled_at,
timestamp: typeof a.created_at === "string" ? new Date(a.created_at).getTime() / 1000 : a.created_at,
preimage: a.preimage,
state: WalletInvoiceState.Paid,
direction: a.type === "incoming" ? "in" : "out",
}) as WalletInvoice,
) ?? []
);

View File

@ -80,6 +80,7 @@ export interface WalletInvoice {
timestamp: number;
preimage?: string;
state: WalletInvoiceState;
direction: "in" | "out";
}
export function prToWalletInvoice(pr: string) {
@ -92,6 +93,7 @@ export function prToWalletInvoice(pr: string) {
timestamp: parsedInvoice.timestamp ?? 0,
state: parsedInvoice.expired ? WalletInvoiceState.Expired : WalletInvoiceState.Pending,
pr,
direction: "in",
} as WalletInvoice;
}
}
@ -163,6 +165,9 @@ export class WalletStore extends ExternalStore<WalletStoreSnapshot> {
this.notifyChange();
});
return undefined;
} else {
this.#instance.set(activeConfig.id, w);
this.notifyChange();
}
return w;
} else {
@ -230,10 +235,10 @@ export class WalletStore extends ExternalStore<WalletStoreSnapshot> {
return new WebLNWallet();
}
case WalletKind.LNDHub: {
return new LNDHubWallet(unwrap(cfg.data));
return new LNDHubWallet(unwrap(cfg.data), () => this.notifyChange());
}
case WalletKind.NWC: {
return new NostrConnectWallet(unwrap(cfg.data));
return new NostrConnectWallet(unwrap(cfg.data), () => this.notifyChange());
}
}
}

View File

@ -89,7 +89,10 @@ export class Nip29ChatSystem extends ExternalStore<Array<Chat>> implements ChatS
];
},
sendMessage: async (ev, system: SystemInterface) => {
ev.forEach(async a => await system.WriteOnceToRelay(`wss://${relay}`, a));
ev.forEach(async a => {
system.HandleEvent({ ...a, relays: [] });
await system.WriteOnceToRelay(`wss://${relay}`, a);
});
},
} as Chat;
});

View File

@ -436,8 +436,8 @@
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9733 0.666994C13.0465 0.667127 14.1219 0.667127 15.1931 0.666994C15.4034 0.666969 15.6082 0.666945 15.7817 0.681118C15.9722 0.696682 16.197 0.733404 16.4232 0.848652C16.7368 1.00844 16.9917 1.26341 17.1515 1.57701C17.2668 1.8032 17.3035 2.02798 17.3191 2.21847C17.3332 2.39194 17.3332 2.59677 17.3332 2.8071V6.0269C17.3332 6.23722 17.3332 6.44205 17.3191 6.61552C17.3035 6.80602 17.2668 7.03079 17.1515 7.25698C16.9917 7.57058 16.7368 7.82555 16.4232 7.98534C16.197 8.10059 15.9722 8.13731 15.7817 8.15288C15.6082 8.16705 15.4034 8.16702 15.1931 8.167H11.9733C11.763 8.16702 11.5581 8.16705 11.3847 8.15288C11.1942 8.13731 10.9694 8.10059 10.7432 7.98534C10.4296 7.82555 10.1746 7.57058 10.0148 7.25698C9.89958 7.03079 9.86286 6.80602 9.8473 6.61552C9.83312 6.44206 9.83315 6.23723 9.83317 6.02691C9.83317 6.01806 9.83317 6.0092 9.83317 6.00033V2.83366C9.83317 2.82479 9.83317 2.81593 9.83317 2.80708C9.83315 2.59676 9.83312 2.39194 9.8473 2.21847C9.86286 2.02798 9.89958 1.8032 10.0148 1.57701C10.1746 1.26341 10.4296 1.00844 10.7432 0.848652C10.9694 0.733404 11.1942 0.696682 11.3847 0.681118C11.5581 0.666945 11.7629 0.666969 11.9733 0.666994Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9733 9.83366C13.0465 9.83379 14.1219 9.83379 15.1931 9.83366C15.4034 9.83364 15.6082 9.83361 15.7817 9.84778C15.9722 9.86335 16.197 9.90007 16.4232 10.0153C16.7368 10.1751 16.9917 10.4301 17.1515 10.7437C17.2668 10.9699 17.3035 11.1946 17.3191 11.3851C17.3332 11.5586 17.3332 11.7634 17.3332 11.9738V15.1936C17.3332 15.4039 17.3332 15.6087 17.3191 15.7822C17.3035 15.9727 17.2668 16.1975 17.1515 16.4236C16.9917 16.7373 16.7368 16.9922 16.4232 17.152C16.197 17.2673 15.9722 17.304 15.7817 17.3195C15.6082 17.3337 15.4034 17.3337 15.1931 17.3337H11.9733C11.763 17.3337 11.5581 17.3337 11.3847 17.3195C11.1942 17.304 10.9694 17.2673 10.7432 17.152C10.4296 16.9922 10.1746 16.7373 10.0148 16.4236C9.89958 16.1975 9.86286 15.9727 9.8473 15.7822C9.83312 15.6087 9.83315 15.4039 9.83317 15.1936C9.83317 15.1847 9.83317 15.1759 9.83317 15.167V12.0003C9.83317 11.9915 9.83317 11.9826 9.83317 11.9737C9.83315 11.7634 9.83312 11.5586 9.8473 11.3851C9.86286 11.1946 9.89958 10.9699 10.0148 10.7437C10.1746 10.4301 10.4296 10.1751 10.7432 10.0153C10.9694 9.90007 11.1942 9.86335 11.3847 9.84778C11.5581 9.83361 11.7629 9.83364 11.9733 9.83366Z" fill="currentColor"/>
</symbol>
<symbol id="info-solid" viewBox="0 0 22 22" fill="none" >
<symbol id="info-solid" viewBox="0 0 22 22" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 0C4.92487 0 0 4.92487 0 11C0 17.0751 4.92487 22 11 22C17.0751 22 22 17.0751 22 11C22 4.92487 17.0751 0 11 0ZM11 6C10.4477 6 10 6.44772 10 7C10 7.55228 10.4477 8 11 8H11.01C11.5623 8 12.01 7.55228 12.01 7C12.01 6.44772 11.5623 6 11.01 6H11ZM12 11C12 10.4477 11.5523 10 11 10C10.4477 10 10 10.4477 10 11V15C10 15.5523 10.4477 16 11 16C11.5523 16 12 15.5523 12 15V11Z" fill="currentColor"/>
</symbol>
<symbol id="info-outline" viewBox="0 0 22 22" fill="none">
@ -448,5 +448,18 @@
<path d="M7.5 6.96533C7.5 6.48805 7.5 6.24941 7.59974 6.11618C7.68666 6.00007 7.81971 5.92744 7.96438 5.9171C8.13038 5.90525 8.33112 6.03429 8.73261 6.29239L13.4532 9.32706C13.8016 9.55102 13.9758 9.663 14.0359 9.80539C14.0885 9.9298 14.0885 10.0702 14.0359 10.1946C13.9758 10.337 13.8016 10.449 13.4532 10.6729L8.73261 13.7076C8.33112 13.9657 8.13038 14.0948 7.96438 14.0829C7.81971 14.0726 7.68666 13.9999 7.59974 13.8838C7.5 13.7506 7.5 13.512 7.5 13.0347V6.96533Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1 5.8C1 4.11984 1 3.27976 1.32698 2.63803C1.6146 2.07354 2.07354 1.6146 2.63803 1.32698C3.27976 1 4.11984 1 5.8 1H14.2C15.8802 1 16.7202 1 17.362 1.32698C17.9265 1.6146 18.3854 2.07354 18.673 2.63803C19 3.27976 19 4.11984 19 5.8V14.2C19 15.8802 19 16.7202 18.673 17.362C18.3854 17.9265 17.9265 18.3854 17.362 18.673C16.7202 19 15.8802 19 14.2 19H5.8C4.11984 19 3.27976 19 2.63803 18.673C2.07354 18.3854 1.6146 17.9265 1.32698 17.362C1 16.7202 1 15.8802 1 14.2V5.8Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="tool" viewBox="0 0 22 22" fill="none">
<path d="M14.6314 6.63137C14.2353 6.23535 14.0373 6.03735 13.9631 5.80902C13.8979 5.60817 13.8979 5.39183 13.9631 5.19098C14.0373 4.96265 14.2353 4.76465 14.6314 4.36863L17.4697 1.53026C16.7165 1.18962 15.8804 1 15 1C11.6863 1 8.99998 3.68629 8.99998 7C8.99998 7.49104 9.05897 7.9683 9.17024 8.42509C9.2894 8.91424 9.34898 9.15882 9.33841 9.31333C9.32733 9.47509 9.30321 9.56115 9.22862 9.70511C9.15736 9.84262 9.02084 9.97914 8.7478 10.2522L2.49998 16.5C1.67156 17.3284 1.67156 18.6716 2.49998 19.5C3.32841 20.3284 4.67156 20.3284 5.49998 19.5L11.7478 13.2522C12.0208 12.9791 12.1574 12.8426 12.2949 12.7714C12.4388 12.6968 12.5249 12.6727 12.6867 12.6616C12.8412 12.651 13.0857 12.7106 13.5749 12.8297C14.0317 12.941 14.5089 13 15 13C18.3137 13 21 10.3137 21 7C21 6.11959 20.8104 5.28347 20.4697 4.53026L17.6314 7.36863C17.2353 7.76465 17.0373 7.96265 16.809 8.03684C16.6082 8.1021 16.3918 8.1021 16.191 8.03684C15.9626 7.96265 15.7646 7.76465 15.3686 7.36863L14.6314 6.63137Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="medical-cross" viewBox="0 0 24 24" fill="none">
<path d="M15 4.6C15 4.03995 15 3.75992 14.891 3.54601C14.7951 3.35785 14.6422 3.20487 14.454 3.10899C14.2401 3 13.9601 3 13.4 3H10.6C10.0399 3 9.75992 3 9.54601 3.10899C9.35785 3.20487 9.20487 3.35785 9.10899 3.54601C9 3.75992 9 4.03995 9 4.6V7.4C9 7.96005 9 8.24008 8.89101 8.45399C8.79513 8.64215 8.64215 8.79513 8.45399 8.89101C8.24008 9 7.96005 9 7.4 9H4.6C4.03995 9 3.75992 9 3.54601 9.10899C3.35785 9.20487 3.20487 9.35785 3.10899 9.54601C3 9.75992 3 10.0399 3 10.6V13.4C3 13.9601 3 14.2401 3.10899 14.454C3.20487 14.6422 3.35785 14.7951 3.54601 14.891C3.75992 15 4.03995 15 4.6 15H7.4C7.96005 15 8.24008 15 8.45399 15.109C8.64215 15.2049 8.79513 15.3578 8.89101 15.546C9 15.7599 9 16.0399 9 16.6V19.4C9 19.9601 9 20.2401 9.10899 20.454C9.20487 20.6422 9.35785 20.7951 9.54601 20.891C9.75992 21 10.0399 21 10.6 21H13.4C13.9601 21 14.2401 21 14.454 20.891C14.6422 20.7951 14.7951 20.6422 14.891 20.454C15 20.2401 15 19.9601 15 19.4V16.6C15 16.0399 15 15.7599 15.109 15.546C15.2049 15.3578 15.3578 15.2049 15.546 15.109C15.7599 15 16.0399 15 16.6 15H19.4C19.9601 15 20.2401 15 20.454 14.891C20.6422 14.7951 20.7951 14.6422 20.891 14.454C21 14.2401 21 13.9601 21 13.4V10.6C21 10.0399 21 9.75992 20.891 9.54601C20.7951 9.35785 20.6422 9.20487 20.454 9.10899C20.2401 9 19.9601 9 19.4 9L16.6 9C16.0399 9 15.7599 9 15.546 8.89101C15.3578 8.79513 15.2049 8.64215 15.109 8.45399C15 8.24008 15 7.96005 15 7.4V4.6Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="arrow-up-right" viewBox="0 0 24 24" fill="none">
<path d="M6 18L18 6M18 6H10M18 6V14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="sats" viewBox="0 0 24 25" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M21 12.5C21 13.6819 20.7672 14.8522 20.3149 15.9442C19.8626 17.0361 19.1997 18.0282 18.364 18.864C17.5282 19.6997 16.5361 20.3626 15.4442 20.8149C14.3522 21.2672 13.1819 21.5 12 21.5C10.8181 21.5 9.64778 21.2672 8.55585 20.8149C7.46392 20.3626 6.47177 19.6997 5.63604 18.864C4.80031 18.0282 4.13738 17.0361 3.68508 15.9442C3.23279 14.8522 3 13.6819 3 12.5C3 10.1131 3.94821 7.82387 5.63604 6.13604C7.32387 4.44821 9.61305 3.5 12 3.5C14.3869 3.5 16.6761 4.44821 18.364 6.13604C20.0518 7.82387 21 10.1131 21 12.5ZM8.693 9.242L16.33 11.305L16.667 9.843L9.029 7.78L8.693 9.242ZM14.219 6.192L13.813 7.966L12.365 7.574L12.772 5.8L14.219 6.192ZM11.227 19.2L11.635 17.426L10.187 17.035L9.779 18.809L11.227 19.2ZM15.648 14.266L8.011 12.2L8.347 10.738L15.984 12.804L15.648 14.266ZM7.332 15.156L14.97 17.22L15.306 15.758L7.668 13.694L7.332 15.156Z" fill="currentColor"/>
</symbol>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 129 KiB

View File

@ -75,7 +75,6 @@
}
html {
scroll-behavior: smooth;
-webkit-tap-highlight-color: transparent;
}
@ -481,22 +480,6 @@ input:disabled {
cursor: not-allowed;
}
.f-1 {
flex: 1;
}
.f-2 {
flex: 2;
}
.f-3 {
flex: 3;
}
.f-4 {
flex: 4;
}
.f-ellipsis {
min-width: 0;
white-space: nowrap;

View File

@ -8,7 +8,7 @@ import {
flat_merge,
get_diff,
pow,
schnorr_verify,
schnorr_verify_event,
default as wasmInit,
} from "@snort/system-wasm";
import WasmPath from "@snort/system-wasm/pkg/system_wasm_bg.wasm";
@ -19,7 +19,7 @@ import { createBrowserRouter, RouteObject, RouterProvider } from "react-router-d
import {
NostrSystem,
ProfileLoaderService,
QueryOptimizer,
Optimizer,
FlatReqFilter,
ReqFilter,
PowMiner,
@ -28,6 +28,7 @@ import {
PowWorker,
encodeTLVEntries,
socialGraphInstance,
TaggedNostrEvent,
} from "@snort/system";
import PowWorkerURL from "@snort/system/src/pow-worker.ts?worker&url";
import { SnortContext } from "@snort/system-react";
@ -62,8 +63,12 @@ import { AboutPage } from "@/Pages/About";
import { OnboardingRoutes } from "@/Pages/onboarding";
import { setupWebLNWalletConfig } from "@/Wallet/WebLN";
import { Wallets } from "@/Wallet";
import Fuse from "fuse.js";
import NetworkGraph from "@/Pages/NetworkGraph";
import WalletPage from "./Pages/WalletPage";
import IndexedDBWorker from "./Cache/IndexedDB?worker";
import * as Comlink from "comlink";
import { addEventToFuzzySearch } from "@/FuzzySearch";
declare global {
interface Window {
@ -71,7 +76,7 @@ declare global {
}
}
const WasmQueryOptimizer = {
const WasmOptimizer = {
expandFilter: (f: ReqFilter) => {
return expand_filter(f) as Array<FlatReqFilter>;
},
@ -84,10 +89,10 @@ const WasmQueryOptimizer = {
compress: (all: Array<ReqFilter>) => {
return compress(all) as Array<ReqFilter>;
},
schnorrVerify: (id, sig, pubkey) => {
return schnorr_verify(id, sig, pubkey);
schnorrVerify: ev => {
return schnorr_verify_event(ev);
},
} as QueryOptimizer;
} as Optimizer;
export class WasmPowWorker implements PowMiner {
minePow(ev: NostrEvent, target: number): Promise<NostrEvent> {
@ -100,14 +105,16 @@ const hasWasm = "WebAssembly" in globalThis;
const DefaultPowWorker = hasWasm ? undefined : new PowWorker(PowWorkerURL);
export const GetPowWorker = () => (hasWasm ? new WasmPowWorker() : unwrap(DefaultPowWorker));
const indexedDB = Comlink.wrap(new IndexedDBWorker());
/**
* Singleton nostr system
*/
const System = new NostrSystem({
export const System = new NostrSystem({
relayCache: UserRelays,
profileCache: UserCache,
relayMetrics: RelayMetrics,
queryOptimizer: hasWasm ? WasmQueryOptimizer : undefined,
optimizer: hasWasm ? WasmOptimizer : undefined,
db: SystemDb,
});
@ -119,47 +126,31 @@ System.on("auth", async (c, r, cb) => {
}
});
export type FuzzySearchResult = {
pubkey: string;
name?: string;
display_name?: string;
nip05?: string;
};
export const fuzzySearch = new Fuse<FuzzySearchResult>([], {
keys: ["name", "display_name", { name: "nip05", weight: 0.5 }],
threshold: 0.3,
// sortFn here?
});
const profileTimestamps = new Map<string, number>();
// how to also add entries from ProfileCache?
System.on("event", ev => {
if (ev.kind === 0) {
const existing = profileTimestamps.get(ev.pubkey);
if (existing) {
if (existing > ev.created_at) {
return;
}
fuzzySearch.remove(doc => doc.pubkey === ev.pubkey);
}
profileTimestamps.set(ev.pubkey, ev.created_at);
try {
const data = JSON.parse(ev.content);
if (ev.pubkey && (data.name || data.display_name || data.nip05)) {
data.pubkey = ev.pubkey;
fuzzySearch.add(data);
}
} catch (e) {
console.error(e);
}
}
if (ev.kind === 3) {
socialGraphInstance.handleFollowEvent(ev);
System.on("event", (_, ev) => {
addEventToFuzzySearch(ev);
socialGraphInstance.handleEvent(ev);
if (CONFIG.useIndexedDBEvents && socialGraphInstance.getFollowDistance(ev.pubkey) <= 2) {
indexedDB.handleEvent(ev);
}
});
if (CONFIG.useIndexedDBEvents) {
// load all profiles
indexedDB.find(
{ kinds: [0] },
Comlink.proxy((e: TaggedNostrEvent) => System.HandleEvent(e)),
);
System.on("request", (filter: ReqFilter) => {
indexedDB.find(
filter,
Comlink.proxy((e: TaggedNostrEvent) => {
System.HandleEvent(e);
}),
);
});
}
async function fetchProfile(key: string) {
try {
throwIfOffline();
@ -279,6 +270,14 @@ const mainRoutes = [
path: "/graph",
element: <NetworkGraph />,
},
{
path: "/wallet",
element: (
<div className="p">
<WalletPage showHistory={true} />
</div>
),
},
...OnboardingRoutes,
...SettingsRoutes,
] as Array<RouteObject>;
@ -296,7 +295,7 @@ if (CONFIG.features.subscriptions) {
// add catch all route
mainRoutes.push({
path: "/*",
path: "/:link",
element: <NostrLinkHandler />,
});

View File

@ -75,6 +75,9 @@
"0HFX0T": {
"defaultMessage": "Use Exact Location"
},
"0MndVW": {
"defaultMessage": "Generic LNDHub wallet (BTCPayServer / Alby / LNBits)"
},
"0jOEtS": {
"defaultMessage": "Invalid LNURL"
},
@ -156,12 +159,18 @@
"3KNMbJ": {
"defaultMessage": "Articles"
},
"3QwfJR": {
"defaultMessage": "~{amount}"
},
"3cc4Ct": {
"defaultMessage": "Light"
},
"3gOsZq": {
"defaultMessage": "Translators"
},
"3natuV": {
"defaultMessage": "Cashu mint wallet"
},
"3qnJlS": {
"defaultMessage": "You are voting with {amount} sats"
},
@ -201,6 +210,9 @@
"4Z3t5i": {
"defaultMessage": "Use imgproxy to compress images"
},
"4emo2p": {
"defaultMessage": "Missing Relays"
},
"4rYCjn": {
"defaultMessage": "Note to Self"
},
@ -234,6 +246,9 @@
"62nsdy": {
"defaultMessage": "Retry"
},
"6559gb": {
"defaultMessage": "New follow list length {length}"
},
"65BmHb": {
"defaultMessage": "Failed to proxy image from {host}, click here to load directly"
},
@ -392,6 +407,12 @@
"CHTbO3": {
"defaultMessage": "Failed to load invoice"
},
"CM+Cfj": {
"defaultMessage": "Follow List"
},
"CM0k0d": {
"defaultMessage": "Prune follow list"
},
"CVWeJ6": {
"defaultMessage": "Trending People"
},
@ -443,6 +464,9 @@
"Dx4ey3": {
"defaultMessage": "Toggle all"
},
"E5ZIPD": {
"defaultMessage": "<big>{amount}</big> <small>sats</small>"
},
"EJbFi7": {
"defaultMessage": "Search notes"
},
@ -570,6 +594,9 @@
"HqRNN8": {
"defaultMessage": "Support"
},
"I1AoOu": {
"defaultMessage": "Last post {time}"
},
"IEwZvs": {
"defaultMessage": "Are you sure you want to unpin this note?"
},
@ -930,9 +957,6 @@
"VL900k": {
"defaultMessage": "Recommended Relays"
},
"VN0+Fz": {
"defaultMessage": "Balance: {amount} sats"
},
"VOjC1i": {
"defaultMessage": "Pick which upload service you want to upload attachments to"
},
@ -978,6 +1002,12 @@
"XICsE8": {
"defaultMessage": "File hosts"
},
"XPB8VV": {
"defaultMessage": "Alby wallet connection"
},
"XQiFEl": {
"defaultMessage": "Follows Relay Health"
},
"XXm7jJ": {
"defaultMessage": "Trending Hashtags"
},
@ -1033,6 +1063,9 @@
"aMaLBK": {
"defaultMessage": "Supported Extensions"
},
"aSGz4J": {
"defaultMessage": "Connect to your own LND node with Lightning Node Connect"
},
"aWpBzj": {
"defaultMessage": "Show more"
},
@ -1048,6 +1081,9 @@
"bG00/W": {
"defaultMessage": "Service Worker Running"
},
"bJ+wrA": {
"defaultMessage": "Compute prune list"
},
"bLZL5a": {
"defaultMessage": "Get Address"
},
@ -1078,6 +1114,9 @@
"cFbU1B": {
"defaultMessage": "Using Alby? Go to {link} to get your NWC config!"
},
"cG/bKQ": {
"defaultMessage": "Native nostr wallet connection"
},
"cHCwbF": {
"defaultMessage": "Photography"
},
@ -1109,10 +1148,6 @@
"d2ebEu": {
"defaultMessage": "Not Subscribed to Push"
},
"d6CyG5": {
"defaultMessage": "History",
"description": "Wallet transation history"
},
"d7d0/x": {
"defaultMessage": "LN Address"
},
@ -1227,6 +1262,9 @@
"h8XMJL": {
"defaultMessage": "Badges"
},
"hF6IN2": {
"defaultMessage": "Prune Follow List"
},
"hMzcSq": {
"defaultMessage": "Messages"
},
@ -1266,6 +1304,9 @@
"iGT1eE": {
"defaultMessage": "Prevent fake accounts from imitating you"
},
"iICVoL": {
"defaultMessage": "{x} follows ({y} duplicates)"
},
"iNWbVV": {
"defaultMessage": "Handle"
},
@ -1399,6 +1440,12 @@
"nGGDsi": {
"defaultMessage": "Notifications Allowed"
},
"nIchMQ": {
"defaultMessage": "Searching for account activity ({progress})"
},
"nUT0Lv": {
"defaultMessage": "Tools"
},
"nihgfo": {
"defaultMessage": "Listen to this article"
},
@ -1423,6 +1470,9 @@
"p85Uwy": {
"defaultMessage": "Active Subscriptions"
},
"p9Ps2l": {
"defaultMessage": "{x}/{y} have relays ({percent})"
},
"pI+77w": {
"defaultMessage": "Downloadable backups from Snort relay"
},
@ -1432,6 +1482,10 @@
"puLNUJ": {
"defaultMessage": "Pin"
},
"pukxg/": {
"defaultMessage": "Payments",
"description": "Wallet transation history"
},
"pzTOmv": {
"defaultMessage": "Followers"
},
@ -1522,6 +1576,9 @@
"thnRpU": {
"defaultMessage": "Getting NIP-05 verified can help:"
},
"tj6kdX": {
"defaultMessage": "{sign} {amount} sats"
},
"tjpYlr": {
"defaultMessage": "Relay Metrics"
},
@ -1567,6 +1624,9 @@
"vN5UH8": {
"defaultMessage": "Profile Image"
},
"vU/Q5i": {
"defaultMessage": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months"
},
"vZ4quW": {
"defaultMessage": "NIP-05 is a DNS based verification spec which helps to validate you as a real user."
},
@ -1579,9 +1639,6 @@
"voxBKC": {
"defaultMessage": "Followed by friends"
},
"vrTOHJ": {
"defaultMessage": "{amount} sats"
},
"vxwnbh": {
"defaultMessage": "Amount of work to apply to all published events"
},

View File

@ -7,10 +7,34 @@ import { NostrLink, NostrPrefix, TLVEntryType, encodeTLVEntries, tryParseNostrLi
import { formatShort } from "@/Number";
import { defaultAvatar, hexToBech32 } from "@/SnortUtils";
import { clientsClaim } from "workbox-core";
import { registerRoute } from "workbox-routing";
import { CacheFirst, StaleWhileRevalidate } from "workbox-strategies";
import { PrecacheEntry, precacheAndRoute } from "workbox-precaching";
import { ExpirationPlugin } from "workbox-expiration";
precacheAndRoute(self.__WB_MANIFEST);
clientsClaim();
registerRoute(
({ url }) => url.pathname.endsWith("/.well-known/nostr.json"),
new StaleWhileRevalidate({
cacheName: "nostr-json-cache",
plugins: [new ExpirationPlugin({ maxAgeSeconds: 4 * 60 * 60 })],
}),
);
// Cache images from any domain
registerRoute(
// Match any image request regardless of the origin
({ request }) => request.destination === "image",
new CacheFirst({
cacheName: "image-cache",
plugins: [
new ExpirationPlugin({
maxEntries: 100,
}),
],
}),
);
self.addEventListener("message", event => {
if (event.data && event.data.type === "SKIP_WAITING") {

View File

@ -51,6 +51,7 @@
"2zJXeA": "Profiles",
"39AHJm": "تسجيل الدخول",
"3KNMbJ": "مقالات",
"3QwfJR": "~{amount}",
"3cc4Ct": "فاتح",
"3gOsZq": "المترجمون",
"3qnJlS": "أنت تصوت بـ {amount} ساتوشي",
@ -66,6 +67,7 @@
"4OB335": "استهجان",
"4Vmpt4": "Nostr Plebs هي من أوائل مزودي NIP-05 وتقدم مجموعة من المعرفات بأسعار معقولة",
"4Z3t5i": "استخدم imgproxy لضغط الصور",
"4emo2p": "Missing Relays",
"4rYCjn": "ملاحظة خاصة",
"5BVs2e": "zap",
"5CB6zB": "Zap Splits",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "شاهد إعادة العرض",
"62nsdy": "إعادة المحاولة",
"6559gb": "New follow list length {length}",
"65BmHb": "فشلت الصورة الوكيل من {host}، انقر هنا للتحميل مباشرة",
"6OSOXl": "السبب: <i>{reason}</i>",
"6bgpn+": "ليس كل العملاء يدعمون هذا، قد لا تزال تتلقى بعض الزبابيك كما لو أن تقسيم الزلاب لم يتم تكوينه",
@ -129,6 +132,8 @@
"C81/uG": "تسجيل الخروج",
"C8HhVE": "المتابعة المقترحة",
"CHTbO3": "فشل تحميل البرقية",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "الأشخاص المتظاهرون",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "تحويل",
"Dx4ey3": "تبديل الكل",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "البحث في الملاحظات",
"ELbg9p": "مزودي البيانات",
"EQKRE4": "إظهار الشارات على صفحات الملف الشخصي",
@ -188,6 +194,7 @@
"HbefNb": "افتح المحفظة",
"HhcAVH": "أنت لا تتبع هذا الشخص، انقر هنا لتحميل الوسائط من <i>{link}</i>، أو قم بتحديث <a><i>تفضيلاتك</i></a> لتحميل الوسائط دائما من الجميع.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "هل أنت متأكد من ازالة تثبيت هذا المنشور؟",
"IKKHqV": "يتبع",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "لديك حساب iris.to نشط",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "الرصيد: {amount} ساتوشي",
"VOjC1i": "اختر خدمة التحميل التي تريد رفع المرفقات إليها",
"VR5eHw": "مفتاح عام (npub/nprofile)",
"VcwrfF": "نعم من فضلك",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "إرسال مقاييس الاستخدام",
"XICsE8": "مضيفي الملفات",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "تفاعل",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -345,6 +352,7 @@
"b5vAk0": "سيكون معرفك بمثابة عنوان برق وستتم اعادة التوجيه الى عنوان البرق أو LNURL الذي تختاره",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "الحصول على العنوان",
"bMphls": "Logged in with read-only access",
"bQdA2k": "محتوى حساس",
@ -365,7 +373,6 @@
"cyR7Kh": "الخلف",
"d+6YsV": "قوائم لكتم الصوت:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "التاريخ",
"d7d0/x": "عنوان البرق",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "اسم العرض",
@ -404,6 +411,7 @@
"grQ+mI": "دليل العمل",
"h7jvCs": "{site} is more fun together!",
"h8XMJL": "الأوسمة",
"hF6IN2": "Prune Follow List",
"hMzcSq": "رسائل",
"hRTfTR": "PRO",
"hY4lzx": "يدعم",
@ -417,6 +425,7 @@
"iCqGww": "التفاعل ({n})",
"iEoXYx": "ترجمة DeepL",
"iGT1eE": "منع الحسابات المزيفة من انتحال حسابك",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "معرف",
"iXPL0Z": "لا يمكن تسجيل الدخول باستخدام المفتاح الخاص خلال اتصال غير آمن، الرجاء استخدم إضافة مدير مفاتيح نوستر بدلاً من ذلك",
"iYc3Ld": "الدفعات",
@ -461,6 +470,8 @@
"nDejmx": "رفع الحظر",
"nGBrvw": "المنشورات المرجعية",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "الاستماع إلى هذه المقالة",
"nwZXeh": "محظور {n}",
"o7e+nJ": "{n} المتابِعون",
@ -469,9 +480,11 @@
"ojzbwv": "مهلا، يبدو أنك لا تملك عنوان نوستر بعد، يجب أن تحصل على واحدة! تحقق من {link}",
"p4N05H": "تحميل",
"p85Uwy": "الاشتراكات النشطة",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "نسخ احتياطية قابلة للتنزيل من موصل سنورت",
"pRess9": "ZapPool",
"puLNUJ": "تثبيت",
"pukxg/": "Payments",
"pzTOmv": "المتابِعون",
"qD9EUF": "البريد الإلكتروني <> جسر DM لعنوان Snort الخاص بك",
"qDwvZ4": "خطأ غير معروف",
@ -502,6 +515,7 @@
"tOdNiY": "داكن",
"th5lxp": "إرسال ملاحظة إلى مجموعة فرعية من مرحلات الكتابة الخاصة بك",
"thnRpU": "يمكن أن يساعد الحصول على معرف التوثيق NIP-05 في:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "مقاييس الترحيل",
"ttxS0b": "وسام الداعم",
"u+LyXc": "التفاعلات",
@ -517,11 +531,11 @@
"v8lolG": "بدء الدردشة",
"vB3oQ/": "يجب أن تكون قائمة جهات اتصال أو قائمة حلاقة",
"vN5UH8": "صورة الملف الشخصي",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 هو أحد طرق التحقق المستندة إلى DNS والتي تساعد في التحقق من هويتك كمستخدم حقيقي.",
"vhlWFg": "خيارات الاستطلاع",
"vlbWtt": "احصل على واحد مجاني",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} ساتوشي",
"vxwnbh": "مقدار العمل المراد تطبيقه على جميع الأحداث المنشورة",
"w1Fanr": "الأعمال",
"w6qrwX": "NSFW",

View File

@ -51,6 +51,7 @@
"2zJXeA": "Profile",
"39AHJm": "Registrieren",
"3KNMbJ": "Artikel",
"3QwfJR": "~{amount}",
"3cc4Ct": "Hell",
"3gOsZq": "Übersetzer",
"3qnJlS": "Du stimmst mit {amount} sats ab",
@ -66,6 +67,7 @@
"4OB335": "Gefällt nicht",
"4Vmpt4": "Nostr Plebs ist einer der ersten NIP-05 Anbieter und bietet eine gute Auswahl von Domains zu fairen Preisen",
"4Z3t5i": "Verwende imgproxy um Bilder zu komprimieren",
"4emo2p": "Fehlende Relais",
"4rYCjn": "Notiz an mich selbst",
"5BVs2e": "Zap",
"5CB6zB": "Zap-Aufteilungen",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Wiederholung anschauen",
"62nsdy": "Erneut versuchen",
"6559gb": "Neue Länge der Follow-Liste {length}",
"65BmHb": "Bild von {host} konnte nicht durch Proxy geladen werden, klicke hier, um es direkt zu laden",
"6OSOXl": "Grund: <i>{reason}</i>",
"6bgpn+": "Nicht alle Clients unterstützen dies, deshalb kann es sein, dass du immer noch einige Zaps erhältst, als ob Zap-Aufteilungen nicht konfiguriert wäre",
@ -129,6 +132,8 @@
"C81/uG": "Abmelden",
"C8HhVE": "Vorgeschlagene Follows",
"CHTbO3": "Lightning Zahlungsanforderung konnte nicht geladen werden",
"CM+Cfj": "Follow-Liste",
"CM0k0d": "Follow-Liste kürzen",
"CVWeJ6": "Angesagte Personen",
"CYkOCI": "und {count} anderen, denen du folgst",
"CbM2hK": "Angesagte Hashtags",
@ -146,6 +151,7 @@
"DrZqav": "\"Über\" muss weniger als {limit} Zeichen enthalten",
"DtYelJ": "Transferieren",
"Dx4ey3": "Alle umschalten",
"E5ZIPD": "<big>{amount}</big> <small>Sats</small>",
"EJbFi7": "Notes suchen",
"ELbg9p": "Datenanbieter",
"EQKRE4": "Abzeichen auf Profilseiten anzeigen",
@ -188,6 +194,7 @@
"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?",
"IKKHqV": "Folgt",
"IOu4Xh": "Du musst ein {tier} Abonnent sein, um auf {app} Deck zugreifen zu können",
@ -215,7 +222,7 @@
"KAhAcM": "LNDHub Konfiguration eingeben",
"KHK8B9": "Relais",
"KQvWvD": "Gelöscht",
"KahimY": "Unbekannte Event Art: {kind}",
"KahimY": "Unbekannte Event-Art: {kind}",
"KoFlZg": "Mint-URL eingeben",
"KtsyO0": "PIN eingeben",
"LF5kYT": "Weitere Verbindungen",
@ -306,7 +313,6 @@
"UrKTqQ": "Du hast ein aktives iris.to Konto",
"UxgyeY": "Dein Empfehlungscode ist {code}",
"VL900k": "Empfohlene Relais",
"VN0+Fz": "Guthaben: {amount} Sats",
"VOjC1i": "Wähle einen Upload-Dienst für deine Anhänge",
"VR5eHw": "Öffentlicher Schlüssel (npub/nprofile)",
"VcwrfF": "Ja, bitte",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "Nutzungsmetriken senden",
"XICsE8": "Datei-Hosts",
"XQiFEl": "Folgt-Relais-Gesundheit",
"XXm7jJ": "Angesagte Hashtags",
"XgWvGA": "Reaktionen",
"XhpBfA": "{site} ist ein Open-Source-Projekt, das von passionierten Menschen in ihrer Freizeit entwickelt wird. Eure Spenden werden sehr geschätzt",
@ -345,6 +352,7 @@
"b5vAk0": "Dein Handle ist wie eine Lightning-Adresse und leitet dich zu deiner gewählten LNURL oder Lightning-Adresse weiter",
"bF1MYT": "Du bist ein Community-Anführer und verdienst <b>{percent}</b> an den Abonnements der angeworbenen Nutzer!",
"bG00/W": "Service-Worker läuft",
"bJ+wrA": "Kürzungsliste berechnen",
"bLZL5a": "Adresse erhalten",
"bMphls": "Eingeloggt mit schreibgeschütztem Zugriff",
"bQdA2k": "Empfindlicher Inhalt",
@ -365,7 +373,6 @@
"cyR7Kh": "Zurück",
"d+6YsV": "Listen zum Stummschalten:",
"d2ebEu": "Push nicht abonniert",
"d6CyG5": "Verlauf",
"d7d0/x": "LN-Adresse",
"d8gpCh": "Versuche, weniger als 5 Hashtags zu verwenden, um beim Thema zu bleiben 🙏",
"dOQCL8": "Anzeigename",
@ -404,6 +411,7 @@
"grQ+mI": "Arbeitsnachweis",
"h7jvCs": "{site} macht gemeinsam mehr Spaß!",
"h8XMJL": "Auszeichnungen",
"hF6IN2": "Follow-Liste kürzen",
"hMzcSq": "Nachrichten",
"hRTfTR": "PRO",
"hY4lzx": "Unterstützt",
@ -417,6 +425,7 @@
"iCqGww": "Reaktionen ({n})",
"iEoXYx": "DeepL Übersetzungen",
"iGT1eE": "Verhindere, dass gefälschte Konten dich imitieren",
"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": "Zahlungen",
@ -461,6 +470,8 @@
"nDejmx": "Freigeben",
"nGBrvw": "Lesezeichen",
"nGGDsi": "Benachrichtigungen erlaubt",
"nIchMQ": "Suche nach Kontoaktivität ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "Diesen Artikel anhören",
"nwZXeh": "{n} blockiert",
"o7e+nJ": "{n} Follower",
@ -469,9 +480,11 @@
"ojzbwv": "Hey, es sieht so aus, als hättest du noch keine Nostr-Adresse, du solltest eine anlegen! Sieh dir {link} mal an",
"p4N05H": "Hochladen",
"p85Uwy": "Aktive Abonnements",
"p9Ps2l": "{x}/{y} haben Relais ({percent})",
"pI+77w": "Herunterladbare Backups vom Snort Relais",
"pRess9": "Zap-Pool",
"puLNUJ": "Anheften",
"pukxg/": "Zahlungen",
"pzTOmv": "Follower",
"qD9EUF": "E-Mail <> DM Brücke für deine Snort Nostr-Adresse",
"qDwvZ4": "Unbekannter Fehler",
@ -502,6 +515,7 @@
"tOdNiY": "Dunkel",
"th5lxp": "Sende Note zu einer Untergruppe deiner Schreib-Relays",
"thnRpU": "Eine NIP-05 Verifizierung kann helfen:",
"tj6kdX": "{sign} {amount} Sats",
"tjpYlr": "Relais Metriken",
"ttxS0b": "Unterstützer-Abzeichen",
"u+LyXc": "Interaktionen",
@ -517,11 +531,11 @@
"v8lolG": "Chat starten",
"vB3oQ/": "Muss eine Kontaktliste oder Pubkey-Liste sein",
"vN5UH8": "Profilbild",
"vU/Q5i": "Dieses Tool sucht nach dem letzten Event, das von allen deinen Follows veröffentlicht wurde, und entfernt diejenigen, die in den letzten 6 Monaten nichts veröffentlicht haben.",
"vZ4quW": "NIP-05 ist eine DNS-basierte Verifizierungsspezifikation, die dabei hilft, dich als echten Benutzer zu validieren.",
"vhlWFg": "Umfrageoptionen",
"vlbWtt": "Eine kostenlos erhalten",
"voxBKC": "Von Freunden gefolgt",
"vrTOHJ": "{amount} sats",
"vxwnbh": "Maß an Arbeit, die auf alle veröffentlichte Events angewendet werden soll",
"w1Fanr": "Business",
"w6qrwX": "NSFW",

View File

@ -24,6 +24,7 @@
"0Azlrb": "Manage",
"0BUTMv": "Search...",
"0HFX0T": "Use Exact Location",
"0MndVW": "Generic LNDHub wallet (BTCPayServer / Alby / LNBits)",
"0jOEtS": "Invalid LNURL",
"0mch2Y": "name has disallowed characters",
"0siT4z": "Politics",
@ -51,8 +52,10 @@
"2zJXeA": "Profiles",
"39AHJm": "Sign Up",
"3KNMbJ": "Articles",
"3QwfJR": "~{amount}",
"3cc4Ct": "Light",
"3gOsZq": "Translators",
"3natuV": "Cashu mint wallet",
"3qnJlS": "You are voting with {amount} sats",
"3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}",
"3tVy+Z": "{n} Followers",
@ -66,6 +69,7 @@
"4OB335": "Dislike",
"4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices",
"4Z3t5i": "Use imgproxy to compress images",
"4emo2p": "Missing Relays",
"4rYCjn": "Note to Self",
"5BVs2e": "zap",
"5CB6zB": "Zap Splits",
@ -77,6 +81,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Watch Replay",
"62nsdy": "Retry",
"6559gb": "New follow list length {length}",
"65BmHb": "Failed to proxy image from {host}, click here to load directly",
"6OSOXl": "Reason: <i>{reason}</i>",
"6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured",
@ -129,6 +134,8 @@
"C81/uG": "Logout",
"C8HhVE": "Suggested Follows",
"CHTbO3": "Failed to load invoice",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "Trending People",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +153,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "Transfer",
"Dx4ey3": "Toggle all",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Search notes",
"ELbg9p": "Data Providers",
"EQKRE4": "Show badges on profile pages",
@ -188,6 +196,7 @@
"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?",
"IKKHqV": "Follows",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +315,6 @@
"UrKTqQ": "You have an active iris.to account",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "Balance: {amount} sats",
"VOjC1i": "Pick which upload service you want to upload attachments to",
"VR5eHw": "Public key (npub/nprofile)",
"VcwrfF": "Yes please",
@ -322,6 +330,8 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "Send usage metrics",
"XICsE8": "File hosts",
"XPB8VV": "Alby wallet connection",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "Reactions",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -340,11 +350,13 @@
"a7TDNm": "Notes will stream in real time into global and notes tab",
"aHje0o": "Name or nym",
"aMaLBK": "Supported Extensions",
"aSGz4J": "Connect to your own LND node with Lightning Node Connect",
"aWpBzj": "Show more",
"b12Goz": "Mnemonic",
"b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "Get Address",
"bMphls": "Logged in with read-only access",
"bQdA2k": "Sensitive Content",
@ -355,6 +367,7 @@
"c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}",
"c3g2hL": "Broadcast Again",
"cFbU1B": "Using Alby? Go to {link} to get your NWC config!",
"cG/bKQ": "Native nostr wallet connection",
"cHCwbF": "Photography",
"cPIKU2": "Following",
"cQfLWb": "URL..",
@ -365,7 +378,6 @@
"cyR7Kh": "Back",
"d+6YsV": "Lists to mute:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "History",
"d7d0/x": "LN Address",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "Display name",
@ -404,6 +416,7 @@
"grQ+mI": "Proof of Work",
"h7jvCs": "{site} is more fun together!",
"h8XMJL": "Badges",
"hF6IN2": "Prune Follow List",
"hMzcSq": "Messages",
"hRTfTR": "PRO",
"hY4lzx": "Supports",
@ -417,6 +430,7 @@
"iCqGww": "Reactions ({n})",
"iEoXYx": "DeepL translations",
"iGT1eE": "Prevent fake accounts from imitating you",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "Handle",
"iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead",
"iYc3Ld": "Payments",
@ -461,6 +475,8 @@
"nDejmx": "Unblock",
"nGBrvw": "Bookmarks",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "Listen to this article",
"nwZXeh": "{n} blocked",
"o7e+nJ": "{n} followers",
@ -469,9 +485,11 @@
"ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}",
"p4N05H": "Upload",
"p85Uwy": "Active Subscriptions",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "Downloadable backups from Snort relay",
"pRess9": "ZapPool",
"puLNUJ": "Pin",
"pukxg/": "Payments",
"pzTOmv": "Followers",
"qD9EUF": "Email <> DM bridge for your Snort nostr address",
"qDwvZ4": "Unknown error",
@ -502,6 +520,7 @@
"tOdNiY": "Dark",
"th5lxp": "Send note to a subset of your write relays",
"thnRpU": "Getting NIP-05 verified can help:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Relay Metrics",
"ttxS0b": "Supporter Badge",
"u+LyXc": "Interactions",
@ -517,11 +536,11 @@
"v8lolG": "Start chat",
"vB3oQ/": "Must be a contact list or pubkey list",
"vN5UH8": "Profile Image",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.",
"vhlWFg": "Poll Options",
"vlbWtt": "Get a free one",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} sats",
"vxwnbh": "Amount of work to apply to all published events",
"w1Fanr": "Business",
"w6qrwX": "NSFW",

View File

@ -51,6 +51,7 @@
"2zJXeA": "Perfiles",
"39AHJm": "Inscribirse",
"3KNMbJ": "Artículos",
"3QwfJR": "~{amount}",
"3cc4Ct": "Claro",
"3gOsZq": "traductores",
"3qnJlS": "Estás votando con {amount} sats",
@ -66,6 +67,7 @@
"4OB335": "No me gusta",
"4Vmpt4": "Nostr Plbes es uno de los primeros proveedores de NIP-05 y ofrece una buena colección de dominios a precios razonables.",
"4Z3t5i": "Usar imgproxy para comprimir imágenes",
"4emo2p": "Missing Relays",
"4rYCjn": "Mensajes guardados",
"5BVs2e": "zap",
"5CB6zB": "Zap Splits",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Ver la repetición",
"62nsdy": "Reintentar",
"6559gb": "New follow list length {length}",
"65BmHb": "Fallo al enviar la imagen desde {host}, haga clic aquí para cargarla directamente.",
"6OSOXl": "Razón: <i>{reason}</i>",
"6bgpn+": "No todos los clientes lo soportan, puede que aún reciba algunos zaps como si zap splits no estuviera configurado",
@ -129,6 +132,8 @@
"C81/uG": "Salir",
"C8HhVE": "Sugerencias",
"CHTbO3": "Error al cargar factura",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "Personas de moda",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "Transferir",
"Dx4ey3": "Cambiar todo",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Notas de búsqueda",
"ELbg9p": "Proveedores de datos",
"EQKRE4": "Mostrar insignias en las páginas de perfil",
@ -188,6 +194,7 @@
"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?",
"IKKHqV": "Sigue",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "Tienes una cuenta activa en iris.to",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "Saldo: {amount} sats",
"VOjC1i": "Elige qué servicio de subida de ficheros quieres utilizar",
"VR5eHw": "Clave pública (npub/nprofile)",
"VcwrfF": "Sí, por favor.",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemónico",
"XECMfW": "Enviar métricas de uso",
"XICsE8": "Alojamientos de archivos",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "Reacciones",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -345,6 +352,7 @@
"b5vAk0": "Tu usuario actuará como una dirección de relámpago y redirigirá a tu LNURL o dirección de relámpago seleccionada",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "Obtener dirección",
"bMphls": "Logged in with read-only access",
"bQdA2k": "Contenido sensible",
@ -365,7 +373,6 @@
"cyR7Kh": "Atrás",
"d+6YsV": "Listas para silenciar:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "Historial",
"d7d0/x": "Dirección Lightning",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "Nombre",
@ -404,6 +411,7 @@
"grQ+mI": "Prueba de trabajo",
"h7jvCs": "{site} ¡es más divertido juntos!",
"h8XMJL": "Medallas",
"hF6IN2": "Prune Follow List",
"hMzcSq": "Mensajes",
"hRTfTR": "PRO",
"hY4lzx": "Soporta",
@ -417,6 +425,7 @@
"iCqGww": "Reacciones ({n})",
"iEoXYx": "Traducción de DeepL",
"iGT1eE": "Evita que cuentas falsas se hagan pasar por ti",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "Usuario",
"iXPL0Z": "No se puede iniciar sesión con la clave privada en una conexión insegura, por favor utilice una extensión gestora de claves de Nostr",
"iYc3Ld": "Pagos",
@ -461,6 +470,8 @@
"nDejmx": "Desbloquear",
"nGBrvw": "Favoritos",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "Escuche este artículo",
"nwZXeh": "{n} bloqueados",
"o7e+nJ": "{n} seguidores",
@ -469,9 +480,11 @@
"ojzbwv": "Oye, parece que aún no tienes una dirección Nostr, ¡deberías conseguir una! Echa un vistazo a {link}",
"p4N05H": "Cargar",
"p85Uwy": "Suscripciones activas",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "Copias de seguridad descargables desde relé Snort",
"pRess9": "ZapPool",
"puLNUJ": "Pin",
"pukxg/": "Payments",
"pzTOmv": "Seguidores",
"qD9EUF": "Correo electrónico <> PM puente para su dirección Snort nostr",
"qDwvZ4": "Error desconocido",
@ -502,6 +515,7 @@
"tOdNiY": "Oscuro",
"th5lxp": "Enviar nota a un subconjunto de sus relés de escritura",
"thnRpU": "Obtener un NIP-05 ayuda a:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Métricas de relés",
"ttxS0b": "Insignia de Apoyo",
"u+LyXc": "Interacciones",
@ -517,11 +531,11 @@
"v8lolG": "Iniciar chat",
"vB3oQ/": "Debe ser una lista de contactos o una lista pubkey",
"vN5UH8": "Imagen de perfil",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 es un sistema de verification basado en DNS que permite verificarte como un usuario real.",
"vhlWFg": "Opciones de encuesta",
"vlbWtt": "Consigue uno gratis",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} sats",
"vxwnbh": "Cantidad de trabajo para aplicar a todos los eventos publicados",
"w1Fanr": "Empresas",
"w6qrwX": "NSFW",

View File

@ -51,6 +51,7 @@
"2zJXeA": "نمایه‌ ها",
"39AHJm": "ثبت نام",
"3KNMbJ": "مقالات",
"3QwfJR": "~{amount}",
"3cc4Ct": "روشن",
"3gOsZq": "مترجمان",
"3qnJlS": "با {amount} ساتوشی رای می دهید",
@ -66,6 +67,7 @@
"4OB335": "ناپسند",
"4Vmpt4": "Nostr Plebs یکی از اولین فراهم کنندگان NIP-05 در این محیط است و مجموعه خوبی از دامین ها را با قیمت مناسب ارائه می کند",
"4Z3t5i": "از imgproxy برای فشرده سازی تصویر استفاده کنید",
"4emo2p": "Missing Relays",
"4rYCjn": "یادداشت برای خود",
"5BVs2e": "زَپ",
"5CB6zB": "تقسیم زپ",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> ساتوشی Cashu",
"6/hB3S": "تماشای بازپخش",
"62nsdy": "تلاش دوباره",
"6559gb": "New follow list length {length}",
"65BmHb": "تصویر از {host} آورده نشد، اینجا کلیک کنید تا مستقیم بارگیری شود",
"6OSOXl": "دلیل: <i>{reason}</i>",
"6bgpn+": "همه کلاینت ها از این امکان پشتیبانی نمی کنند. ممکن است همچنان زپ دریافت کنید به صورتی که انگار تقسیم زپ وجود ندارد.",
@ -129,6 +132,8 @@
"C81/uG": "خروج از حساب",
"C8HhVE": "پیشنهاد برای دنبال کردن",
"CHTbO3": "صورتحساب بارگیری نشد",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "افراد محبوب",
"CYkOCI": "و {count} نفر دیگر شما را دنبال می کنند",
"CbM2hK": "برچسب‌های پرطرفدار",
@ -146,6 +151,7 @@
"DrZqav": "درباره باید کمتر از {limit} حرف باشد",
"DtYelJ": "انتقال",
"Dx4ey3": "به هم ریختن همه",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "جستجو در یادداشت‌ها",
"ELbg9p": "ارائه دهندگان داده",
"EQKRE4": "نمایش مدال ها در صفحات نمایه",
@ -188,6 +194,7 @@
"HbefNb": "باز کردن کیف پول",
"HhcAVH": "شما این فرد را دنبال نمی کنید، اینجا کلیک کنید تا فایل های <i>{link}</i> را بارگیری کنید، یا <a><i>ترجیحات خود</i></a> را بروزرسانی کنید تا همیشه رسانه های همه را بارگیری کنید.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "مطمئنید می خواهید سنجاق یادداشت را بردارید؟",
"IKKHqV": "دنبال شوندگان",
"IOu4Xh": "باید عضو {tier} باشید تا به عرشه {app} دسترسی داشته باشید",
@ -306,7 +313,6 @@
"UrKTqQ": "شما یک حساب کاربری فعال iris.to دارید",
"UxgyeY": "کد معرفی شما {code} است",
"VL900k": "رله های توصیه شده",
"VN0+Fz": "تراز: {amount} ساتوشی",
"VOjC1i": "انتخاب کنید درکدام خدمات بارگذاری فایل می خواهید پیوست ها را بارگذاری کنید",
"VR5eHw": "کلید عمومی (npub/nprofile)",
"VcwrfF": "بله، لطفا",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "ارسال مقدار مصرف",
"XICsE8": "میزبانان فایل",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "برچسب‌های پرطرفدار",
"XgWvGA": "واکنش ها",
"XhpBfA": "{site} یک پروژه منبع باز ساخته شده توسط افراد مشتاق در اوقات فراغتشان است، سپاس فراوان از هدایای مالی شما",
@ -345,6 +352,7 @@
"b5vAk0": "شناسه شما به عنوان آدرس لایتنینگ عمل نموده و به LNURL انتخابی و آدرس لایتنینگ ارجاع خواهد داد",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "خدمات دهنده کار می کند",
"bJ+wrA": "Compute prune list",
"bLZL5a": "دریافت آدرس",
"bMphls": "با فقط حق خواندن وارد شدید",
"bQdA2k": "محتوای حساس",
@ -365,7 +373,6 @@
"cyR7Kh": "برگشت",
"d+6YsV": "لیست برای خموش کردن:",
"d2ebEu": "عضو Push نیست",
"d6CyG5": "تاریخچه",
"d7d0/x": "آدرس لایتنینگ",
"d8gpCh": "سعی کنید کمتر از ۵ برچسب استفاده کنید تا از موضوع پرت نشوید 🙏",
"dOQCL8": "نام نمایشی",
@ -404,6 +411,7 @@
"grQ+mI": "اثبات کار",
"h7jvCs": "{site} با هم بیشتر خوش میگذره!",
"h8XMJL": "مدال ها",
"hF6IN2": "Prune Follow List",
"hMzcSq": "پیام‌ها",
"hRTfTR": "PRO",
"hY4lzx": "پشتیبانی",
@ -417,6 +425,7 @@
"iCqGww": "({n}) واکنش",
"iEoXYx": "مترجم DeepL",
"iGT1eE": "از تقلید شدن توسط حساب های کاربری تقلبی جلوگیری کنید",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "شناسه",
"iXPL0Z": "در اتصال ناامن با کلید خصوصی نمی توان وارد شد، لطفا در عوض از افزونه مدیریت کلید ناستر استفاده کنید",
"iYc3Ld": "پرداخت‌ها",
@ -461,6 +470,8 @@
"nDejmx": "لغو مسدودی",
"nGBrvw": "نشانک",
"nGGDsi": "اعلان ها مجازند",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "به این مقاله گوش دهید",
"nwZXeh": "{n} مسدود",
"o7e+nJ": "{n} دنبال کننده",
@ -469,9 +480,11 @@
"ojzbwv": "هی، بنظر می رسد هنوز آدرس ناستر ندارید، باید یکی بگیرید! {link} را ببینید",
"p4N05H": "بارگذاری",
"p85Uwy": "اشتراک فعال",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "نسخه بازیابی قابل دانلود رله اسنورت",
"pRess9": "استخر زپ",
"puLNUJ": "سنجاق",
"pukxg/": "Payments",
"pzTOmv": "دنبال کنندگان",
"qD9EUF": "ایمیل پل پیام مستقیم <> برای آدرس اسنورت ناستر",
"qDwvZ4": "خطای ناشناخته",
@ -502,6 +515,7 @@
"tOdNiY": "تاریک",
"th5lxp": "یادداشت را به زیرشاخه ای از رله نوشتنی تان بفرستید",
"thnRpU": "گرفتن NIP-05 تایید شده می تواند کمک کند:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "مقادیر رله",
"ttxS0b": "مدال پشتیبان",
"u+LyXc": "تعاملات",
@ -517,11 +531,11 @@
"v8lolG": "شروع گپ",
"vB3oQ/": "می بایست یک لیست مخاطبین یا لیست کلیدهای عمومی باشد",
"vN5UH8": "تصویر نمایه",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 یک ویژگی بر مبنای DNS است که کمک می کند هویت شما به عنوان کاربر تصدیق شود.",
"vhlWFg": "گزینه های نظرسنجی",
"vlbWtt": "یکی مجانی بگیرید",
"voxBKC": "دنبال شده توسط دوستان",
"vrTOHJ": "{amount} ساتوشی",
"vxwnbh": "مقداری کاری که برای اِعمال همه رویدادهای منتشر شده باید انجام گیرد",
"w1Fanr": "کسب و کار",
"w6qrwX": "نامناسب برای خانواده و محیط کار",

View File

@ -1,7 +1,7 @@
{
"+D82kt": "Haluatko varmasti jakaa uudelleen: {id}",
"+PzQ9Y": "Maksa nyt",
"+UjDmN": "Logged in with write access",
"+UjDmN": "Kirjauduttu sisään kirjoitusoikeudella",
"+Vxixo": "Salainen ryhmäkeskutelu",
"+aZY2h": "Zap tyyppi",
"+tShPg": "seuraavat",
@ -19,7 +19,7 @@
"/d6vEc": "Tee profiilistasi helpompi löytää ja jakaa",
"/n5KSF": "{n} ms",
"00LcfG": "Lataa lisää",
"01iNut": "Nostr address does not belong to you",
"01iNut": "Nostr-osoite ei kuulu sinulle",
"08zn6O": "Vie avaimet",
"0Azlrb": "Hallitse",
"0BUTMv": "Etsi...",
@ -51,6 +51,7 @@
"2zJXeA": "Profiilit",
"39AHJm": "Rekisteröidy",
"3KNMbJ": "Artikkelit",
"3QwfJR": "~{amount}",
"3cc4Ct": "Vaalea",
"3gOsZq": "Kääntäjät",
"3qnJlS": "Äänestät {amount} satsilla",
@ -66,10 +67,11 @@
"4OB335": "Ei-tykkäys",
"4Vmpt4": "Nostr Plebs on yksi ensimmäisistä NIP-05-tarjoajista alalla ja tarjoaa kohtuuhintaisia verkkotunnuksia",
"4Z3t5i": "Käytä imgproxya pakataksesi kuvat",
"4emo2p": "Puuttuvat välittäjät",
"4rYCjn": "Muistiinpano itselle",
"5BVs2e": "zap",
"5CB6zB": "Zap-jaot",
"5PRWs7": "Notifications API Enabled",
"5PRWs7": "Ilmoitukset API käytössä",
"5oTnfy": "Osta käyttäjätunnus",
"5u6iEc": "Siirrä julkiselle avaimelle",
"5vMmmR": "Käyttäjänimet eivät ole yksilöllisiä Nostrissa. Nostr-osoite on yksilöllinen ihmisystävällinen osoitteesi, joka on yksilöllinen sinulle rekisteröinnin yhteydessä.",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Katso uusinta",
"62nsdy": "Uudelleenyritys",
"6559gb": "Uusi seuraajalistan pituus {length}",
"65BmHb": "Kuvan välittäminen epäonnistui palvelimelta {host}, napsauta tätä ladataksesi suoraan",
"6OSOXl": "Syy: <i>{reason}</i>",
"6bgpn+": "Kaikki asiakkaat eivät tue tätä, saatat silti saada joitakin zappeja ikään kuin zap-jako ei olisi määritetty",
@ -87,7 +90,7 @@
"7+Domh": "Viestit",
"712i26": "Välityspalvelin käyttää HODL-laskuja maksun välittämiseen, mikä piilottaa solmusi julkisen avaimen.",
"7UOvbT": "Offline",
"7YkSA2": "Community Leader",
"7YkSA2": "Yhteisön johtaja",
"7hp70g": "NIP-05",
"8/vBbP": "Uudelleenjakoja ({n})",
"89q5wc": "Vahvista uudelleenjaot",
@ -115,7 +118,7 @@
"AkCxS/": "Syy",
"Am8glJ": "Peli",
"AnLrRC": "Ei-Zap",
"Awq32I": "Push notifications",
"Awq32I": "Push-ilmoitukset",
"AxDOiG": "Kuukaudet",
"AyGauy": "Kirjaudu sisään",
"B4C47Y": "nimi on liian lyhyt",
@ -129,6 +132,8 @@
"C81/uG": "Kirjaudu ulos",
"C8HhVE": "Ehdotettuja seurattavia",
"CHTbO3": "Epäonnistunut laskun lataus",
"CM+Cfj": "Seurattavien lista",
"CM0k0d": "Karsia seurattavien listaa",
"CVWeJ6": "Suositut henkilöt",
"CYkOCI": "ja {count} muuta joita seuraat",
"CbM2hK": "Suositut tunnisteet",
@ -143,9 +148,10 @@
"DZzCem": "Näytä viimeisimmät {n} viestiä",
"Dh3hbq": "Automaattiset zapit",
"Dn82AL": "Live",
"DrZqav": "About must be less than {limit} characters",
"DrZqav": "Tietoja on oltava vähemmän kuin {limit} merkkiä",
"DtYelJ": "Siirto",
"Dx4ey3": "Vaihda kaikki",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Etsi muistiinpanoja",
"ELbg9p": "Datantoimittajat",
"EQKRE4": "Näytä merkit profiilisivuilla",
@ -167,19 +173,19 @@
"FvanT6": "Tilit",
"G/yZLu": "Poista",
"G1BGCg": "Valitse lompakko",
"G3A56c": "Subscribed to Push",
"G3A56c": "Tilattu Push-ilmoitukset",
"GFOoEE": "Suola",
"GL8aXW": "Kirjanmerkkejä ({n})",
"GQPtfk": "Liity Streamiin",
"GSye7T": "Lightning-osoite",
"GUlSVG": "Lunasta sisältyvä Snort nostr-osoite",
"Gcn9NQ": "Magneettilinkki",
"GqQeu/": "Invalid Lightning Address",
"GqQeu/": "Virheellinen Lightning-osoite",
"GspYR7": "{n} Ei-tykkäys",
"Gxcr08": "Lähetystapahtuma",
"H+vHiz": "Hex-avain..",
"H0JBH6": "Kirjaudu ulos",
"H0OG3T": "Leader Info",
"H0OG3T": "Johtajan tiedot",
"H6/kLh": "Tilaus maksettu!",
"HAlOn1": "Nimi",
"HFls6j": "nimi on myöhemmin saatavilla",
@ -187,7 +193,8 @@
"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",
"HqRNN8": "Tuki",
"I1AoOu": "Viimeisin viesti {time}",
"IEwZvs": "Haluatko varmasti poistaa tämän viestin kiinnitetyistä?",
"IKKHqV": "Seuraa",
"IOu4Xh": "Sinun täytyy olla {tier} tilaaja käyttääksesi {app} näkymää",
@ -198,7 +205,7 @@
"IoQq+a": "Klikkaa tästä ladataksesi joka tapauksessa",
"Ix8l+B": "Suositut viestit",
"J+dIsA": "Tilaukset",
"J1iLmb": "Notifications Not Allowed",
"J1iLmb": "Ilmoituksia ei sallita",
"J2HeQ+": "Käytä pilkkuja erottamaan sanat toisistaan, esim. sana1, sana2, sana3.",
"JCIgkj": "Käyttäjätunnus",
"JGrt9q": "Lähetä satseja käyttäjälle {name}",
@ -222,12 +229,12 @@
"LR1XjT": "Liian lyhyt tappi",
"LXxsbk": "Nimetön",
"LgbKvU": "Kommentoi",
"LmdPXO": "Cannot verify Nostr Address",
"LmdPXO": "Nostr-osoitetta ei voida tarkistaa",
"Lu5/Bj": "Avaa Zapstrissa",
"Lw+I+J": "{n,plural,=0{{name} zappasi} other{{name} & {n} muuta zappasivat}}",
"LwYmVi": "Zapit tässä viestissä jaetaan seuraaville käyttäjille.",
"M3Oirc": "Debug-valikot",
"M6C/px": "Become a leader",
"M6C/px": "Ryhdy johtajaksi",
"MBAYRO": "Näyttää \"Kopioi tunniste\" ja \"Kopioi tapahtuman JSON\" jokaisen viestin valikossa",
"MI2jkA": "Ei saatavilla:",
"MP54GY": "Lompakon salasana",
@ -244,7 +251,7 @@
"NdOYJJ": "Hmm ei mitään täällä.. Katso {newUsersPage} seurataksesi joitakin suositeltuja nostricheja!",
"NepkXH": "Ei voida äänestää {amount} satsilla, aseta toinen oletuszap-määrä",
"NndBJE": "Uusien käyttäjien sivu",
"O3Jz4E": "Use your invite code to earn sats!",
"O3Jz4E": "Käytä kutsukoodia ansaitaksesi satseja!",
"O8Z8t9": "Näytä lisää",
"OEW7yJ": "Zapit",
"OKhRC6": "Jaa",
@ -252,7 +259,7 @@
"OQSOJF": "Hanki ilmainen nostr-osoite",
"OQXnew": "Tilauksesi on yhä aktiivinen, et voi uusia sitä vielä",
"ORGv1Q": "Luotu",
"P2o+ZZ": "Invalid Nostr Address",
"P2o+ZZ": "Virheellinen Nostr-osoite",
"P61BTu": "Kopioi tapahtuman JSON",
"P7FD0F": "Järjestelmä (oletus)",
"P7nJT9": "Tänään yhteensä (UTC): {amount} satsia",
@ -266,7 +273,7 @@
"Qxv0B2": "Sinulla on {number} satsia zap-poolissa.",
"R/6nsx": "Tilaus",
"R81upa": "Henkilöt joita seuraat",
"RDha9y": "Service Worker Not Running",
"RDha9y": "Palvelutyöntekijä ei ole käynnissä",
"RSr2uB": "Käyttäjätunnus saa sisältää vain pieniä kirjaimia ja numeroita.",
"RahCRH": "Vanhentunut",
"RfhLwC": "Tekijä: {author}",
@ -289,11 +296,11 @@
"TJo5E6": "Esikatselu",
"TP/cMX": "Lopetettu",
"TaeBqw": "Kirjaudu sisään Nostr-laajennuksella",
"TdTXXf": "Learn more",
"TdTXXf": "Lisätietoja",
"TdtZQ5": "Crypto",
"TpgeGw": "Hex-suola..",
"Tpy00S": "Ihmiset",
"TwyMau": "Account",
"TwyMau": "Tili",
"U1aPPi": "Lopeta kuunteleminen",
"UDYlxu": "Odottavat tilaukset",
"UJTWqI": "Poista releistäni",
@ -302,11 +309,10 @@
"UUPFlt": "Käyttäjien täytyy hyväksyä sisältövaroitus nähdäkseen viestisi sisällön.",
"Ub+AGc": "Kirjaudu sisään",
"Up5U7K": "Estä",
"Ups2/p": "Your application is pending",
"Ups2/p": "Hakemuksesi on vireillä",
"UrKTqQ": "Sinulla on aktiivinen iris.to-tili",
"UxgyeY": "Suosittelukoodisi on {code}",
"VL900k": "Suositellut välittäjät",
"VN0+Fz": "Saldo: {amount} satsia",
"VOjC1i": "Valitse palvelu, johon haluat ladata liitteitä",
"VR5eHw": "Julkinen avain (npub/nprofile)",
"VcwrfF": "Kyllä, kiitos.",
@ -322,9 +328,10 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "Lähetä käyttötietoja",
"XICsE8": "Tiedostojen isäntäpalvelut",
"XQiFEl": "Seuraa välittäjän tila",
"XXm7jJ": "Suositut tunnisteet",
"XgWvGA": "Reaktiot",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
"XhpBfA": "{site} on avoimen lähdekoodin projekti, jota intohimoiset ihmiset rakentavat vapaa-ajallaan, lahjoituksia arvostetaan suuresti",
"Xopqkl": "Oletus zap-määräsi on {number} satsia, esimerkit on laskettu tämän mukaan.",
"XrSk2j": "Lunasta",
"YDURw6": "Palvelun URL",
@ -343,10 +350,11 @@
"aWpBzj": "Näytä lisää",
"b12Goz": "Mnemonic-lause",
"b5vAk0": "Käyttäjätunnuksesi toimii kuin lightning-osoite ja uudelleenohjaa valitsemaasi LNURLiin tai lightning-osoitteeseen",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bF1MYT": "Olet yhteisön johtaja ja ansaitset <b>{percent}</b> viitattujen käyttäjien tilauksista!",
"bG00/W": "Palvelutyöntekijä käynnissä",
"bJ+wrA": "Karsintaluettelon laskeminen",
"bLZL5a": "Hae osoite",
"bMphls": "Logged in with read-only access",
"bMphls": "Kirjauduttu sisään vain lukuoikeudella",
"bQdA2k": "Arkaluontoinen sisältö",
"bep9C3": "Julkinen avain",
"bfvyfs": "Anon",
@ -364,8 +372,7 @@
"cuV2gK": "nimi on rekisteröity",
"cyR7Kh": "Takaisin",
"d+6YsV": "Luettelot mykistykseen:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "Historia",
"d2ebEu": "Ei tilattu Push-ilmoituksia",
"d7d0/x": "LN-osoite",
"d8gpCh": "Yritä käyttää alle 5 tunnistetta pysyäksesi aiheessa 🙏",
"dOQCL8": "Näyttönimi",
@ -382,7 +389,7 @@
"eSzf2G": "Yksittäinen {nIn} satsin zap jakaa {nOut} satsia zap-pooliin.",
"eXT2QQ": "Ryhmächat",
"egib+2": "{n,plural,one {}=1{& {n} muut} other{& {n} muut}}",
"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": "Yhteisön johtajat ovat yksilöitä, jotka kasvattavat nostr ekosysteemiä toimimalla aktiivisesti paikallisyhteisöissä ja auttamalla uusia käyttäjiä. Kuka tahansa voi tulla yhteisön johtajaksi, mutta vain harvoilla on nykyinen kunniajäsenen titteli.",
"fBI91o": "Zap",
"fBlba3": "Kiitos, että käytät {site}, harkitse lahjoitusta, jos voit.",
"fOksnD": "Ei voi äänestää, koska LNURL-palvelu ei tue zappeja",
@ -404,6 +411,7 @@
"grQ+mI": "Todiste työstä",
"h7jvCs": "{site} on hauskempaa yhdessä!",
"h8XMJL": "Merkit",
"hF6IN2": "Karsi seurattavien listaa",
"hMzcSq": "Viestit",
"hRTfTR": "PRO",
"hY4lzx": "Tukee",
@ -412,11 +420,12 @@
"hicxcO": "Näytä vastaukset",
"hmZ3Bz": "Media",
"hniz8Z": "täällä",
"hvFRBo": "Interaction",
"hvFRBo": "Vuorovaikutus",
"i/dBAR": "Zap-pooli",
"iCqGww": "Reaktioita ({n})",
"iEoXYx": "DeepL käännökset",
"iGT1eE": "Estä väärennettyjä tilejä jäljittelemästä sinua",
"iICVoL": "{x} seuraa ({y} duplikaatit)",
"iNWbVV": "Käyttäjätunnus",
"iXPL0Z": "Ei voi kirjautua yksityisavaimella epäturvallisella yhteydellä, käytä Nostr-avainhallintaohjelmalaajennusta",
"iYc3Ld": "Maksut",
@ -431,14 +440,14 @@
"jTrbGf": "{n} km - {location}",
"jvo0vs": "Tallenna",
"jzgQ2z": "{n} Reaktiota",
"k0kCJp": "Apply Now",
"k0kCJp": "Hae nyt",
"k2veDA": "Kirjoita",
"k7sKNy": "Oma NIP-05-varmennuspalvelumme, auta tukemaan tämän sivuston kehitystä ja saat kiiltävän erikoismerkin sivuillemme!",
"kEZUR8": "Rekisteröi Iris-käyttäjätunnus",
"kJYo0u": "{n,plural,=0{{name} jakoi uudelleen} other{{name} & {n} muuta jakoivat uudelleen}}",
"kaaf1E": "nyt",
"kc79d3": "Aiheet",
"kqPQJD": "Configure zap pool",
"kqPQJD": "Määritä zap-pooli",
"kuPHYE": "{n,plural,=0{{name} tykkäsi} other{{name} & {n} muuta tykkäsivät}}",
"l+ikU1": "Kaikki {plan}-suunnitelmassa",
"l3H1EK": "Kutsu kavereita",
@ -460,7 +469,9 @@
"n1Whvj": "Vaihda",
"nDejmx": "Poista esto",
"nGBrvw": "Kirjanmerkit",
"nGGDsi": "Notifications Allowed",
"nGGDsi": "Ilmoitukset sallittu",
"nIchMQ": "Haetaan tilin toimintaa ({progress})",
"nUT0Lv": "Työkalut",
"nihgfo": "Kuuntele tämä artikkeli",
"nwZXeh": "{n} estettyä",
"o7e+nJ": "{n} seuraajaa",
@ -469,9 +480,11 @@
"ojzbwv": "Hei, näyttää siltä ettei sinulla ole vielä Nostr-osoitetta, sinun pitäisi hankkia sellainen! Katso {link}",
"p4N05H": "Lähetä",
"p85Uwy": "Aktiiviset tilaukset",
"p9Ps2l": "{x}/{y} on välittäjiä ({percent})",
"pI+77w": "Ladattavat varmuuskopiot Snort-välittäjältä",
"pRess9": "ZapPool",
"pRess9": "ZapPooli",
"puLNUJ": "Kiinnitä",
"pukxg/": "Payments",
"pzTOmv": "Seuraajat",
"qD9EUF": "Sähköposti-silta Snort nostr-osoitteellesi",
"qDwvZ4": "Tuntematon virhe",
@ -479,7 +492,7 @@
"qMx1sA": "Oletus zap-määrä",
"qUJTsT": "Estetty",
"qZsKBR": "Renew {tier}",
"qcJFEJ": "Notifications API Disabled",
"qcJFEJ": "Ilmoitukset API pois käytöstä",
"qdGuQo": "Yksityisavaimen on (älä jaa tätä kenellekään)",
"qfmMQh": "Tämä huomautus on mykistetty",
"qkvYUb": "Lisää profiiliin",
@ -502,12 +515,13 @@
"tOdNiY": "Tumma",
"th5lxp": "Lähetä viesti osajoukolle kirjoitusvälittäjiäsi",
"thnRpU": "NIP-05-varmennuksen hankkiminen voi auttaa:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Välittäjämittarit",
"ttxS0b": "Tukijamerkki",
"u+LyXc": "Vuorovaikutukset",
"u/vOPu": "Maksettu",
"u4bHcR": "Tarkista koodi täältä: {link}",
"u9NoC1": "Name must be less than {limit} characters",
"u9NoC1": "Nimen on oltava vähemmän kuin {limit} merkkiä",
"uCk8r+": "Onko sinulla jo tili?",
"uKqSN+": "Seuraa Syöttö",
"uSV4Ti": "Uudelleenjaot vaativat manuaalisen vahvistuksen",
@ -517,11 +531,11 @@
"v8lolG": "Aloita keskustelu",
"vB3oQ/": "On oltava yhteystietoluettelo tai pubkey-luettelo",
"vN5UH8": "Profiilikuva",
"vU/Q5i": "Tämä työkalu etsii kaikkien seuraajiesi viimeksi julkaiseman tapahtuman ja poistaa ne, jotka eivät ole julkaisseet 6 kuukauteen",
"vZ4quW": "NIP-05 on DNS-pohjainen varmennusspesifikaatio, joka auttaa vahvistamaan sinut oikeaksi käyttäjäksi.",
"vhlWFg": "Äänestyksen vaihtoehdot",
"vlbWtt": "Hanki ilmainen",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} satsia",
"voxBKC": "Ystävien seuraama",
"vxwnbh": "Tehtävän työmäärä julkaistuille tapahtumille",
"w1Fanr": "Liiketoiminta",
"w6qrwX": "NSFW",
@ -543,7 +557,7 @@
"xl4s/X": "Lisäehdot:",
"xmcVZ0": "Etsi",
"xybOUv": "FAN",
"y/bmsG": "Allow",
"y/bmsG": "Salli",
"y1Z3or": "Kieli",
"yCLnBC": "LNURL tai Lightning-osoite",
"yNBPJp": "Auta rahoittamaan {site}-sivuston kehittämistä.",

View File

@ -51,6 +51,7 @@
"2zJXeA": "Profils",
"39AHJm": "S'inscrire",
"3KNMbJ": "Articles",
"3QwfJR": "~{amount}",
"3cc4Ct": "Clair",
"3gOsZq": "Traducteurs",
"3qnJlS": "Vous votez avec {amount} sats",
@ -66,6 +67,7 @@
"4OB335": "Ne pas aimer",
"4Vmpt4": "Nostr Plebs est l'un des premiers fournisseurs NIP-05 dans l'espace et offre une bonne collection de domaines à des prix raisonnables",
"4Z3t5i": "Utiliser imgproxy pour compresser les images",
"4emo2p": "Missing Relays",
"4rYCjn": "Note à Soi-même",
"5BVs2e": "zap",
"5CB6zB": "Zap Splits",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Regarder la rediffusion",
"62nsdy": "Réessayer",
"6559gb": "New follow list length {length}",
"65BmHb": "Échec du proxy de l'image de {host}, cliquez ici pour la charger directement",
"6OSOXl": "Motif : <i>{reason}</i>",
"6bgpn+": "Tous les clients ne supportent pas cette fonction, il se peut que vous receviez encore quelques zaps comme si la division des zaps n'était pas configurée.",
@ -129,6 +132,8 @@
"C81/uG": "Se déconnecter",
"C8HhVE": "Utilisateurs recommandés",
"CHTbO3": "Échec du chargement de la facture",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "Personnes tendances",
"CYkOCI": "et {count} autres que vous suivez",
"CbM2hK": "Hashtags tendance",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "Transférer",
"Dx4ey3": "Tout afficher",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Notes de recherche",
"ELbg9p": "Fournisseurs de données",
"EQKRE4": "Afficher les badges sur les pages de profil",
@ -188,6 +194,7 @@
"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": "Last post {time}",
"IEwZvs": "Êtes-vous sûr de vouloir désépingler cette note?",
"IKKHqV": "Abonnements",
"IOu4Xh": "Vous devez être abonné à {tier} pour accéder à {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "Vous avez un compte iris.to actif",
"UxgyeY": "Votre code de référence est {code}",
"VL900k": "Relais recommandés",
"VN0+Fz": "Solde : {amount} sats",
"VOjC1i": "Choisissez le service d'hébergement vers lequel vous souhaitez héberger les pièces jointes",
"VR5eHw": "Clé publique (npub/nprofile)",
"VcwrfF": "Oui, s'il vous plaît",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnémonique",
"XECMfW": "Envoyer des données d'utilisation",
"XICsE8": "Hébergeurs de fichiers",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Hashtags tendance",
"XgWvGA": "Réactions",
"XhpBfA": "{site} est un projet open source construit par des passionnés pendant leur temps libre, vos dons sont très appréciés",
@ -345,6 +352,7 @@
"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!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "Obtenir l'adresse",
"bMphls": "Logged in with read-only access",
"bQdA2k": "Contenu sensible",
@ -365,7 +373,6 @@
"cyR7Kh": "Retourner",
"d+6YsV": "Listes pour la mise en sourdine :",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "Historique",
"d7d0/x": "Adresse LN",
"d8gpCh": "Essayez d'utiliser moins de 5 hashtags pour rester sur le sujet 🙏",
"dOQCL8": "Nom à afficher",
@ -404,6 +411,7 @@
"grQ+mI": "Preuve de Travail",
"h7jvCs": "{site} est plus amusant ensemble !",
"h8XMJL": "Badges",
"hF6IN2": "Prune Follow List",
"hMzcSq": "Messages",
"hRTfTR": "PRO",
"hY4lzx": "Supporte",
@ -417,6 +425,7 @@
"iCqGww": "Réactions ({n})",
"iEoXYx": "Traductions DeepL",
"iGT1eE": "Empêcher les faux comptes de vous imiter",
"iICVoL": "{x} follows ({y} duplicates)",
"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": "Paiements",
@ -461,6 +470,8 @@
"nDejmx": "Débloquer",
"nGBrvw": "Favoris",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "Écouter cet article",
"nwZXeh": "{n} bloqué",
"o7e+nJ": "{n} abonnés",
@ -469,9 +480,11 @@
"ojzbwv": "Hé, il semble que vous n'avez pas encore d'adresse Nostr, vous devriez en obtenir une ! Consultez {link}",
"p4N05H": "Importer",
"p85Uwy": "Abonnements actifs",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "Sauvegardes téléchargeables depuis le relais Snort",
"pRess9": "ZapPool",
"puLNUJ": "Épingler",
"pukxg/": "Payments",
"pzTOmv": "Abonnés",
"qD9EUF": "Email <> pont DM pour votre adresse Snort nostr",
"qDwvZ4": "Une erreur inconnue s'est produite",
@ -502,6 +515,7 @@
"tOdNiY": "Sombre",
"th5lxp": "Envoyer la note à un sous-ensemble de vos relais d'écriture",
"thnRpU": "Obtenir la vérification NIP-05 peut aider :",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Métriques du relais",
"ttxS0b": "Badge de Supporter",
"u+LyXc": "Interactions",
@ -517,11 +531,11 @@
"v8lolG": "Démarrer la discussion",
"vB3oQ/": "Il doit s'agir d'une liste de contacts ou d'une liste de clés publiques",
"vN5UH8": "Image de profil",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 est une spécification de vérification basée sur DNS qui permet de vous valider en tant qu'utilisateur réel.",
"vhlWFg": "Choix proposés",
"vlbWtt": "En obtenir un gratuit",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} sats",
"vxwnbh": "Quantité de travail à appliquer à tous les événements publiés",
"w1Fanr": "Entreprises",
"w6qrwX": "NSFW",

View File

@ -51,6 +51,7 @@
"2zJXeA": "Profiles",
"39AHJm": "Sign Up",
"3KNMbJ": "Articles",
"3QwfJR": "~{amount}",
"3cc4Ct": "Svijetlo",
"3gOsZq": "Prevoditelji",
"3qnJlS": "Glasate sa {amount} sats-a",
@ -66,6 +67,7 @@
"4OB335": "Ne sviđa mi se",
"4Vmpt4": "Nostr Plebs je jedan od prvih NIP-05 pružatelja usluge u svemiru i nudi široku kolekciju domena po razumnim cijenama",
"4Z3t5i": "Koristi imgproxy za komprimiranje slika",
"4emo2p": "Missing Relays",
"4rYCjn": "Bilješka sebi",
"5BVs2e": "zap",
"5CB6zB": "Zap Splits",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Watch Replay",
"62nsdy": "Retry",
"6559gb": "New follow list length {length}",
"65BmHb": "Failed to proxy image from {host}, click here to load directly",
"6OSOXl": "Reason: <i>{reason}</i>",
"6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured",
@ -129,6 +132,8 @@
"C81/uG": "Odjava",
"C8HhVE": "Suggested Follows",
"CHTbO3": "Neuspješno učitavanje računa",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "Trending People",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "Prijenos",
"Dx4ey3": "Toggle all",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Search notes",
"ELbg9p": "Data Providers",
"EQKRE4": "Show badges on profile pages",
@ -188,6 +194,7 @@
"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?",
"IKKHqV": "Follows",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "You have an active iris.to account",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "Iznos: {amount} sati",
"VOjC1i": "Odaberite kroz koji servis želite učitati privitke",
"VR5eHw": "Javni ključ (npub/nprofile)",
"VcwrfF": "Yes please",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "Send usage metrics",
"XICsE8": "File hosts",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "Reakcije",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -345,6 +352,7 @@
"b5vAk0": "Vaš nadimak će se ponašati kao lightning adresa i preusmjerit će Vas na odabrani LNURL ili Lightning adresu",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "Get Address",
"bMphls": "Logged in with read-only access",
"bQdA2k": "Osjetljiv sadržaj",
@ -365,7 +373,6 @@
"cyR7Kh": "Natrag",
"d+6YsV": "Lists to mute:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "Povijest",
"d7d0/x": "LN Adresa",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "Ime za prikaz",
@ -404,6 +411,7 @@
"grQ+mI": "Proof of Work",
"h7jvCs": "{site} is more fun together!",
"h8XMJL": "Značke",
"hF6IN2": "Prune Follow List",
"hMzcSq": "Poruke",
"hRTfTR": "PRO",
"hY4lzx": "Podržava",
@ -417,6 +425,7 @@
"iCqGww": "Reakcije ({n})",
"iEoXYx": "DeepL prijevodi",
"iGT1eE": "Spriječite lažne račune da vas oponašaju",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "Nadimak",
"iXPL0Z": "Nije moguće prijaviti se pomoću privatnog ključa na nesigurnoj vezi, molimo koristite Nostr produžetak za upravljanje ključevima",
"iYc3Ld": "Payments",
@ -461,6 +470,8 @@
"nDejmx": "Odblokiraj",
"nGBrvw": "Oznake",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "Listen to this article",
"nwZXeh": "{n} blokirano",
"o7e+nJ": "{n} pratitelja",
@ -469,9 +480,11 @@
"ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}",
"p4N05H": "Upload",
"p85Uwy": "Aktivne Pretplate",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "Sigurnosne kopije koje se mogu preuzeti sa Snort releja",
"pRess9": "ZapPool",
"puLNUJ": "Prikači",
"pukxg/": "Payments",
"pzTOmv": "Pratitelji",
"qD9EUF": "Pošaljite e-poruku <> DM bridge za svoju Snort nostr adresu",
"qDwvZ4": "Nepoznata pogreška",
@ -502,6 +515,7 @@
"tOdNiY": "Tamno",
"th5lxp": "Send note to a subset of your write relays",
"thnRpU": "Provjera NIP-05 može pomoći:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Relay Metrics",
"ttxS0b": "Značka za pruženu podršku",
"u+LyXc": "Interactions",
@ -517,11 +531,11 @@
"v8lolG": "Start chat",
"vB3oQ/": "Must be a contact list or pubkey list",
"vN5UH8": "Profile Image",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 je specifikacija za provjeru temeljena na DNS-u koja vam pomaže potvrditi da ste pravi korisnik.",
"vhlWFg": "Mogućnosti ankete",
"vlbWtt": "Get a free one",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} sati",
"vxwnbh": "Amount of work to apply to all published events",
"w1Fanr": "Business",
"w6qrwX": "NSFW",

View File

@ -1,7 +1,7 @@
{
"+D82kt": "Biztos hogy ezt meg akarod osztani: {id}",
"+PzQ9Y": "Fizetés Most",
"+UjDmN": "Logged in with write access",
"+UjDmN": "Bejelentkezve írási hozzáféréssel",
"+Vxixo": "Titkos Csoportos Chat",
"+aZY2h": "Zap típusa",
"+tShPg": "követek",
@ -19,7 +19,7 @@
"/d6vEc": "Legyen a fiókod könnyebben megtalálható és megosztható",
"/n5KSF": "{n} ms",
"00LcfG": "Továbbiak betöltése",
"01iNut": "Nostr address does not belong to you",
"01iNut": "A Nostr cím nem tiéd",
"08zn6O": "Kulcsok exportálása",
"0Azlrb": "Menedzselés",
"0BUTMv": "Keresés...",
@ -51,6 +51,7 @@
"2zJXeA": "Profilok",
"39AHJm": "Feliratkozás",
"3KNMbJ": "Cikkek",
"3QwfJR": "~{amount}",
"3cc4Ct": "Világos",
"3gOsZq": "Fordítók",
"3qnJlS": "{amount} sats-al szavazol",
@ -66,10 +67,11 @@
"4OB335": "Nem tetszik",
"4Vmpt4": "A Nostr Plebs-ék az egyik legelső megfelelő feltételekkel és árakkal NIP-05 azonosítást biztosító szolgáltató",
"4Z3t5i": "Az imgproxy használata a képek tömörítéséhez",
"4emo2p": "Hiányzó csomópontok",
"4rYCjn": "Jegyzet magamnak",
"5BVs2e": "zap",
"5CB6zB": "Zap-ek megosztása",
"5PRWs7": "Notifications API Enabled",
"5PRWs7": "Értesítési API engedélyezve",
"5oTnfy": "Azonosító vásárlása",
"5u6iEc": "Átvitel a Publikus kulcsra",
"5vMmmR": "A Nostr-án a felhasználónevek nem egyediek. A nostr cím az Ön egyedi, ember által olvasható címe, amely regisztrációjától egyedi.",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Visszajátszás Megtekintése",
"62nsdy": "Retry",
"6559gb": "Új követési lista hossza {length}",
"65BmHb": "Nem sikerült a proxyképet a(z) {host} webhelyről betölteni. Kattintson ide a közvetlen betöltéshez",
"6OSOXl": "Ok: <i>{reason}</i>",
"6bgpn+": "Nem minden kliens támogatja ezt, ezért előfordulhat, hogy továbbra is a zap-eket úgy kapja, mintha a zap felosztás nem lett volna konfigurálva",
@ -87,7 +90,7 @@
"7+Domh": "Bejegyzések",
"712i26": "A proxy HODL számlákat használ a fizetés továbbítására, ami elrejti a csomópont pubkey-jét.",
"7UOvbT": "Offline",
"7YkSA2": "Community Leader",
"7YkSA2": "Közösségi Vezető",
"7hp70g": "NIP-05",
"8/vBbP": "Megosztva ({n})",
"89q5wc": "Megosztás megerősítése",
@ -115,7 +118,7 @@
"AkCxS/": "Indok",
"Am8glJ": "Játék",
"AnLrRC": "Nem Zap",
"Awq32I": "Push notifications",
"Awq32I": "Push értesítések",
"AxDOiG": "Hónapok",
"AyGauy": "Bejelentkezés",
"B4C47Y": "név túl rövid",
@ -129,6 +132,8 @@
"C81/uG": "Kijelentkezés",
"C8HhVE": "Javasolt követendő személyek",
"CHTbO3": "A számla betöltése nem sikerült",
"CM+Cfj": "Követő lista",
"CM0k0d": "Követői lista metszése",
"CVWeJ6": "Felkapott Emberek",
"CYkOCI": "és {count} másik személy, akiket követsz",
"CbM2hK": "Trendi hashtagek",
@ -143,9 +148,10 @@
"DZzCem": "A legutóbbi {n} bejegyzés mutatása",
"Dh3hbq": "Auto Zap",
"Dn82AL": "Élő",
"DrZqav": "About must be less than {limit} characters",
"DrZqav": "A leírásnak kevesebbnek kell lennie, mint {limit} karakter",
"DtYelJ": "Átvitel",
"Dx4ey3": "Toggle all",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Bejegyzések keresése",
"ELbg9p": "Adatszolgáltatók",
"EQKRE4": "Jelvények megjelenítése a profiloldalakon",
@ -167,19 +173,19 @@
"FvanT6": "Fiókok",
"G/yZLu": "Eltávolítás",
"G1BGCg": "Tárca kiválasztása",
"G3A56c": "Subscribed to Push",
"G3A56c": "Feliratkoztál a Push szolgáltatásra",
"GFOoEE": "Salt",
"GL8aXW": "Könyvjelzők ({n})",
"GQPtfk": "Csatlakozz a Streamhez",
"GSye7T": "Lightning Cím",
"GUlSVG": "Igényeld a hozzá tartozó Snort nostr címet",
"Gcn9NQ": "Mágnes Link",
"GqQeu/": "Invalid Lightning Address",
"GqQeu/": "Érvénytelen LN cím",
"GspYR7": "{n} Nem tetszik",
"Gxcr08": "Esemény közvetítése",
"H+vHiz": "Hex kulcs..",
"H0JBH6": "Kijelentkezés",
"H0OG3T": "Leader Info",
"H0OG3T": "Vezető információ",
"H6/kLh": "Rendelés fizetve!",
"HAlOn1": "Név",
"HFls6j": "név később elérhető",
@ -187,7 +193,8 @@
"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": "Support",
"HqRNN8": "Segítség",
"I1AoOu": "Utolsó hozzászólás {time}",
"IEwZvs": "Biztos hogy a bejegyzés kiemelését visszavonod?",
"IKKHqV": "Követőim",
"IOu4Xh": "A {tier} előfizetőjének kell lenned ahhoz, hogy a {app} fedélzetéhez hozzáférj",
@ -198,7 +205,7 @@
"IoQq+a": "Kattintson ide a betöltéshez",
"Ix8l+B": "Felkapott Bejegyzések",
"J+dIsA": "Előfizetések",
"J1iLmb": "Notifications Not Allowed",
"J1iLmb": "Az értesítések nincsenek engedélyezve",
"J2HeQ+": "Szavak elválasztása vesszőkkel, pl. word1, word2, word3.",
"JCIgkj": "Felhasználónév",
"JGrt9q": "Sat-ok küldése {name}",
@ -222,12 +229,12 @@
"LR1XjT": "Pin túl rövid",
"LXxsbk": "Ismeretlen",
"LgbKvU": "Hozzászólás",
"LmdPXO": "Cannot verify Nostr Address",
"LmdPXO": "A Nostr címet nem lehet ellenőrizni",
"Lu5/Bj": "Zapstr-en megnyitása",
"Lw+I+J": "{n,plural,=0{{name} zapp-elt} other{{name} és {n} mások is zapp-elték}}",
"LwYmVi": "Ezen bejegyzésben lévő zap-ek a következő felhasználók között lesznek megosztva.",
"M3Oirc": "Hibaelhárító menü",
"M6C/px": "Become a leader",
"M6C/px": "Legyél vezető",
"MBAYRO": "Mutasd az \"Egyedi azonosítót\" és a \"JSON esetet\" minden üzenet szövegmezőjében",
"MI2jkA": "Nem elérhető:",
"MP54GY": "Pénztárca jelszava",
@ -244,7 +251,7 @@
"NdOYJJ": "Hmm nincs itt semmi.. Ellenőrizd a {newUsersPage}, hogy tudj néhány javasolt Nostr felhasználót követni!",
"NepkXH": "Nem lehetséges {amount} sats-al szavazni, kérlek alapértelmezettnek más összeget állíts be",
"NndBJE": "Új felhasználók oldal",
"O3Jz4E": "Use your invite code to earn sats!",
"O3Jz4E": "Használd a meghívó kódodat, hogy satokat keress!",
"O8Z8t9": "Többet mutasd meg",
"OEW7yJ": "Zap-ek",
"OKhRC6": "Megosztás",
@ -252,7 +259,7 @@
"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",
"P2o+ZZ": "Invalid Nostr Address",
"P2o+ZZ": "Érvénytelen Nostr cím",
"P61BTu": "JSON eset",
"P7FD0F": "Rendszer (Alapértelmezett)",
"P7nJT9": "Összesen ma (UTC): {amount} sat",
@ -266,7 +273,7 @@
"Qxv0B2": "Jelenleg a zap-medencében {number} sats van.",
"R/6nsx": "Előfizetés",
"R81upa": "Általad követett személyek",
"RDha9y": "Service Worker Not Running",
"RDha9y": "A szervizmunkás nem fut",
"RSr2uB": "A felhasználónév csak kisbetűket és számokat tartalmazhat",
"RahCRH": "Lejárt",
"RfhLwC": "Írta: {author}",
@ -289,11 +296,11 @@
"TJo5E6": "Előnézet",
"TP/cMX": "Befejezve",
"TaeBqw": "Bejelentkezés a Nostr kiterjesztéssel",
"TdTXXf": "Learn more",
"TdTXXf": "Részletek",
"TdtZQ5": "Crypto",
"TpgeGw": "Hex Salt..",
"Tpy00S": "Személyek",
"TwyMau": "Account",
"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",
@ -302,11 +309,10 @@
"UUPFlt": "Ahhoz hogy a bejegyzés tartalma megjelenjen, a felhasználóknak a tartalomra vonatkozó figyelmeztetést el kell fogadniuk.",
"Ub+AGc": "Bejelentkezés",
"Up5U7K": "Tiltás",
"Ups2/p": "Your application is pending",
"Ups2/p": "Jelentkezésed folyamatban van",
"UrKTqQ": "Van aktív iris.to fiókod",
"UxgyeY": "A Te ajánlói kódod: {code}",
"VL900k": "Ajánlott csomópontok",
"VN0+Fz": "Egyenleg: {amount} sats",
"VOjC1i": "Válaszd ki mely szolgáltatóhoz legyenek a fájlok feltöltve",
"VR5eHw": "Publikus kulcs (npub/nprofile)",
"VcwrfF": "Igen, kérem",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, helyreállító (mnemonikus)",
"XECMfW": "Használati mutatók küldése",
"XICsE8": "Fájl tárhelyszolgáltatók",
"XQiFEl": "Követői csomópontok minősége",
"XXm7jJ": "Trendi hashtagek",
"XgWvGA": "Reakciók",
"XhpBfA": "{site} egy nyílt forráskódú projekt, amelyet szenvedélyes emberek építenek szabadidejükben, adományaitokat nagyra értékeljük",
@ -343,10 +350,11 @@
"aWpBzj": "Mutass többet",
"b12Goz": "Emlékezeterősítő",
"b5vAk0": "Az azonosító mint egy Lightning cím fog működni, és a kiválasztott LNURL- vagy Lightning-címre irányít át",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bF1MYT": "Közösségi vezető vagy, és keres <b>{percent}</b> az ajánlott felhasználók előfizetéseiből!",
"bG00/W": "A szervizmunkás fut",
"bJ+wrA": "Prune-lista kiszámítása",
"bLZL5a": "Cím lekérése",
"bMphls": "Logged in with read-only access",
"bMphls": "Bejelentkezve csak olvasási hozzáféréssel",
"bQdA2k": "Érzékeny tartalom",
"bep9C3": "Publikus Kulcs",
"bfvyfs": "Névtelen",
@ -364,8 +372,7 @@
"cuV2gK": "név már foglalt",
"cyR7Kh": "Vissza",
"d+6YsV": "Listák a némításhoz:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "Előzmények",
"d2ebEu": "A Push-ra nem vagy feliratkozva",
"d7d0/x": "LN cím",
"d8gpCh": "Próbálj meg 5-nél kevesebb hashtaget használni, hogy a témánál maradj 🙏",
"dOQCL8": "Megjelenítendő név",
@ -382,7 +389,7 @@
"eSzf2G": "Egyetlen {nIn} sats zap a zap-medencéhez {nOut} sats-ot foglal le.",
"eXT2QQ": "Csoportbeszélgetés",
"egib+2": "{n,plural,=1{és még {n} valaki} other{és {n} másik felhasználó}}",
"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": "A közösségi vezetők olyan személyek, akik a nostr ökoszisztémát növelik azáltal, hogy aktívak a helyi közösségükben, és segítenek az új felhasználók felvételében. Bárki lehet közösségi vezető, de jelenleg csak kevesen viselik ezt a megtisztelő címet.",
"fBI91o": "Zap",
"fBlba3": "Köszönjük, hogy a(z) {site} használod, ha teheted fontold meg az adományozást.",
"fOksnD": "Nem szavazhatsz, mert az LNURL szolgáltatód a zap-eket nem támogatja",
@ -404,6 +411,7 @@
"grQ+mI": "Kiszabott munka elvégzése (PoW)",
"h7jvCs": "{site} együtt sokkal szórakoztatóbb!",
"h8XMJL": "Kitüntetések",
"hF6IN2": "Követői lista metszése",
"hMzcSq": "Üzenetek",
"hRTfTR": "PRO",
"hY4lzx": "Támogatás",
@ -412,11 +420,12 @@
"hicxcO": "Válaszok megjelenítése",
"hmZ3Bz": "Média",
"hniz8Z": "itt",
"hvFRBo": "Interaction",
"hvFRBo": "Interakció",
"i/dBAR": "Zap Medence",
"iCqGww": "({n}) reakciók",
"iEoXYx": "DeepL fordító",
"iGT1eE": "Akadályozd meg hogy a kamu fiókok utánozzanak",
"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": "Fizetések",
@ -431,7 +440,7 @@
"jTrbGf": "{n} km - {location}",
"jvo0vs": "Mentés",
"jzgQ2z": "{n} Reakció",
"k0kCJp": "Apply Now",
"k0kCJp": "Jelentkezz most",
"k2veDA": "Írás",
"k7sKNy": "A mi saját NIP-05 azonosítási szolgáltatásunk, amelynek a használatával ennek az oldalnak a fejlesztését segítheted és ezzel egy speciális kitüntetést is szerezhetsz!",
"kEZUR8": "Regisztráljon egy Iris felhasználónevet",
@ -460,7 +469,9 @@
"n1Whvj": "Váltás",
"nDejmx": "Tiltás visszavonása",
"nGBrvw": "Könyvjelzők",
"nGGDsi": "Notifications Allowed",
"nGGDsi": "Értesítések engedélyezve",
"nIchMQ": "Fióktevékenység keresése ({progress})",
"nUT0Lv": "Eszközök",
"nihgfo": "Hallgassa meg ezt a cikket",
"nwZXeh": "{n} tiltott",
"o7e+nJ": "{n} követők",
@ -469,9 +480,11 @@
"ojzbwv": "Úgy néz ki Nostr címmel még nem rendelkezel, érdemes lenne szerezned egyett! Ezt nézd {link}",
"p4N05H": "Feltöltés",
"p85Uwy": "Aktív Előfizetések",
"p9Ps2l": "{x}/{y} csomóponttal rendelkezel ({percent})",
"pI+77w": "A Snort csomópontból letölthető biztonsági másolatok",
"pRess9": "ZapPool",
"puLNUJ": "Kiemel",
"pukxg/": "Fizetések",
"pzTOmv": "Követők",
"qD9EUF": "E-mail <> PÜ híd a Snort nostr címedhez",
"qDwvZ4": "Ismeretlen hiba",
@ -479,7 +492,7 @@
"qMx1sA": "Alapértelmezett Zap összeg",
"qUJTsT": "Tiltva",
"qZsKBR": "{tier} megújítása",
"qcJFEJ": "Notifications API Disabled",
"qcJFEJ": "Értesítési API letiltva",
"qdGuQo": "A te privát kulcsod (senkivel se oszd meg)",
"qfmMQh": "Ez a bejegyzés némítva",
"qkvYUb": "Hozzáadás a profilhoz",
@ -502,12 +515,13 @@
"tOdNiY": "Sötét",
"th5lxp": "Az írási joggal rendelkező csomópontok egy részhalmazának bejegyzés küldése",
"thnRpU": "A NIP-05 azonosítás segíthet:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Csomópont adatok",
"ttxS0b": "Támogatói jelvény",
"u+LyXc": "Interakciók",
"u/vOPu": "Fizetve",
"u4bHcR": "Nézze meg a kódot itt: {link}",
"u9NoC1": "Name must be less than {limit} characters",
"u9NoC1": "A névnek kevesebbnek kell lennie, mint {limit} karakter",
"uCk8r+": "Már van fiókja?",
"uKqSN+": "Követői hírfolyam",
"uSV4Ti": "A megosztásokhoz manuális megerősítés szükséges",
@ -517,11 +531,11 @@
"v8lolG": "Csevegés indítása",
"vB3oQ/": "Névjegylistának vagy publikus kulcs listának kell lennie",
"vN5UH8": "Profilkép",
"vU/Q5i": "Ez az eszköz megkeresi az összes követője által utoljára közzétett eseményt, és eltávolítja azokat, akik 6 hónapja nem tettek közzé eseményt.",
"vZ4quW": "A NIP-05 egy DNS alapú azonosítási specifikáció, ami segít a valós személyed bizonyításában.",
"vhlWFg": "Szavazási opciók",
"vlbWtt": "Szerezz ingyen egyet",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} sats",
"voxBKC": "Barátok által követve",
"vxwnbh": "Az összes közzétett eseményre alkalmazandó munka mennyisége",
"w1Fanr": "Üzleti",
"w6qrwX": "NSFW",
@ -543,7 +557,7 @@
"xl4s/X": "További feltételek:",
"xmcVZ0": "Keresés",
"xybOUv": "FAN",
"y/bmsG": "Allow",
"y/bmsG": "Engedélyez",
"y1Z3or": "Nyelv",
"yCLnBC": "LNURL vagy Lightning cím",
"yNBPJp": "Segítsen finanszírozni a(z) {site} fejlesztését",

View File

@ -51,6 +51,7 @@
"2zJXeA": "Profil",
"39AHJm": "Daftar",
"3KNMbJ": "Artikel",
"3QwfJR": "~{amount}",
"3cc4Ct": "Cahaya",
"3gOsZq": "Penerjemah",
"3qnJlS": "Anda memberikan suara dengan {amount} sats",
@ -66,6 +67,7 @@
"4OB335": "Tidak disukai",
"4Vmpt4": "Nostr Plebs adalah salah satu penyedia NIP-05 pertama di ruang ini dan menawarkan koleksi domain yang bagus dengan harga terjangkau",
"4Z3t5i": "Gunakan imgproxy untuk mengkompresi gambar",
"4emo2p": "Missing Relays",
"4rYCjn": "Catatan untuk diri sendiri",
"5BVs2e": "zap",
"5CB6zB": "Zap Splits",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Kursi Cashu",
"6/hB3S": "Menonton Pemutaran Ulang",
"62nsdy": "Coba lagi.",
"6559gb": "New follow list length {length}",
"65BmHb": "Gagal memproxy gambar dari {host}, klik di sini untuk memuat secara langsung",
"6OSOXl": "Alasan: <i>{reason}</i>",
"6bgpn+": "Tidak semua klien mendukung ini, Anda mungkin masih menerima beberapa zap seolah-olah zap splits tidak dikonfigurasi",
@ -129,6 +132,8 @@
"C81/uG": "Keluar",
"C8HhVE": "Berikut yang Disarankan",
"CHTbO3": "Gagal memuat faktur",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "Orang-orang yang sedang tren",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "Transfer",
"Dx4ey3": "Alihkan semua",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Catatan pencarian",
"ELbg9p": "Penyedia Data",
"EQKRE4": "Menampilkan lencana di halaman profil",
@ -188,6 +194,7 @@
"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?",
"IKKHqV": "Mengikuti",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "Anda memiliki akun iris.to yang aktif",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "Saldo: {amount} sats",
"VOjC1i": "Pilih layanan unggah mana yang ingin Anda unggahkan lampirannya",
"VR5eHw": "Kunci publik (npub/nprofile)",
"VcwrfF": "Ya, silakan.",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "Kirim metrik penggunaan",
"XICsE8": "Host file",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "Reaksi",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -345,6 +352,7 @@
"b5vAk0": "Pegangan Anda akan bertindak seperti alamat kilat dan akan mengarahkan ke LNURL atau alamat Lightning yang Anda pilih",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "Dapatkan Alamat",
"bMphls": "Logged in with read-only access",
"bQdA2k": "Konten Sensitif",
@ -365,7 +373,6 @@
"cyR7Kh": "Kembali",
"d+6YsV": "Daftar yang akan dibisukan:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "Sejarah",
"d7d0/x": "Alamat LN",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "Nama tampilan",
@ -404,6 +411,7 @@
"grQ+mI": "Bukti Pekerjaan",
"h7jvCs": "{site} lebih menyenangkan jika dilakukan bersama-sama!",
"h8XMJL": "Lencana",
"hF6IN2": "Prune Follow List",
"hMzcSq": "Pesan-pesan",
"hRTfTR": "PRO",
"hY4lzx": "Dukungan",
@ -417,6 +425,7 @@
"iCqGww": "Reaksi-reaksi ({n})",
"iEoXYx": "DeepL terjemahan",
"iGT1eE": "Cegah akun palsu meniru Anda",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "Menangani",
"iXPL0Z": "Tidak dapat masuk dengan kunci pribadi pada koneksi yang tidak aman, gunakan ekstensi pengelola kunci Nostr sebagai gantinya",
"iYc3Ld": "Pembayaran",
@ -461,6 +470,8 @@
"nDejmx": "Buka blokir",
"nGBrvw": "Penanda-penanda buku",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "Dengarkan artikel ini",
"nwZXeh": "{n} diblokir",
"o7e+nJ": "{n} pengikut",
@ -469,9 +480,11 @@
"ojzbwv": "Hei, sepertinya Anda belum memiliki Nostr Address, Anda harus mendapatkannya! Lihat {link}",
"p4N05H": "Unggah",
"p85Uwy": "Langganan Aktif",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "Cadangan yang dapat diunduh dari relai Snort",
"pRess9": "ZapPool",
"puLNUJ": "Sematkan",
"pukxg/": "Payments",
"pzTOmv": "Pengikut",
"qD9EUF": "Email <> Jembatan DM untuk alamat nostr Snort Anda",
"qDwvZ4": "Kesalahan yang tidak diketahui",
@ -502,6 +515,7 @@
"tOdNiY": "Gelap",
"th5lxp": "Mengirim catatan ke subset dari relai tulis Anda",
"thnRpU": "Mendapatkan verifikasi NIP-05 dapat membantu:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Metrik Relai",
"ttxS0b": "Lencana Pendukung",
"u+LyXc": "Interaksi",
@ -517,11 +531,11 @@
"v8lolG": "Mulai obrolan",
"vB3oQ/": "Harus berupa daftar kontak atau daftar pubkey",
"vN5UH8": "Gambar Profil",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 adalah spesifikasi verifikasi berbasis DNS yang membantu memvalidasi Anda sebagai pengguna nyata.",
"vhlWFg": "Opsi Jajak Pendapat",
"vlbWtt": "Dapatkan yang gratis",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} sats",
"vxwnbh": "Jumlah pekerjaan yang harus diterapkan pada semua acara yang dipublikasikan",
"w1Fanr": "Bisnis",
"w6qrwX": "NSFW",

View File

@ -51,6 +51,7 @@
"2zJXeA": "Profili",
"39AHJm": "Iscriviti",
"3KNMbJ": "Articoli",
"3QwfJR": "~{amount}",
"3cc4Ct": "Chiaro",
"3gOsZq": "Traduttori",
"3qnJlS": "Stai votando con {amount} sats",
@ -66,6 +67,7 @@
"4OB335": "Non mi piace",
"4Vmpt4": "Nostr Plebs è uno dei primi provider NIP-05 e offre una grande collezione di domini a prezzi ragionevoli",
"4Z3t5i": "Utilizza imgproxy per comprimere le immagini",
"4emo2p": "Missing Relays",
"4rYCjn": "Note personali",
"5BVs2e": "zap",
"5CB6zB": "Spaccati Zap",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Guarda il replay",
"62nsdy": "Riprova",
"6559gb": "New follow list length {length}",
"65BmHb": "Non è stato possibile inviare l'immagine di proxy da {host}, fare clic qui per caricarla direttamente",
"6OSOXl": "Motivo: <i>{reason}</i>",
"6bgpn+": "Non tutti i client supportano questa funzione, per cui è possibile che si ricevano ancora alcuni zap come se la suddivisione in zap non fosse configurata.",
@ -129,6 +132,8 @@
"C81/uG": "Disconnettiti",
"C8HhVE": "Seguiti suggeriti",
"CHTbO3": "Impossibile caricare la fattura",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "Persone di tendenza",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "Trasferisci",
"Dx4ey3": "Seleziona tutti",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Note di ricerca",
"ELbg9p": "Fornitori di dati",
"EQKRE4": "Mostra i badge sulle pagine del profilo",
@ -188,6 +194,7 @@
"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?",
"IKKHqV": "Segue",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "Avete un account iris.to attivo",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "Saldo: {amount} sats",
"VOjC1i": "Scegli quale servizio vuoi usare per caricare gli allegati",
"VR5eHw": "Chiave pubblica (npub/nprofile)",
"VcwrfF": "Sì, per favore",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "Invio di metriche di utilizzo",
"XICsE8": "Ospiti di file",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "Reazioni",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -345,6 +352,7 @@
"b5vAk0": "Il tuo handle si comporterà come un indirizzo lightning e reindirizzerà al LNURL o all'indirizzo Lightning selezionato",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "Ottieni l'indirizzo",
"bMphls": "Logged in with read-only access",
"bQdA2k": "Contenuto sensibile",
@ -365,7 +373,6 @@
"cyR7Kh": "Indietro",
"d+6YsV": "Elenchi da silenziare:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "Cronologia",
"d7d0/x": "Indirizzo LN",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "Nome visualizzato",
@ -404,6 +411,7 @@
"grQ+mI": "Prova di lavoro",
"h7jvCs": "{site} è più divertente insieme!",
"h8XMJL": "Distintivi",
"hF6IN2": "Prune Follow List",
"hMzcSq": "Messaggi",
"hRTfTR": "PRO",
"hY4lzx": "Supporto",
@ -417,6 +425,7 @@
"iCqGww": "Reazioni ({n})",
"iEoXYx": "DeepL traduzioni",
"iGT1eE": "Impedisci agli account falsi di imitarti",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "Handle",
"iXPL0Z": "Impossibile effettuare il login con la chiave privata su una connessione insicura. Utilizza un'estensione Nostr key manager",
"iYc3Ld": "Pagamenti",
@ -461,6 +470,8 @@
"nDejmx": "Sblocca",
"nGBrvw": "Preferiti",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "Ascolta questo articolo",
"nwZXeh": "{n} bloccato",
"o7e+nJ": "{n} seguaci",
@ -469,9 +480,11 @@
"ojzbwv": "Ehi, sembra che tu non abbia ancora un indirizzo Nostr, dovresti procurartene uno! Dai un'occhiata a {link}",
"p4N05H": "Caricare",
"p85Uwy": "Iscrizioni attive",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "Backup scaricabili dal relè Snort",
"pRess9": "ZapPool",
"puLNUJ": "Pin",
"pukxg/": "Payments",
"pzTOmv": "Seguaci",
"qD9EUF": "Email <> DM bridge per il tuo indirizzo nostr Snort",
"qDwvZ4": "Errore sconosciuto",
@ -502,6 +515,7 @@
"tOdNiY": "Scuro",
"th5lxp": "Inviare la nota a un sottoinsieme di relè di scrittura",
"thnRpU": "Ottenere la verifica NIP-05 può aiutare:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Metriche del relè",
"ttxS0b": "Distintivo del sostenitore",
"u+LyXc": "Interazioni",
@ -517,11 +531,11 @@
"v8lolG": "Avviare la chat",
"vB3oQ/": "Deve essere un elenco di contatti o un elenco di pubkey",
"vN5UH8": "Immagine del profilo",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 è una specifica di verifica basata su DNS che ti aiuta a convalidare come un utente reale.",
"vhlWFg": "Opzioni del sondaggio",
"vlbWtt": "Ottenere un omaggio",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} sats",
"vxwnbh": "Quantità di lavoro da applicare a tutti gli eventi pubblicati",
"w1Fanr": "Affari",
"w6qrwX": "NSFW",

View File

@ -51,6 +51,7 @@
"2zJXeA": "プロフィール",
"39AHJm": "会員登録",
"3KNMbJ": "記事",
"3QwfJR": "~{amount}",
"3cc4Ct": "ライト",
"3gOsZq": "翻訳者",
"3qnJlS": "{amount}satsで投票します",
@ -66,6 +67,7 @@
"4OB335": "イヤ",
"4Vmpt4": "Nostr Plebsは宇宙で最初のNIP-05プロバイダのひとつで、豊富なドメインをリーズナブルな価格で提供します",
"4Z3t5i": "imgproxyを使って画像を圧縮します",
"4emo2p": "Missing Relays",
"4rYCjn": "自分用メモ",
"5BVs2e": "ザップ",
"5CB6zB": "ザップ・スプリッツ",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> カシューサッツ",
"6/hB3S": "リプレイを見る",
"62nsdy": "リトライ",
"6559gb": "New follow list length {length}",
"65BmHb": "{host}から画像のプロキシに失敗しました。ここをクリックして直接読み込みます",
"6OSOXl": "理由: <i>{reason}</i>",
"6bgpn+": "すべてのクライアントがこれをサポートしているわけではありません。",
@ -129,6 +132,8 @@
"C81/uG": "ログアウト",
"C8HhVE": "おすすめのフォロー",
"CHTbO3": "インボイスの読み込みに失敗しました",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "話題のユーザー",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "転送",
"Dx4ey3": "すべてトグル",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "検索メモ",
"ELbg9p": "データプロバイダ",
"EQKRE4": "プロフィールページにバッジを表示",
@ -188,6 +194,7 @@
"HbefNb": "ウォレットを開く",
"HhcAVH": "この人物をフォローしていない場合は、ここをクリックして <i>{link}</i>からメディアを読み込むか、または <a><i>設定を更新して</i></a> から常に全員からメディアを読み込むようにしてください。",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "本当にこの投稿のピン留めを解除しますか?",
"IKKHqV": " フォロー",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "iris.toアカウントをお持ちの方",
"UxgyeY": "Your referral code is {code}",
"VL900k": "おすすめリレー",
"VN0+Fz": "残高: {amount} sats",
"VOjC1i": "添付ファイルをアップロードするためのサービスを選択してください",
"VR5eHw": "公開鍵 (npub/nprofile)",
"VcwrfF": "はい、お願いします",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "利用状況データを送信する",
"XICsE8": "ファイルホスト",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "話題のハッシュタグ",
"XgWvGA": "リアクション",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -345,6 +352,7 @@
"b5vAk0": "あなたのハンドルはライトニングアドレスのように動作し、選択したLNURLまたはライトニングアドレスに転送されます",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "アドレス取得",
"bMphls": "Logged in with read-only access",
"bQdA2k": "センシティブなコンテンツ",
@ -365,7 +373,6 @@
"cyR7Kh": "戻る",
"d+6YsV": "ミュートするリスト",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "履歴",
"d7d0/x": "ライトニング アドレス",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "表示名",
@ -404,6 +411,7 @@
"grQ+mI": "プルーフオブワーク",
"h7jvCs": "{site} 一緒にいる方が楽しい!",
"h8XMJL": "バッジ",
"hF6IN2": "Prune Follow List",
"hMzcSq": "メッセージ",
"hRTfTR": "プロ",
"hY4lzx": "対応",
@ -417,6 +425,7 @@
"iCqGww": "リアクション ({n})",
"iEoXYx": "DeepL翻訳",
"iGT1eE": "なりすましを防ぎます",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "ハンドル",
"iXPL0Z": "安全でない接続では秘密鍵でログインすることはできません。代わりにNostrキーマネージャー拡張機能を使用してください。",
"iYc3Ld": "支払い",
@ -461,6 +470,8 @@
"nDejmx": "ブロック解除",
"nGBrvw": "ブックマーク",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "この記事を聴く",
"nwZXeh": "{n} ブロック",
"o7e+nJ": "{n} フォロワー",
@ -469,9 +480,11 @@
"ojzbwv": "まだNostrアドレスを持ってないようだね以下のリンクをチェックして、ぜひ手に入れよう{link}",
"p4N05H": "アップロード",
"p85Uwy": "有効なサブスクリプション",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "Snortリレーからダウンロードできるバックアップ",
"pRess9": "ZapPool",
"puLNUJ": "ピン留め",
"pukxg/": "Payments",
"pzTOmv": "フォロワー",
"qD9EUF": "Snort Nostrアドレス用の、EmailとDMの中継",
"qDwvZ4": "不明なエラー",
@ -502,6 +515,7 @@
"tOdNiY": "ダーク",
"th5lxp": "書き込みリレーの一部に投稿を送信する",
"thnRpU": "NIP-05認証を得るメリット:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "リレー・メトリクス",
"ttxS0b": "サポーターバッジ",
"u+LyXc": "相互作用",
@ -517,11 +531,11 @@
"v8lolG": "チャットを開始",
"vB3oQ/": "コンタクトリストまたはパブキーリストであること",
"vN5UH8": "プロフィール画像",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05は、ユーザが本物であることを検証するDNSベースの仕組みです。",
"vhlWFg": "投票オプション",
"vlbWtt": "無料版を入手",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} sats",
"vxwnbh": "公開イベントに適用する作業量",
"w1Fanr": "ビジネス",
"w6qrwX": "NSFW",

View File

@ -51,6 +51,7 @@
"2zJXeA": "Profil",
"39AHJm": "Sign Up",
"3KNMbJ": "Articles",
"3QwfJR": "~{amount}",
"3cc4Ct": "Pagi",
"3gOsZq": "Penerjemah",
"3qnJlS": "You are voting with {amount} sats",
@ -66,6 +67,7 @@
"4OB335": "Bpp",
"4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices",
"4Z3t5i": "Use imgproxy to compress images",
"4emo2p": "Missing Relays",
"4rYCjn": "Nota tersendiri",
"5BVs2e": "zap",
"5CB6zB": "Zap Splits",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Watch Replay",
"62nsdy": "Cuba lagi.",
"6559gb": "New follow list length {length}",
"65BmHb": "Failed to proxy image from {host}, click here to load directly",
"6OSOXl": "Alasan: <i>{reason}</i>",
"6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured",
@ -129,6 +132,8 @@
"C81/uG": "Logout",
"C8HhVE": "Suggested Follows",
"CHTbO3": "Failed to load invoice",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "Trending People",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "Transfer",
"Dx4ey3": "Toggle semua-nya",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Search notes",
"ELbg9p": "Data Providers",
"EQKRE4": "Show badges on profile pages",
@ -188,6 +194,7 @@
"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?",
"IKKHqV": "Pengintip",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "anda memiliki akun iris.to yang aktif",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "Baki: {amount} sats",
"VOjC1i": "Pick which upload service you want to upload attachments to",
"VR5eHw": "Kunci pub (npub/nprofile)",
"VcwrfF": "Ya, silakan.",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemo",
"XECMfW": "Kirim metrik penggunaan",
"XICsE8": "Host fail",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "Emojis",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -345,6 +352,7 @@
"b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "Dapatkan Alamat",
"bMphls": "Logged in with read-only access",
"bQdA2k": "Sensitive Content",
@ -365,7 +373,6 @@
"cyR7Kh": "Pulang",
"d+6YsV": "Lists to mute:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "Sejarah",
"d7d0/x": "Alamat LN",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "Nama tampilan",
@ -404,6 +411,7 @@
"grQ+mI": "Proof-of-Work",
"h7jvCs": "{site} lebih seronok jikalau bersama!",
"h8XMJL": "Lencana",
"hF6IN2": "Prune Follow List",
"hMzcSq": "Msg2",
"hRTfTR": "PRO",
"hY4lzx": "Supports",
@ -417,6 +425,7 @@
"iCqGww": "Reactions ({n})",
"iEoXYx": "terjemahan DeepL",
"iGT1eE": "Cegah akaun palsu meniru Anda",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "Handle",
"iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead",
"iYc3Ld": "Pembayaran",
@ -461,6 +470,8 @@
"nDejmx": "Unblock",
"nGBrvw": "Penanda2 buku",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "Dengar audio artikel ini",
"nwZXeh": "{n} di-blok",
"o7e+nJ": "{n} followers",
@ -469,9 +480,11 @@
"ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}",
"p4N05H": "Upload",
"p85Uwy": "Langganan Aktif",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "Downloadable backups from Snort relay",
"pRess9": "ZapPool",
"puLNUJ": "Sematkan",
"pukxg/": "Payments",
"pzTOmv": "Followers",
"qD9EUF": "Email <> DM bridge for your Snort nostr address",
"qDwvZ4": "'Error' yang tidak diketahui",
@ -502,6 +515,7 @@
"tOdNiY": "Malam",
"th5lxp": "Mengirim catatan ke subset dari relay tulis Anda",
"thnRpU": "Getting NIP-05 verified can help:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Metrik Relay",
"ttxS0b": "Lencana Pendukung",
"u+LyXc": "Inter-aksi",
@ -517,11 +531,11 @@
"v8lolG": "Mulai chat",
"vB3oQ/": "Must be a contact list or pubkey list",
"vN5UH8": "Gambar Profil",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.",
"vhlWFg": "Poll Options",
"vlbWtt": "Dapatkan yang gratis",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} sats",
"vxwnbh": "Amount of work to apply to all published events",
"w1Fanr": "Perniagaan",
"w6qrwX": "NSFW",

View File

@ -51,6 +51,7 @@
"2zJXeA": "Profielen",
"39AHJm": "Aanmelden",
"3KNMbJ": "Artikelen",
"3QwfJR": "~{amount}",
"3cc4Ct": "Licht",
"3gOsZq": "Vertalers",
"3qnJlS": "Je stemt met {amount} sats",
@ -66,6 +67,7 @@
"4OB335": "Vind-ik-niet-leuks",
"4Vmpt4": "NostrPlebs is een van de eerste Nostr NIP-05 providers en biedt een goede verzameling domeinen aan tegen redelijke prijzen",
"4Z3t5i": "Gebruik imgproxy om foto's te comprimeren",
"4emo2p": "Missing Relays",
"4rYCjn": "Notitie aan mijzelf",
"5BVs2e": "zappen",
"5CB6zB": "Zap Splitsingen",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Kasjmoezen",
"6/hB3S": "Bekijk herhaling",
"62nsdy": "Opnieuw proberen",
"6559gb": "New follow list length {length}",
"65BmHb": "Proxy afbeelding laden van {host} is mislukt, klik hier om direct te laden",
"6OSOXl": "Reden: <i>{reason}</i>",
"6bgpn+": "Niet alle clients ondersteunen dit, het kan zijn dat je nog steeds zaps ontvangt alsof zap-splitsingen niet is geconfigureerd.",
@ -129,6 +132,8 @@
"C81/uG": "Uitloggen",
"C8HhVE": "Voorgestelde Accounts",
"CHTbO3": "Invoice laden mislukt",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "Trending Accounts",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "Transfer",
"Dx4ey3": "Alles Toggeren",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Opmerkingen zoeken",
"ELbg9p": "Data aanbieders",
"EQKRE4": "Badges tonen op profielpagina's",
@ -188,6 +194,7 @@
"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?",
"IKKHqV": "Volgers",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "Je hebt een actieve iris.to account",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "Saldo: {amount} sats",
"VOjC1i": "Kies naar welke service u bijlagen wilt uploaden",
"VR5eHw": "Publieke sleutel (npub/nprofile)",
"VcwrfF": "Ja graag",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "Gebruiksgegevens verzenden",
"XICsE8": "File hosts",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "Reacties",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -345,6 +352,7 @@
"b5vAk0": "Uw Nostr adres gedraagt zich als een Lightning adres en stuurt zaps door naar uw gekozen LNURL/Lightning adres",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "Adres krijgen",
"bMphls": "Logged in with read-only access",
"bQdA2k": "Gevoelige inhoud",
@ -365,7 +373,6 @@
"cyR7Kh": "Terug",
"d+6YsV": "Lijsten om te dempen:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "Geschiedenis",
"d7d0/x": "LN Address",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "Weergavenaam",
@ -404,6 +411,7 @@
"grQ+mI": "Proof of Work",
"h7jvCs": "{site} is samen leuker!",
"h8XMJL": "Badges",
"hF6IN2": "Prune Follow List",
"hMzcSq": "Berichten",
"hRTfTR": "PRO",
"hY4lzx": "Ondersteuningen",
@ -417,6 +425,7 @@
"iCqGww": "Reacties ({n})",
"iEoXYx": "DeepL vertalingen",
"iGT1eE": "Voorkom dat nepaccounts je imiteren",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "Hendel",
"iXPL0Z": "Kan niet inloggen met een privésleutel in een onbeveiligde omgeving, gebruik in plaats daarvan een Nostr key manager extensie",
"iYc3Ld": "Betalingen",
@ -461,6 +470,8 @@
"nDejmx": "Deblokkeer",
"nGBrvw": "Bladwijzers",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "Luister naar dit artikel",
"nwZXeh": "{n} geblokkeerd",
"o7e+nJ": "{n} volgers",
@ -469,9 +480,11 @@
"ojzbwv": "Hey, het lijkt erop dat je nog geen Nostr Adres hebt. Overweeg om er een te nemen! Bekijk {link}",
"p4N05H": "Uploaden",
"p85Uwy": "Actieve Abonnementen",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "Downloadbare back-ups van Snort relay",
"pRess9": "ZapPool",
"puLNUJ": "Vastmaken aan profielpagina",
"pukxg/": "Payments",
"pzTOmv": "Volgers",
"qD9EUF": "E-mail <> DM bridge voor je Snort Nostr adres",
"qDwvZ4": "Onbekende fout",
@ -502,6 +515,7 @@
"tOdNiY": "Donker thema",
"th5lxp": "Stuur notitie naar een subset van schrijfrelays",
"thnRpU": "NIP-05 geverifieerd worden kan helpen:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Relaisgegevens",
"ttxS0b": "Supporter Badge",
"u+LyXc": "Interacties",
@ -517,11 +531,11 @@
"v8lolG": "Start chat",
"vB3oQ/": "Moet een contactlijst of pubkey-lijst zijn",
"vN5UH8": "Profiel afbeelding",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 is een DNS-gebaseerde identificatie die helpt u te valideren als een bepaalde gebruiker.",
"vhlWFg": "Poll Opties",
"vlbWtt": "Verkrijg er één gratis",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} sats",
"vxwnbh": "Hoeveelheid werk van toepassing op alle gepubliceerde events",
"w1Fanr": "Bedrijf",
"w6qrwX": "NSFW",

View File

@ -51,6 +51,7 @@
"2zJXeA": "Perfis",
"39AHJm": "Registrar-se",
"3KNMbJ": "Artigos",
"3QwfJR": "~{amount}",
"3cc4Ct": "Claro",
"3gOsZq": "Tradutores",
"3qnJlS": "Você está votando com {amount} sats",
@ -66,6 +67,7 @@
"4OB335": "Descurtir",
"4Vmpt4": "Nostr Plebs é um dos primeiros provedores NIP-05 e oferece uma boa coleção de domínios a preços razoáveis",
"4Z3t5i": "Usar imgproxy para comprimir imagens",
"4emo2p": "Missing Relays",
"4rYCjn": "Nota pessoal",
"5BVs2e": "zap",
"5CB6zB": "Zap Splits",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Assistir ao replay",
"62nsdy": "Repetir",
"6559gb": "New follow list length {length}",
"65BmHb": "Falha ao fazer proxy da imagem de {host}, clique aqui para carregar diretamente",
"6OSOXl": "Motivo: <i>{reason}</i>",
"6bgpn+": "Nem todos os clientes suportam isso; você ainda poderá receber alguns zaps como se a divisão de zaps não estivesse configurada",
@ -129,6 +132,8 @@
"C81/uG": "Sair",
"C8HhVE": "Usuários sugeridos",
"CHTbO3": "Falha ao carregar invoice",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "Pessoas em destaque",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "Transferir",
"Dx4ey3": "Alternar entre todos",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Notas de pesquisa",
"ELbg9p": "Provedores de dados",
"EQKRE4": "Mostrar emblemas nas páginas de perfil",
@ -188,6 +194,7 @@
"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?",
"IKKHqV": "Seguindo",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "Você tem uma conta ativa no iris.to",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "Saldo: {amount} sats",
"VOjC1i": "Escolha para qual serviço fazer upload dos arquivos",
"VR5eHw": "Chave pública (npub/nprofile)",
"VcwrfF": "Sim, por favor",
@ -322,6 +328,7 @@
"X7xU8J": "nseg, npub, nip-05, hex, mnemônico",
"XECMfW": "Enviar métricas de uso",
"XICsE8": "Servidores de arquivos",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "Reações",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -345,6 +352,7 @@
"b5vAk0": "Seu identificador atuará como um endereço lightning e redirecionará para a LNURL escolhida ou endereço Lightning",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "Obter endereço",
"bMphls": "Logged in with read-only access",
"bQdA2k": "Conteúdo sensível",
@ -365,7 +373,6 @@
"cyR7Kh": "Voltar",
"d+6YsV": "Listas para silenciar:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "Histórico",
"d7d0/x": "Endereço LN",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "Exibir nome",
@ -404,6 +411,7 @@
"grQ+mI": "Prova de trabalho",
"h7jvCs": "{site} é mais divertido juntos!",
"h8XMJL": "Emblemas",
"hF6IN2": "Prune Follow List",
"hMzcSq": "Mensagens",
"hRTfTR": "PRO",
"hY4lzx": "Suporta",
@ -417,6 +425,7 @@
"iCqGww": "Reações ({n})",
"iEoXYx": "Traduções DeepL",
"iGT1eE": "Impedir que contas falsas imitem você",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "Identificador",
"iXPL0Z": "Não é possível fazer login com a chave privada em uma conexão insegura, por favor, use uma extensão para gerenciar chaves do Nostr",
"iYc3Ld": "Pagamentos",
@ -461,6 +470,8 @@
"nDejmx": "Desbloquear",
"nGBrvw": "Favoritos",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "Ouça este artigo",
"nwZXeh": "{n} bloqueados",
"o7e+nJ": "{n} seguidores",
@ -469,9 +480,11 @@
"ojzbwv": "Hey, parece que você ainda não tem um endereço no Nostr, você deveria ter um! Confira {link}",
"p4N05H": "Upload",
"p85Uwy": "Assinaturas ativas",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "Cópias baixáveis do relé do Snort",
"pRess9": "ZapPool",
"puLNUJ": "Fixar",
"pukxg/": "Payments",
"pzTOmv": "Seguidores",
"qD9EUF": "E-mail <> ponte DM para seu endereço de usuário do Snort",
"qDwvZ4": "Erro desconhecido",
@ -502,6 +515,7 @@
"tOdNiY": "Noturno",
"th5lxp": "Enviar nota a um subconjunto dos seus relés de escrita",
"thnRpU": "Obter a verificação do NIP-05 pode ajudar a:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Métricas de relé",
"ttxS0b": "Emblema de apoiador",
"u+LyXc": "Interações",
@ -517,11 +531,11 @@
"v8lolG": "Iniciar chat",
"vB3oQ/": "Deve ser uma lista de contatos ou uma lista de chaves públicas",
"vN5UH8": "Imagem do perfil",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 é uma especificação de verificação baseada em DNS que ajuda a validar você como um usuário real.",
"vhlWFg": "Opções de enquete",
"vlbWtt": "Obtenha um gratuitamente",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} sats",
"vxwnbh": "Quantidade de trabalho para aplicar a todos os eventos publicados",
"w1Fanr": "Negócios",
"w6qrwX": "NSFW",

View File

@ -51,6 +51,7 @@
"2zJXeA": "Профили",
"39AHJm": "Зарегистрироваться",
"3KNMbJ": "Статьи",
"3QwfJR": "~{amount}",
"3cc4Ct": "Светлый",
"3gOsZq": "Переводчики",
"3qnJlS": "Вы голосуете {amount} сатами",
@ -66,6 +67,7 @@
"4OB335": "Дизлайк",
"4Vmpt4": "Nostr Plebs является одним из первых сервисов NIP-05 и предлагает хорошую коллекцию доменов по приемлемым ценам",
"4Z3t5i": "Использовать imgproxy для сжатия изображений",
"4emo2p": "Missing Relays",
"4rYCjn": "Заметка самому себе",
"5BVs2e": "zap",
"5CB6zB": "Zap Splits",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Смотреть повтор",
"62nsdy": "Retry",
"6559gb": "New follow list length {length}",
"65BmHb": "Не удалось загрузить изображение через {host}, нажмите здесь, чтобы загрузить напрямую",
"6OSOXl": "Причина: <i>{reason}</i>",
"6bgpn+": "Не все клиенты поддерживают это, вы все равно можете получить некоторые запы, как если бы запределение не было настроено",
@ -129,6 +132,8 @@
"C81/uG": "Выход",
"C8HhVE": "Предложенные подписки",
"CHTbO3": "Не удалось загрузить инвойс",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "Популярные профили",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "Перевод",
"Dx4ey3": "Toggle all",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Поисковые заметки",
"ELbg9p": "Данные провайдера",
"EQKRE4": "Показывать значки на страницах профиля",
@ -188,6 +194,7 @@
"HbefNb": "Открыть кошелек",
"HhcAVH": "Вы не подписаны на этого человека, нажмите здесь, чтобы загрузить медиа из <i>{link}</i>, или обновите <a><i>ваши предпочтения</i></a> чтобы всегда загружать медиа от всех.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "Вы уверены, что хотите открепить эту заметку?",
"IKKHqV": "Подписки",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "У вас есть активная учетная запись iris.to",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "Баланс: {amount} сат",
"VOjC1i": "Выберите каким сервисом Вы бы хотели пользоваться для загрузки медиа",
"VR5eHw": "Публичный ключ (npub/nprofile)",
"VcwrfF": "Да, пожалуйста",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "Отправка показателей использования",
"XICsE8": "Хозяева файлов",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "Реакции",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -345,6 +352,7 @@
"b5vAk0": "Ваш хэндл будет выступать в качестве лайтнинг-адреса и перенаправлять запы на выбранный вами LNURL или LN адрес",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "Получить адрес",
"bMphls": "Logged in with read-only access",
"bQdA2k": "Контент 18+",
@ -365,7 +373,6 @@
"cyR7Kh": "Назад",
"d+6YsV": "Списки для отключения звука:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "История",
"d7d0/x": "Лайтнинг-адрес",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "Отображаемое имя",
@ -404,6 +411,7 @@
"grQ+mI": "Доказательство работы",
"h7jvCs": "{site} вместе веселее!",
"h8XMJL": "Бейджи",
"hF6IN2": "Prune Follow List",
"hMzcSq": "Сообщения",
"hRTfTR": "PRO",
"hY4lzx": "Поддержка",
@ -417,6 +425,7 @@
"iCqGww": "Реакции ({n})",
"iEoXYx": "Переводы от DeepL",
"iGT1eE": "Бороться с имитирующими Вас поддельными аккаунтами",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "Хэндл",
"iXPL0Z": "Не удается войти с помощью приватного ключа - ваше соединение небезопасно, пожалуйста, используйте расширение-менеджер ключей",
"iYc3Ld": "Платежи",
@ -461,6 +470,8 @@
"nDejmx": "Разблокировать",
"nGBrvw": "Закладки",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "Прослушать статью",
"nwZXeh": "{n} заблокировано",
"o7e+nJ": "{n} подписчиков",
@ -469,9 +480,11 @@
"ojzbwv": "Эй, похоже, что у вас еще нет адреса Nostr, вы должны его получить! Загляните на {link}",
"p4N05H": "Загрузить",
"p85Uwy": "Активные Подписки",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "Доступ к резервным копиям на релее Snort",
"pRess9": "ZapPool",
"puLNUJ": "Пин",
"pukxg/": "Payments",
"pzTOmv": "Подписчики",
"qD9EUF": "Мост между email и личными сообщениями для Вашего Snort-адреса",
"qDwvZ4": "Неизвестная ошибка",
@ -502,6 +515,7 @@
"tOdNiY": "Тёмная",
"th5lxp": "Отправить заметку на подмножество Ваших пишущих релеев",
"thnRpU": "Получение подтверждения NIP-05 может помочь:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Метрики реле",
"ttxS0b": "Бейдж Саппортера",
"u+LyXc": "Взаимодействие",
@ -517,11 +531,11 @@
"v8lolG": "Начать общение",
"vB3oQ/": "Должен быть список контактов или список pubkey",
"vN5UH8": "Изображение профиля",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 - это спецификация проверки на основе DNS, которая помогает подтвердить тебя как реального пользователя.",
"vhlWFg": "Настройки опроса",
"vlbWtt": "Получить бесплатно",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} сат",
"vxwnbh": "Объем работы по применению ко всем опубликованным событиям",
"w1Fanr": "Бизнес",
"w6qrwX": "NSFW",

View File

@ -1,7 +1,7 @@
{
"+D82kt": "Är du säker på att du vill dela vidare:{id}",
"+PzQ9Y": "Utbetalning nu",
"+UjDmN": "Logged in with write access",
"+UjDmN": "Inloggad med skrivbehörighet",
"+Vxixo": "Hemlig grupp chatt",
"+aZY2h": "Zap Typ",
"+tShPg": "följer",
@ -19,7 +19,7 @@
"/d6vEc": "Gör din profil enklare att hitta och dela",
"/n5KSF": "{n} ms",
"00LcfG": "Ladda fler",
"01iNut": "Nostr address does not belong to you",
"01iNut": "Nostr-adressen tillhör inte dig",
"08zn6O": "Exportera nycklar",
"0Azlrb": "Hantera",
"0BUTMv": "Sök...",
@ -51,6 +51,7 @@
"2zJXeA": "Profiler",
"39AHJm": "Registrera dig",
"3KNMbJ": "Artiklar",
"3QwfJR": "~{amount}",
"3cc4Ct": "Ljust",
"3gOsZq": "Översättare",
"3qnJlS": "Du röstar med {amount} sats",
@ -66,10 +67,11 @@
"4OB335": "Ogilla",
"4Vmpt4": "Nostr Plebs är en av de första NIP-05 leverantörerna och erbjuder en bra samling domäner till rimliga priser",
"4Z3t5i": "Använd imgproxy för att komprimera bilder",
"4emo2p": "Saknade reläer",
"4rYCjn": "Anteckning till mig själv",
"5BVs2e": "zap",
"5CB6zB": "Zap Splits",
"5PRWs7": "Notifications API Enabled",
"5PRWs7": "API för notifieringar aktiverat",
"5oTnfy": "Köp Namn",
"5u6iEc": "Överför till Pubkey",
"5vMmmR": "Användarnamn är inte unika på Nostr. Nostr-adressen är din unika människoläsbara adress som är unik för dig vid registrering.",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Titta på repris",
"62nsdy": "Försök igen",
"6559gb": "Ny längd på följelistan {length}",
"65BmHb": "Det gick inte att proxybilden från {host}, klicka här för att ladda direkt",
"6OSOXl": "Anledning: <i>{reason}</i>",
"6bgpn+": "Inte alla klienter stöder detta, kan du fortfarande få några zaps som om zap delning inte konfigurerades",
@ -87,7 +90,7 @@
"7+Domh": "Anteckningar",
"712i26": "Proxy använder HODL-fakturor för att vidarebefordra betalningen, vilket döljer pubkey för din nod",
"7UOvbT": "Frånkopplad",
"7YkSA2": "Community Leader",
"7YkSA2": "Gemenskapens ledare",
"7hp70g": "NIP-05",
"8/vBbP": "Delningar ({n})",
"89q5wc": "Bekräfta Dela vidare",
@ -115,7 +118,7 @@
"AkCxS/": "Anledning",
"Am8glJ": "Spel",
"AnLrRC": "Non-Zap",
"Awq32I": "Push notifications",
"Awq32I": "Push-meddelanden",
"AxDOiG": "Månader",
"AyGauy": "Logga in",
"B4C47Y": "namnet är för kort",
@ -129,6 +132,8 @@
"C81/uG": "Logga ut",
"C8HhVE": "Föreslagna personer att följa",
"CHTbO3": "Det gick inte att ladda fakturan",
"CM+Cfj": "Följ lista",
"CM0k0d": "Rensa följelistan",
"CVWeJ6": "Trendande personer",
"CYkOCI": "och {count} andra du följer",
"CbM2hK": "Trendande hashtaggar",
@ -143,9 +148,10 @@
"DZzCem": "Visa senaste {n} anteckningar",
"Dh3hbq": "Auto Zap",
"Dn82AL": "Live",
"DrZqav": "About must be less than {limit} characters",
"DrZqav": "Om-sektionen måste vara mindre än {limit} tecken",
"DtYelJ": "Överför",
"Dx4ey3": "Växla alla",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Sök anteckningar",
"ELbg9p": "Dataleverantörer",
"EQKRE4": "Visa emblem på profilsidor",
@ -167,19 +173,19 @@
"FvanT6": "Konton",
"G/yZLu": "Ta bort",
"G1BGCg": "Välj plånbok",
"G3A56c": "Subscribed to Push",
"G3A56c": "Prenumererar på Push",
"GFOoEE": "Salt",
"GL8aXW": "Bokmärken ({n})",
"GQPtfk": "Anslut till Stream",
"GSye7T": "Lightning-adress",
"GUlSVG": "Hämta din inkluderade Snort nostr adress",
"Gcn9NQ": "Magnet Link",
"GqQeu/": "Invalid Lightning Address",
"GqQeu/": "Ogiltig Lightning-adress",
"GspYR7": "{n} Ogillar",
"Gxcr08": "Sänd händelse",
"H+vHiz": "Hex Key..",
"H0JBH6": "Logga Ut",
"H0OG3T": "Leader Info",
"H0OG3T": "Info om ledare",
"H6/kLh": "Beställningen är betald!",
"HAlOn1": "Namn",
"HFls6j": "namnet kommer att vara tillgängligt senare",
@ -188,6 +194,7 @@
"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?",
"IKKHqV": "Följer",
"IOu4Xh": "Du måste vara {tier} prenumerant för att få tillgång till {app} deck",
@ -198,7 +205,7 @@
"IoQq+a": "Klicka här för att ladda ändå",
"Ix8l+B": "Trendande anteckningar",
"J+dIsA": "Prenumerationer",
"J1iLmb": "Notifications Not Allowed",
"J1iLmb": "Notiser inte tillåtna",
"J2HeQ+": "Använd kommatecken för att skilja ord åt, t.ex. ord1, ord2, ord3",
"JCIgkj": "Användarnamn",
"JGrt9q": "Skicka sats till {name}",
@ -222,12 +229,12 @@
"LR1XjT": "PIN-koden är för kort",
"LXxsbk": "Anonymt",
"LgbKvU": "Kommentar",
"LmdPXO": "Cannot verify Nostr Address",
"LmdPXO": "Kan inte verifiera Nostr-adress",
"Lu5/Bj": "Öppna på Zapstr",
"Lw+I+J": "{n,plural,one {}=0{{name} zappade} other{{name} & {n} andra zappade}}",
"LwYmVi": "Zaps på denna anteckning kommer att delas upp till följande användare.",
"M3Oirc": "Debug Menus",
"M6C/px": "Become a leader",
"M6C/px": "Bli en ledare",
"MBAYRO": "Visar \"Kopiera ID\" och \"Kopiera händelse JSON\" i kontextmenyn på varje meddelande",
"MI2jkA": "Inte tillgänglig:",
"MP54GY": "Plånbokens lösenord",
@ -244,7 +251,7 @@
"NdOYJJ": "Hmm inget här.. Kolla in {newUsersPage} för att följa några rekommenderade nostrich's!",
"NepkXH": "Kan inte rösta med {amount} sats, vänligen ange ett annat förvalt zap-belopp",
"NndBJE": "Nya användares sida",
"O3Jz4E": "Use your invite code to earn sats!",
"O3Jz4E": "Använd din inbjudningskod för att tjäna sats!",
"O8Z8t9": "Visa mer",
"OEW7yJ": "Zaps",
"OKhRC6": "Dela",
@ -252,7 +259,7 @@
"OQSOJF": "Få en gratis nostr adress",
"OQXnew": "Din prenumeration är fortfarande aktiv, du kan inte förnya ännu",
"ORGv1Q": "Skapad",
"P2o+ZZ": "Invalid Nostr Address",
"P2o+ZZ": "Ogiltig Nostr-adress",
"P61BTu": "Kopiera händelse JSON",
"P7FD0F": "System (standard)",
"P7nJT9": "Totalt idag (UTC): {amount} sats",
@ -266,7 +273,7 @@
"Qxv0B2": "Du har för närvarande {number} sats i din zap pool.",
"R/6nsx": "Prenumeration",
"R81upa": "Personer du följer",
"RDha9y": "Service Worker Not Running",
"RDha9y": "Service Worker körs inte",
"RSr2uB": "Användarnamnet får bara innehålla gemener och siffror",
"RahCRH": "Förfallen",
"RfhLwC": "Av: {author}",
@ -289,11 +296,11 @@
"TJo5E6": "Förhandsvisning",
"TP/cMX": "Avslutade",
"TaeBqw": "Logga in med Nostr Extension",
"TdTXXf": "Learn more",
"TdTXXf": "Läs mer",
"TdtZQ5": "Krypto",
"TpgeGw": "Hex Salt..",
"Tpy00S": "Personer",
"TwyMau": "Account",
"TwyMau": "Konto",
"U1aPPi": "Sluta lyssna",
"UDYlxu": "Väntande prenumerationer",
"UJTWqI": "Ta bort från mina reläer",
@ -302,11 +309,10 @@
"UUPFlt": "Användare måste acceptera innehållsvarningen för att visa innehållet i din anteckning.",
"Ub+AGc": "Logga in",
"Up5U7K": "Blockera",
"Ups2/p": "Your application is pending",
"Ups2/p": "Din ansökan är under behandling",
"UrKTqQ": "Du har ett aktivt iris.to konto",
"UxgyeY": "Din värvningskod är {code}",
"VL900k": "Rekommenderade reläer",
"VN0+Fz": "Saldo: {amount} sats",
"VOjC1i": "Välj vilken uppladdningstjänst du vill ladda upp bilagor till",
"VR5eHw": "Publik nyckel (npub/nprofile)",
"VcwrfF": "Ja tack",
@ -322,9 +328,10 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "Skicka användningsstatistik",
"XICsE8": "Filvärdar",
"XQiFEl": "Följer relä-hälsa",
"XXm7jJ": "Trendande hashtaggar",
"XgWvGA": "Reaktioner",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
"XhpBfA": "{site} är ett open source-projekt som byggs av passionerade människor på sin fritid, dina donationer är mycket uppskattade",
"Xopqkl": "Ditt förvalda zap-belopp är {number} sats, exempelvärden beräknas utifrån detta.",
"XrSk2j": "Lös in",
"YDURw6": "Service URL",
@ -343,10 +350,11 @@
"aWpBzj": "Visa mer",
"b12Goz": "Mnemonic",
"b5vAk0": "Ditt namn kommer att fungera som en Lightning adress och kommer att omdirigeras till din valda LNURL eller Lightning adress",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bF1MYT": "Du är en community leader och tjänar <b>{percent}</b> på hänvisade användares prenumerationer!",
"bG00/W": "Service Worker i drift",
"bJ+wrA": "Utarbeta en lista över gallringar",
"bLZL5a": "Skaffa adress",
"bMphls": "Logged in with read-only access",
"bMphls": "Inloggad med skrivskyddad åtkomst",
"bQdA2k": "Känsligt innehåll",
"bep9C3": "Publik nyckel",
"bfvyfs": "Anon",
@ -364,8 +372,7 @@
"cuV2gK": "namnet är registrerat",
"cyR7Kh": "Tillbaka",
"d+6YsV": "Listor för att stänga av ljudet:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "Historik",
"d2ebEu": "Prenumererar inte på Push",
"d7d0/x": "LN Adress",
"d8gpCh": "Försök att använda färre än 5 hashtags för att hålla dig till ämnet 🙏",
"dOQCL8": "Visnings namn",
@ -382,7 +389,7 @@
"eSzf2G": "En enda zap med {nIn} sats kommer att fördela {nOut} sats till zappoolen.",
"eXT2QQ": "Gruppchatt",
"egib+2": "{n,plural,=1{& {n} andra} other{& {n} andras}}",
"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": "Community leaders är personer som får nostrs ekosystem att växa genom att vara aktiva i sina lokala samhällen och hjälpa till att få in nya användare. Vem som helst kan bli community leader, men få har den nuvarande hederstiteln.",
"fBI91o": "Zap",
"fBlba3": "Tack för att du använder {site}, vänligen överväg att donera om du kan.",
"fOksnD": "Kan inte rösta eftersom LNURL-tjänsten inte stöder zaps",
@ -404,6 +411,7 @@
"grQ+mI": "Proof of Work",
"h7jvCs": "{site} är roligare tillsammans!",
"h8XMJL": "Emblem",
"hF6IN2": "Rensa följelistan",
"hMzcSq": "Meddelanden",
"hRTfTR": "PRO",
"hY4lzx": "Support",
@ -412,11 +420,12 @@
"hicxcO": "Visa svar",
"hmZ3Bz": "Media",
"hniz8Z": "här",
"hvFRBo": "Interaction",
"hvFRBo": "Interaktion",
"i/dBAR": "Zap Pool",
"iCqGww": "Reaktioner ({n})",
"iEoXYx": "DeepL översättningar",
"iGT1eE": "Förhindra falska konton från att imitera dig",
"iICVoL": "{x} följer ({y} dubbletter)",
"iNWbVV": "Namn",
"iXPL0Z": "Kan inte logga in med privat nyckel på en osäker anslutning, använd ett tillägg för Nostr key manager istället",
"iYc3Ld": "Betalningar",
@ -431,14 +440,14 @@
"jTrbGf": "{n} km - {location}",
"jvo0vs": "Spara",
"jzgQ2z": "{n} Reaktioner",
"k0kCJp": "Apply Now",
"k0kCJp": "Ansök nu",
"k2veDA": "Skriv",
"k7sKNy": "Vår alldeles egna NIP-05 verifieringstjänst, hjälpa till att stödja utvecklingen av denna webbplats och få ett glänsande speciellt märke på vår webbplats!",
"kEZUR8": "Registrera ett användarnamn för Iris",
"kJYo0u": "{n,plural,one {}=0{{name} delade} other{{name} & {n} andra delade}}",
"kaaf1E": "nu",
"kc79d3": "Ämnen",
"kqPQJD": "Configure zap pool",
"kqPQJD": "Konfigurera zap-pool",
"kuPHYE": "{n,plural,one {}=0{{name} gillade} other{{name} & {n} andra gillade}}",
"l+ikU1": "Allting i {plan}",
"l3H1EK": "Bjud in dina vänner",
@ -460,7 +469,9 @@
"n1Whvj": "Växla",
"nDejmx": "Avblockera",
"nGBrvw": "Bokmärken",
"nGGDsi": "Notifications Allowed",
"nGGDsi": "Notiser tillåtna",
"nIchMQ": "Söker efter kontoaktivitet ({progress})",
"nUT0Lv": "Verktyg",
"nihgfo": "Lyssna på denna artikel",
"nwZXeh": "{n} blockerad",
"o7e+nJ": "{n} följare",
@ -469,9 +480,11 @@
"ojzbwv": "Hej, det ser ut som du inte har en Nostr adress ännu, du bör skaffa en! Kolla in {link}",
"p4N05H": "Ladda upp",
"p85Uwy": "Aktiva prenumerationer",
"p9Ps2l": "{x}/{y} har reläer ({percent})",
"pI+77w": "Nedladdningsbara säkerhetskopior från Snort relä",
"pRess9": "ZapPool",
"puLNUJ": "Fäst",
"pukxg/": "Betalningar",
"pzTOmv": "Följare",
"qD9EUF": "E-post <> DM brygga för din Snort nostr adress",
"qDwvZ4": "Okänt fel",
@ -479,7 +492,7 @@
"qMx1sA": "Förvalt Zap-belopp",
"qUJTsT": "Blockerad",
"qZsKBR": "Förnya {tier}",
"qcJFEJ": "Notifications API Disabled",
"qcJFEJ": "API för notifieringar avaktiverat",
"qdGuQo": "Din privata nyckel är (dela inte detta med någon)",
"qfmMQh": "Den här anteckningen har tystats",
"qkvYUb": "Lägg till i min profil",
@ -502,12 +515,13 @@
"tOdNiY": "Mörkt",
"th5lxp": "Skicka anteckning till en delmängd av dina skrivreläer",
"thnRpU": "Att få NIP-05 verifierat kan hjälpa:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Relä statistik",
"ttxS0b": "Supporter Emblem",
"u+LyXc": "Interaktioner",
"u/vOPu": "Betald",
"u4bHcR": "Kolla in koden här: {link}",
"u9NoC1": "Name must be less than {limit} characters",
"u9NoC1": "Namnet måste vara mindre än {limit} tecken",
"uCk8r+": "Har du redan ett konto?",
"uKqSN+": "Följer Flöde",
"uSV4Ti": "Dela vidare måste bekräftas manuellt",
@ -517,11 +531,11 @@
"v8lolG": "Starta chatt",
"vB3oQ/": "Måste vara en kontaktlista eller en pubkey lista",
"vN5UH8": "Profilbild",
"vU/Q5i": "Detta verktyg kommer att söka efter den senaste händelsen som publiceras av alla dina följande och ta bort dem som inte har postat i 6 månader",
"vZ4quW": "NIP-05 är en DNS-baserad verifieringsspec som hjälper till att validera dig som en riktig användare.",
"vhlWFg": "Alternativ för omröstning",
"vlbWtt": "Få en kostnadsfri",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} sats",
"voxBKC": "Följs av vänner",
"vxwnbh": "Mängd arbete att tillämpas för alla publicerade händelser",
"w1Fanr": "Företag",
"w6qrwX": "NSFW",
@ -543,7 +557,7 @@
"xl4s/X": "Ytterligare villkor:",
"xmcVZ0": "Sök",
"xybOUv": "FAN",
"y/bmsG": "Allow",
"y/bmsG": "Tillåt",
"y1Z3or": "Språk",
"yCLnBC": "LNURL or Lightning Adress",
"yNBPJp": "Hjälp till att finansiera utvecklingen av {site}",

View File

@ -1,211 +1,218 @@
{
"+D82kt": "Je, una uhakika unataka kuchapisha upya: {id}",
"+PzQ9Y": "Malipo sasa",
"+UjDmN": "Logged in with write access",
"+UjDmN": "Umeingia na ufikiaji wa kuandika",
"+Vxixo": "Gumzo la Siri la Kikundi",
"+aZY2h": "Aina ya Zap",
"+tShPg": "following",
"+tShPg": "unafuata",
"+vA//S": "Ingizo",
"+vIQlC": "Tafadhali hakikisha kuwa umehifadhi nenosiri lifuatalo ili kudhibiti mpini wako katika siku zijazo",
"+vVZ/G": "Unganisha",
"+vj0U3": "edit",
"+xliwN": "{name} amerepost",
"/B8zwF": "Your space the way you want it 😌",
"/GCoTA": "Clear",
"+vj0U3": "hariri",
"+xliwN": "{name} amechapisha upya",
"/B8zwF": "Mahali pako kama unavyotaka 😌",
"/GCoTA": "Safisha",
"/JE/X+": "Usaidizi wa Akaunti",
"/PCavi": "Umma",
"/Xf4UW": "Send anonymous usage metrics",
"/clOBU": "Weekly",
"/d6vEc": "Rahisisha wasifu wako kupata na kushiriki",
"/n5KSF": "{n} ms",
"/Xf4UW": "Tuma takwimu za matumizi kwa siri",
"/clOBU": "Kila Wiki",
"/d6vEc": "Fanya wasifu wako kuwa rahisi kupata na kushiriki",
"/n5KSF": "{n} dakika",
"00LcfG": "Pakia zaidi",
"01iNut": "Nostr address does not belong to you",
"08zn6O": "Export Keys",
"01iNut": "Anwani ya Nostr sio yako",
"08zn6O": "Hifadhi Funguo",
"0Azlrb": "Dhibiti",
"0BUTMv": "Tafuta...",
"0HFX0T": "Use Exact Location",
"0jOEtS": "LNURL Batili",
"0mch2Y": "jina limekataza herufi",
"0siT4z": "Politics",
"0uoY11": "Show Status",
"0yO7wF": "{n} sekundi",
"1H4Keq": "{n} users",
"1Mo59U": "Je, una uhakika unataka kuondoa dokezo hili kutoka kwa vialamisho?",
"0HFX0T": "Tumia Mahali Halisi",
"0jOEtS": "LNURL isiyo halali",
"0mch2Y": "herufi katika jina imezuiliwa",
"0siT4z": "Siasa",
"0uoY11": "Onyesha Hali",
"0yO7wF": "{n} sekunde",
"1H4Keq": "{n} watumiaji",
"1Mo59U": "Je, una uhakika unataka kuondoa kidokezo hiki kutoka kwa alamisho?",
"1R43+L": "Ingiza usanidi wa Nostr Wallet Connect",
"1UWegE": "Be sure to back up your keys!",
"1UWegE": "Hakikisha kuhifadhi funguo zako!",
"1c4YST": "Imeunganishwa kwa: {node} 🎉",
"1nYUGC": "{n} Unafuata",
"1o2BgB": "Check Signatures",
"1ozeyg": "Nature",
"1o2BgB": "Angalia Saini",
"1ozeyg": "Asili",
"1udzha": "Mazungumzo",
"2/2yg+": "Ongeza",
"25V4l1": "Bango",
"25WwxF": "Don't have an account?",
"25WwxF": "Huna akaunti?",
"2IFGap": "Changia",
"2LbrkB": "Weka nenosiri",
"2O2sfp": "Finish",
"2LbrkB": "Hifadhi Nenosiri",
"2O2sfp": "Maliza",
"2a2YiP": "{n} Alamisho",
"2k0Cv+": "Haipendwi ({n})",
"2mcwT8": "New Note",
"2mcwT8": "Nakala Mpya",
"2ukA4d": "{n} masaa",
"2zJXeA": "Profiles",
"39AHJm": "Sign Up",
"3KNMbJ": "Articles",
"2zJXeA": "Waprofaili",
"39AHJm": "Jisajili",
"3KNMbJ": "Makala",
"3QwfJR": "~{amount}",
"3cc4Ct": "Mwangaza",
"3gOsZq": "Wafasiri",
"3gOsZq": "Watafsiri",
"3qnJlS": "Unapiga kura kwa kutumia {amount} sats",
"3t3kok": "{n,plural,=1{{n} noti mpya} other{{n} noti mpya}}",
"3t3kok": "{n,plural,=1{{n} kidokezo kipya} other{{n} kidokezo kipya}}",
"3tVy+Z": "{n} Wafuasi",
"3yk8fB": "Wallet",
"3yk8fB": "Pochi",
"450Fty": "Hakuna",
"47FYwb": "Ghairi",
"4IPzdn": "Wasanidi wa Msingi",
"4L2vUY": "NIP-05 yako mpya ni:",
"4MBtMa": "Name must be between 1 and 32 characters",
"4MjsHk": "Life",
"4MBtMa": "Jina linapaswa kuwa kati ya herufi 1 na 32",
"4MjsHk": "Maisha",
"4OB335": "Kutopenda",
"4Vmpt4": "Nostr Plebs ni mmoja wa watoa huduma wa kwanza wa NIP-05 kwenye nafasi na inatoa mkusanyiko mzuri wa vikoa kwa bei nzuri",
"4Z3t5i": "Tumia imgproxy kubana picha",
"4rYCjn": "Kumbuka mwenyewe",
"4Vmpt4": "Nostr Plebs ni mtoa huduma wa kwanza wa NIP-05 katika soko na hutoa mkusanyiko bora wa vikoa kwa bei nzuri",
"4Z3t5i": "Tumia imgproxy kupunguza picha",
"4emo2p": "Relay Zilizopotea",
"4rYCjn": "Kumbuka Wewe Mwenyewe",
"5BVs2e": "zap",
"5CB6zB": "Zap Splits",
"5PRWs7": "Notifications API Enabled",
"5oTnfy": "Nunua Tambulisho",
"5CB6zB": "Mgawanyiko wa Zap",
"5PRWs7": "Taarifa za Kidukizo Zimeruhusiwa",
"5oTnfy": "Nunua Kitambulisho",
"5u6iEc": "Hamisha kwa Pubkey",
"5vMmmR": "Majina ya watumiaji sio ya kipekee kwenye Nostr. Anwani ya nostr ni anwani yako ya kipekee inayoweza kusomeka na binadamu ambayo ni ya kipekee kwako unapojiandikisha.",
"5vMmmR": "Majina ya mtumiaji si ya kipekee kwenye Nostr. Anwani ya nostr ni anwani yako ya kipekee inayoweza kusomwa na binadamu ambayo ni ya kipekee kwako unapojiandikisha.",
"5ykRmX": "Tuma zap",
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Watch Replay",
"62nsdy": "Retry",
"65BmHb": "Imeshindwa kuweka picha ya seva mbadala kutoka kwa {host}, bofya hapa ili kupakia moja kwa moja",
"6OSOXl": "Reason: <i>{reason}</i>",
"6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured",
"6/SF6e": "<h1>{n}</h1> Sats za CashU",
"6/hB3S": "Tazama Upya",
"62nsdy": "Jaribu tena",
"6559gb": "Urefu mpya wa orodha ya kufuatilia {length}",
"65BmHb": "Imeshindwa kufanya upakiaji mbadala kutoka kwa {host}, bonyeza hapa ili upakie moja kwa moja",
"6OSOXl": "Sababu: <i>{reason}</i>",
"6bgpn+": "Haitumiki kwa wateja wote, bado unaweza kupokea zile zile kama kugawanywa kwa zap haiko imewezeshwa",
"6ewQqw": "Vipendwa ({n})",
"6k7xfM": "Trending notes",
"6mr8WU": "Followed by",
"6k7xfM": "Kidokezo kinachotrendi",
"6mr8WU": "Kufuatiliwa na",
"6uMqL1": "Haijalipwa",
"7+Domh": "Noti",
"712i26": "Proxy uses HODL invoices to forward the payment, which hides the pubkey of your node",
"7UOvbT": "Offline",
"7YkSA2": "Community Leader",
"712i26": "Proxy hutumia ankara za HODL kusonga malipo, ambayo huficha pubkey ya nodo yako",
"7UOvbT": "Nje ya mtandao",
"7YkSA2": "Kiongozi wa Jamii",
"7hp70g": "NIP-05",
"8/vBbP": "Machapisho upya ({n})",
"89q5wc": "Thibitisha Reposts",
"8ED/4u": "Reply To",
"8HJxXG": "Sign up",
"8ED/4u": "Jibu Kwa",
"8HJxXG": "Jisajili",
"8QDesP": "Zap {n} sats",
"8Rkoyb": "Recipient",
"8Y6bZQ": "Invalid zap split: {input}",
"8g2vyB": "jina refu sana",
"8v1NN+": "Maneno ya kuoanisha",
"8Rkoyb": "Mpokeaji",
"8Y6bZQ": "Zap isiyofaa: {input}",
"8g2vyB": "Jina refu sana",
"8v1NN+": "Maneno ya Kuoanisha",
"9+Ddtu": "Inayofuata",
"9HU8vw": "Jibu",
"9SvQep": "Anafuata {n}",
"9WRlF4": "Tuma",
"9kSari": "Retry publishing",
"9kSari": "Jaribu tena kuchapisha",
"9pMqYs": "Anwani ya Nostr",
"9wO4wJ": "Ankara ya umeme",
"ABAQyo": "Chats",
"9wO4wJ": "Ankara ya Umeme",
"ABAQyo": "Mazungumzo",
"ADmfQT": "Mzazi",
"AIgmDy": "Add up to 4 hashtags",
"ALdW69": "Note by {name}",
"AN0Z7Q": "Muted Words",
"ASRK0S": "Mwandishi huyu amenyamazishwa",
"Ai8VHU": "Uhifadhi wa noti bila kikomo kwenye relay ya Snort",
"AIgmDy": "Ongeza hadi alama 4 za kufuatilia",
"ALdW69": "Kidokezo kutoka kwa {name}",
"AN0Z7Q": "Maneno Yaliyozuiwa",
"ASRK0S": "Mwandishi amenyamazishwa",
"Ai8VHU": "Hifadhi tukio bila kikomo kwenye reli ya Snort",
"AkCxS/": "Sababu",
"Am8glJ": "Game",
"AnLrRC": "Non-zap",
"Awq32I": "Push notifications",
"AxDOiG": "Months",
"Am8glJ": "Mchezo",
"AnLrRC": "Sio-zap",
"Awq32I": "Taarifa za Push",
"AxDOiG": "Miezi",
"AyGauy": "Ingia",
"B4C47Y": "jina fupi mno",
"B6+XJy": "zapped",
"B4C47Y": "Jina fupi sana",
"B6+XJy": "limezwapishwa",
"B6H7eJ": "nsec, npub, nip-05, hex",
"BGCM48": "Andika ufikiaji wa upeanaji wa Snort, ukiwa na mwaka 1 wa uhifadhi wa tukio",
"BGCM48": "Andika ufikiaji wa upeanaji wa Snort, na uhifadhi wa mwaka 1 wa tukio",
"BWpuKl": "Sasisha",
"BjNwZW": "Nostr address (nip05)",
"C1LjMx": "Lightning Donation",
"C7642/": "Quote Repost",
"C81/uG": "Toka nje",
"BjNwZW": "Anwani ya nostr (nip05)",
"C1LjMx": "Toleo la Umeme",
"C7642/": "Reposti ya Ujumbe",
"C81/uG": "Toka",
"C8HhVE": "Inayopendekezwa Inafuata",
"CHTbO3": "Imeshindwa kupakia ankara",
"CVWeJ6": "Watu Wanaovuma",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
"CHTbO3": "Hakuna uwezo wa kupakia ankara",
"CM+Cfj": "Orodha ya Kufuata",
"CM0k0d": "Katiza orodha ya kufuata",
"CVWeJ6": "Watu Wanaofanya Vizuri",
"CYkOCI": "na {count} wengine unao wafuatilia",
"CbM2hK": "Hashtags Zinazotrendi",
"CmZ9ls": "{n} Imenyamazishwa",
"CsCUYo": "{n} sats",
"Cu/K85": "Imetafsiriwa kutoka {lang}",
"CzHZoc": "Social Graph",
"D+KzKd": "Zap kila noti kiotomatiki inapopakiwa",
"CzHZoc": "Grafu ya Jamii",
"D+KzKd": "Zap kila kidokezo kiotomatiki inapopakia",
"D3idYv": "Mipangilio",
"DBiVK1": "Cache",
"DBiVK1": "Hifadhi",
"DKnriN": "Tuma sats",
"DZzCem": "Onyesha vidokezo vya {n} hivi karibuni",
"Dh3hbq": "Zap ya kiotomatiki",
"Dn82AL": "Live",
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "Uhamisho",
"Dx4ey3": "Toggle all",
"EJbFi7": "Search notes",
"ELbg9p": "Watoa Data",
"EQKRE4": "Show badges on profile pages",
"EWyQH5": "Ulimwenguni",
"Dh3hbq": "Zap ya Kiotomatiki",
"Dn82AL": "Moja kwa moja",
"DrZqav": "Kuhusu inapaswa kuwa chini ya {limit} herufi",
"DtYelJ": "Hamisha",
"Dx4ey3": "Badilisha yote",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Tafuta vidokezo",
"ELbg9p": "Watoa Taarifa",
"EQKRE4": "Onyesha bendera kwenye kurasa za wasifu",
"EWyQH5": "Kote Duniani",
"Ebl/B2": "Tafsiri hadi {lang}",
"EcZF24": "Reli maalum",
"EcfIwB": "Username is available",
"EcZF24": "Reli Maalum",
"EcfIwB": "Jina la mtumiaji linapatikana",
"EcglP9": "Funguo",
"EjFyoR": "On-chain Donation Address",
"EjFyoR": "Anwani ya Mchango wa On-chain",
"EnCOBJ": "Nunua",
"F3l7xL": "Ongeza Akaunti",
"FDguSC": "{n} Zaps",
"FMfjrl": "Show status messages on profile pages",
"FSYL8G": "Watumiaji wanaovuma",
"FcNSft": "Redirect issues HTTP redirect to the supplied lightning address",
"FMfjrl": "Onyesha taarifa za hali kwenye kurasa za wasifu",
"FSYL8G": "Watu Wanaofanya Vizuri",
"FcNSft": "Maelekezio ya HTTP yanarudishiwa kwa anwani ya lightning iliyotolewa",
"FdhSU2": "Dai sasa",
"FfYsOb": "Hitilafu imetokea!",
"FmXUJg": "anakufuata",
"FvanT6": "Accounts",
"FmXUJg": "anafuata",
"FvanT6": "Akaunti",
"G/yZLu": "Ondoa",
"G1BGCg": "Chagua Pochi",
"G3A56c": "Subscribed to Push",
"G3A56c": "Imejiunga na Push",
"GFOoEE": "Chumvi",
"GL8aXW": "Alamisho ({n})",
"GQPtfk": "Join Stream",
"GQPtfk": "Jiunge na Stream",
"GSye7T": "Anwani ya Umeme",
"GUlSVG": "Dai anwani yako ya Nostr iliyojumuishwa",
"Gcn9NQ": "Kiungo cha Sumaku",
"GqQeu/": "Invalid Lightning Address",
"GspYR7": "{n} Kutopenda",
"Gxcr08": "Broadcast Event",
"GUlSVG": "Dai anwani yako ya Nostr iliyoundwa",
"Gcn9NQ": "Kiungo cha Magneti",
"GqQeu/": "Anwani isiyo halali ya Lightning",
"GspYR7": "{n}",
"Gxcr08": "Tukio la Tangaza",
"H+vHiz": "Ufunguo wa Hex..",
"H0JBH6": "Toka nje",
"H0OG3T": "Leader Info",
"H0OG3T": "Habari za Kiongozi",
"H6/kLh": "Agizo limelipwa!",
"HAlOn1": "Jina",
"HFls6j": "jina litapatikana baadae",
"HOzFdo": "Imenyamazishwa",
"HWbkEK": "Futa akiba na upakie upya",
"HbefNb": "Fungua Pochi",
"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",
"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": "Msaada",
"I1AoOu": "Chapisho la mwisho {time}",
"IEwZvs": "Je, una uhakika unataka kubandua kidokezo hiki?",
"IKKHqV": "Anafuata",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
"IVbtTS": "Zap all {n} sats",
"IWz1ta": "Auto Translate",
"IOu4Xh": "Lazima uwe mnunuzi wa {tier} ili ufikie kisanduku cha {app}",
"IVbtTS": "Zap kila {n} ameketi",
"IWz1ta": "Tafsiri Kiotomatiki",
"Ig9/a1": "Imetumwa sats {n} kwa {name}",
"IgsWFG": "Not followed by anyone you follow",
"IoQq+a": "Click here to load anyway",
"IgsWFG": "Haifuatwi na mtu yeyote unayemfuata",
"IoQq+a": "Bofya hapa ili kupakia hata hivyo",
"Ix8l+B": "Vidokezo vinavyovuma",
"J+dIsA": "Usajili",
"J1iLmb": "Notifications Not Allowed",
"J2HeQ+": "Use commas to separate words e.g. word1, word2, word3",
"J1iLmb": "Arifa Haziruhusiwi",
"J2HeQ+": "Tumia koma kutenganisha maneno k.m. neno1, neno2, neno3",
"JCIgkj": "Jina la mtumiaji",
"JGrt9q": "Send sats to {name}",
"JGrt9q": "Tuma sats kwa {name}",
"JHEHCk": "Zaps ({n})",
"JIVWWA": "Sport",
"JIVWWA": "Michezo",
"JPFYIM": "Hakuna anwani ya umeme",
"JSx7y9": "Subscribe to {site_name} {plan} for {price} and receive the following rewards",
"JSx7y9": "Jiunge na {site_name} {plan} kwa {price} na upokee tuzo zifuatazo",
"JeoS4y": "Reposti",
"JjGgXI": "Tafuta watumiaji",
"JkLHGw": "Tovuti",
@ -217,57 +224,57 @@
"KQvWvD": "Imefutwa",
"KahimY": "Aina ya tukio lisilojulikana: {kind}",
"KoFlZg": "Weka URL ndogo",
"KtsyO0": "Enter Pin",
"KtsyO0": "Weka Pini",
"LF5kYT": "Viunganisho Vingine",
"LR1XjT": "Pin too short",
"LR1XjT": "Pini fupi sana",
"LXxsbk": "Asiyejulikana",
"LgbKvU": "Toa maoni",
"LmdPXO": "Cannot verify Nostr Address",
"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}}",
"LwYmVi": "Zaps on this note will be split to the following users.",
"LwYmVi": "Zaps kwenye taarifa hii zitagawanywa kwa watumiaji wafuatao.",
"M3Oirc": "Menyu ya Utatuzi",
"M6C/px": "Become a leader",
"MBAYRO": "Inaonyesha \"Nakili Kitambulisho\" na \"Nakili Tukio JSON\" katika menyu ya muktadha kwenye kila ujumbe",
"M6C/px": "Kuwa kiongozi",
"MBAYRO": "Onyesha 'Nakili Kitambulisho' na 'Nakili JSON' kwenye menyu ya muktadha kwa kila ujumbe",
"MI2jkA": "Haipatikani:",
"MP54GY": "Nenosiri la Pochi",
"MWTx65": "Ukurasa Chaguomsingi",
"MiMipu": "Set as primary Nostr address (nip05)",
"Ml7+RS": "Send this link to your friends and share the magic of the nostr.",
"MiMipu": "Wekeza kama Anwani Kuu ya Nostr (nip05)",
"Ml7+RS": "Tuma kiungo hiki kwa marafiki wako na uishiriki uchawi wa nostr.",
"Mrpkot": "Lipia usajili",
"MuVeKe": "Nunua anwani ya nostr",
"MzRYWH": "Unanunua {item}",
"Mzizei": "Iris.to account",
"Mzizei": "Akaunti ya Iris.to",
"N2IrpM": "Thibitisha",
"NAidKb": "Notifications",
"NAuFNH": "Tayari una usajili wa aina hii, tafadhali usasishe au ulipe",
"NdOYJJ": "Hmm hakuna kitu hapa.. Angalia {newUsersPage} ili kufuata nostrich's wanaopendekezwa!",
"NepkXH": "Can't vote with {amount} sats, please set a different default zap amount",
"NndBJE": "Ukurasa mpya wa watumiaji",
"O3Jz4E": "Use your invite code to earn sats!",
"O8Z8t9": "Show More",
"NAidKb": "Arifa",
"NAuFNH": "Tayari una aina hii ya usajili, tafadhali sasisha au lipa",
"NdOYJJ": "Hmm hakuna kitu hapa.. Angalia {newUsersPage} ili kufuata wanaopendekezwa wa nostrich!",
"NepkXH": "Haiwezi kupiga kura na {amount} sats, tafadhali weka kiwango kingine cha default cha zap",
"NndBJE": "Ukurasa Mpya wa Watumiaji",
"O3Jz4E": "Tumia msimbo wako wa mwaliko kupata sats!",
"O8Z8t9": "Onesha Zaidi",
"OEW7yJ": "Zaps",
"OKhRC6": "Shiriki",
"OLEm6z": "Hitilafu isiyojulikana ya kuingia",
"OQSOJF": "Get a free nostr address",
"OQXnew": "Usajili wako bado unatumika, bado huwezi kusasisha",
"OQSOJF": "Pata anwani ya nostr bure",
"OQXnew": "Usajili wako bado unaendelea, bado huwezi kusasisha",
"ORGv1Q": "Imeundwa",
"P2o+ZZ": "Invalid Nostr Address",
"P2o+ZZ": "Anwani Isiyofaa ya Nostr",
"P61BTu": "Nakili Tukio JSON",
"P7FD0F": "Mfumo (Chaguo-msingi)",
"P7nJT9": "Jumla ya leo (UTC): {amount} sats",
"PCSt5T": "Preferences",
"PJeJFc": "Summary",
"PCSt5T": "Mapendeleo",
"PJeJFc": "Muhtasari",
"PamNxw": "Kijajuu cha faili kisichojulikana: {name}",
"Pe0ogR": "Mandhari",
"PrsIg7": "Maoni yataonyeshwa kwenye kila ukurasa, ikiwa imezimwa hakuna maoni yataonyeshwa",
"QDFTjG": "{n} Relays",
"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",
"R/6nsx": "Usajili",
"R81upa": "Watu unaowafuata",
"RDha9y": "Service Worker Not Running",
"RSr2uB": "Username must only contain lowercase letters and numbers",
"RDha9y": "Service Worker Haujatekelezwa",
"RSr2uB": "Jina la mtumiaji linapaswa kuwa na herufi ndogo na nambari pekee",
"RahCRH": "Muda wake umeisha",
"RfhLwC": "Na: {author}",
"RhDAoS": "Je, una uhakika unataka kufuta {id}",
@ -275,173 +282,175 @@
"RoOyAh": "Relays",
"Rs4kCE": "Alamisho",
"RwFaYs": "Panga",
"SLZGPn": "Enter a pin to encrypt your private key, you must enter this pin every time you open {site}.",
"SMO+on": "Send zap to {name}",
"SLZGPn": "Ingiza pin kuweka funguo yako binafsi, unapaswa kuingiza pin hii kila wakati unapofungua {site}.",
"SMO+on": "Tuma zap kwa {name}",
"SOqbe9": "Sasisha Anwani ya Umeme",
"SP0+yi": "Nunua Usajili",
"SYQtZ7": "Wakala wa Anwani ya LN",
"ShdEie": "Weka alama kuwa zote zimesomwa",
"Sjo1P4": "Desturi",
"SmuYUd": "What should we call you?",
"SmuYUd": "Utaitwa nini?",
"Ss0sWu": "Lipa Sasa",
"StKzTE": "The author has marked this note as a <i>sensitive topic</i>",
"StKzTE": "Mwandishi ametaja madokezo haya kama <i>mada nyeti</i>",
"TDR5ge": "Maudhui katika madokezo yataonyeshwa kiotomatiki kwa watu waliochaguliwa, vinginevyo kiungo pekee ndicho kitakachoonyeshwa",
"TJo5E6": "Preview",
"TP/cMX": "Ended",
"TaeBqw": "Sign in with Nostr Extension",
"TdTXXf": "Learn more",
"TJo5E6": "Onesha Mapema",
"TP/cMX": "Imemalizika",
"TaeBqw": "Ingia kwa Upanuzi wa Nostr",
"TdTXXf": "Jifunze zaidi",
"TdtZQ5": "Crypto",
"TpgeGw": "Hex chumvi..",
"Tpy00S": "Watu",
"TwyMau": "Account",
"U1aPPi": "Stop listening",
"TwyMau": "Akaunti",
"U1aPPi": "Acha kusikiliza",
"UDYlxu": "Usajili Unaosubiri",
"UJTWqI": "Remove from my relays",
"UNjfWJ": "Check all event signatures received from relays",
"UJTWqI": "Ondoa kwenye relays yangu",
"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.",
"Ub+AGc": "Sign In",
"Ub+AGc": "Ingia",
"Up5U7K": "Zuia",
"Ups2/p": "Your application is pending",
"UrKTqQ": "You have an active iris.to account",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "Salio: {amount} sats",
"VOjC1i": "Chagua ni huduma gani ya upakiaji ungependa kupakia viambatisho",
"Ups2/p": "Maombi yako yanangojea",
"UrKTqQ": "Una akaunti ya iris.to iliyopo",
"UxgyeY": "Msimbo wako wa rufaa ni {code}",
"VL900k": "Relay Zilizopendekezwa",
"VOjC1i": "Chagua huduma ya kupakia viambatisho",
"VR5eHw": "Ufunguo wa umma (npub/nprofile)",
"VcwrfF": "Yes please",
"VfhYxG": "To see a full list of changes you can view the changelog {here}",
"VlJkSk": "{n} imenyamazishwa",
"VcwrfF": "Ndio tafadhali",
"VfhYxG": "Ili kuona orodha kamili ya mabadiliko, unaweza kuangalia mabadiliko {hapa}",
"VlJkSk": "{n} zimefanywa kimya",
"VnXp8Z": "Picha",
"W1yoZY": "Inaonekana huna usajili wowote, unaweza kupata moja {link}",
"W2PiAr": "{n} Imezuiwa",
"W9355R": "Rejesha sauti",
"WmZhfL": "Automatically translate notes to your local language",
"WvGmZT": "npub / nprofile / nostr address",
"X6tipZ": "Sign in with key",
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "Send usage metrics",
"XICsE8": "Wapangishi wa faili",
"XXm7jJ": "Trending Hashtags",
"W2PiAr": "{n} Zimezuiliwa",
"W9355R": "Rudisha sauti",
"WmZhfL": "Tafsiri moja kwa moja madokezo kwa lugha yako ya asili",
"WvGmZT": "npub / nprofile / anwani ya Nostr",
"X6tipZ": "Ingia kwa ufunguo",
"X7xU8J": "nsec, npub, nip-05, hex, mnemoniki",
"XECMfW": "Tuma takwimu za matumizi",
"XICsE8": "Wapangaji wa Faili",
"XQiFEl": "Afya za Ufuatiliaji",
"XXm7jJ": "Vishazi Vya Kupanda",
"XgWvGA": "Miitikio",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
"Xopqkl": "Kiasi chako chaguomsingi cha zap ni {number} sats, thamani za mfano zinakokotolewa kutoka hii.",
"XrSk2j": "Komboa",
"YDURw6": "URL ya huduma",
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
"YXA3AH": "Washa maitikio",
"XhpBfA": "{site} ni mradi wa chanzo wazi uliojengwa na watu wenye shauku katika wakati wao wa ziada, michango yako inathaminiwa sana",
"Xopqkl": "Kiasi chako chaguo-msingi cha zap ni {number} sats, thamani zilizopo zinakadiriwa kutoka hapo.",
"XrSk2j": "Kukomboa",
"YDURw6": "URL ya Huduma",
"YR2I9M": "Hakuna ufunguo, hakuna {app}, Hakuna njia ya kuzirejesha ikiwa haujafanya nakala rudufu. Inachukua dakika tu.",
"YXA3AH": "Washa Mijibu",
"Z4BMCZ": "Weka maneno ya kuoanisha",
"ZKORll": "Washa Sasa",
"ZLmyG9": "Wachangiaji",
"ZS+jRE": "Send zap splits to",
"Zff6lu": "Username iris.to/<b>{name}</b> is reserved for you!",
"ZlmK/p": "{name} invited you to {app}",
"a5UPxh": "Wasanidi wa mfuko na mifumo inayotoa huduma za uthibitishaji wa NIP-05",
"a7TDNm": "Vidokezo vitatiririka katika muda halisi hadi kwenye kichupo cha madokezo cha kimataifa",
"aHje0o": "Name or nym",
"aMaLBK": "Supported Extensions",
"aWpBzj": "Onyesha zaidi",
"b12Goz": "Mnemonic",
"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}",
"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",
"aWpBzj": "Onyesha Zaidi",
"b12Goz": "Mnemoniki",
"b5vAk0": "Nchi yako itafanya kama anwani ya umeme na itaelekeza kwenye LNURL uliyochagua au anwani ya Umeme",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bLZL5a": "Get Address",
"bMphls": "Logged in with read-only access",
"bQdA2k": "Maudhui Nyeti",
"bF1MYT": "Wewe ni kiongozi wa jamii na unapata <b>{percent}</b> ya michango ya watumiaji waliotajwa!",
"bG00/W": "Mhudumu wa Huduma Unafanya Kazi",
"bJ+wrA": "Hesabu Orodha ya Kupogoa",
"bLZL5a": "Pata Anwani",
"bMphls": "Umeingia kwa upatikanaji wa kusoma tu",
"bQdA2k": "Maudhui Yanayofunuliwa",
"bep9C3": "Ufunguo wa Umma",
"bfvyfs": "Hajulikan",
"bxv59V": "Sasa hivi",
"c+JYNI": "No thanks",
"bfvyfs": "Hajulikani",
"bxv59V": "Sasa Hivi",
"c+JYNI": "Hapana asante",
"c35bj2": "Ikiwa una swali kuhusu agizo lako la NIP-05 tafadhali DM {link}",
"c3g2hL": "Tangaza Tena",
"cFbU1B": "Unatumia Alby? Nenda kwenye {link} ili upate usanidi wako wa NWC!",
"cHCwbF": "Photography",
"cPIKU2": "Nafuata",
"cFbU1B": "Je, unatumia Alby? Nenda kwenye {link} kupata usanidi wako wa NWC!",
"cHCwbF": "Ufotografia",
"cPIKU2": "Ninafuata",
"cQfLWb": "URL..",
"cWx9t8": "Nyamazisha wote",
"cWx9t8": "Funga Mijibu Yote",
"cg1VJ2": "Unganisha Pochi",
"cuP16y": "Usaidizi wa akaunti nyingi",
"cuV2gK": "jina limesajiliwa",
"cuP16y": "Msaada wa Akaunti Nyingi",
"cuV2gK": "Jina limehifadhiwa",
"cyR7Kh": "Rudi",
"d+6YsV": "Lists to mute:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "Historia",
"d+6YsV": "Orodha za kufuta sauti:",
"d2ebEu": "Hajajiandikisha kwa Piga",
"d7d0/x": "Anwani ya LN",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"d8gpCh": "Jaribu kutumia hashtags chini ya 5 ili kubaki kwenye mada 🙏",
"dOQCL8": "Jina la kuonyesha",
"ddd3JX": "Popular Hashtags",
"deEeEI": "Register",
"djNL6D": "Read-only",
"dmsiLv": "A default Zap Pool split of {n} has been configured for {site} developers, you can disable it at any time in {link}",
"ddd3JX": "Vishazi maarufu",
"deEeEI": "Jisajili",
"djNL6D": "Soma pekee",
"dmsiLv": "Mgawo wa default wa Zap Pool wa {n} umewekwa kwa watengenezaji wa {site}, unaweza kuzima wakati wowote kwenye {link}",
"e61Jf3": "Inakuja hivi karibuni",
"e7VmYP": "Enter pin to unlock your private key",
"e7VmYP": "Ingiza pin kufuli funguo yako binafsi",
"e7qqly": "Weka Alama Yote Yamesomwa",
"eF0Re7": "Use a nostr signer extension to sign in",
"eF0Re7": "Tumia kiendelezi cha kusaini Nostr kuingia",
"eHAneD": "Emoji ya majibu",
"eJj8HD": "Pata Kuthibitishwa",
"eSzf2G": "Zap moja ya {nIn} sats itatenga {nOut} sats kwenye bwawa la zap.",
"eXT2QQ": "Gumzo la Kikundi",
"egib+2": "{n,plural,=1{& {n} other} other{& {n} others}}",
"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.",
"eXT2QQ": "Mazungumzo ya Kikundi",
"egib+2": "{n,plural,=1{& {n} mwingine} other{& {n} wengine}}",
"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.",
"fBI91o": "Zap",
"fBlba3": "Thanks for using {site}, please consider donating if you can.",
"fBlba3": "Asante kwa kutumia {site}, tafadhali fikiria kuchangia ikiwa unaweza.",
"fOksnD": "Haiwezi kupiga kura kwa sababu huduma ya LNURL haitumii zaps",
"fQN+tq": "Show posts that have a content warning tag",
"fQN+tq": "Onyesha machapisho yenye lebo ya onyo la yaliyomo",
"fWZYP5": "Imebandikwa",
"fX5RYm": "Pick a few topics of interest",
"fX5RYm": "Chagua mada kadhaa za kuvutia",
"filwqD": "Soma",
"fjAcWo": "Gift Wraps",
"fjAcWo": "Vifurushi vya Zawadi",
"flnGvv": "Unafikiria nini?",
"fqwcJ1": "On-chain Donation",
"fqwcJ1": "Michango kwenye Mnyororo",
"fsB/4p": "Imehifadhiwa",
"g5pX+a": "Kuhusu",
"g985Wp": "Imeshindwa kutuma kura",
"g985Wp": "Haiwezi kutuma kura",
"gDzDRs": "Emoji za kutuma unapojibu ujumbe kwa dokezo",
"gXgY3+": "Sio wateja wote wanaounga mkono hii bado",
"gczcC5": "Jisajili",
"geppt8": "{count} ({count2} in memory)",
"geppt8": "{count} ({count2} katika kumbukumbu)",
"gjBiyj": "Inapakia...",
"grQ+mI": "Ushahidi wa Kazi",
"h7jvCs": "{site} is more fun together!",
"h7jvCs": "{site} ina furaha zaidi pamoja!",
"h8XMJL": "Beji",
"hF6IN2": "Pogoa Orodha ya Ufuatao",
"hMzcSq": "Ujumbe",
"hRTfTR": "PRO",
"hY4lzx": "Inasaidia",
"hYOE+U": "Invite",
"ha8JKG": "Show graph",
"hYOE+U": "Alubuni",
"ha8JKG": "Onyesha grafu",
"hicxcO": "Onyesha majibu",
"hmZ3Bz": "Media",
"hniz8Z": "hapa",
"hvFRBo": "Interaction",
"hvFRBo": "Mwingiliano",
"i/dBAR": "Zap Pool",
"iCqGww": "Maoni ({n})",
"iEoXYx": "Tafsiri za DeepL",
"iGT1eE": "Zuia akaunti ghushi zisikuige",
"iGT1eE": "Afya ya Kufuatilia Ufuatao ({x})",
"iICVoL": "{x} follows ({y} duplicates)",
"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",
"itPgxd": "Wasifu",
"izWS4J": "Acha kufuata",
"j9xbzF": "Already backed up",
"j9xbzF": "Tayari umefanya nakala rudufu",
"jA3OE/": "{n,plural,one {}=1{{n} sat} other{{n} sats}}",
"jAmfGl": "Your {site_name} subscription is expired",
"jHa/ko": "Clean up your feed",
"jAmfGl": "Usajili wako wa {site_name} umekwisha",
"jHa/ko": "Safisha chakula chako",
"jMzO1S": "Hitilafu ya ndani: {msg}",
"jTrbGf": "{n} km - {location}",
"jvo0vs": "Hifadhi",
"jzgQ2z": "{n} Maoni",
"k0kCJp": "Apply Now",
"k0kCJp": "Tumia Sasa",
"k2veDA": "Andika",
"k7sKNy": "Huduma yetu wenyewe ya uthibitishaji wa NIP-05, inasaidia kusaidia uundaji wa tovuti hii na kupata beji maalum inayong'aa kwenye tovuti yetu!",
"kEZUR8": "Register an Iris username",
"kEZUR8": "Jisajili jina la mtumiaji wa Iris",
"kJYo0u": "{n,plural,one {}=0{{name} alireposted} other{{name} & {n} na wengine wkarepost}}",
"kaaf1E": "sasa hivi",
"kc79d3": "Topics",
"kqPQJD": "Configure zap pool",
"kc79d3": "Mada",
"kqPQJD": "Sanidi bwawa la zap",
"kuPHYE": "{n,plural,one {}=0{{name} amependa} other{{name} & {n} na wengine wamependa}}",
"l+ikU1": "Kila kitu katika {plan}",
"l3H1EK": "Invite your friends",
"l3H1EK": "Waalika marafiki zako",
"lCILNz": "Nunua Sasa hivi",
"lD3+8a": "Lipa",
"lPWASz": "Anwani ya nostr ya Snort",
@ -460,101 +469,106 @@
"n1Whvj": "Badili",
"nDejmx": "Ondoa kizuizi",
"nGBrvw": "Alamisho",
"nGGDsi": "Notifications Allowed",
"nihgfo": "Listen to this article",
"nwZXeh": "{n} amezuiwa",
"nGGDsi": "Ruhusu Arifa",
"nIchMQ": "Inatafuta shughuli za akaunti ({progress})",
"nUT0Lv": "Vyombo",
"nihgfo": "Sikiliza makala hii",
"nwZXeh": "{n} imezuiliwa",
"o7e+nJ": "wafuasi {n}",
"oJ+JJN": "Hakuna kilichopatikana:/",
"odFwjL": "Inafuata pekee",
"ojzbwv": "Hujambo, inaonekana kama bado huna Anwani ya Nostr, unapaswa kuipata! Angalia {link}",
"p4N05H": "Pakia",
"p85Uwy": "Usajili Unaotumika",
"p9Ps2l": "{x}/{y} wana relay ({percent})",
"pI+77w": "Hifadhi rudufu zinazoweza kupakuliwa kutoka kwa relay ya Snort",
"pRess9": "ZapPool",
"puLNUJ": "Bandika",
"pukxg/": "Payments",
"pzTOmv": "Wafuasi",
"qD9EUF": "Barua pepe <> Daraja la DM kwa anwani yako ya nostr ya Snort",
"qDwvZ4": "Hitilafu isiyojulikana",
"qMePPG": "Note",
"qMx1sA": "Kiasi chaguo-msingi cha Zap",
"qMePPG": "Taarifa",
"qMx1sA": "Chaguo-msingi cha Zap",
"qUJTsT": "Imezuiwa",
"qZsKBR": "Renew {tier}",
"qcJFEJ": "Notifications API Disabled",
"qZsKBR": "Jiandikishie upya {tier}",
"qcJFEJ": "API za Arifa zimelemazwa",
"qdGuQo": "Ufunguo wako wa Faragha Ni (usishiriki hii na mtu yeyote)",
"qfmMQh": "This note has been muted",
"qfmMQh": "Hii taarifa imezimwa",
"qkvYUb": "Ongeza kwa Wasifu",
"qmJ8kD": "Tafsiri imeshindwa",
"qtWLmt": "Penda",
"qyJtWy": "Show less",
"qydxOd": "Science",
"qz9fty": "Incorrect pin",
"qyJtWy": "Onyesha kidogo",
"qydxOd": "Sayansi",
"qz9fty": "Pin isiyo sahihi",
"r3C4x/": "Programu",
"r5srDR": "Ingiza nenosiri la pochi",
"rMgF34": "Back up now",
"rMgF34": "Hifadhi nakala sasa",
"rT14Ow": "Ongeza Relay",
"rfuMjE": "(Chaguo-msingi)",
"rmdsT4": "siku {n}",
"rx1i0i": "Short link",
"sKDn4e": "Show Badges",
"rx1i0i": "Kiungo fupi",
"sKDn4e": "Onyesha Vialamisho",
"sUNhQE": "mtumiaji",
"sZQzjQ": "Failed to parse zap split: {input}",
"tGXF0Q": "Relay Lists",
"sZQzjQ": "Imeshindwa kuchambua mgawanyo wa zap: {input}",
"tGXF0Q": "Orodha za Relay",
"tOdNiY": "Giza",
"th5lxp": "Tuma dokezo kwa kikundi kidogo cha relay zako za uandishi",
"thnRpU": "Kuthibitishwa kwa NIP-05 kunaweza kusaidia:",
"tjpYlr": "Relay Metrics",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Takwimu za Relay",
"ttxS0b": "Beji ya Msaidizi",
"u+LyXc": "Interactions",
"u+LyXc": "Mwingiliano",
"u/vOPu": "Imelipwa",
"u4bHcR": "Angalia msimbo hapa: {link}",
"u9NoC1": "Name must be less than {limit} characters",
"uCk8r+": "Already have an account?",
"uKqSN+": "Follows Feed",
"u9NoC1": "Jina lazima liwe chini ya wahusika {limit}",
"uCk8r+": "Je, tayari una akaunti?",
"uKqSN+": "Fuata Chakula",
"uSV4Ti": "Machapisho mapya yanahitaji kuthibitishwa mwenyewe",
"uc0din": "Send sats splits to",
"un1nGw": "{n} notes",
"uc0din": "Tuma sats zilizogawanywa kwa",
"un1nGw": "{n} taarifa",
"usAvMr": "Badilisha Wasifu",
"v8lolG": "Anzisha gumzo",
"vB3oQ/": "Must be a contact list or pubkey list",
"vN5UH8": "Profile Image",
"vB3oQ/": "Lazima iwe orodha ya mawasiliano au orodha ya pubkey",
"vN5UH8": "Pata ya bure",
"vU/Q5i": "Zana hii itatafuta tukio la mwisho lililochapishwa na wafuatiliaji wako wote na kuondoa wale ambao hawajachapisha kwa miezi 6",
"vZ4quW": "NIP-05 ni uthibitishaji wa msingi wa DNS ambao husaidia kukuthibitisha kama mtumiaji halisi.",
"vhlWFg": "Chaguo za Kura",
"vlbWtt": "Pata ya bure",
"voxBKC": "Followed by friends",
"vrTOHJ": "sats {amount}",
"voxBKC": "Imefuatiliwa na marafiki",
"vxwnbh": "Kiasi cha kazi ya kuomba kwa matukio yote yaliyochapishwa",
"w1Fanr": "Business",
"w1Fanr": "Biashara",
"w6qrwX": "NSFW",
"wEQDC6": "Hariri",
"wSZR47": "Submit",
"wWLwvh": "Hajulikan",
"wSZR47": "Tuma",
"wWLwvh": "Hajulikani",
"wih7iJ": "jina limezuiwa",
"wofVHy": "Moderation",
"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",
"xIcAOU": "Kura za {type}",
"xIoGG9": "Enda kwa",
"xIoGG9": "Nenda kwa",
"xQtL3v": "Fungua",
"xaj9Ba": "Mtoa huduma",
"xbVgIm": "Pakia midia kiotomatiki",
"xhQMeQ": "Muda wake unaisha",
"xl4s/X": "Additional Terms:",
"xl4s/X": "Vigezo vingine:",
"xmcVZ0": "Tafuta",
"xybOUv": "FAN",
"y/bmsG": "Allow",
"y/bmsG": "Ruhusu",
"y1Z3or": "Lugha",
"yCLnBC": "LNURL au Anwani ya Umeme",
"yNBPJp": "Help fund the development of {site}",
"zCb8fX": "Weight",
"yNBPJp": "Saidia ufadhili wa maendeleo ya {site}",
"zCb8fX": "Uzito",
"zFegDD": "Wasiliana",
"zINlao": "Mmiliki",
"zQvVDJ": "Yote",
"zcaOTs": "Kiasi cha Zap katika seti",
"zm6qS1": "{n} mins to read",
"zm6qS1": "{n} dakika kusoma",
"zonsdq": "Imeshindwa kupakia huduma ya LNURL",
"zvCDao": "Onyesha madokezo mapya kiotomatiki",
"zvCDao": "Onyesha taarifa mpya kiotomatiki",
"zwb6LR": "<b>Mint:</b> {url}",
"zxvhnE": "Daily"
"zxvhnE": "Kila siku"
}

View File

@ -51,6 +51,7 @@
"2zJXeA": "சுயவிவரங்கள்",
"39AHJm": "பதிவு செய்",
"3KNMbJ": "கட்டுரைகள்",
"3QwfJR": "~{amount}",
"3cc4Ct": "ஒளி",
"3gOsZq": "மொழிபெயர்ப்பாளர்கள்",
"3qnJlS": "{amount} சேட்களைக் கொண்டு நீங்கள் வாக்களிக்கிறீர்கள்",
@ -66,6 +67,7 @@
"4OB335": "விருப்பமில்லை",
"4Vmpt4": "Nostr Plebs முதல் NIP-05 வழங்குநர்களில் ஒன்றாகும். மேலும், இது நியாயமான விலையில் டொமைன்களின் நல்ல தொகுப்பை வழங்குகிறது",
"4Z3t5i": "படங்களை சுருக்க imgproxy உபயோகிக்கவும்",
"4emo2p": "Missing Relays",
"4rYCjn": "சுய குறிப்பு",
"5BVs2e": "ஜாப்",
"5CB6zB": "ஜாப் பிரிப்புகள்",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> காஷூ சாட்கள்",
"6/hB3S": "மறு ஓட்டத்தைப் பார்க்கவும்",
"62nsdy": "மீண்டும் முயற்சிக்கவும்",
"6559gb": "New follow list length {length}",
"65BmHb": "{host} இல் இருந்து படத்தைப் பதிலி செய்ய முடியவில்லை, நேரடியாகப் பெற இங்கு கிளிக் செய்யவும்",
"6OSOXl": "காரணம்: <i>{reason}</i>",
"6bgpn+": "எல்லா நாஸ்டர் மென்பொருட்களும் இதை இன்னும் ஆதரிக்கவில்லை, ஜாப் பிரிப்புகள் கட்டமைக்காததைப் போல நீங்கள் சில ஜாப்களை பெற வாய்ப்பு உள்ளது",
@ -129,6 +132,8 @@
"C81/uG": "வெளியேறு",
"C8HhVE": "பரிந்துரைக்கப்படும் பயனர்கள்",
"CHTbO3": "விலைப்பட்டியலை பெற முடியவில்லை",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "டிரெண்டிங் நபர்கள்",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "பரிமாற்றம்",
"Dx4ey3": "அனைத்தையும் நிலைமாற்று",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "குறிப்புகளைத் தேடு",
"ELbg9p": "தரவு வழங்குநர்",
"EQKRE4": "சுயவிவரப் பக்கங்களில் பேட்ஜ்களைக் காட்டு",
@ -188,6 +194,7 @@
"HbefNb": "திறந்த பணப்பை",
"HhcAVH": "நீங்கள் இவரைப் பின்தொடரவில்லை, மீடியாவை ஏற்ற இங்கே கிளிக் செய்யவும் <i>{link}</i>, அல்லது புதுப்பிக்கவும் <a><i>உங்கள் விருப்பங்களை</i></a> எல்லாரிடமிருந்தும் எப்போதும் மீடியாவை ஏற்றுவதற்கு.",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "இந்தக் குறிப்பின் நிலையான பொறுத்தத்தை நிச்சயமாக நீக்க விரும்புகிறீர்களா?",
"IKKHqV": "பின்தொடர்வுகள்",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "You have an active iris.to account",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "இருப்பு: {amount} ஸாட்கள்",
"VOjC1i": "எந்தப் பதிவேற்ற சேவையில் இணைப்புகளைப் பதிவேற்ற விரும்புகிறீர்கள் என்பதைத் தேர்ந்தெடுக்கவும்",
"VR5eHw": "பொது சாவி (npub/nprofile)",
"VcwrfF": "Yes please",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "Send usage metrics",
"XICsE8": "File hosts",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "எதிர்வினைகள்",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -345,6 +352,7 @@
"b5vAk0": "உங்கள் பயனர் அடையாளம் மின்னல் முகவரிபோல் செயல்படும் மற்றும் நீங்கள் தேர்ந்தெடுத்த LNURL அல்லது மின்னல் முகவரிக்குத் திருப்பிவிடும்",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "Get Address",
"bMphls": "Logged in with read-only access",
"bQdA2k": "உணர்திறன் மிக்க உள்ளடக்கம்",
@ -365,7 +373,6 @@
"cyR7Kh": "பின்",
"d+6YsV": "Lists to mute:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "வரலாறு",
"d7d0/x": "LN முகவரி",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "காட்சி பெயர்",
@ -404,6 +411,7 @@
"grQ+mI": "Proof of Work",
"h7jvCs": "{site} is more fun together!",
"h8XMJL": "பேட்ஜ்கள்",
"hF6IN2": "Prune Follow List",
"hMzcSq": "அஞ்சல்கள்",
"hRTfTR": "PRO",
"hY4lzx": "ஆதரவு",
@ -417,6 +425,7 @@
"iCqGww": "எதிர்வினைகள் ({n})",
"iEoXYx": "DeepL மொழிபெயர்ப்புகள்",
"iGT1eE": "போலி கணக்குகள் உங்களைப் போல் நடிப்பதை தடுக்கவும்",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "பயனர்பெயர்",
"iXPL0Z": "பாதுகாப்பற்ற இணைப்பில் தனிப்பட்ட சாவியுடன் உள்நுழைய முடியாது, அதற்குப் பதிலாக நாஸ்டர் விசை மேலாளர் நீட்டிப்பைப் பயன்படுத்தவும்",
"iYc3Ld": "Payments",
@ -461,6 +470,8 @@
"nDejmx": "முடக்கத்தை நீக்கு",
"nGBrvw": "புக்மார்க்குகள்",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "Listen to this article",
"nwZXeh": "{n} முடக்கப்பட்டவை",
"o7e+nJ": "{n} பின்தொடர்பவர்கள்",
@ -469,9 +480,11 @@
"ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}",
"p4N05H": "Upload",
"p85Uwy": "செயலில் உள்ள சந்தாக்கள்",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "ஸ்நார்ட் ரிலேயிலிருந்து பதிவிறக்கக்கூடிய காப்புப்பிரதிகள்",
"pRess9": "ZapPool",
"puLNUJ": "நிலையாக பொறுத்து",
"pukxg/": "Payments",
"pzTOmv": "பின்தொடர்வோர்",
"qD9EUF": "உங்கள் ஸ்நார்ட் நாஸ்டர் முகவரிக்கு மின்னஞ்சல் <> DM பாலம்",
"qDwvZ4": "அறியப்படாத பிழை",
@ -502,6 +515,7 @@
"tOdNiY": "இருள்",
"th5lxp": "Send note to a subset of your write relays",
"thnRpU": "NIP-05 மூலம் சரிபார்த்துக் கொள்வது கீழ்க்கண்டவற்றில் உதவலாம்:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Relay Metrics",
"ttxS0b": "ஆதரவாளர் பேட்ஜ்",
"u+LyXc": "Interactions",
@ -517,11 +531,11 @@
"v8lolG": "பேசத் தொடங்குக",
"vB3oQ/": "Must be a contact list or pubkey list",
"vN5UH8": "Profile Image",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 என்பது DNS அடிப்படையிலான சரிபார்ப்பு விவரக்குறிப்பாகும், இது உங்களை உண்மையான பயனராகச் சரிபார்க்க உதவுகிறது.",
"vhlWFg": "வாக்கெடுப்பு விருப்பங்கள்",
"vlbWtt": "விலையின்றிப் பெறுக",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} ஸாட்கள்",
"vxwnbh": "அனைத்து பதியப்பட்ட நிகழ்வுகளிலும் மாற்றம் செய்யத் தேவைப்படும் பணிச் சுமை",
"w1Fanr": "Business",
"w6qrwX": "NSFW",

View File

@ -51,6 +51,7 @@
"2zJXeA": "Profiles",
"39AHJm": "Sign Up",
"3KNMbJ": "Articles",
"3QwfJR": "~{amount}",
"3cc4Ct": "สว่าง",
"3gOsZq": "แปลภาษา",
"3qnJlS": "คุณกำลังลงคะแนนด้วย {amount} sats",
@ -66,6 +67,7 @@
"4OB335": "ไม่ถูกใจ",
"4Vmpt4": "Nostr Plebs เป็นหนึ่งในผู้ให้บริการ NIP-05 รายแรก ๆ ในพื้นที่นี้ และเสนอชุดโดเมนที่ดีในราคาที่สมเหตุสมผล",
"4Z3t5i": "ใช้ imgproxy เพื่อบีบอัดรูปภาพ",
"4emo2p": "Missing Relays",
"4rYCjn": "หมายเหตุถึงตนเอง",
"5BVs2e": "zaps",
"5CB6zB": "Zap Splits",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu sats",
"6/hB3S": "Watch Replay",
"62nsdy": "Retry",
"6559gb": "New follow list length {length}",
"65BmHb": "อิมเมจพร็อกซีจาก {host} ไม่สำเร็จ คลิกที่นี่เพื่อโหลดโดยตรง",
"6OSOXl": "Reason: <i>{reason}</i>",
"6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured",
@ -129,6 +132,8 @@
"C81/uG": "ออกจากระบบ",
"C8HhVE": "แนะนำให้ติดตาม",
"CHTbO3": "ไม่สามารถโหลด invoice",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "บุตคลที่กำลังมาแรง",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "โอนย้าย",
"Dx4ey3": "Toggle all",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "Search notes",
"ELbg9p": "ผู้ให้บริการข้อมูล",
"EQKRE4": "Show badges on profile pages",
@ -188,6 +194,7 @@
"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": "คุณแน่ใจแล้วใช่มั้ยว่าต้องการลบโน้ตนี้?",
"IKKHqV": "Follows",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "You have an active iris.to account",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "คงเหลือ: {amount} sats",
"VOjC1i": "เลือกบริการอัปโหลดที่คุณต้องการอัปโหลดไฟล์แนบ",
"VR5eHw": "Public key (npub/nprofile)",
"VcwrfF": "Yes please",
@ -322,6 +328,7 @@
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
"XECMfW": "Send usage metrics",
"XICsE8": "สถานที่เก็บไฟล์",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "Reactions",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -345,6 +352,7 @@
"b5vAk0": "สิ่งนี้เหมือนกับ Lightning Address และจะเปลี่ยนการเชื่อมต่อไปที่ LNURL หรือ Lightning Addresses ที่คุณเลือก",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "Get Address",
"bMphls": "Logged in with read-only access",
"bQdA2k": "เนื้อหาที่ละเอียดอ่อน",
@ -365,7 +373,6 @@
"cyR7Kh": "ย้อนกลับ",
"d+6YsV": "Lists to mute:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "ประวัติย้อนหลัง",
"d7d0/x": "LN address",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "ชื่อที่แสดง",
@ -404,6 +411,7 @@
"grQ+mI": "Proof of Work",
"h7jvCs": "{site} is more fun together!",
"h8XMJL": "เหรียญตรา",
"hF6IN2": "Prune Follow List",
"hMzcSq": "ข้อความ",
"hRTfTR": "PRO",
"hY4lzx": "สนับสนุน",
@ -417,6 +425,7 @@
"iCqGww": "Reactions ({n})",
"iEoXYx": "แปล DeepL",
"iGT1eE": "ป้องกันไม่ให้บัญชีปลอมเลียนแบบคุณ",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "จัดการ",
"iXPL0Z": "ไม่สามารถเข้าสู่ระบบด้วย private key ในการเชื่อมต่อที่ไม่ปลอดภัย โปรดใช้ Nostr key manager extension ในการจัดการ",
"iYc3Ld": "Payments",
@ -461,6 +470,8 @@
"nDejmx": "ยกเลิกการบล็อค",
"nGBrvw": "บุ๊คมาร์ค",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "Listen to this article",
"nwZXeh": "{n} บล็อค",
"o7e+nJ": "ผู้ติดตาม {n}",
@ -469,9 +480,11 @@
"ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}",
"p4N05H": "Upload",
"p85Uwy": "เปิดใช้งานการเป็นสมาชิก",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "ดาวน์โหลดข้อมูลสำรองจากรีเลย์ของ Snort",
"pRess9": "ZapPool",
"puLNUJ": "ปักหมุด",
"pukxg/": "Payments",
"pzTOmv": "ผู้ติดตาม",
"qD9EUF": "Email <> DM bridge ของคุณจาก Snort nostr address",
"qDwvZ4": "ข้อผิดพลาดที่ไม่รู้จัก",
@ -502,6 +515,7 @@
"tOdNiY": "มืด",
"th5lxp": "ส่งโน้ตของคุณไปยังชุดของรีเลย์ที่คุณอนุญาติให้เขียน",
"thnRpU": "การได้รับการยืนยัน NIP-05 สามารถช่วย:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "Relay Metrics",
"ttxS0b": "เหรียญตราของผู้สนับสนุน",
"u+LyXc": "Interactions",
@ -517,11 +531,11 @@
"v8lolG": "Start chat",
"vB3oQ/": "Must be a contact list or pubkey list",
"vN5UH8": "Profile Image",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "จ่ายด้วย {wallet}",
"vhlWFg": "ตัวเลือกโพลล์",
"vlbWtt": "Get a free one",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} sats",
"vxwnbh": "Amount of work to apply to all published events",
"w1Fanr": "Business",
"w6qrwX": "NSFW",

View File

@ -51,6 +51,7 @@
"2zJXeA": "个人资料",
"39AHJm": "注册",
"3KNMbJ": "文章",
"3QwfJR": "~{amount}",
"3cc4Ct": "浅色",
"3gOsZq": "翻译人员",
"3qnJlS": "你正在用 {amount} 聪投票",
@ -66,6 +67,7 @@
"4OB335": "踩",
"4Vmpt4": "Nostr Plebs 是该领域首批 NIP-05 提供商之一,以合理的价格提供大量域名",
"4Z3t5i": "使用 imgproxy 压缩图片",
"4emo2p": "Missing Relays",
"4rYCjn": "自用笔记",
"5BVs2e": "打闪",
"5CB6zB": "打闪拆分",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu 聪",
"6/hB3S": "观看重播",
"62nsdy": "重试",
"6559gb": "New follow list length {length}",
"65BmHb": "从 {host} 代理图像失败,点击此处直接加载",
"6OSOXl": "原因:<i>{reason}</i>",
"6bgpn+": "并非所有客户端都支持,就算已配置了打闪拆分,你仍然可能会收到一些打闪",
@ -129,6 +132,8 @@
"C81/uG": "登出",
"C8HhVE": "推荐关注",
"CHTbO3": "加载发票失败",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "热门用户",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "转移",
"Dx4ey3": "全部切换",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "搜索笔记",
"ELbg9p": "数据提供方",
"EQKRE4": "在个人档案页面上显示徽章",
@ -164,7 +170,7 @@
"FdhSU2": "立即领取",
"FfYsOb": "发生错误!",
"FmXUJg": "正在关注你",
"FvanT6": "Accounts",
"FvanT6": "帐户",
"G/yZLu": "移除",
"G1BGCg": "选择钱包",
"G3A56c": "Subscribed to Push",
@ -187,7 +193,8 @@
"HWbkEK": "清除缓存并重新加载",
"HbefNb": "打开钱包",
"HhcAVH": "你不关注这个用户,点击此处从<i>{link}</i>加载多媒体,或更新<a><i>你的选项</i></a>来自动加载来自任何人的多媒体。",
"HqRNN8": "Support",
"HqRNN8": "支持",
"I1AoOu": "Last post {time}",
"IEwZvs": "是否确定要取消置顶此条笔记?",
"IKKHqV": "关注",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -264,7 +271,7 @@
"QDFTjG": "{n} 个中继器",
"QWhotP": "打闪池只有当你使用支持的钱包连接WebLN、LNC、LNDHub 或 Nostr Wallet Connect时才能启用",
"Qxv0B2": "目前你的打闪池中有 {number} 聪。",
"R/6nsx": "Subscription",
"R/6nsx": "订阅",
"R81upa": "你关注的用户",
"RDha9y": "Service Worker Not Running",
"RSr2uB": "用户名必须只包含小写字母和数字",
@ -289,11 +296,11 @@
"TJo5E6": "预览",
"TP/cMX": "已结束",
"TaeBqw": "使用 Nostr 扩展登录",
"TdTXXf": "Learn more",
"TdTXXf": "了解更多",
"TdtZQ5": "加密货币",
"TpgeGw": "十六进制盐..",
"Tpy00S": "用户",
"TwyMau": "Account",
"TwyMau": "帐户",
"U1aPPi": "停止收听",
"UDYlxu": "待定订阅",
"UJTWqI": "从我的中继器中移除",
@ -306,7 +313,6 @@
"UrKTqQ": "你有一个活跃的 iris.to 帐户",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "余额: {amount} 聪",
"VOjC1i": "选择你要将附件上传到哪个上传服务",
"VR5eHw": "公钥 (npub/nprofile)",
"VcwrfF": "好的",
@ -322,6 +328,7 @@
"X7xU8J": "nsec、npub、NIP-05、十六进制、助记词句",
"XECMfW": "发送使用资料",
"XICsE8": "文件主机",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "回应",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -335,7 +342,7 @@
"ZLmyG9": "贡献者",
"ZS+jRE": "将打闪拆分发送到",
"Zff6lu": "用户名 iris.to/<b>{name}</b> 已为你保留!",
"ZlmK/p": "{name} invited you to {app}",
"ZlmK/p": "{name} 邀请你访问 {app}",
"a5UPxh": "资助提供 NIP-05 验证服务的开发人员和平台",
"a7TDNm": "笔记将实时流式传输到全球和帖子选项卡",
"aHje0o": "姓名",
@ -345,8 +352,9 @@
"b5vAk0": "你的代号将像闪电地址一样重定向至你所选的 LNURL 或闪电地址",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "计算剪枝列表",
"bLZL5a": "获取地址",
"bMphls": "Logged in with read-only access",
"bMphls": "以只读权限登录",
"bQdA2k": "敏感内容",
"bep9C3": "公钥",
"bfvyfs": "匿名",
@ -364,14 +372,13 @@
"cuV2gK": "名称已被注册",
"cyR7Kh": "返回",
"d+6YsV": "应静音的列表:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "历史",
"d2ebEu": "未订阅推送",
"d7d0/x": "闪电地址",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "显示名称",
"ddd3JX": "Popular Hashtags",
"ddd3JX": "热门标签",
"deEeEI": "注册",
"djNL6D": "Read-only",
"djNL6D": "只读",
"dmsiLv": "已为 {site} 开发人员配置了 {n} 的默认打闪池分割,你随时可以在 {link}中禁用它。",
"e61Jf3": "即将上线",
"e7VmYP": "输入 PIN 码解锁你的私钥",
@ -386,7 +393,7 @@
"fBI91o": "打闪",
"fBlba3": "感谢你使用 {site},请考虑捐赠。",
"fOksnD": "无法投票因为LNURL 服务不支持打闪",
"fQN+tq": "Show posts that have a content warning tag",
"fQN+tq": "显示有内容警告标签的帖子",
"fWZYP5": "已置顶",
"fX5RYm": "选择几个感兴趣的主题",
"filwqD": "读",
@ -404,31 +411,33 @@
"grQ+mI": "工作量证明",
"h7jvCs": "{site} 在一起更有趣!",
"h8XMJL": "徽章",
"hF6IN2": "Prune Follow List",
"hMzcSq": "消息",
"hRTfTR": "专业",
"hY4lzx": "支持",
"hYOE+U": "Invite",
"hYOE+U": "邀请",
"ha8JKG": "Show graph",
"hicxcO": "显示回复",
"hmZ3Bz": "多媒体",
"hniz8Z": "这里",
"hvFRBo": "Interaction",
"hvFRBo": "互动",
"i/dBAR": "打闪池",
"iCqGww": "回应({n}",
"iEoXYx": "DeepL 翻译",
"iGT1eE": "防止虚假帐户冒充你",
"iICVoL": "{x} 关注 ({y} 重复)",
"iNWbVV": "代号",
"iXPL0Z": "无法在不安全的连接上使用私钥登录请使用Nostr密钥管理器扩展程序",
"iYc3Ld": "付款",
"ieGrWo": "关注",
"itPgxd": "个人档案",
"izWS4J": "取消关注",
"j9xbzF": "Already backed up",
"j9xbzF": "已备份",
"jA3OE/": "{n,plural,=1{{n}聪} other{{n}聪}}",
"jAmfGl": "你的 {site_name} 订阅已过期",
"jHa/ko": "清理你的订阅",
"jMzO1S": "内部错误: {msg}",
"jTrbGf": "{n} km - {location}",
"jTrbGf": "{n} 公里 - {location}",
"jvo0vs": "保存",
"jzgQ2z": "{n} 个回应",
"k0kCJp": "Apply Now",
@ -437,11 +446,11 @@
"kEZUR8": "注册 Iris 用户名",
"kJYo0u": "{n,plural,=0{{name}已转发} other{{name}和{n}个其他用户已转发}}",
"kaaf1E": "现在",
"kc79d3": "Topics",
"kqPQJD": "Configure zap pool",
"kc79d3": "话题",
"kqPQJD": "配置打闪池",
"kuPHYE": "{n,plural,=0{{name}已点赞} other{{name}和{n}个其他用户已点赞}}",
"l+ikU1": "{plan}提供",
"l3H1EK": "Invite your friends",
"l3H1EK": "邀请你的朋友们",
"lCILNz": "立即购买",
"lD3+8a": "付款",
"lPWASz": "Snort 的 Nostr 地址",
@ -460,7 +469,9 @@
"n1Whvj": "切换",
"nDejmx": "解除屏蔽",
"nGBrvw": "收藏",
"nGGDsi": "Notifications Allowed",
"nGGDsi": "已允许通知",
"nIchMQ": "正在搜索帐户活动 ({progress})",
"nUT0Lv": "工具",
"nihgfo": "聆听本文",
"nwZXeh": "{n} 已屏蔽",
"o7e+nJ": "{n} 个粉丝",
@ -469,28 +480,30 @@
"ojzbwv": "嘿,看起来你还没有 Nostr 地址,你应该去获取一个!查看 {link}",
"p4N05H": "上传",
"p85Uwy": "活跃订阅",
"p9Ps2l": "{x}/{y} 有中继器 ({percent})",
"pI+77w": "可从 Snort 中继器下载备份",
"pRess9": "ZapPool",
"pRess9": "打闪池",
"puLNUJ": "置顶",
"pukxg/": "Payments",
"pzTOmv": "粉丝",
"qD9EUF": "给你的 Snort Nostr 地址建立电子邮件 <> 私信桥",
"qDwvZ4": "未知错误",
"qMePPG": "Note",
"qMePPG": "笔记",
"qMx1sA": "默认打闪金额",
"qUJTsT": "已屏蔽",
"qZsKBR": "更新 {tier}",
"qcJFEJ": "Notifications API Disabled",
"qcJFEJ": "通知 API 已禁用",
"qdGuQo": "你的私钥是(不要与任何人分享)",
"qfmMQh": "该笔记已被静音",
"qkvYUb": "添加至个人档案",
"qmJ8kD": "翻译失败",
"qtWLmt": "点赞",
"qyJtWy": "Show less",
"qyJtWy": "显示更少",
"qydxOd": "科学",
"qz9fty": "PIN 码不正确",
"r3C4x/": "软件",
"r5srDR": "输入钱包密码",
"rMgF34": "Back up now",
"rMgF34": "立即备份",
"rT14Ow": "添加中继",
"rfuMjE": "(默认)",
"rmdsT4": "{n} 天",
@ -502,26 +515,27 @@
"tOdNiY": "深色",
"th5lxp": "将笔记发送到你的写入中继器的子集",
"thnRpU": "验证 NIP-05 可以帮助:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "中继器指标",
"ttxS0b": "支持者徽章",
"u+LyXc": "互动",
"u/vOPu": "已付款",
"u4bHcR": "在此处查看代码:{link}",
"u9NoC1": "Name must be less than {limit} characters",
"u9NoC1": "名称必须少于 {limit} 字符",
"uCk8r+": "已有账户?",
"uKqSN+": "关注源",
"uSV4Ti": "转发需要人工确认",
"uc0din": "将聪拆分发送到",
"un1nGw": "{n} notes",
"un1nGw": "{n} 条笔记",
"usAvMr": "编辑个人档案",
"v8lolG": "开始聊天",
"vB3oQ/": "必须是一个联系人列表或公钥列表",
"vN5UH8": "简介图片",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 是一种基于 DNS 的验证规范,可帮助验证你是真实用户。",
"vhlWFg": "投票选项",
"vlbWtt": "获取免费的",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} 聪",
"vxwnbh": "适用于所有发布事件的工作量",
"w1Fanr": "商业",
"w6qrwX": "NSFW",
@ -543,7 +557,7 @@
"xl4s/X": "附加条款:",
"xmcVZ0": "搜索",
"xybOUv": "FAN",
"y/bmsG": "Allow",
"y/bmsG": "允许",
"y1Z3or": "语言",
"yCLnBC": "LNURL 或闪电地址",
"yNBPJp": "帮助资助 {site} 的开发",

View File

@ -51,6 +51,7 @@
"2zJXeA": "個人檔案",
"39AHJm": "註冊",
"3KNMbJ": "文章",
"3QwfJR": "~{amount}",
"3cc4Ct": "淺色",
"3gOsZq": "翻譯人員",
"3qnJlS": "你正在用{amount}聰投票",
@ -66,6 +67,7 @@
"4OB335": "踩",
"4Vmpt4": "Nostr Plebs 是該領域首批 NIP-05 供應商之一,以合理的價格提供大量域名",
"4Z3t5i": "使用 imgproxy 壓縮圖片",
"4emo2p": "Missing Relays",
"4rYCjn": "自用筆記",
"5BVs2e": "打閃",
"5CB6zB": "打閃拆分",
@ -77,6 +79,7 @@
"6/SF6e": "<h1>{n}</h1> Cashu 聰",
"6/hB3S": "觀看重播",
"62nsdy": "重試",
"6559gb": "New follow list length {length}",
"65BmHb": "從 {host} 代理圖像失敗,點擊此處直接加載",
"6OSOXl": "原因:<i>{reason}</i>",
"6bgpn+": "並非所有客戶端都支持,就算已配置了打閃拆分,你仍然可能會收到一些打閃",
@ -129,6 +132,8 @@
"C81/uG": "登出",
"C8HhVE": "推薦關注",
"CHTbO3": "加載發票失敗",
"CM+Cfj": "Follow List",
"CM0k0d": "Prune follow list",
"CVWeJ6": "熱門用戶",
"CYkOCI": "and {count} others you follow",
"CbM2hK": "Trending hashtags",
@ -146,6 +151,7 @@
"DrZqav": "About must be less than {limit} characters",
"DtYelJ": "轉移",
"Dx4ey3": "切換全部",
"E5ZIPD": "<big>{amount}</big> <small>sats</small>",
"EJbFi7": "搜索筆記",
"ELbg9p": "數據提供方",
"EQKRE4": "在個人檔案頁面上顯示徽章",
@ -188,6 +194,7 @@
"HbefNb": "打開錢包",
"HhcAVH": "你不關注此用戶,點擊此處從<i>{link}</i>加載多媒體,或更新<a><i>你的選項</i></a>來自動加載來自任何人的多媒體。",
"HqRNN8": "Support",
"I1AoOu": "Last post {time}",
"IEwZvs": "是否確定要取消置頂此條筆記?",
"IKKHqV": "關注",
"IOu4Xh": "You must be a {tier} subscriber to access {app} deck",
@ -306,7 +313,6 @@
"UrKTqQ": "你有一個活躍的 iris.to 帳戶",
"UxgyeY": "Your referral code is {code}",
"VL900k": "Recommended Relays",
"VN0+Fz": "餘額:{amount} 聰",
"VOjC1i": "選擇你要將附件上傳到哪個上傳服務",
"VR5eHw": "公鑰npub/nprofile",
"VcwrfF": "好的",
@ -322,6 +328,7 @@
"X7xU8J": "nsec、nsec、NIP-05、十六進制、助記詞句",
"XECMfW": "傳送使用資料",
"XICsE8": "文件主機",
"XQiFEl": "Follows Relay Health",
"XXm7jJ": "Trending Hashtags",
"XgWvGA": "回應",
"XhpBfA": "{site} is an open source project built by passionate people in their free time, your donations are greatly appreciated",
@ -345,6 +352,7 @@
"b5vAk0": "你的代號將像閃電地址一樣重定向至你所選的 LNURL 或閃電地址",
"bF1MYT": "You are a community leader and are earning <b>{percent}</b> of referred users subscriptions!",
"bG00/W": "Service Worker Running",
"bJ+wrA": "Compute prune list",
"bLZL5a": "獲取地址",
"bMphls": "Logged in with read-only access",
"bQdA2k": "敏感內容",
@ -365,7 +373,6 @@
"cyR7Kh": "返回",
"d+6YsV": "應靜音的列表:",
"d2ebEu": "Not Subscribed to Push",
"d6CyG5": "歷史",
"d7d0/x": "閃電地址",
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
"dOQCL8": "顯示名稱",
@ -404,6 +411,7 @@
"grQ+mI": "工作量證明",
"h7jvCs": "{site} 一起使用更好玩!",
"h8XMJL": "徽章",
"hF6IN2": "Prune Follow List",
"hMzcSq": "消息",
"hRTfTR": "PRO",
"hY4lzx": "支持",
@ -417,6 +425,7 @@
"iCqGww": "回應({n}",
"iEoXYx": "DeepL 翻譯",
"iGT1eE": "防止虛假帳戶冒充你",
"iICVoL": "{x} follows ({y} duplicates)",
"iNWbVV": "代號",
"iXPL0Z": "無法在不安全的連接上使用私鑰登錄,請使用 nostr 密鑰管理器擴展程序",
"iYc3Ld": "付款",
@ -461,6 +470,8 @@
"nDejmx": "解除屏蔽",
"nGBrvw": "收藏",
"nGGDsi": "Notifications Allowed",
"nIchMQ": "Searching for account activity ({progress})",
"nUT0Lv": "Tools",
"nihgfo": "聆聽本文",
"nwZXeh": "{n} 已屏蔽",
"o7e+nJ": "{n} 個粉絲",
@ -469,9 +480,11 @@
"ojzbwv": "嘿,看起來你還沒有 nostr 地址,你應該去獲取一個!查看 {link}",
"p4N05H": "上傳",
"p85Uwy": "活躍訂閱",
"p9Ps2l": "{x}/{y} have relays ({percent})",
"pI+77w": "可從 Snort 中繼器下載備份",
"pRess9": "ZapPool",
"puLNUJ": "置頂",
"pukxg/": "Payments",
"pzTOmv": "粉絲",
"qD9EUF": "給你的 Snort nostr 地址建立電子郵件 <> 私信橋",
"qDwvZ4": "未知錯誤",
@ -502,6 +515,7 @@
"tOdNiY": "深色",
"th5lxp": "將筆記發送到你的寫入中繼器的子集",
"thnRpU": "驗證 NIP-05 可以幫助:",
"tj6kdX": "{sign} {amount} sats",
"tjpYlr": "中繼器指標",
"ttxS0b": "支持者徽章",
"u+LyXc": "互動",
@ -517,11 +531,11 @@
"v8lolG": "開始聊天",
"vB3oQ/": "必須是一個聯繫人列表或公鑰列表",
"vN5UH8": "頭像",
"vU/Q5i": "This tool will search for the last event published by all of your follows and remove those who have not posted in 6 months",
"vZ4quW": "NIP-05 是一種基於 DNS 的驗證規範,可幫助驗證你是真實用戶。",
"vhlWFg": "投票選項",
"vlbWtt": "獲取免費的",
"voxBKC": "Followed by friends",
"vrTOHJ": "{amount} 聰",
"vxwnbh": "適用於所有發佈事件的工作量",
"w1Fanr": "商業",
"w6qrwX": "敏感內容",

View File

@ -2,7 +2,37 @@
React hooks for @snort/system
Sample:
### Available hooks
#### `useRequestBuilder(NoteStore, RequestBuilder)`
The main hook which allows you to subscribe to nostr relays and returns a reactive store.
#### `useUserProfile(pubkey: string | undefined)`
Profile hook, profile loading is automated, this hook will return the profile from cache and also refresh the cache in the background (`stale-while-revalidate`)
#### `useEventFeed(NostrLink)` / `useEventsFeed(Array<NostrLink>)`
A simple hook which can load events using the `NostrLink` class, this class contains one NIP-19 entity `nevent/naddr` etc.
#### `useReactions(id, Array<NostrLink>)`
Loads reactions for a set of events, this can be a set of posts on a profile or an arbitary list of events.
#### `useEventReactions(NostrLink, Array<NostrEvent>)`
Process a set of related events (usually results from `useReactions`) and return likes/dislikes/reposts/zaps
#### `useUserSearch()`
Search for profiles in the profile cache, this also returns exact links if they match
#### `useSystemState(System)`
Hook state of the nostr system
## Example:
```js
import { useMemo } from "react";

View File

@ -12,9 +12,10 @@ argon2 = "0.5.2"
console_error_panic_hook = "0.1.7"
hex = { version = "0.4.3", features = [], default-features = false }
itertools = "0.11.0"
secp256k1 = "0.28.0"
secp256k1 = { version = "0.28.0", features = ["global-context"] }
serde = { version = "1.0.188", features = ["derive"], default-features = false }
serde-wasm-bindgen = "0.5.0"
serde_json = "1.0.105"
sha256 = { version = "1.4.0", features = [], default-features = false }
wasm-bindgen = "0.2.87"

View File

@ -46,6 +46,11 @@ export function argon2(password: any, salt: any): any;
* @returns {boolean}
*/
export function schnorr_verify(hash: any, sig: any, pub_key: any): boolean;
/**
* @param {any} event
* @returns {boolean}
*/
export function schnorr_verify_event(event: any): boolean;
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
@ -59,6 +64,7 @@ export interface InitOutput {
readonly pow: (a: number, b: number, c: number) => void;
readonly argon2: (a: number, b: number, c: number) => void;
readonly schnorr_verify: (a: number, b: number, c: number, d: number) => void;
readonly schnorr_verify_event: (a: number, b: number) => void;
readonly rustsecp256k1_v0_9_1_context_create: (a: number) => number;
readonly rustsecp256k1_v0_9_1_context_destroy: (a: number) => void;
readonly rustsecp256k1_v0_9_1_default_illegal_callback_fn: (a: number, b: number) => void;

Some files were not shown because too many files have changed in this diff Show More