feat: translations

This commit is contained in:
Alejandro
2023-02-08 22:10:26 +01:00
committed by GitHub
parent 2b44d3b264
commit 2b29fb0897
54 changed files with 1505 additions and 315 deletions

View File

@ -1,4 +1,5 @@
import { useMemo } from "react";
import { FormattedMessage } from "react-intl";
import { useDispatch, useSelector } from "react-redux";
import { HexKey, RawEvent } from "Nostr";
@ -10,6 +11,8 @@ import { RootState } from "State/Store";
import NoteToSelf from "Element/NoteToSelf";
import useModeration from "Hooks/useModeration";
import messages from "./messages";
type DmChat = {
pubkey: HexKey;
unreadMessages: number;
@ -71,9 +74,11 @@ export default function MessagesPage() {
return (
<div className="main-content">
<div className="flex">
<h3 className="f-grow">Messages</h3>
<h3 className="f-grow">
<FormattedMessage {...messages.Messages} />
</h3>
<button type="button" onClick={() => markAllRead()}>
Mark All Read
<FormattedMessage {...messages.MarkAllRead} />
</button>
</div>
{chats

View File

@ -1,6 +1,6 @@
import "./ProfilePage.css";
import { useEffect, useMemo, useState } from "react";
import { useIntl, FormattedMessage } from "react-intl";
import { useSelector } from "react-redux";
import { useNavigate, useParams } from "react-router-dom";
@ -37,17 +37,18 @@ import Modal from "Element/Modal";
import { ProxyImg } from "Element/ProxyImg";
import useHorizontalScroll from "Hooks/useHorizontalScroll";
const ProfileTab = {
Notes: { text: "Notes", value: 0 },
Reactions: { text: "Reactions", value: 1 },
Followers: { text: "Followers", value: 2 },
Follows: { text: "Follows", value: 3 },
Zaps: { text: "Zaps", value: 4 },
Muted: { text: "Muted", value: 5 },
Blocked: { text: "Blocked", value: 6 },
};
import messages from "./messages";
const NOTES = 0;
const REACTIONS = 1;
const FOLLOWERS = 2;
const FOLLOWS = 3;
const ZAPS = 4;
const MUTED = 5;
const BLOCKED = 6;
export default function ProfilePage() {
const { formatMessage } = useIntl();
const params = useParams();
const navigate = useNavigate();
const id = useMemo(() => parseId(params.id!), [params]);
@ -61,7 +62,6 @@ export default function ProfilePage() {
const follows = useSelector<RootState, HexKey[]>((s) => s.login.follows);
const isMe = loginPubKey === id;
const [showLnQr, setShowLnQr] = useState<boolean>(false);
const [tab, setTab] = useState<Tab>(ProfileTab.Notes);
const [showProfileQr, setShowProfileQr] = useState<boolean>(false);
const aboutText = user?.about || "";
const about = Text({
@ -85,6 +85,16 @@ export default function ProfilePage() {
}, [zapFeed.store, id]);
const zapsTotal = zaps.reduce((acc, z) => acc + z.amount, 0);
const horizontalScroll = useHorizontalScroll();
const ProfileTab = {
Notes: { text: formatMessage(messages.Notes), value: NOTES },
Reactions: { text: formatMessage(messages.Reactions), value: REACTIONS },
Followers: { text: formatMessage(messages.Followers), value: FOLLOWERS },
Follows: { text: formatMessage(messages.Follows), value: FOLLOWS },
Zaps: { text: formatMessage(messages.Zaps), value: ZAPS },
Muted: { text: formatMessage(messages.Muted), value: MUTED },
Blocked: { text: formatMessage(messages.Blocked), value: BLOCKED },
};
const [tab, setTab] = useState<Tab>(ProfileTab.Notes);
useEffect(() => {
setTab(ProfileTab.Notes);
@ -149,8 +159,8 @@ export default function ProfilePage() {
}
function tabContent() {
switch (tab) {
case ProfileTab.Notes:
switch (tab.value) {
case NOTES:
return (
<Timeline
key={id}
@ -164,10 +174,15 @@ export default function ProfilePage() {
ignoreModeration={true}
/>
);
case ProfileTab.Zaps: {
case ZAPS: {
return (
<div className="main-content">
<h4 className="zaps-total">{formatShort(zapsTotal)} sats</h4>
<h4 className="zaps-total">
<FormattedMessage
{...messages.Sats}
values={{ n: formatShort(zapsTotal) }}
/>
</h4>
{zaps.map((z) => (
<ZapElement showZapped={false} zap={z} />
))}
@ -175,11 +190,16 @@ export default function ProfilePage() {
);
}
case ProfileTab.Follows: {
case FOLLOWS: {
if (isMe) {
return (
<div className="main-content">
<h4>Following {follows.length}</h4>
<h4>
<FormattedMessage
{...messages.Following}
values={{ n: follows.length }}
/>
</h4>
{follows.map((a) => (
<ProfilePreview
key={a}
@ -193,13 +213,13 @@ export default function ProfilePage() {
return <FollowsList pubkey={id} />;
}
}
case ProfileTab.Followers: {
case FOLLOWERS: {
return <FollowersList pubkey={id} />;
}
case ProfileTab.Muted: {
case MUTED: {
return isMe ? <BlockList variant="muted" /> : <MutedList pubkey={id} />;
}
case ProfileTab.Blocked: {
case BLOCKED: {
return isMe ? <BlockList variant="blocked" /> : null;
}
}
@ -233,7 +253,7 @@ export default function ProfilePage() {
<>
<LogoutButton />
<button type="button" onClick={() => navigate("/settings")}>
Settings
<FormattedMessage {...messages.Settings} />
</button>
</>
) : (

View File

@ -2,6 +2,7 @@ import "./Root.css";
import { useState } from "react";
import { useSelector } from "react-redux";
import { Link } from "react-router-dom";
import { FormattedMessage } from "react-intl";
import Tabs, { Tab } from "Element/Tabs";
import { RootState } from "State/Store";
@ -9,10 +10,21 @@ import Timeline from "Element/Timeline";
import { HexKey } from "Nostr";
import { TimelineSubject } from "Feed/TimelineFeed";
import messages from "./messages";
const RootTab: Record<string, Tab> = {
Posts: { text: "Posts", value: 0 },
PostsAndReplies: { text: "Conversations", value: 1 },
Global: { text: "Global", value: 2 },
Posts: {
text: <FormattedMessage {...messages.Posts} />,
value: 0,
},
PostsAndReplies: {
text: <FormattedMessage {...messages.Conversations} />,
value: 1,
},
Global: {
text: <FormattedMessage {...messages.Global} />,
value: 2,
},
};
export default function RootPage() {
@ -25,10 +37,16 @@ export default function RootPage() {
function followHints() {
if (follows?.length === 0 && pubKey && tab !== RootTab.Global) {
return (
<>
Hmm nothing here.. Checkout <Link to={"/new"}>New users page</Link> to
follow some recommended nostrich's!
</>
<FormattedMessage
{...messages.NoFollows}
values={{
newUsersPage: (
<Link to={"/new"}>
<FormattedMessage {...messages.NewUsers} />
</Link>
),
}}
/>
);
}
}

View File

@ -1,3 +1,4 @@
import { useIntl, FormattedMessage } from "react-intl";
import { useParams } from "react-router-dom";
import Timeline from "Element/Timeline";
import { useEffect, useState } from "react";
@ -6,8 +7,11 @@ import { router } from "index";
import { SearchRelays } from "Const";
import { System } from "Nostr/System";
import messages from "./messages";
const SearchPage = () => {
const params: any = useParams();
const { formatMessage } = useIntl();
const [search, setSearch] = useState<string>();
const [keyword, setKeyword] = useState<string | undefined>(params.keyword);
@ -39,12 +43,14 @@ const SearchPage = () => {
return (
<div className="main-content">
<h2>Search</h2>
<h2>
<FormattedMessage {...messages.Search} />
</h2>
<div className="flex mb10">
<input
type="text"
className="f-grow mr10"
placeholder="Search.."
placeholder={formatMessage(messages.SearchPlaceholder)}
value={search}
onChange={(e) => setSearch(e.target.value)}
/>

View File

@ -1,3 +1,4 @@
import { FormattedMessage } from "react-intl";
import { Outlet, RouteObject, useNavigate } from "react-router-dom";
import SettingsIndex from "Pages/settings/Index";
import Profile from "Pages/settings/Profile";
@ -5,13 +6,15 @@ import Relay from "Pages/settings/Relays";
import Preferences from "Pages/settings/Preferences";
import RelayInfo from "Pages/settings/RelayInfo";
import messages from "./messages";
export default function SettingsPage() {
const navigate = useNavigate();
return (
<div className="main-content">
<h2 onClick={() => navigate("/settings")} className="pointer">
Settings
<FormattedMessage {...messages.Settings} />
</h2>
<Outlet />
</div>

View File

@ -1,6 +1,10 @@
import { FormattedMessage } from "react-intl";
import { ApiHost } from "Const";
import Nip5Service from "Element/Nip5Service";
import messages from "./messages";
import "./Verification.css";
export default function VerificationPage() {
@ -10,42 +14,37 @@ export default function VerificationPage() {
service: `${ApiHost}/api/v1/n5sp`,
link: "https://snort.social/",
supportLink: "https://snort.social/help",
about: (
<>
Our very own NIP-05 verification service, help support the development
of this site and get a shiny special badge on our site!
</>
),
about: <FormattedMessage {...messages.SnortSocialNip} />,
},
{
name: "Nostr Plebs",
service: "https://nostrplebs.com/api/v1",
link: "https://nostrplebs.com/",
supportLink: "https://nostrplebs.com/manage",
about: (
<>
<p>
Nostr Plebs is one of the first NIP-05 providers in the space and
offers a good collection of domains at reasonable prices
</p>
</>
),
about: <FormattedMessage {...messages.NostrPlebsNip} />,
},
];
return (
<div className="main-content verification">
<h2>Get Verified</h2>
<h2>
<FormattedMessage {...messages.GetVerified} />
</h2>
<p>
NIP-05 is a DNS based verification spec which helps to validate you as a
real user.
<FormattedMessage {...messages.Nip05} />
</p>
<p>
<FormattedMessage {...messages.Nip05Pros} />
</p>
<p>Getting NIP-05 verified can help:</p>
<ul>
<li>Prevent fake accounts from imitating you</li>
<li>Make your profile easier to find and share</li>
<li>
Fund developers and platforms providing NIP-05 verification services
<FormattedMessage {...messages.AvoidImpersonators} />
</li>
<li>
<FormattedMessage {...messages.EasierToFind} />
</li>
<li>
<FormattedMessage {...messages.Funding} />
</li>
</ul>

View File

@ -3,6 +3,35 @@ import { addIdAndDefaultMessageToMessages } from "Util";
const messages = defineMessages({
Login: "Login",
Posts: "Posts",
Conversations: "Conversations",
Global: "Global",
NewUsers: "New users page",
NoFollows:
"Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!",
Notes: "Notes",
Reactions: "Reactions",
Followers: "Followers",
Follows: "Follows",
Zaps: "Zaps",
Muted: "Muted",
Blocked: "Blocked",
Sats: "{n} {n, plural, =1 {sat} other {sats}}",
Following: "Following {n}",
Settings: "Settings",
Search: "Search",
SearchPlaceholder: "Search...",
Messages: "Messages",
MarkAllRead: "Mark All Read",
GetVerified: "Get Verified",
Nip05: `NIP-05 is a DNS based verification spec which helps to validate you as a real user.`,
Nip05Pros: `Getting NIP-05 verified can help:`,
AvoidImpersonators: "Prevent fake accounts from imitating you",
EasierToFind: "Make your profile easier to find and share",
Funding:
"Fund developers and platforms providing NIP-05 verification services",
SnortSocialNip: `Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!`,
NostrPlebsNip: `Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices`,
});
export default addIdAndDefaultMessageToMessages(messages, 'Pages');
export default addIdAndDefaultMessageToMessages(messages, "Pages");

View File

@ -1,5 +1,5 @@
import "./Index.css";
import { FormattedMessage } from "react-intl";
import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import ArrowFront from "Icons/ArrowFront";
@ -8,9 +8,10 @@ import Profile from "Icons/Profile";
import Relay from "Icons/Relay";
import Heart from "Icons/Heart";
import Logout from "Icons/Logout";
import { logout } from "State/Login";
import messages from "./messages";
const SettingsIndex = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
@ -27,7 +28,9 @@ const SettingsIndex = () => {
<div className="mr10">
<Profile />
</div>
<span>Profile</span>
<span>
<FormattedMessage {...messages.Profile} />
</span>
<div className="align-end">
<ArrowFront />
</div>
@ -36,7 +39,7 @@ const SettingsIndex = () => {
<div className="mr10">
<Relay />
</div>
Relays
<FormattedMessage {...messages.Relays} />
<div className="align-end">
<ArrowFront />
</div>
@ -45,7 +48,7 @@ const SettingsIndex = () => {
<div className="mr10">
<Gear />
</div>
Preferences
<FormattedMessage {...messages.Preferences} />
<div className="align-end">
<ArrowFront />
</div>
@ -63,7 +66,7 @@ const SettingsIndex = () => {
<div className="mr10">
<Logout />
</div>
Log Out
<FormattedMessage {...messages.LogOut} />
<div className="align-end">
<ArrowFront />
</div>

View File

@ -1,7 +1,12 @@
import "./Preferences.css";
import { useDispatch, useSelector } from "react-redux";
import { FormattedMessage } from "react-intl";
import { DefaultImgProxy, setPreferences, UserPreferences } from "State/Login";
import { RootState } from "State/Store";
import "./Preferences.css";
import messages from "./messages";
const PreferencesPage = () => {
const dispatch = useDispatch();
@ -11,11 +16,15 @@ const PreferencesPage = () => {
return (
<div className="preferences">
<h3>Preferences</h3>
<h3>
<FormattedMessage {...messages.Preferences} />
</h3>
<div className="card flex">
<div className="flex f-col f-grow">
<div>Theme</div>
<div>
<FormattedMessage {...messages.Theme} />
</div>
</div>
<div>
<select
@ -29,18 +38,25 @@ const PreferencesPage = () => {
)
}
>
<option value="system">System (Default)</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="system">
<FormattedMessage {...messages.System} />
</option>
<option value="light">
<FormattedMessage {...messages.Light} />
</option>
<option value="dark">
<FormattedMessage {...messages.Dark} />
</option>
</select>
</div>
</div>
<div className="card flex">
<div className="flex f-col f-grow">
<div>Automatically load media</div>
<div>
<FormattedMessage {...messages.AutoloadMedia} />
</div>
<small>
Media in posts will automatically be shown for selected people,
otherwise only the link will show
<FormattedMessage {...messages.AutoloadMediaHelp} />
</small>
</div>
<div>
@ -55,17 +71,27 @@ const PreferencesPage = () => {
)
}
>
<option value="none">None</option>
<option value="follows-only">Follows only</option>
<option value="all">All</option>
<option value="none">
<FormattedMessage {...messages.None} />
</option>
<option value="follows-only">
<FormattedMessage {...messages.FollowsOnly} />
</option>
<option value="all">
<FormattedMessage {...messages.All} />
</option>
</select>
</div>
</div>
<div className="card flex f-col">
<div className="flex w-max">
<div className="flex f-col f-grow">
<div>Image proxy service</div>
<small>Use imgproxy to compress images</small>
<div>
<FormattedMessage {...messages.ImgProxy} />
</div>
<small>
<FormattedMessage {...messages.ImgProxyHelp} />
</small>
</div>
<div>
<input
@ -85,12 +111,14 @@ const PreferencesPage = () => {
{perf.imgProxyConfig && (
<div className="w-max mt10 form">
<div className="form-group">
<div>Service Url</div>
<div>
<FormattedMessage {...messages.ServiceUrl} />
</div>
<div className="w-max">
<input
type="text"
value={perf.imgProxyConfig?.url}
placeholder="Url.."
placeholder="URL.."
onChange={(e) =>
dispatch(
setPreferences({
@ -106,7 +134,9 @@ const PreferencesPage = () => {
</div>
</div>
<div className="form-group">
<div>Service Key</div>
<div>
<FormattedMessage {...messages.ServiceKey} />
</div>
<div className="w-max">
<input
type="password"
@ -127,7 +157,9 @@ const PreferencesPage = () => {
</div>
</div>
<div className="form-group">
<div>Service Salt</div>
<div>
<FormattedMessage {...messages.ServiceSalt} />
</div>
<div className="w-max">
<input
type="password"
@ -152,10 +184,11 @@ const PreferencesPage = () => {
</div>
<div className="card flex">
<div className="flex f-col f-grow">
<div>Enable reactions</div>
<div>
<FormattedMessage {...messages.EnableReactions} />
</div>
<small>
Reactions will be shown on every page, if disabled no reactions will
be shown
<FormattedMessage {...messages.EnableReactionsHelp} />
</small>
</div>
<div>
@ -172,8 +205,12 @@ const PreferencesPage = () => {
</div>
<div className="card flex">
<div className="flex f-col f-grow">
<div>Confirm reposts</div>
<small>Reposts need to be manually confirmed</small>
<div>
<FormattedMessage {...messages.ConfirmReposts} />
</div>
<small>
<FormattedMessage {...messages.ConfirmRepostsHelp} />
</small>
</div>
<div>
<input
@ -189,9 +226,11 @@ const PreferencesPage = () => {
</div>
<div className="card flex">
<div className="flex f-col f-grow">
<div>Automatically show latest notes</div>
<div>
<FormattedMessage {...messages.ShowLatest} />
</div>
<small>
Notes will stream in real time into global and posts tab
<FormattedMessage {...messages.ShowLatestHelp} />
</small>
</div>
<div>
@ -208,9 +247,11 @@ const PreferencesPage = () => {
</div>
<div className="card flex">
<div className="flex f-col f-grow">
<div>File upload service</div>
<div>
<FormattedMessage {...messages.FileUpload} />
</div>
<small>
Pick which upload service you want to upload attachments to
<FormattedMessage {...messages.FileUploadHelp} />
</small>
</div>
<div>
@ -225,7 +266,9 @@ const PreferencesPage = () => {
)
}
>
<option value="void.cat">void.cat (Default)</option>
<option value="void.cat">
void.cat <FormattedMessage {...messages.Default} />
</option>
<option value="nostr.build">nostr.build</option>
<option value="nostrimg.com">nostrimg.com</option>
</select>
@ -233,10 +276,11 @@ const PreferencesPage = () => {
</div>
<div className="card flex">
<div className="flex f-col f-grow">
<div>Debug Menus</div>
<div>
<FormattedMessage {...messages.DebugMenus} />
</div>
<small>
Shows "Copy ID" and "Copy Event JSON" in the context menu on each
message
<FormattedMessage {...messages.DebugMenusHelp} />
</small>
</div>
<div>

View File

@ -1,7 +1,7 @@
import "./Profile.css";
import Nostrich from "nostrich.webp";
import { useEffect, useState } from "react";
import { FormattedMessage } from "react-intl";
import { useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@ -15,6 +15,8 @@ import { RootState } from "State/Store";
import { HexKey } from "Nostr";
import useFileUpload from "Upload";
import messages from "./messages";
export interface ProfileSettingsProps {
avatar?: boolean;
banner?: boolean;
@ -112,7 +114,9 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
return (
<div className="editor form">
<div className="form-group">
<div>Name:</div>
<div>
<FormattedMessage {...messages.Name} />:
</div>
<div>
<input
type="text"
@ -122,7 +126,9 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
</div>
</div>
<div className="form-group">
<div>Display name:</div>
<div>
<FormattedMessage {...messages.DisplayName} />:
</div>
<div>
<input
type="text"
@ -132,7 +138,9 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
</div>
</div>
<div className="form-group form-col">
<div>About:</div>
<div>
<FormattedMessage {...messages.About} />:
</div>
<div className="w-max">
<textarea
className="w-max"
@ -142,7 +150,9 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
</div>
</div>
<div className="form-group">
<div>Website:</div>
<div>
<FormattedMessage {...messages.Website} />:
</div>
<div>
<input
type="text"
@ -152,7 +162,9 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
</div>
</div>
<div className="form-group">
<div>NIP-05:</div>
<div>
<FormattedMessage {...messages.Nip05} />:
</div>
<div>
<input
type="text"
@ -162,12 +174,14 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
/>
<button type="button" onClick={() => navigate("/verification")}>
<FontAwesomeIcon icon={faShop} />
&nbsp; Buy
&nbsp; <FormattedMessage {...messages.Buy} />
</button>
</div>
</div>
<div className="form-group">
<div>LN Address:</div>
<div>
<FormattedMessage {...messages.LnAddress} />:
</div>
<div>
<input
type="text"
@ -180,7 +194,7 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
<div></div>
<div>
<button type="button" onClick={() => saveProfile()}>
Save
<FormattedMessage {...messages.Save} />
</button>
</div>
</div>
@ -195,20 +209,24 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
<div className="flex f-center image-settings">
{(props.avatar ?? true) && (
<div>
<h2>Avatar</h2>
<h2>
<FormattedMessage {...messages.Avatar} />
</h2>
<div
style={{ backgroundImage: `url(${avatarPicture})` }}
className="avatar"
>
<div className="edit" onClick={() => setNewAvatar()}>
Edit
<FormattedMessage {...messages.Edit} />
</div>
</div>
</div>
)}
{(props.banner ?? true) && (
<div>
<h2>Header</h2>
<h2>
<FormattedMessage {...messages.Banner} />
</h2>
<div
style={{
backgroundImage: `url(${
@ -218,7 +236,7 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
className="banner"
>
<div className="edit" onClick={() => setNewBanner()}>
Edit
<FormattedMessage {...messages.Edit} />
</div>
</div>
</div>
@ -231,12 +249,16 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
return (
<div className="settings">
<h3>Edit Profile</h3>
<h3>
<FormattedMessage {...messages.EditProfile} />
</h3>
{settings()}
{privKey && (props.privateKey ?? true) && (
<div className="flex f-col bg-grey">
<div>
<h4>Your Private Key Is (do not share this with anyone):</h4>
<h4>
<FormattedMessage {...messages.PrivateKey} />:
</h4>
</div>
<div>
<Copy text={hexToBech32("nsec", privKey)} />

View File

@ -1,3 +1,4 @@
import { FormattedMessage } from "react-intl";
import ProfilePreview from "Element/ProfilePreview";
import useRelayState from "Feed/RelayState";
import { System } from "Nostr/System";
@ -6,6 +7,8 @@ import { useNavigate, useParams } from "react-router-dom";
import { removeRelay } from "State/Login";
import { parseId } from "Util";
import messages from "./messages";
const RelayInfo = () => {
const params = useParams();
const navigate = useNavigate();
@ -20,7 +23,7 @@ const RelayInfo = () => {
return (
<>
<h3 className="pointer" onClick={() => navigate("/settings/relays")}>
Relays
<FormattedMessage {...messages.Relays} />
</h3>
<div className="card">
<h3>{stats?.info?.name}</h3>
@ -28,13 +31,17 @@ const RelayInfo = () => {
{stats?.info?.pubkey && (
<>
<h4>Owner</h4>
<h4>
<FormattedMessage {...messages.Owner} />
</h4>
<ProfilePreview pubkey={parseId(stats.info.pubkey)} />
</>
)}
{stats?.info?.software && (
<div className="flex">
<h4 className="f-grow">Software</h4>
<h4 className="f-grow">
<FormattedMessage {...messages.Software} />
</h4>
<div className="flex f-col">
{stats.info.software.startsWith("http") ? (
<a href={stats.info.software} target="_blank" rel="noreferrer">
@ -52,7 +59,9 @@ const RelayInfo = () => {
)}
{stats?.info?.contact && (
<div className="flex">
<h4 className="f-grow">Contact</h4>
<h4 className="f-grow">
<FormattedMessage {...messages.Contact} />
</h4>
<a
href={`${
stats.info.contact.startsWith("mailto:") ? "" : "mailto:"
@ -66,7 +75,9 @@ const RelayInfo = () => {
)}
{stats?.info?.supported_nips && (
<>
<h4>Supports</h4>
<h4>
<FormattedMessage {...messages.Supports} />
</h4>
<div className="f-grow">
{stats.info.supported_nips.map((a) => (
<span
@ -93,7 +104,7 @@ const RelayInfo = () => {
navigate("/settings/relays");
}}
>
Remove
<FormattedMessage {...messages.Remove} />
</div>
</div>
</div>

View File

@ -1,4 +1,5 @@
import { useState } from "react";
import { FormattedMessage } from "react-intl";
import { useDispatch, useSelector } from "react-redux";
import Relay from "Element/Relay";
@ -7,6 +8,8 @@ import { RootState } from "State/Store";
import { RelaySettings } from "Nostr/Connection";
import { setRelays } from "State/Login";
import messages from "./messages";
const RelaySettingsPage = () => {
const dispatch = useDispatch();
const publisher = useEventPublisher();
@ -24,7 +27,9 @@ const RelaySettingsPage = () => {
function addRelay() {
return (
<>
<h4>Add Relays</h4>
<h4>
<FormattedMessage {...messages.AddRelays} />
</h4>
<div className="flex mb10">
<input
type="text"
@ -35,7 +40,7 @@ const RelaySettingsPage = () => {
/>
</div>
<button className="secondary mb10" onClick={() => addNewRelay()}>
Add
<FormattedMessage {...messages.Add} />
</button>
</>
);
@ -66,7 +71,7 @@ const RelaySettingsPage = () => {
<div className="flex mt10">
<div className="f-grow"></div>
<button type="button" onClick={() => saveRelays()}>
Save
<FormattedMessage {...messages.Save} />
</button>
</div>
{addRelay()}

View File

@ -0,0 +1,60 @@
import { defineMessages } from "react-intl";
import { addIdAndDefaultMessageToMessages } from "Util";
const messages = defineMessages({
Profile: "Profile",
Relays: "Relays",
Owner: "Owner",
Software: "Software",
Contact: "Contact",
Supports: "Supports",
Remove: "Remove",
Preferences: "Preferences",
Donate: "Donate",
LogOut: "Log Out",
Theme: "Theme",
System: "System (Default)",
Light: "Light",
Dark: "Dark",
AutoloadMedia: "Automatically load media",
AutoloadMediaHelp:
"Media in posts will automatically be shown for selected people, otherwise only the link will show",
None: "None",
FollowsOnly: "Follows only",
All: "All",
ImgProxy: "Image proxy service",
ImgProxyHelp: "Use imgproxy to compress images",
ServiceUrl: "Service URL",
ServiceKey: "Service Key",
ServiceSalt: "Service Salt",
EnableReactions: "Enable reactions",
EnableReactionsHelp:
"Reactions will be shown on every page, if disabled no reactions will be shown",
ConfirmReposts: "Confirm Reposts",
ConfirmRepostsHelp: "Reposts need to be manually confirmed",
ShowLatest: "Automatically show latest notes",
ShowLatestHelp: "Notes will stream in real time into global and posts tab",
FileUpload: "File upload service",
FileUploadHelp: "Pick which upload service you want to upload attachments to",
Default: "(Default)",
DebugMenus: "Debug Menus",
DebugMenusHelp: `Shows "Copy ID" and "Copy Event JSON" in the context menu on each message`,
EditProfile: "Edit Profile",
About: "About",
LnAddress: "LN Address",
Avatar: "Avatar",
Banner: "Banner",
Edit: "Edit",
PrivateKey: "Your Private Key Is (do not share this with anyone)",
Add: "Add",
AddRelays: "Add Relays",
Name: "Name",
Profile: "Profile",
Website: "Website",
Save: "Save",
DisplayName: "Display name",
Buy: "Buy",
Nip05: "NIP-05",
});
export default addIdAndDefaultMessageToMessages(messages, "Pages.settings");