feat: simple relay page
This commit is contained in:
parent
40ffebb0c2
commit
68b5cd60a6
@ -100,7 +100,7 @@ export function Note(props: NoteProps) {
|
||||
<div
|
||||
className={classNames(baseClassName, {
|
||||
active: highlight,
|
||||
"hover:bg-nearly-bg-color cursor-pointer": !opt?.isRoot,
|
||||
"hover:bg-nearly-bg-background cursor-pointer": !opt?.isRoot,
|
||||
})}
|
||||
onClick={e => goToEvent(e, ev)}
|
||||
ref={ref}>
|
||||
|
@ -1,11 +0,0 @@
|
||||
.relay {
|
||||
border-radius: 5px;
|
||||
display: grid;
|
||||
grid-template-columns: min-content auto;
|
||||
overflow: hidden;
|
||||
font-size: var(--font-size-small);
|
||||
}
|
||||
|
||||
.relay > div {
|
||||
padding: 5px;
|
||||
}
|
@ -1,16 +1,13 @@
|
||||
import "./Relay.css";
|
||||
|
||||
import { RelaySettings } from "@snort/system";
|
||||
import classNames from "classnames";
|
||||
import { useMemo } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { AsyncIcon } from "@/Components/Button/AsyncIcon";
|
||||
import useRelayState from "@/Feed/RelayState";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import { getRelayName } from "@/Utils";
|
||||
|
||||
import { RelayFavicon } from "./RelaysMetadata";
|
||||
import Icon from "../Icons/Icon";
|
||||
|
||||
export interface RelayProps {
|
||||
addr: string;
|
||||
@ -19,67 +16,68 @@ export interface RelayProps {
|
||||
export default function Relay(props: RelayProps) {
|
||||
const navigate = useNavigate();
|
||||
const { state } = useLogin(s => ({ v: s.state.version, state: s.state }));
|
||||
|
||||
const name = useMemo(() => getRelayName(props.addr), [props.addr]);
|
||||
const connection = useRelayState(props.addr);
|
||||
|
||||
const relaySettings = state.relays?.find(a => a.url === props.addr)?.settings;
|
||||
if (!relaySettings || !connection) return;
|
||||
const settings = state.relays?.find(a => a.url === props.addr)?.settings;
|
||||
if (!connection || !settings) return;
|
||||
|
||||
async function configure(o: RelaySettings) {
|
||||
await state.updateRelay(props.addr, o);
|
||||
}
|
||||
|
||||
const name = connection.info?.name ?? getRelayName(props.addr);
|
||||
return (
|
||||
<>
|
||||
<div className="relay bg-dark">
|
||||
<div className={classNames("flex items-center", connection.isOpen ? "bg-success" : "bg-error")}>
|
||||
<RelayFavicon url={props.addr} />
|
||||
</div>
|
||||
<div className="flex flex-col g8">
|
||||
<div>
|
||||
<b>{name}</b>
|
||||
</div>
|
||||
{!connection?.ephemeral && (
|
||||
<div className="flex g8">
|
||||
<AsyncIcon
|
||||
iconName="write"
|
||||
iconSize={16}
|
||||
className={classNames("button-icon-sm transparent", { active: relaySettings.write })}
|
||||
onClick={() =>
|
||||
configure({
|
||||
write: !relaySettings.write,
|
||||
read: relaySettings.read,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<AsyncIcon
|
||||
iconName="read"
|
||||
iconSize={16}
|
||||
className={classNames("button-icon-sm transparent", { active: relaySettings.read })}
|
||||
onClick={() =>
|
||||
configure({
|
||||
write: relaySettings.write,
|
||||
read: !relaySettings.read,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<AsyncIcon
|
||||
iconName="trash"
|
||||
iconSize={16}
|
||||
className="button-icon-sm transparent trash-icon"
|
||||
onClick={() => state.removeRelay(props.addr)}
|
||||
/>
|
||||
<AsyncIcon
|
||||
iconName="gear"
|
||||
iconSize={16}
|
||||
className="button-icon-sm transparent"
|
||||
onClick={() => navigate(connection?.id ?? "")}
|
||||
/>
|
||||
</div>
|
||||
<tr>
|
||||
<td className="text-ellipsis" title={props.addr}>
|
||||
{name.length > 20 ? <>{name.slice(0, 20)}...</> : name}
|
||||
</td>
|
||||
<td>
|
||||
<div className="flex gap-1 items-center">
|
||||
<div
|
||||
className={classNames("rounded-full w-4 h-4", {
|
||||
"bg-success": connection.isOpen,
|
||||
"bg-error": !connection.isOpen,
|
||||
})}></div>
|
||||
{connection.isOpen ? (
|
||||
<FormattedMessage defaultMessage="Connected" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="Offline" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</td>
|
||||
<td>
|
||||
<div className="flex gap-2 cursor-pointer select-none justify-center">
|
||||
<div
|
||||
className={settings.read ? "" : "text-gray"}
|
||||
onClick={() =>
|
||||
configure({
|
||||
read: !settings.read,
|
||||
write: settings.write,
|
||||
})
|
||||
}>
|
||||
<FormattedMessage defaultMessage="Read" />
|
||||
</div>
|
||||
<div
|
||||
className={settings.write ? "" : "text-gray"}
|
||||
onClick={() =>
|
||||
configure({
|
||||
read: settings.read,
|
||||
write: !settings.write,
|
||||
})
|
||||
}>
|
||||
<FormattedMessage defaultMessage="Write" />
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<Icon
|
||||
name="trash"
|
||||
className="text-gray-light cursor-pointer"
|
||||
onClick={() => {
|
||||
state.removeRelay(props.addr, true);
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
@ -134,9 +134,7 @@ export default function SearchBox() {
|
||||
className="absolute top-full mt-2 w-full border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-black shadow-lg rounded-lg z-10 overflow-hidden"
|
||||
ref={resultListRef}>
|
||||
<div
|
||||
className={`p-2 cursor-pointer ${
|
||||
activeIndex === 0 ? "bg-bg-secondary" : "bg-bg-color hover:bg-bg-secondary"
|
||||
}`}
|
||||
className={`p-2 cursor-pointer ${activeIndex === 0 ? "bg-secondary" : "bg-background hover:bg-secondary"}`}
|
||||
onMouseEnter={() => setActiveIndex(0)}
|
||||
onClick={() => navigate(`/search/${encodeURIComponent(search)}`, { state: { forceRefresh: true } })}>
|
||||
<FormattedMessage defaultMessage="Search notes" />: <b>{search}</b>
|
||||
@ -145,7 +143,7 @@ export default function SearchBox() {
|
||||
<div
|
||||
key={idx}
|
||||
className={`p-2 cursor-pointer ${
|
||||
activeIndex === idx + 1 ? "bg-bg-secondary" : "bg-bg-color hover:bg-bg-secondary"
|
||||
activeIndex === idx + 1 ? "bg-secondary" : "bg-background hover:bg-secondary"
|
||||
}`}
|
||||
onMouseEnter={() => setActiveIndex(idx + 1)}>
|
||||
<ProfileImage pubkey={result.pubkey} showProfileCard={false} />
|
||||
|
@ -98,7 +98,7 @@ export function SpotlightMedia(props: SpotlightMediaProps) {
|
||||
{mediaEl}
|
||||
<div className="absolute flex flex-row items-center gap-4 left-0 top-0 p-4">
|
||||
<span
|
||||
className="p-2 bg-bg-color rounded-full cursor-pointer opacity-80 hover:opacity-70"
|
||||
className="p-2 bg-background rounded-full cursor-pointer opacity-80 hover:opacity-70"
|
||||
onClick={props.onClose}>
|
||||
<Icon name="x-close" size={24} />
|
||||
</span>
|
||||
|
@ -43,7 +43,7 @@ export function SpotlightThreadModal(props: SpotlightThreadModalProps) {
|
||||
onPrev={props.onPrev}
|
||||
/>
|
||||
</div>
|
||||
<div className="hidden md:flex w-1/3 min-w-[400px] flex-shrink-0 overflow-y-auto bg-bg-color">
|
||||
<div className="hidden md:flex w-1/3 min-w-[400px] flex-shrink-0 overflow-y-auto bg-background">
|
||||
<Thread onBack={onBack} disableSpotlight={true} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -48,7 +48,7 @@ const Footer = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<footer className="md:hidden fixed bottom-0 z-10 w-full bg-base-200 pb-safe-area bg-bg-color">
|
||||
<footer className="md:hidden fixed bottom-0 z-10 w-full bg-base-200 pb-safe-area bg-background">
|
||||
<div className="flex">
|
||||
{MENU_ITEMS.map((item, index) => (
|
||||
<FooterNavItem key={index} item={item} readonly={readonly} />
|
||||
|
@ -81,7 +81,7 @@ export function Header() {
|
||||
<header
|
||||
className={classNames(
|
||||
{ "md:hidden": pageName === "messages" },
|
||||
"flex justify-between items-center self-stretch gap-6 sticky top-0 z-10 bg-bg-color md:bg-header md:bg-opacity-50 md:backdrop-blur-lg",
|
||||
"flex justify-between items-center self-stretch gap-6 sticky top-0 z-10 bg-background md:bg-header md:bg-opacity-50 md:backdrop-blur-lg",
|
||||
)}>
|
||||
<div
|
||||
onClick={handleBackButtonClick}
|
||||
|
@ -65,7 +65,7 @@ const MENU_ITEMS = [
|
||||
|
||||
const getNavLinkClass = (isActive: boolean, narrow: boolean) => {
|
||||
const baseClasses =
|
||||
"rounded-full p-3 flex flex-row items-center transition-colors duration-200 hover:bg-bg-secondary hover:no-underline";
|
||||
"rounded-full p-3 flex flex-row items-center transition-colors duration-200 hover:bg-secondary hover:no-underline";
|
||||
const activeClasses = "active font-bold";
|
||||
|
||||
return classNames(baseClasses, {
|
||||
@ -159,7 +159,7 @@ export default function NavSidebar({ narrow = false }: { narrow?: boolean }) {
|
||||
{publicKey && (
|
||||
<>
|
||||
<ProfileLink pubkey={publicKey} user={profile} className="hover:no-underline">
|
||||
<div className="mt-2 flex flex-row items-center justify-center font-bold text-md p-1 xl:px-4 xl:py-3 hover:bg-bg-secondary rounded-full cursor-pointer">
|
||||
<div className="mt-2 flex flex-row items-center justify-center font-bold text-md p-1 xl:px-4 xl:py-3 hover:bg-secondary rounded-full cursor-pointer">
|
||||
<Avatar pubkey={publicKey} user={profile} size={40} icons={readOnlyIcon} />
|
||||
{!narrow && <span className="hidden xl:inline ml-3">{profile?.name}</span>}
|
||||
</div>
|
||||
|
@ -1,18 +1,14 @@
|
||||
import { unwrap } from "@snort/shared";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { removeUndefined } from "@snort/shared";
|
||||
import { useState } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import AsyncButton from "@/Components/Button/AsyncButton";
|
||||
import Relay from "@/Components/Relay/Relay";
|
||||
import SnortApi, { RelayDistance } from "@/External/SnortApi";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import useRelays from "@/Hooks/useRelays";
|
||||
import { saveRelays } from "@/Pages/settings/saveRelays";
|
||||
import { getCountry, getRelayName, sanitizeRelayUrl } from "@/Utils";
|
||||
import { formatShort } from "@/Utils/Number";
|
||||
|
||||
import messages from "./messages";
|
||||
import { sanitizeRelayUrl } from "@/Utils";
|
||||
|
||||
const RelaySettingsPage = () => {
|
||||
const { publisher, system } = useEventPublisher();
|
||||
@ -20,155 +16,92 @@ const RelaySettingsPage = () => {
|
||||
const { readonly, state } = useLogin(s => ({ v: s.state.version, state: s.state, readonly: s.readonly }));
|
||||
const [newRelay, setNewRelay] = useState<string>();
|
||||
|
||||
const otherConnections = useMemo(() => {
|
||||
return [...system.pool].filter(([k]) => relays[k] === undefined).map(([, v]) => v);
|
||||
}, [system.pool, relays]);
|
||||
|
||||
const handleNewRelayChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const inputValue = event.target.value;
|
||||
setNewRelay(inputValue);
|
||||
};
|
||||
|
||||
async function addNewRelay() {
|
||||
const url = sanitizeRelayUrl(newRelay);
|
||||
if (url) {
|
||||
const urls = removeUndefined(
|
||||
(newRelay?.trim()?.split("\n") ?? []).map(a => {
|
||||
if (!a.startsWith("wss://")) {
|
||||
a = `wss://${a}`;
|
||||
}
|
||||
return sanitizeRelayUrl(a);
|
||||
}),
|
||||
);
|
||||
for (const url of urls) {
|
||||
await state.addRelay(url, { read: true, write: true }, false);
|
||||
|
||||
setNewRelay("");
|
||||
}
|
||||
setNewRelay("");
|
||||
}
|
||||
|
||||
function addRelay() {
|
||||
return (
|
||||
<div className="flex flex-col g8">
|
||||
<h4>
|
||||
<FormattedMessage {...messages.AddRelays} />
|
||||
</h4>
|
||||
<input
|
||||
type="text"
|
||||
className="grow"
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-xl">
|
||||
<FormattedMessage defaultMessage="Add Relays" />
|
||||
</div>
|
||||
<small>
|
||||
<FormattedMessage defaultMessage="You can add a single or multiple relays, one per line." />
|
||||
</small>
|
||||
<textarea
|
||||
placeholder="wss://my-relay.com"
|
||||
rows={4}
|
||||
value={newRelay}
|
||||
onChange={handleNewRelayChange}
|
||||
onChange={e => setNewRelay(e.target.value)}
|
||||
/>
|
||||
<AsyncButton className="secondary" onClick={() => addNewRelay()}>
|
||||
<FormattedMessage {...messages.Add} />
|
||||
</AsyncButton>
|
||||
<div>
|
||||
<AsyncButton className="secondary" onClick={() => addNewRelay()}>
|
||||
<FormattedMessage defaultMessage="Add" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function myRelays() {
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-xl font-medium">
|
||||
<FormattedMessage defaultMessage="My Relays" />
|
||||
</div>
|
||||
<small>
|
||||
<FormattedMessage defaultMessage="Relays are servers you connect to for sending and receiving events. Aim for 4-8 relays." />
|
||||
</small>
|
||||
<small>
|
||||
<FormattedMessage defaultMessage="The relay name shown is not the same as the full URL entered." />
|
||||
</small>
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr className="uppercase text-secondary">
|
||||
<th>
|
||||
<FormattedMessage defaultMessage="Relay" description="Relay name (URL)" />
|
||||
</th>
|
||||
<th>
|
||||
<FormattedMessage defaultMessage="Status" />
|
||||
</th>
|
||||
<th>
|
||||
<FormattedMessage defaultMessage="Permissions" />
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Object.keys(relays || {}).map(a => (
|
||||
<Relay addr={a} key={a} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div>
|
||||
<AsyncButton onClick={() => saveRelays(system, publisher, relays)} disabled={readonly}>
|
||||
<FormattedMessage defaultMessage="Save" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col g8">
|
||||
<h3>
|
||||
<FormattedMessage {...messages.Relays} />
|
||||
</h3>
|
||||
<div className="flex flex-col g8">
|
||||
{Object.keys(relays || {}).map(a => (
|
||||
<Relay addr={a} key={a} />
|
||||
))}
|
||||
</div>
|
||||
<AsyncButton type="button" onClick={() => saveRelays(system, publisher, relays.item)} disabled={readonly}>
|
||||
<FormattedMessage {...messages.Save} />
|
||||
</AsyncButton>
|
||||
<div className="flex flex-col gap-4">
|
||||
{myRelays()}
|
||||
{addRelay()}
|
||||
<CloseRelays />
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Other Connections" />
|
||||
</h3>
|
||||
<div className="flex flex-col g8">
|
||||
{otherConnections.map(a => (
|
||||
<Relay addr={a.address} key={a.id} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RelaySettingsPage;
|
||||
|
||||
export function CloseRelays() {
|
||||
const [relays, setRecommendedRelays] = useState<Array<RelayDistance>>();
|
||||
const country = getCountry();
|
||||
const [location, setLocation] = useState<{ lat: number; lon: number }>(country);
|
||||
const currentRelays = useRelays();
|
||||
const { state } = useLogin(s => ({ v: s.state.version, state: s.state }));
|
||||
const relayUrls = Object.keys(currentRelays);
|
||||
|
||||
async function loadRelays() {
|
||||
const api = new SnortApi();
|
||||
setRecommendedRelays(await api.closeRelays(location.lat, location.lon, 10));
|
||||
}
|
||||
|
||||
async function addNewRelay(newRelay: string) {
|
||||
const url = sanitizeRelayUrl(newRelay);
|
||||
if (url) {
|
||||
await state.addRelay(url, { read: true, write: true }, false);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadRelays().catch(console.error);
|
||||
}, [location]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Recommended Relays" />
|
||||
</h3>
|
||||
{"geolocation" in navigator && (
|
||||
<AsyncButton
|
||||
onClick={async () => {
|
||||
try {
|
||||
const pos = await new Promise<GeolocationPosition>((resolve, reject) => {
|
||||
navigator.geolocation.getCurrentPosition(resolve, reject);
|
||||
});
|
||||
setLocation({
|
||||
lat: pos.coords.latitude,
|
||||
lon: pos.coords.longitude,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}}>
|
||||
<FormattedMessage defaultMessage="Use Exact Location" />
|
||||
</AsyncButton>
|
||||
)}
|
||||
{relays
|
||||
?.filter(a => !relayUrls.includes(unwrap(sanitizeRelayUrl(a.url))) && !a.is_paid)
|
||||
.sort((a, b) => (a.distance > b.distance ? 1 : -1))
|
||||
.map(a => (
|
||||
<div key={a.url} className="bg-dark p br flex flex-col g8">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="bold">{getRelayName(a.url)}</div>
|
||||
<AsyncButton onClick={() => addNewRelay(a.url)}>
|
||||
<FormattedMessage defaultMessage="Add" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
<div className="flex flex-col g8">
|
||||
<span>{a.description}</span>
|
||||
<small>
|
||||
<FormattedMessage
|
||||
defaultMessage="{n} km - {location}"
|
||||
id="jTrbGf"
|
||||
values={{
|
||||
n: (a.distance / 1000).toFixed(0),
|
||||
location: a.city ? `${a.city}, ${a.country}` : a.country,
|
||||
}}
|
||||
/>
|
||||
</small>
|
||||
<small>
|
||||
<FormattedMessage
|
||||
defaultMessage="{n} users"
|
||||
id="1H4Keq"
|
||||
values={{
|
||||
n: formatShort(a.users),
|
||||
}}
|
||||
/>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ export async function saveRelays(
|
||||
) {
|
||||
if (publisher) {
|
||||
const ev = await publisher.relayList(relays);
|
||||
await system.BroadcastEvent(ev);
|
||||
await Promise.all(Blasters.map(a => system.WriteOnceToRelay(a, ev)));
|
||||
system.BroadcastEvent(ev);
|
||||
Promise.all(Blasters.map(a => system.WriteOnceToRelay(a, ev)));
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ export function PruneFollowList() {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="px-4 pb-5 pt-2 rounded-2xl bg-bg-secondary">
|
||||
<div className="px-4 pb-5 pt-2 rounded-2xl bg-secondary">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="New follow list length {length}"
|
||||
|
@ -71,9 +71,6 @@
|
||||
"0BUTMv": {
|
||||
"defaultMessage": "Search..."
|
||||
},
|
||||
"0HFX0T": {
|
||||
"defaultMessage": "Use Exact Location"
|
||||
},
|
||||
"0MndVW": {
|
||||
"defaultMessage": "Generic LNDHub wallet (BTCPayServer / Alby / LNBits)"
|
||||
},
|
||||
@ -96,9 +93,6 @@
|
||||
"defaultMessage": "Go",
|
||||
"description": "Button text after entering username in quick signup"
|
||||
},
|
||||
"1H4Keq": {
|
||||
"defaultMessage": "{n} users"
|
||||
},
|
||||
"1Mo59U": {
|
||||
"defaultMessage": "Are you sure you want to remove this note from bookmarks?"
|
||||
},
|
||||
@ -373,6 +367,9 @@
|
||||
"B6H7eJ": {
|
||||
"defaultMessage": "nsec, npub, nip-05, hex"
|
||||
},
|
||||
"B7wvUM": {
|
||||
"defaultMessage": "You can add a single or multiple relays, one per line."
|
||||
},
|
||||
"BGCM48": {
|
||||
"defaultMessage": "Write access to Snort relay, with 1 year of event retention"
|
||||
},
|
||||
@ -382,6 +379,10 @@
|
||||
"BjNwZW": {
|
||||
"defaultMessage": "Nostr address (nip05)"
|
||||
},
|
||||
"Blxcdx": {
|
||||
"defaultMessage": "Relay",
|
||||
"description": "Relay name (URL)"
|
||||
},
|
||||
"C1LjMx": {
|
||||
"defaultMessage": "Lightning Donation"
|
||||
},
|
||||
@ -617,6 +618,9 @@
|
||||
"IoQq+a": {
|
||||
"defaultMessage": "Click here to load anyway"
|
||||
},
|
||||
"IvjoDS": {
|
||||
"defaultMessage": "Connected"
|
||||
},
|
||||
"Ix8l+B": {
|
||||
"defaultMessage": "Trending Notes"
|
||||
},
|
||||
@ -662,6 +666,9 @@
|
||||
"JkLHGw": {
|
||||
"defaultMessage": "Website"
|
||||
},
|
||||
"JmcxzF": {
|
||||
"defaultMessage": "Relays are servers you connect to for sending and receiving events. Aim for 4-8 relays."
|
||||
},
|
||||
"JymXbw": {
|
||||
"defaultMessage": "Private Key"
|
||||
},
|
||||
@ -686,9 +693,6 @@
|
||||
"LBAnc7": {
|
||||
"defaultMessage": "View as user?"
|
||||
},
|
||||
"LF5kYT": {
|
||||
"defaultMessage": "Other Connections"
|
||||
},
|
||||
"LKw/ue": {
|
||||
"defaultMessage": "Check out the code {link}"
|
||||
},
|
||||
@ -867,6 +871,9 @@
|
||||
"Rs4kCE": {
|
||||
"defaultMessage": "Bookmark"
|
||||
},
|
||||
"SFuk1v": {
|
||||
"defaultMessage": "Permissions"
|
||||
},
|
||||
"SLZGPn": {
|
||||
"defaultMessage": "Enter a pin to encrypt your private key, you must enter this pin every time you open {site}."
|
||||
},
|
||||
@ -970,9 +977,6 @@
|
||||
"UxgyeY": {
|
||||
"defaultMessage": "Your referral code is {code}"
|
||||
},
|
||||
"VL900k": {
|
||||
"defaultMessage": "Recommended Relays"
|
||||
},
|
||||
"VOjC1i": {
|
||||
"defaultMessage": "Pick which upload service you want to upload attachments to"
|
||||
},
|
||||
@ -1391,9 +1395,6 @@
|
||||
"jMzO1S": {
|
||||
"defaultMessage": "Internal error: {msg}"
|
||||
},
|
||||
"jTrbGf": {
|
||||
"defaultMessage": "{n} km - {location}"
|
||||
},
|
||||
"jvo0vs": {
|
||||
"defaultMessage": "Save"
|
||||
},
|
||||
@ -1628,6 +1629,9 @@
|
||||
"sZQzjQ": {
|
||||
"defaultMessage": "Failed to parse zap split: {input}"
|
||||
},
|
||||
"saInmO": {
|
||||
"defaultMessage": "The relay name shown is not the same as the full URL entered."
|
||||
},
|
||||
"sfL/O+": {
|
||||
"defaultMessage": "Muted notes will not be shown"
|
||||
},
|
||||
@ -1649,6 +1653,9 @@
|
||||
"ttxS0b": {
|
||||
"defaultMessage": "Supporter Badge"
|
||||
},
|
||||
"tzMNF3": {
|
||||
"defaultMessage": "Status"
|
||||
},
|
||||
"u/vOPu": {
|
||||
"defaultMessage": "Paid"
|
||||
},
|
||||
@ -1730,6 +1737,9 @@
|
||||
"wtLjP6": {
|
||||
"defaultMessage": "Copy ID"
|
||||
},
|
||||
"x+3fl6": {
|
||||
"defaultMessage": "My Relays"
|
||||
},
|
||||
"x/Fx2P": {
|
||||
"defaultMessage": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!"
|
||||
},
|
||||
|
@ -23,7 +23,6 @@
|
||||
"08zn6O": "Export Keys",
|
||||
"0Azlrb": "Manage",
|
||||
"0BUTMv": "Search...",
|
||||
"0HFX0T": "Use Exact Location",
|
||||
"0MndVW": "Generic LNDHub wallet (BTCPayServer / Alby / LNBits)",
|
||||
"0jOEtS": "Invalid LNURL",
|
||||
"0mch2Y": "name has disallowed characters",
|
||||
@ -31,7 +30,6 @@
|
||||
"0uoY11": "Show Status",
|
||||
"0yO7wF": "{n} secs",
|
||||
"0zASjL": "Go",
|
||||
"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!",
|
||||
@ -123,9 +121,11 @@
|
||||
"B4C47Y": "name too short",
|
||||
"B6+XJy": "zapped",
|
||||
"B6H7eJ": "nsec, npub, nip-05, hex",
|
||||
"B7wvUM": "You can add a single or multiple relays, one per line.",
|
||||
"BGCM48": "Write access to Snort relay, with 1 year of event retention",
|
||||
"BWpuKl": "Update",
|
||||
"BjNwZW": "Nostr address (nip05)",
|
||||
"Blxcdx": "Relay",
|
||||
"C1LjMx": "Lightning Donation",
|
||||
"C7642/": "Quote Repost",
|
||||
"C81/uG": "Logout",
|
||||
@ -204,6 +204,7 @@
|
||||
"Ig9/a1": "Sent {n} sats to {name}",
|
||||
"IgsWFG": "Not followed by anyone you follow",
|
||||
"IoQq+a": "Click here to load anyway",
|
||||
"IvjoDS": "Connected",
|
||||
"Ix8l+B": "Trending Notes",
|
||||
"J+dIsA": "Subscriptions",
|
||||
"J1iLmb": "Notifications Not Allowed",
|
||||
@ -219,6 +220,7 @@
|
||||
"JeoS4y": "Repost",
|
||||
"JjGgXI": "Search users",
|
||||
"JkLHGw": "Website",
|
||||
"JmcxzF": "Relays are servers you connect to for sending and receiving events. Aim for 4-8 relays.",
|
||||
"JymXbw": "Private Key",
|
||||
"K3r6DQ": "Delete",
|
||||
"K7AkdL": "Show",
|
||||
@ -227,7 +229,6 @@
|
||||
"KahimY": "Unknown event kind: {kind}",
|
||||
"KtsyO0": "Enter Pin",
|
||||
"LBAnc7": "View as user?",
|
||||
"LF5kYT": "Other Connections",
|
||||
"LKw/ue": "Check out the code {link}",
|
||||
"LR1XjT": "Pin too short",
|
||||
"LXxsbk": "Anonymous",
|
||||
@ -287,6 +288,7 @@
|
||||
"RoOyAh": "Relays",
|
||||
"RrCui3": "Summary",
|
||||
"Rs4kCE": "Bookmark",
|
||||
"SFuk1v": "Permissions",
|
||||
"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}",
|
||||
"SOqbe9": "Update Lightning Address",
|
||||
@ -321,7 +323,6 @@
|
||||
"Ups2/p": "Your application is pending",
|
||||
"UrKTqQ": "You have an active iris.to account",
|
||||
"UxgyeY": "Your referral code is {code}",
|
||||
"VL900k": "Recommended Relays",
|
||||
"VOjC1i": "Pick which upload service you want to upload attachments to",
|
||||
"VR5eHw": "Public key (npub/nprofile)",
|
||||
"VcwrfF": "Yes please",
|
||||
@ -461,7 +462,6 @@
|
||||
"jAmfGl": "Your {site_name} subscription is expired",
|
||||
"jHa/ko": "Clean up your feed",
|
||||
"jMzO1S": "Internal error: {msg}",
|
||||
"jTrbGf": "{n} km - {location}",
|
||||
"jvo0vs": "Save",
|
||||
"jzgQ2z": "{n} Reactions",
|
||||
"k0kCJp": "Apply Now",
|
||||
@ -540,6 +540,7 @@
|
||||
"sKDn4e": "Show Badges",
|
||||
"sUNhQE": "user",
|
||||
"sZQzjQ": "Failed to parse zap split: {input}",
|
||||
"saInmO": "The relay name shown is not the same as the full URL entered.",
|
||||
"sfL/O+": "Muted notes will not be shown",
|
||||
"tOdNiY": "Dark",
|
||||
"th5lxp": "Send note to a subset of your write relays",
|
||||
@ -547,6 +548,7 @@
|
||||
"tj6kdX": "{sign} {amount} sats",
|
||||
"tjpYlr": "Relay Metrics",
|
||||
"ttxS0b": "Supporter Badge",
|
||||
"tzMNF3": "Status",
|
||||
"u/vOPu": "Paid",
|
||||
"u9NoC1": "Name must be less than {limit} characters",
|
||||
"uCk8r+": "Already have an account?",
|
||||
@ -574,6 +576,7 @@
|
||||
"wofVHy": "Moderation",
|
||||
"wqyN/i": "Find out more info about {service} at {link}",
|
||||
"wtLjP6": "Copy ID",
|
||||
"x+3fl6": "My Relays",
|
||||
"x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!",
|
||||
"x82IOl": "Mute",
|
||||
"xEjBS7": "For you",
|
||||
|
@ -8,16 +8,28 @@ module.exports = {
|
||||
"nearly-bg-color": "var(--nearly-bg-color)",
|
||||
"border-color": "var(--border-color)",
|
||||
highlight: "var(--highlight)",
|
||||
"bg-color": "var(--bg-color)",
|
||||
"bg-secondary": "var(--bg-secondary)",
|
||||
"nostr-blue": "var(--repost)",
|
||||
"nostr-green": "var(--success)",
|
||||
"nostr-orange": "var(--zap)",
|
||||
"nostr-red": "var(--heart)",
|
||||
"nostr-purple": "var(--highlight)",
|
||||
secondary: "var(--font-secondary-color)",
|
||||
warning: "var(--warning)",
|
||||
error: "var(--error)",
|
||||
"gray-light": "var(--gray-light)",
|
||||
"gray-medium": "var(--gray-medium)",
|
||||
gray: "var(--gray)",
|
||||
"gray-secondary": "var(--gray-secondary)",
|
||||
"gray-tertiary": "var(--gray-tertiary)",
|
||||
"gray-dark": "var(--gray-dark)",
|
||||
"gray-superdark": "var(--gray-superdark)",
|
||||
"gray-ultradark": "var(--gray-ultradark)",
|
||||
},
|
||||
backgroundColor: {
|
||||
background: "var(--bg-color)",
|
||||
secondary: "var(--bg-secondary)",
|
||||
},
|
||||
textColor: {
|
||||
secondary: "var(--font-secondary-color)",
|
||||
},
|
||||
spacing: {
|
||||
px: "1px",
|
||||
|
Loading…
x
Reference in New Issue
Block a user