Merge remote-tracking branch 'kieran/main'
This commit is contained in:
@ -1,3 +1,55 @@
|
||||
# v0.1.23
|
||||
|
||||
## Added
|
||||
|
||||
- DeepL translate api (Automatic for PRO subscribers)
|
||||
- Add nostr:nprofile1qqsydl97xpj74udw0qg5vkfyujyjxd3l706jd0t0w0turp93d0vvungfgfewr to contributors
|
||||
- Proxy LN address type enabled on Nostr Address settings pages
|
||||
- Infinite scrol on notifications page
|
||||
- Default 0.5% ZapPool rate for Snort donation address
|
||||
- Collect relay metrics in `@snort/system` for better relay selection algo in Outbox Model (NIP-65)
|
||||
- New sign up / login flow!
|
||||
- Topics / Mute words on sign up for easier onboarding
|
||||
- Drag & Drop for uploads on note creator - nostr:nprofile1qqs8tchhwf5smv3r2g0vkswz58c837uu456x59m3dh380gtrhqzydeqz4wlka
|
||||
- Mixin topics (hashtags) into timeline feed
|
||||
- Language specific trending posts
|
||||
- Show following info for hashtags
|
||||
- Sync preferences to network (`NIP-78` support)
|
||||
- Trending hashtags page
|
||||
- Note creator hashtag input
|
||||
- Top trending hashtags on note creator
|
||||
- Social Graph - nostr:nprofile1qqsy2ga7trfetvd3j65m3jptqw9k39wtq2mg85xz2w542p5dhg06e5qpr9mhxue69uhhyetvv9ujuumwdae8gtnnda3kjctv9uh8am0r
|
||||
- New users relay list based off "close" relays
|
||||
- `NIP-96` support for nostr native image/file uploaders
|
||||
- Write replies/reactions to `p` tagged users read relays (Outbox model)
|
||||
- Sync joined public chats (`NIP-28`) using `PublicChatList` kind `10_005`
|
||||
|
||||
## Changed
|
||||
|
||||
- Read/Write relays only on kind `10_002` (NIP-65)
|
||||
- Removed `nostr.watch` code for adding new users to random relays
|
||||
- Render kind `10_002` on profile relays tab
|
||||
- `@snort/system` using eventemitter3 for triggering events
|
||||
- Use latest `NIP-51` spec (Bookmarks/Interests/`NIP-28` PublicChatList)
|
||||
- `nreq` support (Demo)
|
||||
- Write profile/relays to blasters
|
||||
- `@snort/system` automated outbox model (automatic fetching of relay metadata)
|
||||
|
||||
## Fixes
|
||||
|
||||
- Upgrade ephermal connection to non-ephemeral
|
||||
- Remove relay tag from zaps (Some zap services dont support it)
|
||||
- Fix zap parsing for goals
|
||||
- Remove extra chars from quoted events to fix loading (`'s` etc)
|
||||
- CSS Fixes for profile card on light theme
|
||||
- Zap counting on replacable events
|
||||
- `NIP-28` chats loading
|
||||
- Overflowing modal UI
|
||||
- Live stream widget layout with long titles
|
||||
- Notifications marker has returned from its long slumber
|
||||
|
||||
---
|
||||
|
||||
# v0.1.22
|
||||
|
||||
## Fixes
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@snort/app",
|
||||
"version": "0.1.22",
|
||||
"version": "0.1.23",
|
||||
"dependencies": {
|
||||
"@cashu/cashu-ts": "^0.6.1",
|
||||
"@lightninglabs/lnc-web": "^0.2.3-alpha",
|
||||
@ -35,7 +35,6 @@
|
||||
"react-router-dom": "^6.5.0",
|
||||
"react-tag-input-component": "^2.0.2",
|
||||
"react-textarea-autosize": "^8.4.0",
|
||||
"react-twitter-embed": "^4.0.4",
|
||||
"recharts": "^2.8.0",
|
||||
"three": "^0.157.0",
|
||||
"use-long-press": "^3.2.0",
|
||||
|
@ -93,11 +93,6 @@ export const InvoiceRegex = /(lnbc\w+)/i;
|
||||
export const YoutubeUrlRegex =
|
||||
/(?:https?:\/\/)?(?:www|m\.)?(?:youtu\.be\/|youtube\.com\/(?:live\/|shorts\/|embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})/;
|
||||
|
||||
/**
|
||||
* Tweet Regex
|
||||
*/
|
||||
export const TweetUrlRegex = /https?:\/\/twitter\.com\/(?:#!\/)?(\w+)\/status(?:es)?\/(\d+)/;
|
||||
|
||||
/**
|
||||
* Hashtag regex
|
||||
*/
|
||||
|
@ -51,7 +51,7 @@ export default function Note(props: NoteProps) {
|
||||
if (ev.kind === EventKind.ZapstrTrack) {
|
||||
return <ZapstrEmbed ev={ev} />;
|
||||
}
|
||||
if (ev.kind === EventKind.CategorizedPeople || ev.kind === EventKind.ContactList) {
|
||||
if (ev.kind === EventKind.FollowSet || ev.kind === EventKind.ContactList) {
|
||||
return <PubkeyList ev={ev} className={className} />;
|
||||
}
|
||||
if (ev.kind === EventKind.LiveEvent) {
|
||||
|
@ -1,8 +1,5 @@
|
||||
// import { TwitterTweetEmbed } from "react-twitter-embed";
|
||||
|
||||
import {
|
||||
YoutubeUrlRegex,
|
||||
//TweetUrlRegex,
|
||||
TidalRegex,
|
||||
SoundCloudRegex,
|
||||
MixCloudRegex,
|
||||
@ -37,7 +34,6 @@ export default function HyperText({ link, depth, showLinkPreview, children }: Hy
|
||||
try {
|
||||
const url = new URL(a);
|
||||
const youtubeId = YoutubeUrlRegex.test(a) && RegExp.$1;
|
||||
//const tweetId = TweetUrlRegex.test(a) && RegExp.$2;
|
||||
const tidalId = TidalRegex.test(a) && RegExp.$1;
|
||||
const soundcloundId = SoundCloudRegex.test(a) && RegExp.$1;
|
||||
const mixcloudId = MixCloudRegex.test(a) && RegExp.$1;
|
||||
@ -46,13 +42,8 @@ export default function HyperText({ link, depth, showLinkPreview, children }: Hy
|
||||
const isAppleMusicLink = AppleMusicRegex.test(a);
|
||||
const isNostrNestsLink = NostrNestsRegex.test(a);
|
||||
const isWavlakeLink = WavlakeRegex.test(a);
|
||||
/*if (tweetId) { // tmp disabled, react-twitter-embed causes "require is not defined" error
|
||||
return (
|
||||
<div className="tweet" key={tweetId}>
|
||||
<TwitterTweetEmbed tweetId={tweetId} />
|
||||
</div>
|
||||
);
|
||||
} else*/ if (youtubeId) {
|
||||
|
||||
if (youtubeId) {
|
||||
return (
|
||||
<iframe
|
||||
className="w-max"
|
||||
|
@ -16,7 +16,7 @@ export function LiveEvent({ ev }: { ev: NostrEvent }) {
|
||||
switch (status) {
|
||||
case "live": {
|
||||
return (
|
||||
<div className="flex g4">
|
||||
<div className="flex g4 items-center">
|
||||
<Icon name="signal-01" />
|
||||
<b className="uppercase">
|
||||
<FormattedMessage defaultMessage="Live" id="Dn82AL" />
|
||||
@ -49,7 +49,7 @@ export function LiveEvent({ ev }: { ev: NostrEvent }) {
|
||||
case "live": {
|
||||
return (
|
||||
<Link to={link} target="_blank">
|
||||
<button type="button">
|
||||
<button className="nowrap">
|
||||
<FormattedMessage defaultMessage="Join Stream" id="GQPtfk" />
|
||||
</button>
|
||||
</Link>
|
||||
@ -59,7 +59,7 @@ export function LiveEvent({ ev }: { ev: NostrEvent }) {
|
||||
if (findTag(ev, "recording")) {
|
||||
return (
|
||||
<Link to={link} target="_blank">
|
||||
<button type="button">
|
||||
<button className="nowrap">
|
||||
<FormattedMessage defaultMessage="Watch Replay" id="6/hB3S" />
|
||||
</button>
|
||||
</Link>
|
||||
@ -70,13 +70,13 @@ export function LiveEvent({ ev }: { ev: NostrEvent }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex justify-between br p24 bg-primary">
|
||||
<div className="flex g12">
|
||||
<div className="sm:flex g12 br p24 bg-primary items-center">
|
||||
<div>
|
||||
<ProfileImage pubkey={host} showUsername={false} size={56} />
|
||||
<div>
|
||||
<h2>{title}</h2>
|
||||
{statusLine()}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col g8 grow">
|
||||
<div className="font-semibold text-3xl">{title}</div>
|
||||
<div>{statusLine()}</div>
|
||||
</div>
|
||||
<div>{cta()}</div>
|
||||
</div>
|
||||
|
@ -20,6 +20,7 @@
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
--border-color: var(--gray);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
@ -28,6 +29,11 @@
|
||||
width: 600px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.modal-body {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body button.secondary:hover {
|
||||
background-color: var(--gray);
|
||||
|
@ -16,7 +16,7 @@ enum Provider {
|
||||
}
|
||||
|
||||
export default function SuggestedProfiles() {
|
||||
const login = useLogin();
|
||||
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>();
|
||||
@ -37,7 +37,7 @@ export default function SuggestedProfiles() {
|
||||
}
|
||||
case Provider.SemisolDev: {
|
||||
const api = new SemisolDevApi();
|
||||
const users = await api.sugguestedFollows(login.publicKey, login.follows.item);
|
||||
const users = await api.sugguestedFollows(login.publicKey, login.follows);
|
||||
const keys = users.recommendations.sort(a => a[1]).map(a => a[0]);
|
||||
setUserList(keys);
|
||||
break;
|
||||
@ -52,7 +52,7 @@ export default function SuggestedProfiles() {
|
||||
|
||||
useEffect(() => {
|
||||
loadSuggestedProfiles();
|
||||
}, [login, provider]);
|
||||
}, [login.publicKey, login.follows, provider]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -1,13 +1,12 @@
|
||||
import "./ProfileImage.css";
|
||||
|
||||
import React, { ReactNode, useCallback, useRef, useState } from "react";
|
||||
import { HexKey, socialGraphInstance, UserMetadata } from "@snort/system";
|
||||
import { HexKey, UserMetadata } from "@snort/system";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
import classNames from "classnames";
|
||||
|
||||
import Avatar from "@/Element/User/Avatar";
|
||||
import Nip05 from "@/Element/User/Nip05";
|
||||
import Icon from "@/Icons/Icon";
|
||||
import DisplayName from "./DisplayName";
|
||||
import { ProfileLink } from "./ProfileLink";
|
||||
import { ProfileCard } from "./ProfileCard";
|
||||
|
@ -1,13 +1,12 @@
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { TaggedNostrEvent, EventKind, RequestBuilder, NoteCollection, NostrLink } from "@snort/system";
|
||||
import { TaggedNostrEvent, EventKind, RequestBuilder, NoteCollection, NostrLink, parseRelayTags } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
|
||||
import { bech32ToHex, debounce, findTag, getNewest, getNewestEventTagsByKey, unwrap } from "@/SnortUtils";
|
||||
import { makeNotification, sendNotification } from "@/Notifications";
|
||||
import { bech32ToHex, debounce, getNewest, getNewestEventTagsByKey, unwrap } from "@/SnortUtils";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import useModeration from "@/Hooks/useModeration";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import {
|
||||
LoginStore,
|
||||
SnortAppData,
|
||||
addSubscription,
|
||||
setAppData,
|
||||
@ -21,18 +20,18 @@ import {
|
||||
} from "@/Login";
|
||||
import { SnortPubKey } from "@/Const";
|
||||
import { SubscriptionEvent } from "@/Subscription";
|
||||
import useRelaysFeedFollows from "./RelaysFeedFollows";
|
||||
import { FollowLists, FollowsFeed, GiftsCache, Notifications, UserRelays } from "@/Cache";
|
||||
import { Nip28Chats, Nip4Chats } from "@/chat";
|
||||
import { useRefreshFeedCache } from "@/Hooks/useRefreshFeedcache";
|
||||
import { usePrevious } from "@uidotdev/usehooks";
|
||||
import { Nip28ChatSystem } from "@/chat/nip28";
|
||||
|
||||
/**
|
||||
* Managed loading data for the current logged in user
|
||||
*/
|
||||
export default function useLoginFeed() {
|
||||
const login = useLogin();
|
||||
const { publicKey: pubKey, readNotifications, follows } = login;
|
||||
const { isMuted } = useModeration();
|
||||
const { publicKey: pubKey, follows } = login;
|
||||
const { publisher, system } = useEventPublisher();
|
||||
|
||||
useRefreshFeedCache(Notifications, true);
|
||||
@ -44,15 +43,19 @@ export default function useLoginFeed() {
|
||||
system.checkSigs = login.appData.item.preferences.checkSigs;
|
||||
}, [login]);
|
||||
|
||||
const previous = usePrevious(login.appData.item);
|
||||
// write appdata after 10s of no changes
|
||||
useEffect(() => {
|
||||
if (!previous || JSON.stringify(previous) === JSON.stringify(login.appData.item)) {
|
||||
return;
|
||||
}
|
||||
return debounce(10_000, async () => {
|
||||
if (publisher && login.appData.item) {
|
||||
const ev = await publisher.appData(login.appData.item, "snort");
|
||||
await system.BroadcastEvent(ev);
|
||||
}
|
||||
});
|
||||
}, [login.appData.timestamp]);
|
||||
}, [previous]);
|
||||
|
||||
const subLogin = useMemo(() => {
|
||||
if (!login || !pubKey) return null;
|
||||
@ -63,8 +66,16 @@ export default function useLoginFeed() {
|
||||
});
|
||||
b.withFilter()
|
||||
.authors([pubKey])
|
||||
.kinds([EventKind.ContactList, EventKind.Relays, EventKind.MuteList, EventKind.PinList]);
|
||||
b.withFilter().authors([pubKey]).kinds([EventKind.CategorizedBookmarks]).tag("d", ["follow", "bookmark"]);
|
||||
.kinds([
|
||||
EventKind.ContactList,
|
||||
EventKind.Relays,
|
||||
EventKind.MuteList,
|
||||
EventKind.PinList,
|
||||
EventKind.BookmarksList,
|
||||
EventKind.InterestsList,
|
||||
EventKind.PublicChatsList,
|
||||
]);
|
||||
b.withFilter().authors([pubKey]).kinds([]);
|
||||
if (CONFIG.features.subscriptions && !login.readonly) {
|
||||
b.withFilter().authors([pubKey]).kinds([EventKind.AppData]).tag("d", ["snort"]);
|
||||
b.withFilter()
|
||||
@ -101,17 +112,7 @@ export default function useLoginFeed() {
|
||||
|
||||
const relays = getNewest(loginFeed.data.filter(a => a.kind === EventKind.Relays));
|
||||
if (relays) {
|
||||
const parsedRelays = relays.tags
|
||||
.filter(a => a[0] === "r")
|
||||
.map(a => {
|
||||
return [
|
||||
a[1],
|
||||
{
|
||||
read: a[2] === "read" || a[2] === undefined,
|
||||
write: a[2] === "write" || a[2] === undefined,
|
||||
},
|
||||
];
|
||||
});
|
||||
const parsedRelays = parseRelayTags(relays.tags.filter(a => a[0] === "r")).map(a => [a.url, a.settings]);
|
||||
setRelays(login, Object.fromEntries(parsedRelays), relays.created_at * 1000);
|
||||
}
|
||||
|
||||
@ -145,21 +146,6 @@ export default function useLoginFeed() {
|
||||
}
|
||||
}, [loginFeed, publisher]);
|
||||
|
||||
// send out notifications
|
||||
useEffect(() => {
|
||||
if (loginFeed.data) {
|
||||
const replies = loginFeed.data.filter(
|
||||
a => a.kind === EventKind.TextNote && !isMuted(a.pubkey) && a.created_at > readNotifications,
|
||||
);
|
||||
replies.forEach(async nx => {
|
||||
const n = await makeNotification(nx);
|
||||
if (n) {
|
||||
sendNotification(login, n);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [loginFeed, readNotifications]);
|
||||
|
||||
async function handleMutedFeed(mutedFeed: TaggedNostrEvent[]) {
|
||||
const latest = getNewest(mutedFeed);
|
||||
if (!latest) return;
|
||||
@ -204,6 +190,16 @@ export default function useLoginFeed() {
|
||||
}
|
||||
}
|
||||
|
||||
function handlePublicChatsListFeed(bookmarkFeed: TaggedNostrEvent[]) {
|
||||
const newest = getNewestEventTagsByKey(bookmarkFeed, "e");
|
||||
if (newest) {
|
||||
LoginStore.updateSession({
|
||||
...login,
|
||||
extraChats: newest.keys.map(Nip28ChatSystem.chatId),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (loginFeed.data) {
|
||||
const mutedFeed = loginFeed.data.filter(a => a.kind === EventKind.MuteList);
|
||||
@ -212,25 +208,19 @@ export default function useLoginFeed() {
|
||||
const pinnedFeed = loginFeed.data.filter(a => a.kind === EventKind.PinList);
|
||||
handlePinnedFeed(pinnedFeed);
|
||||
|
||||
const tagsFeed = loginFeed.data.filter(
|
||||
a => a.kind === EventKind.CategorizedBookmarks && findTag(a, "d") === "follow",
|
||||
);
|
||||
const tagsFeed = loginFeed.data.filter(a => a.kind === EventKind.InterestsList);
|
||||
handleTagFeed(tagsFeed);
|
||||
|
||||
const bookmarkFeed = loginFeed.data.filter(
|
||||
a => a.kind === EventKind.CategorizedBookmarks && findTag(a, "d") === "bookmark",
|
||||
);
|
||||
const bookmarkFeed = loginFeed.data.filter(a => a.kind === EventKind.BookmarksList);
|
||||
handleBookmarkFeed(bookmarkFeed);
|
||||
|
||||
const publicChatsFeed = loginFeed.data.filter(a => a.kind === EventKind.PublicChatsList);
|
||||
handlePublicChatsListFeed(publicChatsFeed);
|
||||
}
|
||||
}, [loginFeed]);
|
||||
|
||||
useEffect(() => {
|
||||
UserRelays.buffer(follows.item).catch(console.error);
|
||||
system.ProfileLoader.TrackMetadata(follows.item); // always track follows profiles
|
||||
system.ProfileLoader.TrackKeys(follows.item); // always track follows profiles
|
||||
}, [follows.item]);
|
||||
|
||||
const fRelays = useRelaysFeedFollows(follows.item);
|
||||
useEffect(() => {
|
||||
UserRelays.bulkSet(fRelays).catch(console.error);
|
||||
}, [fRelays]);
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { useMemo } from "react";
|
||||
import { HexKey, EventKind, RequestBuilder, ReplaceableNoteStore } from "@snort/system";
|
||||
import { HexKey, EventKind, RequestBuilder, ReplaceableNoteStore, parseRelayTags } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import { parseRelayTag } from "./RelaysFeedFollows";
|
||||
|
||||
export default function useRelaysFeed(pubkey?: HexKey) {
|
||||
const sub = useMemo(() => {
|
||||
@ -12,5 +11,5 @@ export default function useRelaysFeed(pubkey?: HexKey) {
|
||||
}, [pubkey]);
|
||||
|
||||
const relays = useRequestBuilder(ReplaceableNoteStore, sub);
|
||||
return relays.data?.tags.filter(a => a[0] === "r").map(parseRelayTag) ?? [];
|
||||
return parseRelayTags(relays.data?.tags.filter(a => a[0] === "r") ?? []);
|
||||
}
|
||||
|
@ -1,49 +0,0 @@
|
||||
import { useMemo } from "react";
|
||||
import { HexKey, FullRelaySettings, TaggedNostrEvent, EventKind, NoteCollection, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import debug from "debug";
|
||||
|
||||
import { sanitizeRelayUrl } from "@/SnortUtils";
|
||||
import { UserRelays } from "@/Cache";
|
||||
|
||||
interface RelayList {
|
||||
pubkey: string;
|
||||
created_at: number;
|
||||
relays: FullRelaySettings[];
|
||||
}
|
||||
|
||||
export default function useRelaysFeedFollows(pubkeys: HexKey[]): Array<RelayList> {
|
||||
const sub = useMemo(() => {
|
||||
const b = new RequestBuilder(`relays:follows`);
|
||||
const since = UserRelays.newest();
|
||||
debug("LoginFeed")("Loading relay lists since %s", new Date(since * 1000).toISOString());
|
||||
b.withFilter().authors(pubkeys).kinds([EventKind.Relays]).since(since);
|
||||
return b;
|
||||
}, [pubkeys]);
|
||||
|
||||
function mapFromRelays(notes: Array<TaggedNostrEvent>): Array<RelayList> {
|
||||
return notes.map(ev => {
|
||||
return {
|
||||
pubkey: ev.pubkey,
|
||||
created_at: ev.created_at,
|
||||
relays: ev.tags.map(parseRelayTag).filter(a => a.url !== undefined),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const relays = useRequestBuilder(NoteCollection, sub);
|
||||
const notesRelays = relays.data?.filter(a => a.kind === EventKind.Relays) ?? [];
|
||||
return useMemo(() => {
|
||||
return mapFromRelays(notesRelays);
|
||||
}, [relays]);
|
||||
}
|
||||
|
||||
export function parseRelayTag(tag: Array<string>) {
|
||||
return {
|
||||
url: sanitizeRelayUrl(tag[1]),
|
||||
settings: {
|
||||
read: tag[2] === "read" || tag[2] === undefined,
|
||||
write: tag[2] === "write" || tag[2] === undefined,
|
||||
},
|
||||
} as FullRelaySettings;
|
||||
}
|
@ -27,7 +27,7 @@ export function useLinkListEvents(id: string, fn: (rb: RequestBuilder) => void)
|
||||
}
|
||||
|
||||
export function usePinList(pubkey: string | undefined) {
|
||||
return useLinkListEvents(`pins:${pubkey?.slice(0, 12)}`, rb => {
|
||||
return useLinkListEvents(`list:pins:${pubkey?.slice(0, 12)}`, rb => {
|
||||
if (pubkey) {
|
||||
rb.withFilter().kinds([EventKind.PinList]).authors([pubkey]);
|
||||
}
|
||||
@ -35,17 +35,25 @@ export function usePinList(pubkey: string | undefined) {
|
||||
}
|
||||
|
||||
export function useMuteList(pubkey: string | undefined) {
|
||||
return useLinkList(`pins:${pubkey?.slice(0, 12)}`, rb => {
|
||||
return useLinkList(`list:mute:${pubkey?.slice(0, 12)}`, rb => {
|
||||
if (pubkey) {
|
||||
rb.withFilter().kinds([EventKind.MuteList]).authors([pubkey]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default function useCategorizedBookmarks(pubkey: string | undefined, list: string) {
|
||||
return useLinkListEvents(`categorized-bookmarks:${list}:${pubkey?.slice(0, 12)}`, rb => {
|
||||
export function useBookmarkList(pubkey: string | undefined) {
|
||||
return useLinkListEvents(`list:bookmark:${pubkey?.slice(0, 12)}`, rb => {
|
||||
if (pubkey) {
|
||||
rb.withFilter().kinds([EventKind.CategorizedBookmarks]).authors([pubkey]).tag("d", [list]);
|
||||
rb.withFilter().kinds([EventKind.BookmarksList]).authors([pubkey]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function useInterestsList(pubkey: string | undefined) {
|
||||
return useLinkList(`list:interest:${pubkey?.slice(0, 12)}`, rb => {
|
||||
if (pubkey) {
|
||||
rb.withFilter().kinds([EventKind.InterestsList]).authors([pubkey]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -95,11 +95,6 @@ export interface LoginSession {
|
||||
*/
|
||||
blocked: Newest<Array<HexKey>>;
|
||||
|
||||
/**
|
||||
* Latest notification
|
||||
*/
|
||||
latestNotification: number;
|
||||
|
||||
/**
|
||||
* Timestamp of last read notification
|
||||
*/
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useMemo } from "react";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import { FormattedMessage, FormattedNumber } from "react-intl";
|
||||
import { EventKind, NostrHashtagLink, NoteCollection, RequestBuilder } from "@snort/system";
|
||||
import { EventKind, NoteCollection, RequestBuilder } from "@snort/system";
|
||||
import { dedupe } from "@snort/shared";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
|
||||
@ -44,10 +44,11 @@ export function HashTagHeader({ tag, events, className }: { tag: string; events?
|
||||
|
||||
async function followTags(ts: string[]) {
|
||||
if (publisher) {
|
||||
const ev = await publisher.bookmarks(
|
||||
ts.map(a => new NostrHashtagLink(a)),
|
||||
"follow",
|
||||
);
|
||||
const ev = await publisher.generic(eb => {
|
||||
eb.kind(EventKind.InterestsList);
|
||||
ts.forEach(a => eb.tag(["t", a]));
|
||||
return eb;
|
||||
});
|
||||
setTags(login, ts, ev.created_at * 1000);
|
||||
await system.BroadcastEvent(ev);
|
||||
}
|
||||
@ -55,7 +56,7 @@ export function HashTagHeader({ tag, events, className }: { tag: string; events?
|
||||
|
||||
const sub = useMemo(() => {
|
||||
const rb = new RequestBuilder(`hashtag-counts:${tag}`);
|
||||
rb.withFilter().kinds([EventKind.CategorizedBookmarks]).tag("d", ["follow"]).tag("t", [tag.toLowerCase()]);
|
||||
rb.withFilter().kinds([EventKind.InterestsList]).tag("t", [tag.toLowerCase()]);
|
||||
return rb;
|
||||
}, [tag]);
|
||||
const followsTag = useRequestBuilder(NoteCollection, sub);
|
||||
|
@ -1,18 +1,19 @@
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
import { useMemo } from "react";
|
||||
import { useMemo, useSyncExternalStore } from "react";
|
||||
import { base64 } from "@scure/base";
|
||||
import { unwrap } from "@snort/shared";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import SearchBox from "../../Element/SearchBox";
|
||||
import { ProfileLink } from "../../Element/User/ProfileLink";
|
||||
import Avatar from "../../Element/User/Avatar";
|
||||
import Icon from "../../Icons/Icon";
|
||||
import useKeyboardShortcut from "../../Hooks/useKeyboardShortcut";
|
||||
import { isFormElement } from "../../SnortUtils";
|
||||
import useLogin from "../../Hooks/useLogin";
|
||||
import useEventPublisher from "../../Hooks/useEventPublisher";
|
||||
import SnortApi from "../../External/SnortApi";
|
||||
import SearchBox from "@/Element/SearchBox";
|
||||
import { ProfileLink } from "@/Element/User/ProfileLink";
|
||||
import Avatar from "@/Element/User/Avatar";
|
||||
import Icon from "@/Icons/Icon";
|
||||
import useKeyboardShortcut from "@/Hooks/useKeyboardShortcut";
|
||||
import { isFormElement } from "@/SnortUtils";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import SnortApi from "@/External/SnortApi";
|
||||
import { Notifications } from "@/Cache";
|
||||
|
||||
const AccountHeader = () => {
|
||||
const navigate = useNavigate();
|
||||
@ -25,19 +26,13 @@ const AccountHeader = () => {
|
||||
}
|
||||
});
|
||||
|
||||
const { publicKey, latestNotification, readNotifications, readonly } = useLogin(s => ({
|
||||
const { publicKey, readonly } = useLogin(s => ({
|
||||
publicKey: s.publicKey,
|
||||
latestNotification: s.latestNotification,
|
||||
readNotifications: s.readNotifications,
|
||||
readonly: s.readonly,
|
||||
}));
|
||||
const profile = useUserProfile(publicKey);
|
||||
const { publisher } = useEventPublisher();
|
||||
|
||||
const hasNotifications = useMemo(
|
||||
() => latestNotification > readNotifications,
|
||||
[latestNotification, readNotifications],
|
||||
);
|
||||
const unreadDms = useMemo(() => (publicKey ? 0 : 0), [publicKey]);
|
||||
|
||||
async function goToNotifications() {
|
||||
@ -92,7 +87,7 @@ const AccountHeader = () => {
|
||||
)}
|
||||
<Link className="btn" to="/notifications" onClick={goToNotifications}>
|
||||
<Icon name="bell-02" size={24} />
|
||||
{hasNotifications && <span className="has-unread"></span>}
|
||||
<HasNotificationsMarker />
|
||||
</Link>
|
||||
<ProfileLink pubkey={publicKey} user={profile}>
|
||||
<Avatar pubkey={publicKey} user={profile} />
|
||||
@ -101,4 +96,24 @@ const AccountHeader = () => {
|
||||
);
|
||||
};
|
||||
|
||||
function HasNotificationsMarker() {
|
||||
const readNotifications = useLogin(s => s.readNotifications);
|
||||
const notifications = useSyncExternalStore(
|
||||
c => Notifications.hook(c, "*"),
|
||||
() => Notifications.snapshot(),
|
||||
);
|
||||
const latestNotification = useMemo(
|
||||
() => notifications.reduce((acc, v) => (v.created_at > acc ? v.created_at : acc), 0),
|
||||
[notifications],
|
||||
);
|
||||
const hasNotifications = useMemo(
|
||||
() => latestNotification * 1000 > readNotifications,
|
||||
[notifications, readNotifications],
|
||||
);
|
||||
|
||||
if (hasNotifications) {
|
||||
return <span className="has-unread"></span>;
|
||||
}
|
||||
}
|
||||
|
||||
export default AccountHeader;
|
||||
|
@ -48,9 +48,9 @@ header {
|
||||
border-radius: 100%;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
position: relative;
|
||||
top: -4px;
|
||||
right: 7px;
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
|
@ -3,7 +3,7 @@ import "./MessagesPage.css";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { NostrLink, TLVEntryType, UserMetadata, decodeTLV } from "@snort/system";
|
||||
import { EventKind, NostrLink, TLVEntryType, UserMetadata, decodeTLV } from "@snort/system";
|
||||
import { useEventFeed, useUserProfile, useUserSearch } from "@snort/system-react";
|
||||
|
||||
import UnreadCount from "@/Element/UnreadCount";
|
||||
@ -25,6 +25,7 @@ import { LoginSession, LoginStore } from "@/Login";
|
||||
import { Nip28ChatSystem } from "@/chat/nip28";
|
||||
import { ChatParticipantProfile } from "@/Element/Chat/ChatParticipant";
|
||||
import classNames from "classnames";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
|
||||
const TwoCol = 768;
|
||||
const ThreeCol = 1500;
|
||||
@ -182,6 +183,7 @@ function NewChatWindow() {
|
||||
const navigate = useNavigate();
|
||||
const search = useUserSearch();
|
||||
const login = useLogin();
|
||||
const { system, publisher } = useEventPublisher();
|
||||
|
||||
useEffect(() => {
|
||||
setNewChat([]);
|
||||
@ -270,12 +272,25 @@ function NewChatWindow() {
|
||||
{results.length === 1 && (
|
||||
<Nip28ChatProfile
|
||||
id={results[0]}
|
||||
onClick={id => {
|
||||
onClick={async id => {
|
||||
setShow(false);
|
||||
const chats = appendDedupe(login.extraChats, [Nip28ChatSystem.chatId(id)]);
|
||||
LoginStore.updateSession({
|
||||
...login,
|
||||
extraChats: appendDedupe(login.extraChats, [Nip28ChatSystem.chatId(id)]),
|
||||
extraChats: chats,
|
||||
} as LoginSession);
|
||||
const evList = await publisher?.generic(eb => {
|
||||
eb.kind(EventKind.PublicChatsList);
|
||||
chats.forEach(c => {
|
||||
if (c.startsWith("chat281")) {
|
||||
eb.tag(["e", decodeTLV(c)[0].value as string]);
|
||||
}
|
||||
});
|
||||
return eb;
|
||||
});
|
||||
if (evList) {
|
||||
await system.BroadcastEvent(evList);
|
||||
}
|
||||
navigate(createChatLink(ChatType.PublicGroupChat, id));
|
||||
}}
|
||||
/>
|
||||
|
@ -13,7 +13,7 @@ import Bookmarks from "@/Element/User/Bookmarks";
|
||||
import Icon from "@/Icons/Icon";
|
||||
import { Tab } from "@/Element/Tabs";
|
||||
import { default as ZapElement } from "@/Element/Event/Zap";
|
||||
import useCategorizedBookmarks from "@/Hooks/useLists";
|
||||
import { useBookmarkList } from "@/Hooks/useLists";
|
||||
|
||||
import messages from "../messages";
|
||||
|
||||
@ -60,7 +60,7 @@ export function RelaysTab({ id }: { id: HexKey }) {
|
||||
}
|
||||
|
||||
export function BookMarksTab({ id }: { id: HexKey }) {
|
||||
const bookmarks = useCategorizedBookmarks(id, "bookmark");
|
||||
const bookmarks = useBookmarkList(id);
|
||||
const reactions = useReactions(`bookmark:reactions:{id}`, bookmarks.map(NostrLink.fromEvent));
|
||||
return <Bookmarks pubkey={id} bookmarks={bookmarks} related={reactions.data ?? []} />;
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import AsyncButton from "@/Element/AsyncButton";
|
||||
import classNames from "classnames";
|
||||
import { appendDedupe } from "@/SnortUtils";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import { NostrHashtagLink } from "@snort/system";
|
||||
import { EventKind } from "@snort/system";
|
||||
|
||||
export const FixedTopics = {
|
||||
life: {
|
||||
@ -283,11 +283,14 @@ export function Topics() {
|
||||
const tags = Object.entries(FixedTopics)
|
||||
.filter(([k]) => topics.includes(k))
|
||||
.map(([, v]) => v.tags)
|
||||
.flat()
|
||||
.map(a => new NostrHashtagLink(a));
|
||||
.flat();
|
||||
|
||||
if (tags.length > 0) {
|
||||
const ev = await publisher?.bookmarks(tags, "follow");
|
||||
const ev = await publisher?.generic(eb => {
|
||||
eb.kind(EventKind.InterestsList);
|
||||
tags.forEach(a => eb.tag(["t", a]));
|
||||
return eb;
|
||||
});
|
||||
if (ev) {
|
||||
await system.BroadcastEvent(ev);
|
||||
}
|
||||
|
53
packages/app/src/Tasks/BackupKey.tsx
Normal file
53
packages/app/src/Tasks/BackupKey.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { Link } from "react-router-dom";
|
||||
import { BaseUITask } from "@/Tasks";
|
||||
import { MetadataCache } from "@snort/system";
|
||||
import { LoginSession } from "@/Login";
|
||||
import Icon from "@/Icons/Icon";
|
||||
|
||||
export class BackupKeyTask extends BaseUITask {
|
||||
id = "backup-key";
|
||||
noBaseStyle = true;
|
||||
|
||||
check(_: MetadataCache, session: LoginSession): boolean {
|
||||
return !this.state.muted && session.type == "private_key";
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="p card">
|
||||
<div className="flex g12 bg-superdark p24 br">
|
||||
<div>
|
||||
<div className="p12 bg-dark circle">
|
||||
<Icon name="key" size={21} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col g8">
|
||||
<div className="font-semibold text-xl">
|
||||
<FormattedMessage defaultMessage="Be sure to back up your keys!" id="1UWegE" />
|
||||
</div>
|
||||
<small>
|
||||
<FormattedMessage
|
||||
defaultMessage="No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute."
|
||||
id="YR2I9M"
|
||||
values={{
|
||||
app: CONFIG.appNameCapitalized,
|
||||
}}
|
||||
/>
|
||||
</small>
|
||||
<div className="flex g8">
|
||||
<Link to="/settings/keys">
|
||||
<button>
|
||||
<FormattedMessage defaultMessage="Back up now" id="rMgF34" />
|
||||
</button>
|
||||
</Link>
|
||||
<button className="secondary" onClick={() => this.mute()}>
|
||||
<FormattedMessage defaultMessage="Already backed up" id="j9xbzF" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import "./TaskList.css";
|
||||
import { useState } from "react";
|
||||
import { useSyncExternalStore } from "react";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
@ -9,38 +9,65 @@ import { DonateTask } from "./DonateTask";
|
||||
import { Nip5Task } from "./Nip5Task";
|
||||
import { RenewSubTask } from "./RenewSubscription";
|
||||
import { NoticeZapPoolDefault } from "./NoticeZapPool";
|
||||
import { BackupKeyTask } from "./BackupKey";
|
||||
import { ExternalStore } from "@snort/shared";
|
||||
|
||||
const AllTasks: Array<UITask> = [new Nip5Task(), new DonateTask(), new NoticeZapPoolDefault()];
|
||||
if (CONFIG.features.subscriptions) {
|
||||
AllTasks.push(new RenewSubTask());
|
||||
class TaskStore extends ExternalStore<Array<UITask>> {
|
||||
#tasks: Array<UITask>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const AllTasks: Array<UITask> = [new BackupKeyTask(), new Nip5Task(), new DonateTask(), new NoticeZapPoolDefault()];
|
||||
if (CONFIG.features.subscriptions) {
|
||||
AllTasks.push(new RenewSubTask());
|
||||
}
|
||||
AllTasks.forEach(a =>
|
||||
a.load(() => {
|
||||
this.notifyChange();
|
||||
}),
|
||||
);
|
||||
this.#tasks = AllTasks;
|
||||
}
|
||||
|
||||
takeSnapshot(): UITask[] {
|
||||
return [...this.#tasks];
|
||||
}
|
||||
}
|
||||
AllTasks.forEach(a => a.load());
|
||||
|
||||
const AllTasks = new TaskStore();
|
||||
export const TaskList = () => {
|
||||
const session = useLogin();
|
||||
const user = useUserProfile(session.publicKey);
|
||||
const [, setTick] = useState<number>(0);
|
||||
const tasks = useSyncExternalStore(
|
||||
c => AllTasks.hook(c),
|
||||
() => AllTasks.snapshot(),
|
||||
);
|
||||
|
||||
function muteTask(t: UITask) {
|
||||
t.mute();
|
||||
setTick(x => (x += 1));
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="task-list">
|
||||
{AllTasks.filter(a => (user ? a.check(user, session) : false)).map(a => {
|
||||
return (
|
||||
<div key={a.id} className="card">
|
||||
<div className="header">
|
||||
<Icon name="lightbulb" />
|
||||
<div className="close" onClick={() => muteTask(a)}>
|
||||
<Icon name="close" size={14} />
|
||||
{tasks
|
||||
.filter(a => (user ? a.check(user, session) : false))
|
||||
.map(a => {
|
||||
if (a.noBaseStyle) {
|
||||
return a.render();
|
||||
} else {
|
||||
return (
|
||||
<div key={a.id} className="card">
|
||||
<div className="header">
|
||||
<Icon name="lightbulb" />
|
||||
<div className="close" onClick={() => muteTask(a)}>
|
||||
<Icon name="close" size={14} />
|
||||
</div>
|
||||
</div>
|
||||
{a.render()}
|
||||
</div>
|
||||
</div>
|
||||
{a.render()}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -3,12 +3,13 @@ import { LoginSession } from "@/Login";
|
||||
|
||||
export interface UITask {
|
||||
id: string;
|
||||
noBaseStyle: boolean;
|
||||
/**
|
||||
* Run checks to determine if this Task should be triggered for this user
|
||||
*/
|
||||
check(user: MetadataCache, session: LoginSession): boolean;
|
||||
mute(): void;
|
||||
load(): void;
|
||||
load(cb: () => void): void;
|
||||
render(): JSX.Element;
|
||||
}
|
||||
|
||||
@ -19,9 +20,11 @@ export interface UITaskState {
|
||||
}
|
||||
|
||||
export abstract class BaseUITask implements UITask {
|
||||
#cb?: () => void;
|
||||
protected state: UITaskState;
|
||||
|
||||
abstract id: string;
|
||||
noBaseStyle = false;
|
||||
abstract check(user: MetadataCache, session: LoginSession): boolean;
|
||||
abstract render(): JSX.Element;
|
||||
|
||||
@ -34,7 +37,8 @@ export abstract class BaseUITask implements UITask {
|
||||
this.#save();
|
||||
}
|
||||
|
||||
load() {
|
||||
load(cb: () => void) {
|
||||
this.#cb = cb;
|
||||
const state = window.localStorage.getItem(`task:${this.id}`);
|
||||
if (state) {
|
||||
this.state = JSON.parse(state);
|
||||
@ -43,5 +47,6 @@ export abstract class BaseUITask implements UITask {
|
||||
|
||||
#save() {
|
||||
window.localStorage.setItem(`task:${this.id}`, JSON.stringify(this.state));
|
||||
this.#cb?.();
|
||||
}
|
||||
}
|
||||
|
@ -204,6 +204,6 @@ export function useChatSystem() {
|
||||
|
||||
return [...nip4, ...nip28].filter(a => {
|
||||
const authors = a.participants.filter(a => a.type === "pubkey").map(a => a.id);
|
||||
return !authors.every(a => isBlocked(a));
|
||||
return authors.length === 0 || !authors.every(a => isBlocked(a));
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import debug from "debug";
|
||||
import { ExternalStore, FeedCache, unixNow, unwrap } from "@snort/shared";
|
||||
import { ExternalStore, FeedCache, unwrap } from "@snort/shared";
|
||||
import {
|
||||
EventKind,
|
||||
NostrEvent,
|
||||
@ -16,7 +16,6 @@ import {
|
||||
import { LoginSession } from "@/Login";
|
||||
import { findTag } from "@/SnortUtils";
|
||||
import { Chat, ChatParticipant, ChatSystem, ChatType, lastReadInChat } from "@/chat";
|
||||
import { Day } from "@/Const";
|
||||
|
||||
export class Nip28ChatSystem extends ExternalStore<Array<Chat>> implements ChatSystem {
|
||||
#cache: FeedCache<NostrEvent>;
|
||||
@ -50,7 +49,7 @@ export class Nip28ChatSystem extends ExternalStore<Array<Chat>> implements ChatS
|
||||
const lastMessage = messages[id]?.reduce((acc, v) => (v.created_at > acc ? v.created_at : acc), 0) ?? 0;
|
||||
rb.withFilter()
|
||||
.tag("e", [id])
|
||||
.since(lastMessage === 0 ? unixNow() - 2 * Day : lastMessage)
|
||||
.since(lastMessage === 0 ? undefined : lastMessage)
|
||||
.kinds(this.ChannelKinds);
|
||||
}
|
||||
|
||||
@ -67,9 +66,10 @@ export class Nip28ChatSystem extends ExternalStore<Array<Chat>> implements ChatS
|
||||
|
||||
listChats(): Chat[] {
|
||||
const chats = this.#chatChannels();
|
||||
return Object.entries(chats).map(([k, v]) => {
|
||||
const ret = Object.entries(chats).map(([k, v]) => {
|
||||
return Nip28ChatSystem.createChatObj(Nip28ChatSystem.chatId(k), v);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
static chatId(id: string) {
|
||||
|
@ -710,24 +710,6 @@ div.form-col {
|
||||
color: var(--gray-light);
|
||||
}
|
||||
|
||||
.tweet {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tweet div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tweet div .twitter-tweet {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.tweet div .twitter-tweet > iframe {
|
||||
max-height: unset;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
div.form {
|
||||
grid-auto-flow: dense;
|
||||
|
@ -90,6 +90,9 @@
|
||||
"1R43+L": {
|
||||
"defaultMessage": "Enter Nostr Wallet Connect config"
|
||||
},
|
||||
"1UWegE": {
|
||||
"defaultMessage": "Be sure to back up your keys!"
|
||||
},
|
||||
"1c4YST": {
|
||||
"defaultMessage": "Connected to: {node} 🎉"
|
||||
},
|
||||
@ -913,6 +916,9 @@
|
||||
"YDURw6": {
|
||||
"defaultMessage": "Service URL"
|
||||
},
|
||||
"YR2I9M": {
|
||||
"defaultMessage": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute."
|
||||
},
|
||||
"YXA3AH": {
|
||||
"defaultMessage": "Enable reactions"
|
||||
},
|
||||
@ -1170,6 +1176,9 @@
|
||||
"izWS4J": {
|
||||
"defaultMessage": "Unfollow"
|
||||
},
|
||||
"j9xbzF": {
|
||||
"defaultMessage": "Already backed up"
|
||||
},
|
||||
"jA3OE/": {
|
||||
"defaultMessage": "{n,plural,=1{{n} sat} other{{n} sats}}"
|
||||
},
|
||||
@ -1358,6 +1367,9 @@
|
||||
"r5srDR": {
|
||||
"defaultMessage": "Enter wallet password"
|
||||
},
|
||||
"rMgF34": {
|
||||
"defaultMessage": "Back up now"
|
||||
},
|
||||
"rT14Ow": {
|
||||
"defaultMessage": "Add Relays"
|
||||
},
|
||||
|
@ -17,6 +17,21 @@ self.addEventListener("message", event => {
|
||||
self.skipWaiting();
|
||||
}
|
||||
});
|
||||
self.addEventListener("install", event => {
|
||||
// delete all cache on install
|
||||
event.waitUntil(
|
||||
caches.keys().then(cacheNames => {
|
||||
return Promise.all(
|
||||
cacheNames.map(cacheName => {
|
||||
console.debug("Deleting cache: ", cacheName);
|
||||
return caches.delete(cacheName);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
// always skip waiting
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
const enum PushType {
|
||||
Mention = 1,
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "هل أنت متأكد من حذف هذا المنشور من المنشورات المرجعية؟",
|
||||
"1R43+L": "أدخل تكوين اتصال محفظة Nostr",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "متصل بـ: {node}🎉",
|
||||
"1nYUGC": "المتابَعون {n}",
|
||||
"1o2BgB": "التحقق من التوقيعات",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "مبلغ زابي الافتراضي الخاص بك هو {number} جلسة، على سبيل المثال يتم حساب القيم من هذا.",
|
||||
"XrSk2j": "Redeem",
|
||||
"YDURw6": "رابط الخدمة",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "تمكين التفاعل",
|
||||
"Z4BMCZ": "أدخل عبارة الاقتران",
|
||||
"ZKORll": "نشط الآن",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "متابعة",
|
||||
"itPgxd": "الملف التعريفي",
|
||||
"izWS4J": "الغاء المتابعة",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n} ساتوشي}other{{n} ساتوشي}}",
|
||||
"jAmfGl": "إنتهت صلاحية اشتراكك {site_name}",
|
||||
"jHa/ko": "تنظيف موجز الويب الخاص بك",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "دبوس غير صحيح",
|
||||
"r3C4x/": "برنامج",
|
||||
"r5srDR": "أدخل كلمة مرور المحفظة",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "إضافة موصّلات",
|
||||
"rbrahO": "أغلق",
|
||||
"rfuMjE": "(افتراضي)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "Bu qeydi əlfəcinlərdən silmək istədiyinizə əminsiniz?",
|
||||
"1R43+L": "Nostr Wallet Connect konfiqurasiyasını daxil edin",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "Qoşuldu: {node} 🎉",
|
||||
"1nYUGC": "{n} İzləyir",
|
||||
"1o2BgB": "Check Signatures",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.",
|
||||
"XrSk2j": "Redeem",
|
||||
"YDURw6": "Service URL",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "Enable reactions",
|
||||
"Z4BMCZ": "Enter pairing phrase",
|
||||
"ZKORll": "Activate Now",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Follow",
|
||||
"itPgxd": "Profile",
|
||||
"izWS4J": "Unfollow",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}",
|
||||
"jAmfGl": "Your {site_name} subscription is expired",
|
||||
"jHa/ko": "Clean up your feed",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Incorrect pin",
|
||||
"r3C4x/": "Software",
|
||||
"r5srDR": "Enter wallet password",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "Add Relays",
|
||||
"rbrahO": "Close",
|
||||
"rfuMjE": "(Default)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} Benutzer",
|
||||
"1Mo59U": "Bist du sicher, dass du diese Note aus deinen Lesezeichen entfernen möchtest?",
|
||||
"1R43+L": "Nostr Wallet Connect Konfiguration eingeben",
|
||||
"1UWegE": "Stelle sicher, dass du deine Schlüssel absicherst!",
|
||||
"1c4YST": "Verbunden mit: {node}🎉",
|
||||
"1nYUGC": "{n} Folgen",
|
||||
"1o2BgB": "Signaturen prüfen",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "Dein standardmäßiger Zap-Betrag ist {number} sats, Beispielwerte werden daraus berechnet.",
|
||||
"XrSk2j": "Einlösen",
|
||||
"YDURw6": "URL des Dienstes",
|
||||
"YR2I9M": "Keine Schlüssel, kein {app}. Es gibt keine Möglichkeit, sie zurückzusetzen, wenn du keine Sicherungskopie gemacht hast. Es dauert nur eine Minute.",
|
||||
"YXA3AH": "Reaktionen aktivieren",
|
||||
"Z4BMCZ": "Verbindungs-Passphrase eingeben",
|
||||
"ZKORll": "Jetzt aktivieren",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Folgen",
|
||||
"itPgxd": "Profil",
|
||||
"izWS4J": "Entfolgen",
|
||||
"j9xbzF": "Bereits gesichert",
|
||||
"jA3OE/": "{n,plural,one {}=1{{n} sat} other{{n} sats}}",
|
||||
"jAmfGl": "Dein {site_name} Abonnement ist abgelaufen",
|
||||
"jHa/ko": "Räume deinen Feed auf",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Falsche PIN",
|
||||
"r3C4x/": "Software",
|
||||
"r5srDR": "Wallet Passwort eingeben",
|
||||
"rMgF34": "Jetzt sichern",
|
||||
"rT14Ow": "Relais hinzufügen",
|
||||
"rbrahO": "Schließen",
|
||||
"rfuMjE": "(Standard)",
|
||||
|
@ -29,6 +29,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "Are you sure you want to remove this note from bookmarks?",
|
||||
"1R43+L": "Enter Nostr Wallet Connect config",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "Connected to: {node} 🎉",
|
||||
"1nYUGC": "{n} Following",
|
||||
"1o2BgB": "Check Signatures",
|
||||
@ -300,6 +301,7 @@
|
||||
"Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.",
|
||||
"XrSk2j": "Redeem",
|
||||
"YDURw6": "Service URL",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "Enable reactions",
|
||||
"Z4BMCZ": "Enter pairing phrase",
|
||||
"ZKORll": "Activate Now",
|
||||
@ -385,6 +387,7 @@
|
||||
"ieGrWo": "Follow",
|
||||
"itPgxd": "Profile",
|
||||
"izWS4J": "Unfollow",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}",
|
||||
"jAmfGl": "Your {site_name} subscription is expired",
|
||||
"jHa/ko": "Clean up your feed",
|
||||
@ -447,6 +450,7 @@
|
||||
"qz9fty": "Incorrect pin",
|
||||
"r3C4x/": "Software",
|
||||
"r5srDR": "Enter wallet password",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "Add Relays",
|
||||
"rbrahO": "Close",
|
||||
"rfuMjE": "(Default)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "¿Estás seguro de que quieres eliminar esta nota de tus favoritos?",
|
||||
"1R43+L": "Introduzca la configuración de Nostr Wallet Connect",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "Conectado a: {node}🎉",
|
||||
"1nYUGC": "{n} Siguiendo",
|
||||
"1o2BgB": "Firmas de cheques",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "Tu cantidad de zap por defecto es {number} sats, los valores de ejemplo se calculan a partir de esto.",
|
||||
"XrSk2j": "Canjear",
|
||||
"YDURw6": "URL del Servicio",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "Activar reacciones",
|
||||
"Z4BMCZ": "Introduce la frase de emparejamiento",
|
||||
"ZKORll": "Activar Ahora",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Seguir",
|
||||
"itPgxd": "Perfil",
|
||||
"izWS4J": "No seguir",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n} {n, plural, =1 {sat} other {sats}}",
|
||||
"jAmfGl": "Su suscripción a {site_name} ha caducado",
|
||||
"jHa/ko": "Limpie su alimentación",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Clavija incorrecta",
|
||||
"r3C4x/": "Software",
|
||||
"r5srDR": "Introducir contraseña de cartera",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "Añadir Relays",
|
||||
"rbrahO": "Cerrar",
|
||||
"rfuMjE": "(Por defecto)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "مطمئنید می خواهید این یادداشت را از نشانک ها خارج کنید؟",
|
||||
"1R43+L": "پیکربندی اتصال ناستر به کیف پول",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "متصل به:{node}🎉",
|
||||
"1nYUGC": "{n} دنبال شونده",
|
||||
"1o2BgB": "بررسی امضا",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "مبلغ پیش فرض زپ شما {number} ساتوشی است، مقادیر نمونه از روی این محاسبه شده اند.",
|
||||
"XrSk2j": "بازخرید",
|
||||
"YDURw6": "خدمات URL",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "فعال سازی واکنش ها",
|
||||
"Z4BMCZ": "عبارت جفت را وارد کنید",
|
||||
"ZKORll": "اکنون فعال کن",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "دنبال کردن",
|
||||
"itPgxd": "نمایه",
|
||||
"izWS4J": "لغو دنبال کردن",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,one {}=1{{n} نوت جدید} other{{n} نوت جدید}}",
|
||||
"jAmfGl": "عضویت شما در {site_name} منقضی شده است",
|
||||
"jHa/ko": "پاک سازی خبرنامه",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "پین نادرست",
|
||||
"r3C4x/": "نرم افزار",
|
||||
"r5srDR": "رمز کیفپول را وارد کنید",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "افزودن رله",
|
||||
"rbrahO": "بستن",
|
||||
"rfuMjE": "(پیشفرض)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "Haluatko varmasti poistaa tämän viestin kirjanmerkeistä?",
|
||||
"1R43+L": "Anna Nostr Wallet Connect asetukset",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "Yhdistetty: {node} 🎉",
|
||||
"1nYUGC": "{n} Seuraa",
|
||||
"1o2BgB": "Tarkista allekirjoitukset",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "Oletus zap-määräsi on {number} satsia, esimerkit on laskettu tämän mukaan.",
|
||||
"XrSk2j": "Lunasta",
|
||||
"YDURw6": "Palvelun URL",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "Ota reaktiot käyttöön",
|
||||
"Z4BMCZ": "Anna pariliitoslause",
|
||||
"ZKORll": "Aktivoi nyt",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Seuraa",
|
||||
"itPgxd": "Profiili",
|
||||
"izWS4J": "Lopeta seuraaminen",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n} sat} other{{n} satia}}",
|
||||
"jAmfGl": "{site_name} tilauksesi on päättynyt",
|
||||
"jHa/ko": "Siivoa rehusi",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Väärä tappi",
|
||||
"r3C4x/": "Ohjelmisto",
|
||||
"r5srDR": "Anna lompakon salasana",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "Lisää välittäjiä",
|
||||
"rbrahO": "Sulje",
|
||||
"rfuMjE": "(Oletus)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "Êtes-vous sûr de vouloir supprimer cette note de vos favoris ?",
|
||||
"1R43+L": "Accéder à la configuration de Nostr Wallet Connect",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "Connecté à : {node} 🎉",
|
||||
"1nYUGC": "{n} Abonnements",
|
||||
"1o2BgB": "Signatures de chèques",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "Votre montant de zap par défaut est {number} sats, les valeurs d'exemple sont calculées à partir de ceci.",
|
||||
"XrSk2j": "Retirer",
|
||||
"YDURw6": "URL de service",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "Activer les réactions",
|
||||
"Z4BMCZ": "Entrez la phrase d'appairage",
|
||||
"ZKORll": "Activer Maintenant",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Suivre",
|
||||
"itPgxd": "Profil",
|
||||
"izWS4J": "Ne plus suivre",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n} {n, plural, =1 {sat} other {sats}}",
|
||||
"jAmfGl": "Votre abonnement à {site_name} a expiré",
|
||||
"jHa/ko": "Nettoyez votre flux",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Broche incorrecte",
|
||||
"r3C4x/": "Logiciel",
|
||||
"r5srDR": "Entrez le mot de passe du portefeuille",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "Ajouter Relais",
|
||||
"rbrahO": "Fermer",
|
||||
"rfuMjE": "(Défaut)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "Jeste li sigurni da želite ukloniti ovu bilješku iz trake s oznakama?",
|
||||
"1R43+L": "Enter Nostr Wallet Connect config",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "Povezano s: {node} 🎉",
|
||||
"1nYUGC": "{n} Pratitelja",
|
||||
"1o2BgB": "Check Signatures",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.",
|
||||
"XrSk2j": "Redeem",
|
||||
"YDURw6": "URL usluge",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "Omogući reakcije",
|
||||
"Z4BMCZ": "Unesite frazu za uparivanje",
|
||||
"ZKORll": "Aktivirajte Odmah",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Prati",
|
||||
"itPgxd": "Profil",
|
||||
"izWS4J": "Prestani pratiti",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}",
|
||||
"jAmfGl": "Your {site_name} subscription is expired",
|
||||
"jHa/ko": "Clean up your feed",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Incorrect pin",
|
||||
"r3C4x/": "Software",
|
||||
"r5srDR": "Unesite lozinku novčanika",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "Dodaj relej",
|
||||
"rbrahO": "Close",
|
||||
"rfuMjE": "(Zadano)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} felhasználó",
|
||||
"1Mo59U": "Biztos hogy a kedvencekből ezt a bejegyzést el akarod távolítani?",
|
||||
"1R43+L": "Írd be a Nostr Wallet Connect konfigurációt",
|
||||
"1UWegE": "A kulcsaidról, mindenképpen készíts biztonsági másolatot!",
|
||||
"1c4YST": "Kapcsolódás a: {node} 🎉",
|
||||
"1nYUGC": "{n} Követek",
|
||||
"1o2BgB": "Aláírások ellenörzése",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "Az alapértelmezett zap összeg {number} sats, a példaértékek kiszámítása ebből történik.",
|
||||
"XrSk2j": "Beváltás",
|
||||
"YDURw6": "Szervíz cím",
|
||||
"YR2I9M": "Nincsenek kulcsok, nincs {app}, Nincs mód a visszaállításra, ha nem készítesz biztonsági mentést. Csak egy percet vesz igénybe.",
|
||||
"YXA3AH": "Reakciók engedélyezése",
|
||||
"Z4BMCZ": "Párosító kifejezés megadása",
|
||||
"ZKORll": "Aktiválás",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Követem",
|
||||
"itPgxd": "Profil",
|
||||
"izWS4J": "Követés visszavonása",
|
||||
"j9xbzF": "Már kész a biztonsági mentés",
|
||||
"jA3OE/": "{n,plural,one {}=1{{n} sat} other{{n} sats}}",
|
||||
"jAmfGl": "{site_name}-előfizetése lejárt",
|
||||
"jHa/ko": "Tisztítsa meg a takarmányt",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Helytelen Pin-kód",
|
||||
"r3C4x/": "Szoftver",
|
||||
"r5srDR": "Add meg a pénztárcád jelszavát",
|
||||
"rMgF34": "Biztonsági mentés",
|
||||
"rT14Ow": "Csomópont hozzáadása",
|
||||
"rbrahO": "Bezárás",
|
||||
"rfuMjE": "(Alapértelmezett)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "Apa Anda yakin Anda ingin memindahkan catatan ini dari penanda buku?",
|
||||
"1R43+L": "Masukkan konfigurasi Nostr Wallet Connect",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "Terhubung ke: {node} 🎉",
|
||||
"1nYUGC": "{n} Mengikuti",
|
||||
"1o2BgB": "Periksa Tanda Tangan",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "Jumlah zap default Anda adalah {number} sats, nilai contoh dihitung dari ini.",
|
||||
"XrSk2j": "Tebus",
|
||||
"YDURw6": "URL layanan",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "Aktifkan reaksi",
|
||||
"Z4BMCZ": "Masukkan frasa pasangan",
|
||||
"ZKORll": "Aktifkan Sekarang",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Ikuti",
|
||||
"itPgxd": "Profil",
|
||||
"izWS4J": "Berhenti mengikuti",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n} sat} other{{n} sat}}",
|
||||
"jAmfGl": "Langganan {site_name} Anda telah kedaluwarsa",
|
||||
"jHa/ko": "Bersihkan feed Anda",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Pin salah",
|
||||
"r3C4x/": "Perangkat lunak",
|
||||
"r5srDR": "Masukkan kata sandi dompet",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "Tambahkan Relay",
|
||||
"rbrahO": "Tutup",
|
||||
"rfuMjE": "(Bawaan)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "Vuoi davvero rimuovere questa nota dai preferiti?",
|
||||
"1R43+L": "Inserire la configurazione di Nostr Wallet Connect",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "Connessione a: {node}🎉",
|
||||
"1nYUGC": "{n} seguiti",
|
||||
"1o2BgB": "Firma degli assegni",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "La quantità di zap predefinita è {number} sats, i valori di esempio sono calcolati da questo valore.",
|
||||
"XrSk2j": "Riscatto",
|
||||
"YDURw6": "URL del servizio",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "Abilita reazioni",
|
||||
"Z4BMCZ": "Inserisci la frase di accoppiamento",
|
||||
"ZKORll": "Attiva adesso",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Segui",
|
||||
"itPgxd": "Profilo",
|
||||
"izWS4J": "Smetti di seguire",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}",
|
||||
"jAmfGl": "L'abbonamento a {site_name} è scaduto",
|
||||
"jHa/ko": "Pulite il vostro feed",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Pin non corretto",
|
||||
"r3C4x/": "Software",
|
||||
"r5srDR": "Inserire la password del portafogli",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "Aggiungi Relays",
|
||||
"rbrahO": "Chiudere",
|
||||
"rfuMjE": "(Predefinito)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "本当にこの投稿をブックマークから削除しますか?",
|
||||
"1R43+L": "Nostr Wallet Connectの設定値を入力",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "{node}に接続しました🎉",
|
||||
"1nYUGC": "{n} フォロー",
|
||||
"1o2BgB": "小切手署名",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "デフォルトのザップ量は{number} satsで、例示された値はここから算出されます。",
|
||||
"XrSk2j": "交換",
|
||||
"YDURw6": "サービスURL",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "リアクションを有効にする",
|
||||
"Z4BMCZ": "ペアリングフレーズを入力",
|
||||
"ZKORll": "今すぐ有効化",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "フォロー",
|
||||
"itPgxd": "プロフィール",
|
||||
"izWS4J": "フォロー解除",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}",
|
||||
"jAmfGl": "{site_name} 、有効期限が切れています。",
|
||||
"jHa/ko": "フィードをきれいにする",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "ピンが正しくない",
|
||||
"r3C4x/": "ソフトウェア",
|
||||
"r5srDR": "ウォレットのパスワードを入力",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "リレーを追加する",
|
||||
"rbrahO": "閉じる",
|
||||
"rfuMjE": "(デフォルト)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "Weet u zeker dat u deze notitie uit bladwijzers wilt verwijderen?",
|
||||
"1R43+L": "Voer Nostr Wallet Connect configuratie in",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "Verbonden met: {node}🎉",
|
||||
"1nYUGC": "{n} Volgend",
|
||||
"1o2BgB": "Handtekeningen controleren",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "Uw standaard zap bedrag is {number} sats, voorbeeldwaarden worden hiermee berekend.",
|
||||
"XrSk2j": "Inwisselen",
|
||||
"YDURw6": "Service-URL",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "Reacties inschakelen",
|
||||
"Z4BMCZ": "Voer koppelingszin in",
|
||||
"ZKORll": "Activeer Nu",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Volgen",
|
||||
"itPgxd": "Profiel",
|
||||
"izWS4J": "Ontvolgen",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,one {}=1{{n} sat} other{{n} sats}}",
|
||||
"jAmfGl": "Uw {site_name} abonnement is verlopen",
|
||||
"jHa/ko": "Ruim je feed op",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Verkeerde pin",
|
||||
"r3C4x/": "Software",
|
||||
"r5srDR": "Voer wallet wachtwoord in",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "Relays toevoegen",
|
||||
"rbrahO": "Sluit",
|
||||
"rfuMjE": "(Standaard)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "Tem certeza que deseja remover esta nota dos favoritos?",
|
||||
"1R43+L": "Insira a configuração da Nostr Wallet Connect",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "Conectado em: {node} 🎉",
|
||||
"1nYUGC": "{n} Seguindo",
|
||||
"1o2BgB": "Assinaturas de cheques",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "Sua quantidade padrão de zap é de {number} sats, valores de exemplo são calculados a partir disso.",
|
||||
"XrSk2j": "Resgatar",
|
||||
"YDURw6": "URL do serviço",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "Habilitar reações",
|
||||
"Z4BMCZ": "Inserir frase de pareamento",
|
||||
"ZKORll": "Ativar agora",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Seguir",
|
||||
"itPgxd": "Perfil",
|
||||
"izWS4J": "Deixar de seguir",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}",
|
||||
"jAmfGl": "Sua assinatura do {site_name} expirou",
|
||||
"jHa/ko": "Limpe seu feed",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Pino incorreto",
|
||||
"r3C4x/": "Software",
|
||||
"r5srDR": "Digite a senha da carteira",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "Adicionar Relés",
|
||||
"rbrahO": "Fechar",
|
||||
"rfuMjE": "(Padrão)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "Вы уверены, что хотите удалить эту заметку из закладок?",
|
||||
"1R43+L": "Введите конфигурацию Nostr Wallet Connect",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "Подключен к: {node} 🎉",
|
||||
"1nYUGC": "{n} Подписчиков",
|
||||
"1o2BgB": "Контрольные подписи",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "По умолчанию величина zap равна {number} sats, примерные значения рассчитываются исходя из этого.",
|
||||
"XrSk2j": "Получить",
|
||||
"YDURw6": "URL-адрес сервиса",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "Включить реакции",
|
||||
"Z4BMCZ": "Введи фразу для сопряжения",
|
||||
"ZKORll": "Активировать",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Подписаться",
|
||||
"itPgxd": "Профиль",
|
||||
"izWS4J": "Отписаться",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}",
|
||||
"jAmfGl": "Срок действия вашей подписки {site_name} истек",
|
||||
"jHa/ko": "Очистка корма",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Неправильный вывод",
|
||||
"r3C4x/": "Программное обеспечение",
|
||||
"r5srDR": "Введите пароль кошелька",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "Добавить реле",
|
||||
"rbrahO": "Закрыть",
|
||||
"rfuMjE": "(по умолчанию)",
|
||||
|
@ -21,15 +21,16 @@
|
||||
"08zn6O": "Exportera nycklar",
|
||||
"0Azlrb": "Hantera",
|
||||
"0BUTMv": "Sök...",
|
||||
"0HFX0T": "Use Exact Location",
|
||||
"0HFX0T": "Använd exakt plats",
|
||||
"0jOEtS": "Ogiltig LNURL",
|
||||
"0mch2Y": "namnet har otillåtna tecken",
|
||||
"0siT4z": "Politik",
|
||||
"0uoY11": "Visa status",
|
||||
"0yO7wF": "{n} secs",
|
||||
"1H4Keq": "{n} users",
|
||||
"1H4Keq": "{n} användare",
|
||||
"1Mo59U": "Är du säker på att du vill ta bort den här anteckningen från bokmärken?",
|
||||
"1R43+L": "Skriv in Nostr Wallet Connect konfiguration",
|
||||
"1UWegE": "Se till att säkerhetskopiera dina nycklar!",
|
||||
"1c4YST": "Ansluten till: {node}🎉",
|
||||
"1nYUGC": "{n} Följer",
|
||||
"1o2BgB": "Kontrollera signaturer",
|
||||
@ -100,7 +101,7 @@
|
||||
"9wO4wJ": "Lightning-faktura",
|
||||
"ABAQyo": "Chattar",
|
||||
"ADmfQT": "Förälder",
|
||||
"AIgmDy": "Add up to 4 hashtags",
|
||||
"AIgmDy": "Lägg till upp till 4 hashtaggar",
|
||||
"AN0Z7Q": "Tystade ord",
|
||||
"ASRK0S": "Denna författare har tystats",
|
||||
"Ai8VHU": "Obegränsat antal anteckningar på Snort-relä",
|
||||
@ -124,7 +125,7 @@
|
||||
"CmZ9ls": "{n} tystad",
|
||||
"CsCUYo": "{n} sats",
|
||||
"Cu/K85": "Översatt från {lang}",
|
||||
"CzHZoc": "Social Graph",
|
||||
"CzHZoc": "Social graf",
|
||||
"D+KzKd": "Zappa automatiskt varje anteckning när den är laddad",
|
||||
"D3idYv": "Inställningar",
|
||||
"DBiVK1": "Cache",
|
||||
@ -276,7 +277,7 @@
|
||||
"Ub+AGc": "Logga in",
|
||||
"Up5U7K": "Blockera",
|
||||
"UrKTqQ": "Du har ett aktivt iris.to konto",
|
||||
"VL900k": "Recommended Relays",
|
||||
"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)",
|
||||
@ -293,11 +294,12 @@
|
||||
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
|
||||
"XECMfW": "Skicka användningsstatistik",
|
||||
"XICsE8": "Filvärdar",
|
||||
"XXm7jJ": "Trending Hashtags",
|
||||
"XXm7jJ": "Trendande hashtaggar",
|
||||
"XgWvGA": "Reaktioner",
|
||||
"Xopqkl": "Ditt förvalda zap-belopp är {number} sats, exempelvärden beräknas utifrån detta.",
|
||||
"XrSk2j": "Lös in",
|
||||
"YDURw6": "Service URL",
|
||||
"YR2I9M": "Inga nycklar, ingen {app}, Det finns inget sätt att återställa den om du inte säkerhetskopierar. Det tar bara en minut.",
|
||||
"YXA3AH": "Aktivera reaktioner",
|
||||
"Z4BMCZ": "Ange parningsfras",
|
||||
"ZKORll": "Aktivera nu",
|
||||
@ -331,9 +333,9 @@
|
||||
"d+6YsV": "Listor för att stänga av ljudet:",
|
||||
"d6CyG5": "Historik",
|
||||
"d7d0/x": "LN Adress",
|
||||
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
|
||||
"d8gpCh": "Försök att använda färre än 5 hashtags för att hålla dig till ämnet 🙏",
|
||||
"dOQCL8": "Visnings namn",
|
||||
"ddd3JX": "Popular Hashtags",
|
||||
"ddd3JX": "Populära Hashtaggar",
|
||||
"deEeEI": "Registrering",
|
||||
"dmsiLv": "En standard Zap Pool split av {n} har konfigurerats för {site} utvecklare, du kan inaktivera den när som helst i {link}",
|
||||
"e61Jf3": "Kommer snart",
|
||||
@ -344,7 +346,7 @@
|
||||
"eJj8HD": "Bli Verifierad",
|
||||
"eSzf2G": "En enda zap med {nIn} sats kommer att fördela {nOut} sats till zappoolen.",
|
||||
"eXT2QQ": "Gruppchatt",
|
||||
"egib+2": "{n,plural,=1{& {n} other} other{& {n} others}}",
|
||||
"egib+2": "{n,plural,=1{& {n} andra} other{& {n} andras}}",
|
||||
"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",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Följ",
|
||||
"itPgxd": "Profil",
|
||||
"izWS4J": "Sluta följ",
|
||||
"j9xbzF": "Redan säkerhetskopierad",
|
||||
"jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}",
|
||||
"jAmfGl": "Ditt {site_name} abonnemang har löpt ut",
|
||||
"jHa/ko": "Städa upp i ditt flöde",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Felaktig pin",
|
||||
"r3C4x/": "Mjukvara",
|
||||
"r5srDR": "Ange lösenord för plånboken",
|
||||
"rMgF34": "Säkerhetskopiera nu",
|
||||
"rT14Ow": "Lägg till reläer",
|
||||
"rbrahO": "Stäng",
|
||||
"rfuMjE": "(Standard)",
|
||||
@ -464,7 +468,7 @@
|
||||
"uSV4Ti": "Dela vidare måste bekräftas manuellt",
|
||||
"uc0din": "Skicka sats-delningar till",
|
||||
"ugyJnE": "Skicka anteckningar och andra saker",
|
||||
"un1nGw": "{n} notes",
|
||||
"un1nGw": "{n} anteckningar",
|
||||
"usAvMr": "Redigera profil",
|
||||
"v8lolG": "Starta chatt",
|
||||
"vB3oQ/": "Måste vara en kontaktlista eller en pubkey lista",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "Je, una uhakika unataka kuondoa dokezo hili kutoka kwa vialamisho?",
|
||||
"1R43+L": "Ingiza usanidi wa Nostr Wallet Connect",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "Imeunganishwa kwa: {node} 🎉",
|
||||
"1nYUGC": "{n} Unafuata",
|
||||
"1o2BgB": "Check Signatures",
|
||||
@ -298,6 +299,7 @@
|
||||
"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",
|
||||
"Z4BMCZ": "Weka maneno ya kuoanisha",
|
||||
"ZKORll": "Washa Sasa",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "Fuata",
|
||||
"itPgxd": "Wasifu",
|
||||
"izWS4J": "Acha kufuata",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,one {}=1{{n} sat} other{{n} sats}}",
|
||||
"jAmfGl": "Your {site_name} subscription is expired",
|
||||
"jHa/ko": "Clean up your feed",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Incorrect pin",
|
||||
"r3C4x/": "Programu",
|
||||
"r5srDR": "Ingiza nenosiri la pochi",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "Ongeza Relay",
|
||||
"rbrahO": "Close",
|
||||
"rfuMjE": "(Chaguo-msingi)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "இந்தக் குறிப்பைப் புக்மார்க்குகளிலிருந்து அகற்ற நிச்சயமாக விரும்புகிறீர்களா?",
|
||||
"1R43+L": "நாஸ்டர் பணப்பை இணைப்புக் கட்டமைப்பை உள்ளிடவும்",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "{node} உடன் இணைக்கப் பட்டது 🎉",
|
||||
"1nYUGC": "{n} பின்தொடரப் படுவோர்",
|
||||
"1o2BgB": "கையெழுத்துக்களை சரிபார்",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.",
|
||||
"XrSk2j": "Redeem",
|
||||
"YDURw6": "சேவை URL",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "எதிர்வினைகளை அனுமதி",
|
||||
"Z4BMCZ": "இணைத்தல் சொற்றொடரை உள்ளிடவும்",
|
||||
"ZKORll": "இப்போது செயல்படுத்துக",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "பின்தொடர்",
|
||||
"itPgxd": "சுயவிவரம்",
|
||||
"izWS4J": "பின்தொடர்வதை நிறுத்துக",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n} ஸாட்} other{{n} ஸாட்கள்}}",
|
||||
"jAmfGl": "Your {site_name} subscription is expired",
|
||||
"jHa/ko": "Clean up your feed",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Incorrect pin",
|
||||
"r3C4x/": "மென்பொருள்",
|
||||
"r5srDR": "பணப்பையின் கடவுச்சொல்லை உள்ளிடவும்",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "ரிலேகளைச் சேர்க்கவும்",
|
||||
"rbrahO": "Close",
|
||||
"rfuMjE": "(இயல்புநிலை)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "คุณแน่ใจหรือว่าต้องการลบโน้ตนี้ออกจากบุ๊คมาร์ค?",
|
||||
"1R43+L": "ใส่ Nostr Wallet Connect config",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "เชื่อมต่อกับ: {node} 🎉",
|
||||
"1nYUGC": "กำลังติดตาม {n}",
|
||||
"1o2BgB": "Check Signatures",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "จํานวน zap เริ่มต้นของคุณคือ {number} sats ค่าตัวอย่างคํานวณจากสิ่งนี้",
|
||||
"XrSk2j": "รับคืน",
|
||||
"YDURw6": "Service URL",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "เปิดใช้งาน reactions",
|
||||
"Z4BMCZ": "ใส่ pairing phrase",
|
||||
"ZKORll": "เปิดใช้งานทันที",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "ติดตาม",
|
||||
"itPgxd": "โปรไฟล์",
|
||||
"izWS4J": "เลิกติดตาม",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}",
|
||||
"jAmfGl": "Your {site_name} subscription is expired",
|
||||
"jHa/ko": "Clean up your feed",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "Incorrect pin",
|
||||
"r3C4x/": "ซอฟต์แวร์",
|
||||
"r5srDR": "โปรดใส่รหัส wallet",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "เพิ่มรีเลย์",
|
||||
"rbrahO": "ปิด",
|
||||
"rfuMjE": "(ค่าเริ่มต้น)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "是否确定要从收藏中移除此条笔记?",
|
||||
"1R43+L": "输入 Nostr Wallet Connect 配置",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "已连接到:{node}🎉",
|
||||
"1nYUGC": "{n} 个关注",
|
||||
"1o2BgB": "检查签名",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "你的默认打闪金额是 {number} 聪,示例值是以此计算的。",
|
||||
"XrSk2j": "兑现",
|
||||
"YDURw6": "服务网址",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "启用回应",
|
||||
"Z4BMCZ": "输入配对词句",
|
||||
"ZKORll": "立即激活",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "关注",
|
||||
"itPgxd": "个人档案",
|
||||
"izWS4J": "取消关注",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n}聪} other{{n}聪}}",
|
||||
"jAmfGl": "你的 {site_name} 订阅已过期",
|
||||
"jHa/ko": "清理你的订阅",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "PIN 码不正确",
|
||||
"r3C4x/": "软件",
|
||||
"r5srDR": "输入钱包密码",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "添加中继",
|
||||
"rbrahO": "关闭",
|
||||
"rfuMjE": "(默认)",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "是否確定要從收藏中移除此條筆記?",
|
||||
"1R43+L": "輸入 Nostr Wallet Connect 配置",
|
||||
"1UWegE": "Be sure to back up your keys!",
|
||||
"1c4YST": "已連接到:{node} 🎉",
|
||||
"1nYUGC": "{n} 個關注",
|
||||
"1o2BgB": "檢查簽名",
|
||||
@ -298,6 +299,7 @@
|
||||
"Xopqkl": "你的默認打閃金額是 {number} 聰,示例值是以此計算的。",
|
||||
"XrSk2j": "兌現",
|
||||
"YDURw6": "服務 URL",
|
||||
"YR2I9M": "No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute.",
|
||||
"YXA3AH": "启用回應",
|
||||
"Z4BMCZ": "輸入配對詞句",
|
||||
"ZKORll": "立即激活",
|
||||
@ -381,6 +383,7 @@
|
||||
"ieGrWo": "關注",
|
||||
"itPgxd": "個人檔案",
|
||||
"izWS4J": "取消關注",
|
||||
"j9xbzF": "Already backed up",
|
||||
"jA3OE/": "{n,plural,=1{{n} 聰} other{{n} 聰}}",
|
||||
"jAmfGl": "你的 {site_name} 訂閱已過期了",
|
||||
"jHa/ko": "清理你的訂閱",
|
||||
@ -442,6 +445,7 @@
|
||||
"qz9fty": "PIN 碼不正確",
|
||||
"r3C4x/": "軟件",
|
||||
"r5srDR": "輸入錢包密碼",
|
||||
"rMgF34": "Back up now",
|
||||
"rT14Ow": "添加中繼器",
|
||||
"rbrahO": "關閉",
|
||||
"rfuMjE": "(默認)",
|
||||
|
@ -3,7 +3,6 @@ import { VitePWA } from "vite-plugin-pwa";
|
||||
import { visualizer } from "rollup-plugin-visualizer";
|
||||
import { defineConfig } from "vite";
|
||||
import { vitePluginVersionMark } from "vite-plugin-version-mark";
|
||||
|
||||
import appConfig from "config";
|
||||
|
||||
export default defineConfig({
|
||||
@ -33,7 +32,7 @@ export default defineConfig({
|
||||
build: {
|
||||
outDir: "build",
|
||||
},
|
||||
base: "",
|
||||
clearScreen: false,
|
||||
publicDir: appConfig.get("publicDir"),
|
||||
resolve: {
|
||||
alias: {
|
||||
|
Reference in New Issue
Block a user