refactor: flex styles / fixes / profile links
This commit is contained in:
parent
6479a18cb2
commit
faaeb6af4a
@ -10,6 +10,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.light .spinner-button {
|
.light .spinner-button {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useState, useMemo, ChangeEvent } from "react";
|
import { useState, useMemo, ChangeEvent } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey, TaggedNostrEvent } from "@snort/system";
|
import { HexKey, TaggedNostrEvent } from "@snort/system";
|
||||||
|
|
||||||
import Note from "Element/Event/Note";
|
import Note from "Element/Event/Note";
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { MetadataCache } from "@snort/system";
|
||||||
|
|
||||||
import { ChatParticipant } from "chat";
|
import { ChatParticipant } from "chat";
|
||||||
import NoteToSelf from "../User/NoteToSelf";
|
import NoteToSelf from "../User/NoteToSelf";
|
||||||
import ProfileImage from "../User/ProfileImage";
|
import ProfileImage from "../User/ProfileImage";
|
||||||
@ -6,7 +8,7 @@ import useLogin from "Hooks/useLogin";
|
|||||||
export function ChatParticipantProfile({ participant }: { participant: ChatParticipant }) {
|
export function ChatParticipantProfile({ participant }: { participant: ChatParticipant }) {
|
||||||
const { publicKey } = useLogin(s => ({ publicKey: s.publicKey }));
|
const { publicKey } = useLogin(s => ({ publicKey: s.publicKey }));
|
||||||
if (participant.id === publicKey) {
|
if (participant.id === publicKey) {
|
||||||
return <NoteToSelf className="f-grow" pubkey={participant.id} />;
|
return <NoteToSelf className="grow" />;
|
||||||
}
|
}
|
||||||
return <ProfileImage pubkey={participant.id} className="f-grow" profile={participant.profile} />;
|
return <ProfileImage pubkey={participant.id} className="grow" profile={participant.profile as MetadataCache} />;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import DM from "Element/Chat/DM";
|
|||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import WriteMessage from "Element/Chat/WriteMessage";
|
import WriteMessage from "Element/Chat/WriteMessage";
|
||||||
import { Chat, createEmptyChatObject, useChatSystem } from "chat";
|
import { Chat, createEmptyChatObject, useChatSystem } from "chat";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { ChatParticipantProfile } from "./ChatParticipant";
|
import { ChatParticipantProfile } from "./ChatParticipant";
|
||||||
|
|
||||||
export default function DmWindow({ id }: { id: string }) {
|
export default function DmWindow({ id }: { id: string }) {
|
||||||
@ -32,7 +32,7 @@ export default function DmWindow({ id }: { id: string }) {
|
|||||||
<div className="dm-window">
|
<div className="dm-window">
|
||||||
<div>{sender()}</div>
|
<div>{sender()}</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="flex f-col">{chat && <DmChatSelected chat={chat} />}</div>
|
<div className="flex flex-col">{chat && <DmChatSelected chat={chat} />}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<WriteMessage chat={chat} />
|
<WriteMessage chat={chat} />
|
||||||
|
@ -80,7 +80,7 @@ export default function WriteMessage({ chat }: { chat: Chat }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button className="circle flex f-center" onClick={() => attachFile()}>
|
<button className="circle flex items-center" onClick={() => attachFile()}>
|
||||||
{uploading ? <Spinner width={20} /> : <Icon name="attachment" size={20} />}
|
{uploading ? <Spinner width={20} /> : <Icon name="attachment" size={20} />}
|
||||||
</button>
|
</button>
|
||||||
<div className="w-max">
|
<div className="w-max">
|
||||||
@ -97,7 +97,7 @@ export default function WriteMessage({ chat }: { chat: Chat }) {
|
|||||||
/>
|
/>
|
||||||
{error && <b className="error">{error}</b>}
|
{error && <b className="error">{error}</b>}
|
||||||
</div>
|
</div>
|
||||||
<button className="circle flex f-center" onClick={() => sendMessage()}>
|
<button className="circle flex items-center" onClick={() => sendMessage()}>
|
||||||
{sending ? <Spinner width={20} /> : <Icon name="arrow-right" size={20} />}
|
{sending ? <Spinner width={20} /> : <Icon name="arrow-right" size={20} />}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
|
@ -4,8 +4,8 @@ import useLogin from "Hooks/useLogin";
|
|||||||
import "./Nav.css";
|
import "./Nav.css";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { profileLink } from "SnortUtils";
|
|
||||||
import { NoteCreatorButton } from "Element/Event/NoteCreatorButton";
|
import { NoteCreatorButton } from "Element/Event/NoteCreatorButton";
|
||||||
|
import { ProfileLink } from "Element/User/ProfileLink";
|
||||||
|
|
||||||
export function DeckNav() {
|
export function DeckNav() {
|
||||||
const { publicKey } = useLogin();
|
const { publicKey } = useLogin();
|
||||||
@ -14,24 +14,24 @@ export function DeckNav() {
|
|||||||
const unreadDms = 0;
|
const unreadDms = 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="deck flex-column f-space">
|
<nav className="deck flex flex-col justify-between">
|
||||||
<div className="flex-column f-center g24">
|
<div className="flex flex-col items-center g24">
|
||||||
<Link className="btn" to="/messages">
|
<Link className="btn" to="/messages">
|
||||||
<Icon name="mail" size={24} />
|
<Icon name="mail" size={24} />
|
||||||
{unreadDms > 0 && <span className="has-unread"></span>}
|
{unreadDms > 0 && <span className="has-unread"></span>}
|
||||||
</Link>
|
</Link>
|
||||||
<NoteCreatorButton />
|
<NoteCreatorButton />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column f-center g16">
|
<div className="flex flex-col items-center g16">
|
||||||
{/*<Link className="btn" to="/">
|
{/*<Link className="btn" to="/">
|
||||||
<Icon name="grid-01" size={24} />
|
<Icon name="grid-01" size={24} />
|
||||||
</Link>*/}
|
</Link>*/}
|
||||||
<Link className="btn" to="/settings">
|
<Link className="btn" to="/settings">
|
||||||
<Icon name="settings-02" size={24} />
|
<Icon name="settings-02" size={24} />
|
||||||
</Link>
|
</Link>
|
||||||
<Link to={profileLink(publicKey ?? "")}>
|
<ProfileLink pubkey={publicKey ?? ""} user={profile}>
|
||||||
<Avatar pubkey={publicKey ?? ""} user={profile} />
|
<Avatar pubkey={publicKey ?? ""} user={profile} />
|
||||||
</Link>
|
</ProfileLink>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
|
@ -52,9 +52,9 @@ export default function CashuNuts({ token }: { token: string }) {
|
|||||||
|
|
||||||
const amount = cashu.token[0].proofs.reduce((acc, v) => acc + v.amount, 0);
|
const amount = cashu.token[0].proofs.reduce((acc, v) => acc + v.amount, 0);
|
||||||
return (
|
return (
|
||||||
<div className="cashu flex f-space p24 br">
|
<div className="cashu flex justify-between p24 br">
|
||||||
<div className="flex-column g8 f-ellipsis">
|
<div className="flex flex-col g8 f-ellipsis">
|
||||||
<div className="flex f-center g16">
|
<div className="flex items-center g16">
|
||||||
<svg width="30" height="39" viewBox="0 0 30 39" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="30" height="39" viewBox="0 0 30 39" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g id="Group 47711">
|
<g id="Group 47711">
|
||||||
<path
|
<path
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
line-height: initial;
|
||||||
|
margin: 0.5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-preview-container:hover .link-preview-title > h1 {
|
.link-preview-container:hover .link-preview-title > h1 {
|
||||||
|
@ -81,7 +81,7 @@ const LinkPreview = ({ url }: { url: string }) => {
|
|||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
{!preview && <Spinner className="f-center" />}
|
{!preview && <Spinner className="items-center" />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import { Magnet } from "SnortUtils";
|
import { Magnet } from "SnortUtils";
|
||||||
|
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import { Link } from "react-router-dom";
|
import { NostrLink, NostrPrefix } from "@snort/system";
|
||||||
import { HexKey } from "@snort/system";
|
|
||||||
|
|
||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
import { profileLink } from "SnortUtils";
|
|
||||||
import DisplayName from "../User/DisplayName";
|
|
||||||
|
|
||||||
export default function Mention({ pubkey, relays }: { pubkey: HexKey; relays?: Array<string> | string }) {
|
import DisplayName from "Element/User/DisplayName";
|
||||||
const user = useUserProfile(pubkey);
|
import { ProfileLink } from "Element/User/ProfileLink";
|
||||||
|
|
||||||
|
export default function Mention({ link }: { link: NostrLink }) {
|
||||||
|
const profile = useUserProfile(link.id);
|
||||||
|
|
||||||
|
if (link.type !== NostrPrefix.Profile && link.type !== NostrPrefix.PublicKey) return;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={profileLink(pubkey, relays)} onClick={e => e.stopPropagation()}>
|
<ProfileLink pubkey={link.id} user={profile} onClick={e => e.stopPropagation()}>
|
||||||
@<DisplayName user={user} pubkey={pubkey} />
|
@<DisplayName user={profile} pubkey={link.id} />
|
||||||
</Link>
|
</ProfileLink>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ export default function NostrLink({ link, depth }: { link: string; depth?: numbe
|
|||||||
const nav = tryParseNostrLink(link);
|
const nav = tryParseNostrLink(link);
|
||||||
|
|
||||||
if (nav?.type === NostrPrefix.PublicKey || nav?.type === NostrPrefix.Profile) {
|
if (nav?.type === NostrPrefix.PublicKey || nav?.type === NostrPrefix.Profile) {
|
||||||
return <Mention pubkey={nav.id} relays={nav.relays} />;
|
return <Mention link={nav} />;
|
||||||
} else if (nav?.type === NostrPrefix.Note || nav?.type === NostrPrefix.Event || nav?.type === NostrPrefix.Address) {
|
} else if (nav?.type === NostrPrefix.Note || nav?.type === NostrPrefix.Event || nav?.type === NostrPrefix.Address) {
|
||||||
if ((depth ?? 0) > 0) {
|
if ((depth ?? 0) > 0) {
|
||||||
const evLink = nav.encode();
|
const evLink = nav.encode();
|
||||||
|
@ -4,7 +4,7 @@ import { NostrEvent, NostrLink } from "@snort/system";
|
|||||||
|
|
||||||
import { ProxyImg } from "Element/ProxyImg";
|
import { ProxyImg } from "Element/ProxyImg";
|
||||||
import ProfileImage from "Element/User/ProfileImage";
|
import ProfileImage from "Element/User/ProfileImage";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
export default function ZapstrEmbed({ ev }: { ev: NostrEvent }) {
|
export default function ZapstrEmbed({ ev }: { ev: NostrEvent }) {
|
||||||
const media = ev.tags.find(a => a[0] === "media");
|
const media = ev.tags.find(a => a[0] === "media");
|
||||||
@ -17,7 +17,7 @@ export default function ZapstrEmbed({ ev }: { ev: NostrEvent }) {
|
|||||||
<>
|
<>
|
||||||
<div className="flex zapstr mb10 card">
|
<div className="flex zapstr mb10 card">
|
||||||
<ProxyImg src={cover?.[1] ?? ""} size={100} />
|
<ProxyImg src={cover?.[1] ?? ""} size={100} />
|
||||||
<div className="flex f-col">
|
<div className="flex flex-col">
|
||||||
<div>
|
<div>
|
||||||
<h3>{subject?.[1] ?? ""}</h3>
|
<h3>{subject?.[1] ?? ""}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,9 +3,9 @@ import { UploadProgress } from "Upload";
|
|||||||
|
|
||||||
export default function FileUploadProgress({ progress }: { progress: Array<UploadProgress> }) {
|
export default function FileUploadProgress({ progress }: { progress: Array<UploadProgress> }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
{progress.map(p => (
|
{progress.map(p => (
|
||||||
<div className="flex-column g2" id={p.id}>
|
<div className="flex flex-col g2" id={p.id}>
|
||||||
{p.file.name}
|
{p.file.name}
|
||||||
<Progress value={p.progress} status={p.stage} />
|
<Progress value={p.progress} status={p.stage} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -121,7 +121,7 @@ export function LongFormText(props: LongFormTextProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="long-form-note flex-column g16 p pointer"
|
className="long-form-note flex flex-col g16 p pointer"
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
props.onClick?.();
|
props.onClick?.();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "./Markdown.css";
|
import "./Markdown.css";
|
||||||
|
|
||||||
import { ReactNode, forwardRef, useMemo } from "react";
|
import { ReactNode, forwardRef, useMemo } from "react";
|
||||||
import { transformText } from "@snort/system";
|
import { parseNostrLink, transformText } from "@snort/system";
|
||||||
import { marked, Token } from "marked";
|
import { marked, Token } from "marked";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import markedFootnote, { Footnotes, Footnote, FootnoteRef } from "marked-footnote";
|
import markedFootnote, { Footnotes, Footnote, FootnoteRef } from "marked-footnote";
|
||||||
@ -106,7 +106,7 @@ function renderToken(t: Token | Footnotes | Footnote | FootnoteRef): ReactNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "mention": {
|
case "mention": {
|
||||||
return <Mention pubkey={v.content} />;
|
return <Mention link={parseNostrLink(v.content)} />;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
return v.content;
|
return v.content;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { NostrEvent, NostrLink } from "@snort/system";
|
import { NostrEvent, NostrLink } from "@snort/system";
|
||||||
|
|
||||||
import { findTag } from "SnortUtils";
|
import { findTag } from "SnortUtils";
|
||||||
|
@ -71,7 +71,7 @@ export function NoteBroadcaster({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-column g16">
|
<div className="flex flex-col g16">
|
||||||
<h3>
|
<h3>
|
||||||
<FormattedMessage defaultMessage="Sending notes and other stuff" />
|
<FormattedMessage defaultMessage="Sending notes and other stuff" />
|
||||||
</h3>
|
</h3>
|
||||||
@ -81,7 +81,7 @@ export function NoteBroadcaster({
|
|||||||
.map(r => (
|
.map(r => (
|
||||||
<div className="flex-row g16">
|
<div className="flex-row g16">
|
||||||
<Icon name={r.ok ? "check" : "x"} className={r.ok ? "success" : "error"} size={24} />
|
<Icon name={r.ok ? "check" : "x"} className={r.ok ? "success" : "error"} size={24} />
|
||||||
<div className="flex-column f-grow g4">
|
<div className="flex flex-col grow g4">
|
||||||
<b>{getRelayName(r.relay)}</b>
|
<b>{getRelayName(r.relay)}</b>
|
||||||
{r.message && <small>{r.message}</small>}
|
{r.message && <small>{r.message}</small>}
|
||||||
</div>
|
</div>
|
||||||
@ -89,7 +89,7 @@ export function NoteBroadcaster({
|
|||||||
<div className="flex g8">
|
<div className="flex g8">
|
||||||
<AsyncButton
|
<AsyncButton
|
||||||
onClick={() => retryPublish(r)}
|
onClick={() => retryPublish(r)}
|
||||||
className="p4 br-compact flex f-center secondary"
|
className="p4 br-compact flex items-center secondary"
|
||||||
title={formatMessage({
|
title={formatMessage({
|
||||||
defaultMessage: "Retry publishing",
|
defaultMessage: "Retry publishing",
|
||||||
})}>
|
})}>
|
||||||
@ -97,7 +97,7 @@ export function NoteBroadcaster({
|
|||||||
</AsyncButton>
|
</AsyncButton>
|
||||||
<AsyncButton
|
<AsyncButton
|
||||||
onClick={() => removeRelayFromResult(r)}
|
onClick={() => removeRelayFromResult(r)}
|
||||||
className="p4 br-compact flex f-center secondary"
|
className="p4 br-compact flex items-center secondary"
|
||||||
title={formatMessage({
|
title={formatMessage({
|
||||||
defaultMessage: "Remove from my relays",
|
defaultMessage: "Remove from my relays",
|
||||||
})}>
|
})}>
|
||||||
|
@ -291,11 +291,11 @@ export function NoteCreator() {
|
|||||||
|
|
||||||
function renderRelayCustomisation() {
|
function renderRelayCustomisation() {
|
||||||
return (
|
return (
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
{Object.keys(relays.item || {})
|
{Object.keys(relays.item || {})
|
||||||
.filter(el => relays.item[el].write)
|
.filter(el => relays.item[el].write)
|
||||||
.map((r, i, a) => (
|
.map((r, i, a) => (
|
||||||
<div className="p flex f-space note-creator-relay">
|
<div className="p flex justify-between note-creator-relay">
|
||||||
<div>{r}</div>
|
<div>{r}</div>
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
@ -353,15 +353,15 @@ export function NoteCreator() {
|
|||||||
</p>
|
</p>
|
||||||
{renderRelayCustomisation()}
|
{renderRelayCustomisation()}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Zap Splits" />
|
<FormattedMessage defaultMessage="Zap Splits" />
|
||||||
</h4>
|
</h4>
|
||||||
<FormattedMessage defaultMessage="Zaps on this note will be split to the following users." />
|
<FormattedMessage defaultMessage="Zaps on this note will be split to the following users." />
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
{[...(note.zapSplits ?? [])].map((v, i, arr) => (
|
{[...(note.zapSplits ?? [])].map((v, i, arr) => (
|
||||||
<div className="flex f-center g8">
|
<div className="flex items-center g8">
|
||||||
<div className="flex-column f-4 g4">
|
<div className="flex flex-col f-4 g4">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Recipient" />
|
<FormattedMessage defaultMessage="Recipient" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -376,7 +376,7 @@ export function NoteCreator() {
|
|||||||
placeholder={formatMessage({ defaultMessage: "npub / nprofile / nostr address" })}
|
placeholder={formatMessage({ defaultMessage: "npub / nprofile / nostr address" })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column f-1 g4">
|
<div className="flex flex-col f-1 g4">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Weight" />
|
<FormattedMessage defaultMessage="Weight" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -394,7 +394,7 @@ export function NoteCreator() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column f-shrink g4">
|
<div className="flex flex-col s g4">
|
||||||
<div> </div>
|
<div> </div>
|
||||||
<Icon
|
<Icon
|
||||||
name="close"
|
name="close"
|
||||||
@ -415,7 +415,7 @@ export function NoteCreator() {
|
|||||||
<FormattedMessage defaultMessage="Not all clients support this, you may still receive some zaps as if zap splits was not configured" />
|
<FormattedMessage defaultMessage="Not all clients support this, you may still receive some zaps as if zap splits was not configured" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Sensitive Content" />
|
<FormattedMessage defaultMessage="Sensitive Content" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -441,7 +441,7 @@ export function NoteCreator() {
|
|||||||
|
|
||||||
function noteCreatorFooter() {
|
function noteCreatorFooter() {
|
||||||
return (
|
return (
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<div className="flex g8">
|
<div className="flex g8">
|
||||||
<ProfileImage
|
<ProfileImage
|
||||||
pubkey={login.publicKey ?? ""}
|
pubkey={login.publicKey ?? ""}
|
||||||
|
@ -6,25 +6,26 @@ import classNames from "classnames";
|
|||||||
import { EventExt, EventKind, HexKey, Lists, NostrLink, NostrPrefix, TaggedNostrEvent } from "@snort/system";
|
import { EventExt, EventKind, HexKey, Lists, NostrLink, NostrPrefix, TaggedNostrEvent } from "@snort/system";
|
||||||
import { useEventReactions } from "@snort/system-react";
|
import { useEventReactions } from "@snort/system-react";
|
||||||
|
|
||||||
import { findTag, hexToBech32, profileLink } from "SnortUtils";
|
import { findTag, hexToBech32 } from "SnortUtils";
|
||||||
import useModeration from "Hooks/useModeration";
|
import useModeration from "Hooks/useModeration";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import useEventPublisher from "Hooks/useEventPublisher";
|
import useEventPublisher from "Hooks/useEventPublisher";
|
||||||
import { NoteContextMenu, NoteTranslation } from "./NoteContextMenu";
|
import { NoteContextMenu, NoteTranslation } from "./NoteContextMenu";
|
||||||
import { UserCache } from "../../Cache";
|
import { UserCache } from "Cache";
|
||||||
import messages from "../messages";
|
import messages from "../messages";
|
||||||
import { setBookmarked, setPinned } from "../../Login";
|
import { setBookmarked, setPinned } from "Login";
|
||||||
import Text from "../Text";
|
import Text from "../Text";
|
||||||
import Reveal from "./Reveal";
|
import Reveal from "./Reveal";
|
||||||
import Poll from "./Poll";
|
import Poll from "./Poll";
|
||||||
import ProfileImage from "../User/ProfileImage";
|
import ProfileImage from "../User/ProfileImage";
|
||||||
import Icon from "../../Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import NoteTime from "./NoteTime";
|
import NoteTime from "./NoteTime";
|
||||||
import NoteFooter from "./NoteFooter";
|
import NoteFooter from "./NoteFooter";
|
||||||
import Reactions from "./Reactions";
|
import Reactions from "./Reactions";
|
||||||
import HiddenNote from "./HiddenNote";
|
import HiddenNote from "./HiddenNote";
|
||||||
import { NoteProps } from "./Note";
|
import { NoteProps } from "./Note";
|
||||||
import { chainKey } from "Hooks/useThreadContext";
|
import { chainKey } from "Hooks/useThreadContext";
|
||||||
|
import { ProfileLink } from "Element/User/ProfileLink";
|
||||||
|
|
||||||
export function NoteInner(props: NoteProps) {
|
export function NoteInner(props: NoteProps) {
|
||||||
const { data: ev, related, highlight, options: opt, ignoreModeration = false, className } = props;
|
const { data: ev, related, highlight, options: opt, ignoreModeration = false, className } = props;
|
||||||
@ -182,7 +183,11 @@ export function NoteInner(props: NoteProps) {
|
|||||||
mentions.push({
|
mentions.push({
|
||||||
pk,
|
pk,
|
||||||
name: u?.name ?? shortNpub,
|
name: u?.name ?? shortNpub,
|
||||||
link: <Link to={profileLink(pk)}>{u?.name ? `@${u.name}` : shortNpub}</Link>,
|
link: (
|
||||||
|
<ProfileLink pubkey={pk} user={u}>
|
||||||
|
{u?.name ? `@${u.name}` : shortNpub}
|
||||||
|
</ProfileLink>
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
mentions.sort(a => (a.name.startsWith(NostrPrefix.PublicKey) ? 1 : -1));
|
mentions.sort(a => (a.name.startsWith(NostrPrefix.PublicKey) ? 1 : -1));
|
||||||
|
@ -7,7 +7,7 @@ import Note from "Element/Event/Note";
|
|||||||
import { getDisplayName } from "Element/User/DisplayName";
|
import { getDisplayName } from "Element/User/DisplayName";
|
||||||
import { eventLink, hexToBech32 } from "SnortUtils";
|
import { eventLink, hexToBech32 } from "SnortUtils";
|
||||||
import useModeration from "Hooks/useModeration";
|
import useModeration from "Hooks/useModeration";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
import { useInView } from "react-intersection-observer";
|
import { useInView } from "react-intersection-observer";
|
||||||
|
@ -108,7 +108,7 @@ export default function Poll(props: PollProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex f-space p">
|
<div className="flex justify-between p">
|
||||||
<small>
|
<small>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="You are voting with {amount} sats"
|
defaultMessage="You are voting with {amount} sats"
|
||||||
@ -147,7 +147,7 @@ export default function Poll(props: PollProps) {
|
|||||||
const weight = totalVotes === 0 ? 0 : total / totalVotes;
|
const weight = totalVotes === 0 ? 0 : total / totalVotes;
|
||||||
return (
|
return (
|
||||||
<div key={a[1]} className="flex" onClick={e => zapVote(e, opt)}>
|
<div key={a[1]} className="flex" onClick={e => zapVote(e, opt)}>
|
||||||
<div className="f-grow">{opt === voting ? <Spinner /> : <>{desc}</>}</div>
|
<div className="grow">{opt === voting ? <Spinner /> : <>{desc}</>}</div>
|
||||||
{showResults && (
|
{showResults && (
|
||||||
<>
|
<>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import { FileExtensionRegex } from "Const";
|
import { FileExtensionRegex } from "Const";
|
||||||
import Reveal from "Element/Event/Reveal";
|
import Reveal from "Element/Event/Reveal";
|
||||||
|
@ -17,7 +17,7 @@ const Zap = ({ zap, showZapped = true }: { zap: ParsedZap; showZapped?: boolean
|
|||||||
|
|
||||||
return valid && sender ? (
|
return valid && sender ? (
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<ProfileImage pubkey={sender} />
|
<ProfileImage pubkey={sender} />
|
||||||
{receiver !== pubKey && showZapped && <ProfileImage pubkey={unwrap(receiver)} />}
|
{receiver !== pubKey && showZapped && <ProfileImage pubkey={unwrap(receiver)} />}
|
||||||
<h3>
|
<h3>
|
||||||
|
@ -19,7 +19,7 @@ export function ZapGoal({ ev }: { ev: NostrEvent }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="zap-goal card">
|
<div className="zap-goal card">
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<h2>{ev.content}</h2>
|
<h2>{ev.content}</h2>
|
||||||
<div className="zap-button flex" onClick={() => setZap(true)}>
|
<div className="zap-button flex" onClick={() => setZap(true)}>
|
||||||
<Icon name="zap" size={15} />
|
<Icon name="zap" size={15} />
|
||||||
@ -27,7 +27,7 @@ export function ZapGoal({ ev }: { ev: NostrEvent }) {
|
|||||||
<SendSats targets={Zapper.fromEvent(ev)} show={zap} onClose={() => setZap(false)} />
|
<SendSats targets={Zapper.fromEvent(ev)} show={zap} onClose={() => setZap(false)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<div>
|
<div>
|
||||||
<FormattedNumber value={progress} style="percent" />
|
<FormattedNumber value={progress} style="percent" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useInView } from "react-intersection-observer";
|
import { useInView } from "react-intersection-observer";
|
||||||
|
|
||||||
import messages from "../messages";
|
import messages from "../messages";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import "./Timeline.css";
|
import "./Timeline.css";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useInView } from "react-intersection-observer";
|
import { useInView } from "react-intersection-observer";
|
||||||
import { TaggedNostrEvent, EventKind, u256 } from "@snort/system";
|
import { TaggedNostrEvent, EventKind, u256 } from "@snort/system";
|
||||||
@ -84,7 +84,7 @@ const Timeline = (props: TimelineProps) => {
|
|||||||
<>
|
<>
|
||||||
<div className="card latest-notes" onClick={() => onShowLatest()} ref={ref}>
|
<div className="card latest-notes" onClick={() => onShowLatest()} ref={ref}>
|
||||||
{latestAuthors.slice(0, 3).map(p => {
|
{latestAuthors.slice(0, 3).map(p => {
|
||||||
return <ProfileImage pubkey={p} showUsername={false} link={""} />;
|
return <ProfileImage pubkey={p} showUsername={false} link={""} showProfileCard={false} />;
|
||||||
})}
|
})}
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"
|
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"
|
||||||
@ -117,7 +117,7 @@ const Timeline = (props: TimelineProps) => {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{(props.loadMore === undefined || props.loadMore === true) && (
|
{(props.loadMore === undefined || props.loadMore === true) && (
|
||||||
<div className="flex f-center">
|
<div className="flex items-center">
|
||||||
<button type="button" onClick={() => feed.loadMore()}>
|
<button type="button" onClick={() => feed.loadMore()}>
|
||||||
<FormattedMessage defaultMessage="Load more" />
|
<FormattedMessage defaultMessage="Load more" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "./Timeline.css";
|
import "./Timeline.css";
|
||||||
import { ReactNode, useCallback, useContext, useMemo, useState, useSyncExternalStore } from "react";
|
import { ReactNode, useCallback, useContext, useMemo, useState, useSyncExternalStore } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { TaggedNostrEvent, EventKind, u256, NostrEvent, NostrLink } from "@snort/system";
|
import { TaggedNostrEvent, EventKind, u256, NostrEvent, NostrLink } from "@snort/system";
|
||||||
import { unixNow } from "@snort/shared";
|
import { unixNow } from "@snort/shared";
|
||||||
import { SnortContext } from "@snort/system-react";
|
import { SnortContext } from "@snort/system-react";
|
||||||
@ -126,7 +126,7 @@ const TimelineFollows = (props: TimelineFollowsProps) => {
|
|||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
<div className="flex f-center p">
|
<div className="flex items-center p">
|
||||||
<AsyncButton
|
<AsyncButton
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await FollowsFeed.loadMore(system, login, sortedFeed[sortedFeed.length - 1].created_at);
|
await FollowsFeed.loadMore(system, login, sortedFeed[sortedFeed.length - 1].created_at);
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
import { useState, useEffect, FC, ComponentProps } from "react";
|
|
||||||
import { useIntl, FormattedMessage } from "react-intl";
|
|
||||||
|
|
||||||
type ExtendedProps = ComponentProps<typeof FormattedMessage>;
|
|
||||||
|
|
||||||
const ExtendedFormattedMessage: FC<ExtendedProps> = props => {
|
|
||||||
const { id, defaultMessage, values } = props;
|
|
||||||
const { formatMessage } = useIntl();
|
|
||||||
|
|
||||||
const [processedMessage, setProcessedMessage] = useState<string | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const translatedMessage = formatMessage({ id, defaultMessage }, values);
|
|
||||||
if (typeof translatedMessage === "string") {
|
|
||||||
setProcessedMessage(translatedMessage.replace("Snort", CONFIG.appNameCapitalized || "Snort"));
|
|
||||||
}
|
|
||||||
}, [id, defaultMessage, values, formatMessage]);
|
|
||||||
|
|
||||||
return <>{processedMessage}</>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ExtendedFormattedMessage;
|
|
@ -1,5 +1,5 @@
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
export default function AccountName({ name = "", link = true }) {
|
export default function AccountName({ name = "", link = true }) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
@ -2,10 +2,10 @@ import { mapEventToProfile } from "@snort/system";
|
|||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
|
|
||||||
import AccountName from "./AccountName";
|
import AccountName from "./AccountName";
|
||||||
import useLogin from "../../Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import { UserCache } from "../../Cache";
|
import { UserCache } from "Cache";
|
||||||
import useEventPublisher from "../../Hooks/useEventPublisher";
|
import useEventPublisher from "Hooks/useEventPublisher";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
export default function ActiveAccount({ name = "", setAsPrimary = () => {} }) {
|
export default function ActiveAccount({ name = "", setAsPrimary = () => {} }) {
|
||||||
const { publicKey, readonly } = useLogin(s => ({
|
const { publicKey, readonly } = useLogin(s => ({
|
||||||
|
@ -5,8 +5,8 @@ import { LoginStore } from "Login";
|
|||||||
import AccountName from "./AccountName";
|
import AccountName from "./AccountName";
|
||||||
import ActiveAccount from "./ActiveAccount";
|
import ActiveAccount from "./ActiveAccount";
|
||||||
import ReservedAccount from "./ReservedAccount";
|
import ReservedAccount from "./ReservedAccount";
|
||||||
import { ProfileLoader } from "../../index";
|
import { ProfileLoader } from "index";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { injectIntl } from "react-intl";
|
import { injectIntl } from "react-intl";
|
||||||
import messages from "Element/messages";
|
import messages from "Element/messages";
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import AccountName from "./AccountName";
|
import AccountName from "./AccountName";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
export default function ReservedAccount({ name = "", enableReserved = () => {}, declineReserved = () => {} }) {
|
export default function ReservedAccount({ name = "", enableReserved = () => {}, declineReserved = () => {} }) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { NostrEvent, NostrLink } from "@snort/system";
|
import { NostrEvent, NostrLink } from "@snort/system";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { findTag } from "SnortUtils";
|
import { findTag } from "SnortUtils";
|
||||||
@ -70,7 +70,7 @@ export function LiveEvent({ ev }: { ev: NostrEvent }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex f-space br p24 bg-primary">
|
<div className="flex justify-between br p24 bg-primary">
|
||||||
<div className="flex g12">
|
<div className="flex g12">
|
||||||
<ProfileImage pubkey={host} showUsername={false} size={56} />
|
<ProfileImage pubkey={host} showUsername={false} size={56} />
|
||||||
<div>
|
<div>
|
||||||
|
@ -43,7 +43,7 @@ function LiveStreamEvent({ ev }: { ev: NostrEvent }) {
|
|||||||
"--img": `url(${imageProxy})`,
|
"--img": `url(${imageProxy})`,
|
||||||
} as CSSProperties
|
} as CSSProperties
|
||||||
}></div>
|
}></div>
|
||||||
<div className="flex f-col details">
|
<div className="flex flex-col details">
|
||||||
<div className="flex g2">
|
<div className="flex g2">
|
||||||
<span className="live">{status}</span>
|
<span className="live">{status}</span>
|
||||||
<div className="reaction-pill">
|
<div className="reaction-pill">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { logout } from "Login";
|
import { logout } from "Login";
|
||||||
|
@ -250,7 +250,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
|
|||||||
)}
|
)}
|
||||||
{error && <b className="error">{error.error}</b>}
|
{error && <b className="error">{error.error}</b>}
|
||||||
{!registerStatus && (
|
{!registerStatus && (
|
||||||
<div className="flex mb10">
|
<div className="flex items-center mb10">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="nip-handle"
|
className="nip-handle"
|
||||||
@ -305,7 +305,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
|
|||||||
title={formatMessage(messages.Buying, { item: `${handle}@${domain}` })}
|
title={formatMessage(messages.Buying, { item: `${handle}@${domain}` })}
|
||||||
/>
|
/>
|
||||||
{registerStatus?.paid && (
|
{registerStatus?.paid && (
|
||||||
<div className="flex f-col">
|
<div className="flex flex-col">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage {...messages.OrderPaid} />
|
<FormattedMessage {...messages.OrderPaid} />
|
||||||
</h4>
|
</h4>
|
||||||
|
@ -2,7 +2,7 @@ import Spinner from "Icons/Spinner";
|
|||||||
|
|
||||||
export default function PageSpinner() {
|
export default function PageSpinner() {
|
||||||
return (
|
return (
|
||||||
<div className="flex f-center">
|
<div className="flex items-center">
|
||||||
<Spinner width={50} height={50} />
|
<Spinner width={50} height={50} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -63,7 +63,7 @@ export function PinPrompt({
|
|||||||
submitButtonRef.current.click();
|
submitButtonRef.current.click();
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<div className="flex-column g12">
|
<div className="flex flex-col g12">
|
||||||
<h2>
|
<h2>
|
||||||
<FormattedMessage defaultMessage="Enter Pin" />
|
<FormattedMessage defaultMessage="Enter Pin" />
|
||||||
</h2>
|
</h2>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import useImgProxy from "Hooks/useImgProxy";
|
import useImgProxy from "Hooks/useImgProxy";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { getUrlHostname } from "SnortUtils";
|
import { getUrlHostname } from "SnortUtils";
|
||||||
|
|
||||||
interface ProxyImgProps extends React.DetailedHTMLProps<React.ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement> {
|
interface ProxyImgProps extends React.DetailedHTMLProps<React.ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement> {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useContext, useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { TaggedNostrEvent } from "@snort/system";
|
import { TaggedNostrEvent } from "@snort/system";
|
||||||
import { SnortContext } from "@snort/system-react";
|
import { SnortContext } from "@snort/system-react";
|
||||||
|
|
||||||
@ -23,11 +23,11 @@ export function ReBroadcaster({ onClose, ev }: { onClose: () => void; ev: Tagged
|
|||||||
|
|
||||||
function renderRelayCustomisation() {
|
function renderRelayCustomisation() {
|
||||||
return (
|
return (
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
{Object.keys(relays.item || {})
|
{Object.keys(relays.item || {})
|
||||||
.filter(el => relays.item[el].write)
|
.filter(el => relays.item[el].write)
|
||||||
.map((r, i, a) => (
|
.map((r, i, a) => (
|
||||||
<div className="card flex f-space">
|
<div className="card flex justify-between">
|
||||||
<div>{r}</div>
|
<div>{r}</div>
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "./Relay.css";
|
import "./Relay.css";
|
||||||
import { useContext, useMemo } from "react";
|
import { useContext, useMemo } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RelaySettings } from "@snort/system";
|
import { RelaySettings } from "@snort/system";
|
||||||
import { unixNowMs } from "@snort/shared";
|
import { unixNowMs } from "@snort/shared";
|
||||||
@ -46,7 +46,7 @@ export default function Relay(props: RelayProps) {
|
|||||||
<div className={`flex ${state?.connected ? "bg-success" : "bg-error"}`}>
|
<div className={`flex ${state?.connected ? "bg-success" : "bg-error"}`}>
|
||||||
<Icon name="wifi" />
|
<Icon name="wifi" />
|
||||||
</div>
|
</div>
|
||||||
<div className="f-grow f-col">
|
<div className="grow flex-col">
|
||||||
<div className="flex mb10">
|
<div className="flex mb10">
|
||||||
<b className="f-2">{name}</b>
|
<b className="f-2">{name}</b>
|
||||||
<div className="f-1">
|
<div className="f-1">
|
||||||
@ -77,7 +77,7 @@ export default function Relay(props: RelayProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="f-grow"></div>
|
<div className="grow"></div>
|
||||||
<div>
|
<div>
|
||||||
<span className="icon-btn" onClick={() => navigate(state?.id ?? "")}>
|
<span className="icon-btn" onClick={() => navigate(state?.id ?? "")}>
|
||||||
<Icon name="gear" size={12} />
|
<Icon name="gear" size={12} />
|
||||||
|
@ -2,7 +2,7 @@ import "./RootTabs.css";
|
|||||||
import { useState, ReactNode, useEffect } from "react";
|
import { useState, ReactNode, useEffect } from "react";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import { Menu, MenuItem } from "@szhsin/react-menu";
|
import { Menu, MenuItem } from "@szhsin/react-menu";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
|
@ -101,7 +101,7 @@ export default function SendSats(props: SendSatsProps) {
|
|||||||
function successAction() {
|
function successAction() {
|
||||||
if (!success) return null;
|
if (!success) return null;
|
||||||
return (
|
return (
|
||||||
<div className="flex f-center">
|
<div className="flex items-center">
|
||||||
<p className="flex g12">
|
<p className="flex g12">
|
||||||
<Icon name="check" className="success" />
|
<Icon name="check" className="success" />
|
||||||
{success?.description ?? <FormattedMessage defaultMessage="Paid" />}
|
{success?.description ?? <FormattedMessage defaultMessage="Paid" />}
|
||||||
@ -153,7 +153,7 @@ export default function SendSats(props: SendSatsProps) {
|
|||||||
const total = props.targets.reduce((acc, v) => (acc += v.weight), 0);
|
const total = props.targets.reduce((acc, v) => (acc += v.weight), 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-column g12">
|
<div className="flex flex-col g12">
|
||||||
<h2>
|
<h2>
|
||||||
{zapper?.canZap() ? (
|
{zapper?.canZap() ? (
|
||||||
<FormattedMessage defaultMessage="Send zap splits to" />
|
<FormattedMessage defaultMessage="Send zap splits to" />
|
||||||
@ -179,9 +179,9 @@ export default function SendSats(props: SendSatsProps) {
|
|||||||
if (!(props.show ?? false)) return null;
|
if (!(props.show ?? false)) return null;
|
||||||
return (
|
return (
|
||||||
<Modal id="send-sats" className="lnurl-modal" onClose={onClose}>
|
<Modal id="send-sats" className="lnurl-modal" onClose={onClose}>
|
||||||
<div className="p flex-column g12">
|
<div className="p flex flex-col g12">
|
||||||
<div className="flex g12">
|
<div className="flex g12">
|
||||||
<div className="flex f-grow">{props.title || title()}</div>
|
<div className="flex items-center grow">{props.title || title()}</div>
|
||||||
<div onClick={onClose}>
|
<div onClick={onClose}>
|
||||||
<Icon name="close" />
|
<Icon name="close" />
|
||||||
</div>
|
</div>
|
||||||
@ -310,7 +310,7 @@ function SendSatsInput(props: {
|
|||||||
type="number"
|
type="number"
|
||||||
min={min}
|
min={min}
|
||||||
max={max}
|
max={max}
|
||||||
className="f-grow"
|
className="grow"
|
||||||
placeholder={formatMessage(messages.Custom)}
|
placeholder={formatMessage(messages.Custom)}
|
||||||
value={customAmount}
|
value={customAmount}
|
||||||
onChange={e => setCustomAmount(parseInt(e.target.value))}
|
onChange={e => setCustomAmount(parseInt(e.target.value))}
|
||||||
@ -327,8 +327,8 @@ function SendSatsInput(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-column g24">
|
<div className="flex flex-col g24">
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h3>
|
<h3>
|
||||||
<FormattedMessage defaultMessage="Zap amount in sats" />
|
<FormattedMessage defaultMessage="Zap amount in sats" />
|
||||||
</h3>
|
</h3>
|
||||||
@ -338,7 +338,7 @@ function SendSatsInput(props: {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={formatMessage(messages.Comment)}
|
placeholder={formatMessage(messages.Comment)}
|
||||||
className="f-grow"
|
className="grow"
|
||||||
maxLength={props.zapper.maxComment()}
|
maxLength={props.zapper.maxComment()}
|
||||||
onChange={e => setComment(e.target.value)}
|
onChange={e => setComment(e.target.value)}
|
||||||
/>
|
/>
|
||||||
@ -346,11 +346,9 @@ function SendSatsInput(props: {
|
|||||||
</div>
|
</div>
|
||||||
<SendSatsZapTypeSelector zapType={zapType} setZapType={setZapType} />
|
<SendSatsZapTypeSelector zapType={zapType} setZapType={setZapType} />
|
||||||
{(amount ?? 0) > 0 && (
|
{(amount ?? 0) > 0 && (
|
||||||
<AsyncButton className="zap-action" onClick={() => props.onNextStage(getValue())}>
|
<AsyncButton onClick={() => props.onNextStage(getValue())}>
|
||||||
<div className="zap-action-container">
|
|
||||||
<Icon name="zap" />
|
<Icon name="zap" />
|
||||||
<FormattedMessage defaultMessage="Zap {n} sats" values={{ n: formatShort(amount) }} />
|
<FormattedMessage defaultMessage="Zap {n} sats" values={{ n: formatShort(amount) }} />
|
||||||
</div>
|
|
||||||
</AsyncButton>
|
</AsyncButton>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -365,7 +363,7 @@ function SendSatsZapTypeSelector({ zapType, setZapType }: { zapType: ZapType; se
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h3>
|
<h3>
|
||||||
<FormattedMessage defaultMessage="Zap Type" />
|
<FormattedMessage defaultMessage="Zap Type" />
|
||||||
</h3>
|
</h3>
|
||||||
@ -389,13 +387,13 @@ function SendSatsInvoice(props: {
|
|||||||
onInvoicePaid: () => void;
|
onInvoicePaid: () => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-column g12 txt-center">
|
<div className="flex flex-col items-center g12 txt-center">
|
||||||
{props.notice && <b className="error">{props.notice}</b>}
|
{props.notice && <b className="error">{props.notice}</b>}
|
||||||
{props.invoice.map(v => (
|
{props.invoice.map(v => (
|
||||||
<>
|
<>
|
||||||
<QrCode data={v.pr} link={`lightning:${v.pr}`} />
|
<QrCode data={v.pr} link={`lightning:${v.pr}`} />
|
||||||
<div className="flex-column g12">
|
<div className="flex flex-col g12">
|
||||||
<Copy text={v.pr} maxSize={26} className="f-center" />
|
<Copy text={v.pr} maxSize={26} className="items-center" />
|
||||||
<a href={`lightning:${v.pr}`}>
|
<a href={`lightning:${v.pr}`}>
|
||||||
<button type="button">
|
<button type="button">
|
||||||
<FormattedMessage defaultMessage="Open Wallet" />
|
<FormattedMessage defaultMessage="Open Wallet" />
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { HexKey, NostrPrefix } from "@snort/system";
|
import { HexKey, NostrPrefix } from "@snort/system";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import FollowListBase from "Element/User/FollowListBase";
|
import FollowListBase from "Element/User/FollowListBase";
|
||||||
import PageSpinner from "Element/PageSpinner";
|
import PageSpinner from "Element/PageSpinner";
|
||||||
@ -55,7 +55,7 @@ export default function SuggestedProfiles() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="card flex f-space">
|
<div className="card flex justify-between">
|
||||||
<FormattedMessage defaultMessage="Provider" />
|
<FormattedMessage defaultMessage="Provider" />
|
||||||
<select onChange={e => setProvider(Number(e.target.value))}>
|
<select onChange={e => setProvider(Number(e.target.value))}>
|
||||||
<option value={Provider.NostrBand}>nostr.band</option>
|
<option value={Provider.NostrBand}>nostr.band</option>
|
||||||
|
@ -41,7 +41,7 @@ export default function AvatarEditor({ picture, onPictureChange }: AvatarEditorP
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex f-center">
|
<div className="flex justify-center items-center">
|
||||||
<div style={{ backgroundImage: `url(${picture})` }} className="avatar">
|
<div style={{ backgroundImage: `url(${picture})` }} className="avatar">
|
||||||
<div className={`edit${picture ? "" : " new"}`} onClick={() => uploadFile().catch(console.error)}>
|
<div className={`edit${picture ? "" : " new"}`} onClick={() => uploadFile().catch(console.error)}>
|
||||||
{loading ? <Spinner /> : <Icon name={picture ? "edit" : "camera-plus"} />}
|
{loading ? <Spinner /> : <Icon name={picture ? "edit" : "camera-plus"} />}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "./BadgeList.css";
|
import "./BadgeList.css";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import { TaggedNostrEvent } from "@snort/system";
|
import { TaggedNostrEvent } from "@snort/system";
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey } from "@snort/system";
|
import { HexKey } from "@snort/system";
|
||||||
import useModeration from "Hooks/useModeration";
|
import useModeration from "Hooks/useModeration";
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey } from "@snort/system";
|
import { HexKey } from "@snort/system";
|
||||||
|
|
||||||
import useEventPublisher from "Hooks/useEventPublisher";
|
import useEventPublisher from "Hooks/useEventPublisher";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey } from "@snort/system";
|
import { HexKey } from "@snort/system";
|
||||||
import { dedupe } from "@snort/shared";
|
import { dedupe } from "@snort/shared";
|
||||||
|
|
||||||
@ -38,9 +38,9 @@ export default function FollowListBase({
|
|||||||
if (publisher) {
|
if (publisher) {
|
||||||
const newFollows = dedupe([...pubkeys, ...login.follows.item]);
|
const newFollows = dedupe([...pubkeys, ...login.follows.item]);
|
||||||
const ev = await publisher.contactList(newFollows, login.relays.item);
|
const ev = await publisher.contactList(newFollows, login.relays.item);
|
||||||
system.BroadcastEvent(ev);
|
|
||||||
await FollowsFeed.backFill(system, pubkeys);
|
|
||||||
setFollows(login, newFollows, ev.created_at);
|
setFollows(login, newFollows, ev.created_at);
|
||||||
|
await system.BroadcastEvent(ev);
|
||||||
|
await FollowsFeed.backFill(system, pubkeys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ export default function FollowListBase({
|
|||||||
<div className={className}>
|
<div className={className}>
|
||||||
{(showFollowAll ?? true) && (
|
{(showFollowAll ?? true) && (
|
||||||
<div className="flex mt10 mb10">
|
<div className="flex mt10 mb10">
|
||||||
<div className="f-grow bold">{title}</div>
|
<div className="grow bold">{title}</div>
|
||||||
{actions}
|
{actions}
|
||||||
<AsyncButton className="transparent" type="button" onClick={() => followAll()} disabled={login.readonly}>
|
<AsyncButton className="transparent" type="button" onClick={() => followAll()} disabled={login.readonly}>
|
||||||
<FormattedMessage {...messages.FollowAll} />
|
<FormattedMessage {...messages.FollowAll} />
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import "./Following.css";
|
import "./Following.css";
|
||||||
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
|
||||||
|
|
||||||
export function FollowingMark({ pubkey }: { pubkey: string }) {
|
export function FollowingMark({ pubkey }: { pubkey: string }) {
|
||||||
const { follows } = useLogin(s => ({ follows: s.follows }));
|
const { follows } = useLogin(s => ({ follows: s.follows }));
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey } from "@snort/system";
|
import { HexKey } from "@snort/system";
|
||||||
import useModeration from "Hooks/useModeration";
|
import useModeration from "Hooks/useModeration";
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey } from "@snort/system";
|
import { HexKey } from "@snort/system";
|
||||||
import MuteButton from "Element/User/MuteButton";
|
import MuteButton from "Element/User/MuteButton";
|
||||||
import ProfilePreview from "Element/User/ProfilePreview";
|
import ProfilePreview from "Element/User/ProfilePreview";
|
||||||
@ -16,7 +16,7 @@ export default function MutedList({ pubkeys }: MutedListProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="main-content p">
|
<div className="main-content p">
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<div className="bold">
|
<div className="bold">
|
||||||
<FormattedMessage {...messages.MuteCount} values={{ n: pubkeys?.length }} />
|
<FormattedMessage {...messages.MuteCount} values={{ n: pubkeys?.length }} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,52 +1,31 @@
|
|||||||
import "./NoteToSelf.css";
|
import "./NoteToSelf.css";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
|
||||||
import { profileLink } from "SnortUtils";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import { FormattedMessage } from "react-intl";
|
||||||
import messages from "../messages";
|
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
|
|
||||||
|
import messages from "../messages";
|
||||||
|
|
||||||
export interface NoteToSelfProps {
|
export interface NoteToSelfProps {
|
||||||
pubkey: string;
|
|
||||||
clickable?: boolean;
|
|
||||||
className?: string;
|
className?: string;
|
||||||
link?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function NoteLabel() {
|
function NoteLabel() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="bold flex items-center g4">
|
||||||
<FormattedMessage {...messages.NoteToSelf} /> <Icon name="badge" size={15} />
|
<FormattedMessage {...messages.NoteToSelf} /> <Icon name="badge" size={15} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NoteToSelf({ pubkey, clickable, className, link }: NoteToSelfProps) {
|
export default function NoteToSelf({ className }: NoteToSelfProps) {
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const clickLink = () => {
|
|
||||||
if (clickable) {
|
|
||||||
navigate(link ?? profileLink(pubkey));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames("nts", className)}>
|
<div className={classNames("nts", className)}>
|
||||||
<div className="avatar-wrapper">
|
<div className="avatar-wrapper">
|
||||||
<div className={classNames("avatar", { clickable: clickable })}>
|
<div className="avatar">
|
||||||
<Icon onClick={clickLink} name="book-closed" size={20} />
|
<Icon name="book-closed" size={20} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="f-grow">
|
|
||||||
<div className="name">
|
|
||||||
{(clickable && (
|
|
||||||
<Link to={link ?? profileLink(pubkey)}>
|
|
||||||
<NoteLabel />
|
<NoteLabel />
|
||||||
</Link>
|
|
||||||
)) || <NoteLabel />}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import "./ProfileImage.css";
|
import "./ProfileImage.css";
|
||||||
|
|
||||||
import React, { ReactNode, useEffect, useState } from "react";
|
import React, { ReactNode, useEffect, useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { HexKey, UserMetadata } from "@snort/system";
|
import { HexKey, UserMetadata } from "@snort/system";
|
||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
import { useHover } from "@uidotdev/usehooks";
|
import { useHover } from "@uidotdev/usehooks";
|
||||||
import { ControlledMenu } from "@szhsin/react-menu";
|
import { ControlledMenu } from "@szhsin/react-menu";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
import { profileLink } from "SnortUtils";
|
|
||||||
import Avatar from "Element/User/Avatar";
|
import Avatar from "Element/User/Avatar";
|
||||||
import Nip05 from "Element/User/Nip05";
|
import Nip05 from "Element/User/Nip05";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
@ -17,6 +15,7 @@ import DisplayName from "./DisplayName";
|
|||||||
import Text from "Element/Text";
|
import Text from "Element/Text";
|
||||||
import FollowButton from "Element/User/FollowButton";
|
import FollowButton from "Element/User/FollowButton";
|
||||||
import { UserWebsiteLink } from "Element/User/UserWebsiteLink";
|
import { UserWebsiteLink } from "Element/User/UserWebsiteLink";
|
||||||
|
import { ProfileLink } from "./ProfileLink";
|
||||||
|
|
||||||
export interface ProfileImageProps {
|
export interface ProfileImageProps {
|
||||||
pubkey: HexKey;
|
pubkey: HexKey;
|
||||||
@ -126,8 +125,8 @@ export default function ProfileImage({
|
|||||||
anchorRef={ref}
|
anchorRef={ref}
|
||||||
menuClassName="profile-card"
|
menuClassName="profile-card"
|
||||||
onClose={() => setShowProfileMenu(false)}>
|
onClose={() => setShowProfileMenu(false)}>
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<ProfileImage pubkey={""} profile={user} showProfileCard={false} link="" />
|
<ProfileImage pubkey={""} profile={user} showProfileCard={false} link="" />
|
||||||
<div className="flex g8">
|
<div className="flex g8">
|
||||||
{/*<button type="button" onClick={() => {
|
{/*<button type="button" onClick={() => {
|
||||||
@ -167,12 +166,14 @@ export default function ProfileImage({
|
|||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Link
|
<ProfileLink
|
||||||
|
pubkey={pubkey}
|
||||||
className={classNames("pfp", className)}
|
className={classNames("pfp", className)}
|
||||||
to={link === undefined ? profileLink(pubkey) : link}
|
user={user}
|
||||||
|
explicitLink={link}
|
||||||
onClick={handleClick}>
|
onClick={handleClick}>
|
||||||
{inner()}
|
{inner()}
|
||||||
</Link>
|
</ProfileLink>
|
||||||
{profileCard()}
|
{profileCard()}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
62
packages/app/src/Element/User/ProfileLink.tsx
Normal file
62
packages/app/src/Element/User/ProfileLink.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { ReactNode, useContext } from "react";
|
||||||
|
import { Link, LinkProps } from "react-router-dom";
|
||||||
|
import { UserMetadata, NostrLink, NostrPrefix, MetadataCache } from "@snort/system";
|
||||||
|
import { SnortContext } from "@snort/system-react";
|
||||||
|
import { randomSample } from "SnortUtils";
|
||||||
|
|
||||||
|
export function ProfileLink({
|
||||||
|
pubkey,
|
||||||
|
user,
|
||||||
|
link,
|
||||||
|
explicitLink,
|
||||||
|
children,
|
||||||
|
...others
|
||||||
|
}: {
|
||||||
|
pubkey: string;
|
||||||
|
user?: UserMetadata | MetadataCache;
|
||||||
|
link?: NostrLink;
|
||||||
|
explicitLink?: string;
|
||||||
|
children?: ReactNode;
|
||||||
|
} & Omit<LinkProps, "to">) {
|
||||||
|
const system = useContext(SnortContext);
|
||||||
|
const relays = system.RelayCache.getFromCache(pubkey)
|
||||||
|
?.relays?.filter(a => a.settings.write)
|
||||||
|
?.map(a => a.url);
|
||||||
|
|
||||||
|
function profileLink() {
|
||||||
|
if (explicitLink) {
|
||||||
|
return explicitLink;
|
||||||
|
}
|
||||||
|
if (user) {
|
||||||
|
if (
|
||||||
|
user.nip05 &&
|
||||||
|
user.nip05.endsWith(`@${CONFIG.nip05Domain}`) &&
|
||||||
|
(!("isNostrAddressValid" in user) || user.isNostrAddressValid)
|
||||||
|
) {
|
||||||
|
const [username] = user.nip05.split("@");
|
||||||
|
return `/${username}`;
|
||||||
|
}
|
||||||
|
return `/p/${new NostrLink(
|
||||||
|
NostrPrefix.Profile,
|
||||||
|
pubkey,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
relays ? randomSample(relays, 3) : undefined,
|
||||||
|
).encode()}`;
|
||||||
|
}
|
||||||
|
if (link && (link.type === NostrPrefix.Profile || link.type === NostrPrefix.PublicKey)) {
|
||||||
|
return `/p/${link.encode()}`;
|
||||||
|
}
|
||||||
|
return "#";
|
||||||
|
}
|
||||||
|
|
||||||
|
const oFiltered = others as Record<string, unknown>;
|
||||||
|
delete oFiltered["user"];
|
||||||
|
delete oFiltered["link"];
|
||||||
|
delete oFiltered["children"];
|
||||||
|
return (
|
||||||
|
<Link {...oFiltered} to={profileLink()} state={user}>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
@ -1,24 +1,15 @@
|
|||||||
import { MouseEvent } from "react";
|
|
||||||
import { useNavigate, Link } from "react-router-dom";
|
|
||||||
|
|
||||||
import { HexKey } from "@snort/system";
|
import { HexKey } from "@snort/system";
|
||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
|
|
||||||
import { profileLink } from "SnortUtils";
|
import { ProfileLink } from "./ProfileLink";
|
||||||
|
import DisplayName from "./DisplayName";
|
||||||
|
|
||||||
export default function Username({ pubkey, onLinkVisit }: { pubkey: HexKey; onLinkVisit(): void }) {
|
export default function Username({ pubkey, onLinkVisit }: { pubkey: HexKey; onLinkVisit(): void }) {
|
||||||
const user = useUserProfile(pubkey);
|
const user = useUserProfile(pubkey);
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
function onClick(ev: MouseEvent) {
|
|
||||||
ev.preventDefault();
|
|
||||||
onLinkVisit();
|
|
||||||
navigate(profileLink(pubkey));
|
|
||||||
}
|
|
||||||
|
|
||||||
return user ? (
|
return user ? (
|
||||||
<Link to={profileLink(pubkey)} onClick={onClick}>
|
<ProfileLink pubkey={pubkey} onClick={onLinkVisit} user={user}>
|
||||||
{user.name || pubkey.slice(0, 12)}
|
<DisplayName pubkey={pubkey} user={user} />
|
||||||
</Link>
|
</ProfileLink>
|
||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ export interface TimelineSubject {
|
|||||||
type: "pubkey" | "hashtag" | "global" | "ptag" | "post_keyword" | "profile_keyword";
|
type: "pubkey" | "hashtag" | "global" | "ptag" | "post_keyword" | "profile_keyword";
|
||||||
discriminator: string;
|
discriminator: string;
|
||||||
items: string[];
|
items: string[];
|
||||||
relay?: string;
|
relay?: Array<string>;
|
||||||
streams?: boolean;
|
streams?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ export default function useLoading<T>(fn: ((e: React.MouseEvent) => Promise<T> |
|
|||||||
if (typeof fn === "function") {
|
if (typeof fn === "function") {
|
||||||
await fn(e);
|
await fn(e);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "./Deck.css";
|
import "./Deck.css";
|
||||||
import { CSSProperties, createContext, useContext, useEffect, useState } from "react";
|
import { CSSProperties, createContext, useContext, useEffect, useState } from "react";
|
||||||
import { Outlet, useNavigate } from "react-router-dom";
|
import { Outlet, useNavigate } from "react-router-dom";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { NostrLink, TaggedNostrEvent } from "@snort/system";
|
import { NostrLink, TaggedNostrEvent } from "@snort/system";
|
||||||
|
|
||||||
import { DeckNav } from "Element/Deck/Nav";
|
import { DeckNav } from "Element/Deck/Nav";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey } from "@snort/system";
|
import { HexKey } from "@snort/system";
|
||||||
|
|
||||||
import { ApiHost, DeveloperAccounts, SnortPubKey } from "Const";
|
import { ApiHost, DeveloperAccounts, SnortPubKey } from "Const";
|
||||||
@ -114,9 +114,9 @@ const DonatePage = () => {
|
|||||||
<p>
|
<p>
|
||||||
<FormattedMessage defaultMessage="Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below" />
|
<FormattedMessage defaultMessage="Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below" />
|
||||||
</p>
|
</p>
|
||||||
<div className="flex-column g12">
|
<div className="flex flex-col g12">
|
||||||
<div className="b br p">
|
<div className="b br p">
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<FormattedMessage defaultMessage="Lightning Donation" />
|
<FormattedMessage defaultMessage="Lightning Donation" />
|
||||||
<ZapButton pubkey={bech32ToHex(SnortPubKey)} lnurl={DonateLNURL}>
|
<ZapButton pubkey={bech32ToHex(SnortPubKey)} lnurl={DonateLNURL}>
|
||||||
<FormattedMessage defaultMessage="Donate" />
|
<FormattedMessage defaultMessage="Donate" />
|
||||||
@ -132,7 +132,7 @@ const DonatePage = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="b br p">
|
<div className="b br p">
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<FormattedMessage defaultMessage="On-chain Donation" />
|
<FormattedMessage defaultMessage="On-chain Donation" />
|
||||||
<AsyncButton type="button" onClick={getOnChainAddress}>
|
<AsyncButton type="button" onClick={getOnChainAddress}>
|
||||||
<FormattedMessage defaultMessage="Get Address" />
|
<FormattedMessage defaultMessage="Get Address" />
|
||||||
@ -142,7 +142,7 @@ const DonatePage = () => {
|
|||||||
</div>
|
</div>
|
||||||
{onChain && (
|
{onChain && (
|
||||||
<Modal onClose={() => setOnChain("")} id="donate-on-chain">
|
<Modal onClose={() => setOnChain("")} id="donate-on-chain">
|
||||||
<div className="flex-column f-center g12">
|
<div className="flex flex-col items-center g12">
|
||||||
<h2>
|
<h2>
|
||||||
<FormattedMessage defaultMessage="On-chain Donation Address" />
|
<FormattedMessage defaultMessage="On-chain Donation Address" />
|
||||||
</h2>
|
</h2>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { db } from "Db";
|
import { db } from "Db";
|
||||||
import AsyncButton from "Element/AsyncButton";
|
import AsyncButton from "Element/AsyncButton";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useRouteError } from "react-router-dom";
|
import { useRouteError } from "react-router-dom";
|
||||||
|
|
||||||
const ErrorPage = () => {
|
const ErrorPage = () => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import FormattedMessage from "@snort/app/src/Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
import IrisAccount from "Element/IrisAccount/IrisAccount";
|
||||||
|
|
||||||
import messages from "./messages";
|
import messages from "./messages";
|
||||||
import IrisAccount from "Element/IrisAccount/IrisAccount";
|
|
||||||
|
|
||||||
export default function FreeNostrAddressPage() {
|
export default function FreeNostrAddressPage() {
|
||||||
return (
|
return (
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import Timeline from "Element/Feed/Timeline";
|
import Timeline from "Element/Feed/Timeline";
|
||||||
import useEventPublisher from "Hooks/useEventPublisher";
|
import useEventPublisher from "Hooks/useEventPublisher";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { KieranPubKey } from "Const";
|
import { KieranPubKey } from "Const";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { TLVEntryType, encodeTLVEntries, NostrPrefix } from "@snort/system";
|
import { TLVEntryType, encodeTLVEntries, NostrPrefix } from "@snort/system";
|
||||||
import { bech32ToHex } from "SnortUtils";
|
import { bech32ToHex } from "SnortUtils";
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import useLoginFeed from "Feed/LoginFeed";
|
|||||||
import { mapPlanName } from "./subscribe";
|
import { mapPlanName } from "./subscribe";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import Avatar from "Element/User/Avatar";
|
import Avatar from "Element/User/Avatar";
|
||||||
import { isFormElement, profileLink } from "SnortUtils";
|
import { isFormElement } from "SnortUtils";
|
||||||
import { getCurrentSubscription } from "Subscription";
|
import { getCurrentSubscription } from "Subscription";
|
||||||
import Toaster from "Toaster";
|
import Toaster from "Toaster";
|
||||||
import Spinner from "Icons/Spinner";
|
import Spinner from "Icons/Spinner";
|
||||||
@ -23,6 +23,7 @@ import { LoginUnlock } from "Element/PinPrompt";
|
|||||||
import useKeyboardShortcut from "Hooks/useKeyboardShortcut";
|
import useKeyboardShortcut from "Hooks/useKeyboardShortcut";
|
||||||
import { LoginStore } from "Login";
|
import { LoginStore } from "Login";
|
||||||
import { NoteCreatorButton } from "Element/Event/NoteCreatorButton";
|
import { NoteCreatorButton } from "Element/Event/NoteCreatorButton";
|
||||||
|
import { ProfileLink } from "Element/User/ProfileLink";
|
||||||
|
|
||||||
export default function Layout() {
|
export default function Layout() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@ -78,7 +79,7 @@ export default function Layout() {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
LoginStore.removeSession(id);
|
LoginStore.removeSession(id);
|
||||||
}}>
|
}}>
|
||||||
<button type="button" className="circle flex f-center">
|
<button type="button" className="circle flex items-center">
|
||||||
<Icon name="close" />
|
<Icon name="close" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -192,15 +193,9 @@ const AccountHeader = () => {
|
|||||||
<Icon name="bell-02" size={24} />
|
<Icon name="bell-02" size={24} />
|
||||||
{hasNotifications && <span className="has-unread"></span>}
|
{hasNotifications && <span className="has-unread"></span>}
|
||||||
</Link>
|
</Link>
|
||||||
<Avatar
|
<ProfileLink pubkey={publicKey} user={profile}>
|
||||||
pubkey={publicKey ?? ""}
|
<Avatar pubkey={publicKey} user={profile} />
|
||||||
user={profile}
|
</ProfileLink>
|
||||||
onClick={() => {
|
|
||||||
if (profile) {
|
|
||||||
navigate(profileLink(profile.pubkey));
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -196,7 +196,7 @@ export default function LoginPage() {
|
|||||||
<p>
|
<p>
|
||||||
<FormattedMessage defaultMessage="Scan this QR code with your signer app to get started" />
|
<FormattedMessage defaultMessage="Scan this QR code with your signer app to get started" />
|
||||||
</p>
|
</p>
|
||||||
<div className="flex-column f-center g12">
|
<div className="flex flex-col items-center g12">
|
||||||
<QrCode data={nostrConnect} />
|
<QrCode data={nostrConnect} />
|
||||||
<Copy text={nostrConnect} />
|
<Copy text={nostrConnect} />
|
||||||
</div>
|
</div>
|
||||||
@ -297,14 +297,14 @@ export default function LoginPage() {
|
|||||||
<p dir="auto">
|
<p dir="auto">
|
||||||
<FormattedMessage defaultMessage="Your key" description="Label for key input" />
|
<FormattedMessage defaultMessage="Your key" description="Label for key input" />
|
||||||
</p>
|
</p>
|
||||||
<div className="flex f-center g8">
|
<div className="flex items-center g8">
|
||||||
<input
|
<input
|
||||||
dir="auto"
|
dir="auto"
|
||||||
type={isMasking ? "password" : "text"}
|
type={isMasking ? "password" : "text"}
|
||||||
placeholder={formatMessage({
|
placeholder={formatMessage({
|
||||||
defaultMessage: "nsec, npub, nip-05, hex, mnemonic",
|
defaultMessage: "nsec, npub, nip-05, hex, mnemonic",
|
||||||
})}
|
})}
|
||||||
className="f-grow"
|
className="grow"
|
||||||
onChange={e => setKey(e.target.value)}
|
onChange={e => setKey(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<Icon
|
<Icon
|
||||||
|
@ -26,6 +26,7 @@ import { LoginSession, LoginStore } from "Login";
|
|||||||
import { Nip28ChatSystem } from "chat/nip28";
|
import { Nip28ChatSystem } from "chat/nip28";
|
||||||
import { ChatParticipantProfile } from "Element/Chat/ChatParticipant";
|
import { ChatParticipantProfile } from "Element/Chat/ChatParticipant";
|
||||||
import { getDisplayName } from "Element/User/DisplayName";
|
import { getDisplayName } from "Element/User/DisplayName";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
const TwoCol = 768;
|
const TwoCol = 768;
|
||||||
const ThreeCol = 1500;
|
const ThreeCol = 1500;
|
||||||
@ -55,7 +56,7 @@ export default function MessagesPage() {
|
|||||||
function noteToSelf(chat: Chat) {
|
function noteToSelf(chat: Chat) {
|
||||||
return (
|
return (
|
||||||
<div className="flex p" key={chat.id} onClick={e => openChat(e, chat.type, chat.id)}>
|
<div className="flex p" key={chat.id} onClick={e => openChat(e, chat.type, chat.id)}>
|
||||||
<NoteToSelf clickable={true} className="f-grow" link="" pubkey={chat.id} />
|
<NoteToSelf className="grow" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -65,7 +66,7 @@ export default function MessagesPage() {
|
|||||||
return <ChatParticipantProfile participant={cx.participants[0]} />;
|
return <ChatParticipantProfile participant={cx.participants[0]} />;
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div className="flex f-grow pfp-overlap">
|
<div className="flex items-center grow pfp-overlap">
|
||||||
{cx.participants.map(v => (
|
{cx.participants.map(v => (
|
||||||
<ProfileImage pubkey={v.id} link="" showUsername={false} profile={v.profile} />
|
<ProfileImage pubkey={v.id} link="" showUsername={false} profile={v.profile} />
|
||||||
))}
|
))}
|
||||||
@ -82,7 +83,10 @@ export default function MessagesPage() {
|
|||||||
|
|
||||||
const isActive = cx.id === chat;
|
const isActive = cx.id === chat;
|
||||||
return (
|
return (
|
||||||
<div className={`flex p${isActive ? " active" : ""}`} key={cx.id} onClick={e => openChat(e, cx.type, cx.id)}>
|
<div
|
||||||
|
className={classNames("flex items-center p", { active: isActive })}
|
||||||
|
key={cx.id}
|
||||||
|
onClick={e => openChat(e, cx.type, cx.id)}>
|
||||||
{conversationIdent(cx)}
|
{conversationIdent(cx)}
|
||||||
<div className="nowrap">
|
<div className="nowrap">
|
||||||
<small>
|
<small>
|
||||||
@ -98,7 +102,7 @@ export default function MessagesPage() {
|
|||||||
<div className="dm-page">
|
<div className="dm-page">
|
||||||
{(pageWidth >= TwoCol || !chat) && (
|
{(pageWidth >= TwoCol || !chat) && (
|
||||||
<div className="chat-list">
|
<div className="chat-list">
|
||||||
<div className="flex p f-space">
|
<div className="flex items-center p justify-between">
|
||||||
<button disabled={unreadCount <= 0} type="button">
|
<button disabled={unreadCount <= 0} type="button">
|
||||||
<FormattedMessage defaultMessage="Mark all read" />
|
<FormattedMessage defaultMessage="Mark all read" />
|
||||||
</button>
|
</button>
|
||||||
@ -205,13 +209,13 @@ function NewChatWindow() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button type="button" className="new-chat" onClick={() => setShow(true)}>
|
<button type="button" className="flex justify-center new-chat" onClick={() => setShow(true)}>
|
||||||
<Icon name="plus" size={16} />
|
<Icon name="plus" size={16} />
|
||||||
</button>
|
</button>
|
||||||
{show && (
|
{show && (
|
||||||
<Modal id="new-chat" onClose={() => setShow(false)} className="new-chat-modal">
|
<Modal id="new-chat" onClose={() => setShow(false)} className="new-chat-modal">
|
||||||
<div className="flex-column g16">
|
<div className="flex flex-col g16">
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<h2>
|
<h2>
|
||||||
<FormattedMessage defaultMessage="New Chat" />
|
<FormattedMessage defaultMessage="New Chat" />
|
||||||
</h2>
|
</h2>
|
||||||
@ -219,7 +223,7 @@ function NewChatWindow() {
|
|||||||
<FormattedMessage defaultMessage="Start chat" />
|
<FormattedMessage defaultMessage="Start chat" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h3>
|
<h3>
|
||||||
<FormattedMessage defaultMessage="Search users" />
|
<FormattedMessage defaultMessage="Search users" />
|
||||||
</h3>
|
</h3>
|
||||||
@ -245,7 +249,7 @@ function NewChatWindow() {
|
|||||||
<p>
|
<p>
|
||||||
<FormattedMessage defaultMessage="People you follow" />
|
<FormattedMessage defaultMessage="People you follow" />
|
||||||
</p>
|
</p>
|
||||||
<div className="user-list flex-column g2">
|
<div className="user-list flex flex-col g2">
|
||||||
{results.map(a => {
|
{results.map(a => {
|
||||||
return (
|
return (
|
||||||
<ProfilePreview
|
<ProfilePreview
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import { ApiHost } from "Const";
|
import { ApiHost } from "Const";
|
||||||
import Nip5Service from "Element/Nip5Service";
|
import Nip5Service from "Element/Nip5Service";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { NostrPrefix, tryParseNostrLink } from "@snort/system";
|
import { NostrPrefix, tryParseNostrLink } from "@snort/system";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useParams } from "react-router-dom";
|
import { useLocation, useParams } from "react-router-dom";
|
||||||
|
|
||||||
import Spinner from "Icons/Spinner";
|
import Spinner from "Icons/Spinner";
|
||||||
import { getNip05PubKey } from "Pages/LoginPage";
|
import { getNip05PubKey } from "Pages/LoginPage";
|
||||||
@ -10,8 +10,9 @@ import { ThreadRoute } from "Element/Event/Thread";
|
|||||||
|
|
||||||
export default function NostrLinkHandler() {
|
export default function NostrLinkHandler() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
const { state } = useLocation();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [renderComponent, setRenderComponent] = useState<React.ReactNode | null>(null);
|
const [renderComponent, setRenderComponent] = useState<React.ReactNode>(null);
|
||||||
|
|
||||||
const link = decodeURIComponent(params["*"] ?? "").toLowerCase();
|
const link = decodeURIComponent(params["*"] ?? "").toLowerCase();
|
||||||
|
|
||||||
@ -21,18 +22,22 @@ export default function NostrLinkHandler() {
|
|||||||
if (nav.type === NostrPrefix.Event || nav.type === NostrPrefix.Note || nav.type === NostrPrefix.Address) {
|
if (nav.type === NostrPrefix.Event || nav.type === NostrPrefix.Note || nav.type === NostrPrefix.Address) {
|
||||||
setRenderComponent(<ThreadRoute id={nav.encode()} />); // Directly render ThreadRoute
|
setRenderComponent(<ThreadRoute id={nav.encode()} />); // Directly render ThreadRoute
|
||||||
} else if (nav.type === NostrPrefix.PublicKey || nav.type === NostrPrefix.Profile) {
|
} else if (nav.type === NostrPrefix.PublicKey || nav.type === NostrPrefix.Profile) {
|
||||||
setRenderComponent(<ProfilePage id={nav.encode()} />); // Directly render ProfilePage
|
setRenderComponent(<ProfilePage id={nav.encode()} state={state} />); // Directly render ProfilePage
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (state) {
|
||||||
|
setRenderComponent(<ProfilePage state={state} />); // Directly render ProfilePage from route state
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const pubkey = await getNip05PubKey(`${link}@${CONFIG.nip05Domain}`);
|
const pubkey = await getNip05PubKey(`${link}@${CONFIG.nip05Domain}`);
|
||||||
if (pubkey) {
|
if (pubkey) {
|
||||||
setRenderComponent(<ProfilePage id={pubkey} />); // Directly render ProfilePage
|
setRenderComponent(<ProfilePage id={pubkey} state={state} />); // Directly render ProfilePage
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
//ignored
|
//ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +52,7 @@ export default function NostrLinkHandler() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex f-center">
|
<div className="flex items-center">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Spinner width={50} height={50} />
|
<Spinner width={50} height={50} />
|
||||||
) : (
|
) : (
|
||||||
|
@ -208,12 +208,12 @@ function NotificationSummary({ evs }: { evs: Array<TaggedNostrEvent> }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-column g12 p bb">
|
<div className="flex flex-col g12 p bb">
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<h2>
|
<h2>
|
||||||
<FormattedMessage defaultMessage="Summary" description="Notifications summary" />
|
<FormattedMessage defaultMessage="Summary" description="Notifications summary" />
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex g8">
|
<div className="flex items-center g8">
|
||||||
{filterIcon(NotificationSummaryFilter.Reactions, "heart-solid", "text-heart")}
|
{filterIcon(NotificationSummaryFilter.Reactions, "heart-solid", "text-heart")}
|
||||||
{filterIcon(NotificationSummaryFilter.Zaps, "zap-solid", "text-zap")}
|
{filterIcon(NotificationSummaryFilter.Zaps, "zap-solid", "text-zap")}
|
||||||
{filterIcon(NotificationSummaryFilter.Reposts, "reverse-left", "text-repost")}
|
{filterIcon(NotificationSummaryFilter.Reposts, "reverse-left", "text-repost")}
|
||||||
@ -246,19 +246,19 @@ function NotificationSummary({ evs }: { evs: Array<TaggedNostrEvent> }) {
|
|||||||
if (active && payload && payload.length) {
|
if (active && payload && payload.length) {
|
||||||
return (
|
return (
|
||||||
<div className="summary-tooltip">
|
<div className="summary-tooltip">
|
||||||
<div className="flex-column g12">
|
<div className="flex flex-col g12">
|
||||||
<Icon name="heart-solid" className="text-heart" />
|
<Icon name="heart-solid" className="text-heart" />
|
||||||
{formatShort(payload.find(a => a.name === "reactions")?.value as number)}
|
{formatShort(payload.find(a => a.name === "reactions")?.value as number)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column g12">
|
<div className="flex flex-col g12">
|
||||||
<Icon name="zap-solid" className="text-zap" />
|
<Icon name="zap-solid" className="text-zap" />
|
||||||
{formatShort(payload.find(a => a.name === "zaps")?.value as number)}
|
{formatShort(payload.find(a => a.name === "zaps")?.value as number)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column g12">
|
<div className="flex flex-col g12">
|
||||||
<Icon name="reverse-left" className="text-repost" />
|
<Icon name="reverse-left" className="text-repost" />
|
||||||
{formatShort(payload.find(a => a.name === "reposts")?.value as number)}
|
{formatShort(payload.find(a => a.name === "reposts")?.value as number)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column g12">
|
<div className="flex flex-col g12">
|
||||||
<Icon name="at-sign" className="text-mention" />
|
<Icon name="at-sign" className="text-mention" />
|
||||||
{formatShort(payload.find(a => a.name === "mentions")?.value as number)}
|
{formatShort(payload.find(a => a.name === "mentions")?.value as number)}
|
||||||
</div>
|
</div>
|
||||||
@ -357,13 +357,13 @@ function NotificationGroup({ evs, onClick }: { evs: Array<TaggedNostrEvent>; onC
|
|||||||
<div className="card notification-group" ref={ref}>
|
<div className="card notification-group" ref={ref}>
|
||||||
{inView && (
|
{inView && (
|
||||||
<>
|
<>
|
||||||
<div className="flex-column g12">
|
<div className="flex flex-col g12">
|
||||||
<div>
|
<div>
|
||||||
<Icon name={iconName()} size={24} className={iconName()} />
|
<Icon name={iconName()} size={24} className={iconName()} />
|
||||||
</div>
|
</div>
|
||||||
<div>{kind === EventKind.ZapReceipt && formatShort(totalZaps)}</div>
|
<div>{kind === EventKind.ZapReceipt && formatShort(totalZaps)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column w-max g12">
|
<div className="flex flex-col w-max g12">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
{pubkeys
|
{pubkeys
|
||||||
.filter(a => a !== "anon")
|
.filter(a => a !== "anon")
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import "./ProfilePage.css";
|
import "./ProfilePage.css";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
encodeTLV,
|
encodeTLV,
|
||||||
encodeTLVEntries,
|
encodeTLVEntries,
|
||||||
EventKind,
|
EventKind,
|
||||||
|
MetadataCache,
|
||||||
NostrLink,
|
NostrLink,
|
||||||
NostrPrefix,
|
NostrPrefix,
|
||||||
TLVEntryType,
|
TLVEntryType,
|
||||||
@ -58,18 +59,22 @@ import ProfileTab, {
|
|||||||
RelaysTab,
|
RelaysTab,
|
||||||
ZapsProfileTab,
|
ZapsProfileTab,
|
||||||
} from "Pages/Profile/ProfileTab";
|
} from "Pages/Profile/ProfileTab";
|
||||||
import DisplayName from "../../Element/User/DisplayName";
|
import DisplayName from "Element/User/DisplayName";
|
||||||
import { UserWebsiteLink } from "Element/User/UserWebsiteLink";
|
import { UserWebsiteLink } from "Element/User/UserWebsiteLink";
|
||||||
|
|
||||||
interface ProfilePageProps {
|
interface ProfilePageProps {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
state?: MetadataCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ProfilePage({ id: propId }: ProfilePageProps) {
|
export default function ProfilePage({ id: propId, state }: ProfilePageProps) {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
const location = useLocation();
|
||||||
|
const profileState = (location.state as MetadataCache | undefined) || state;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [id, setId] = useState<string>();
|
const [id, setId] = useState<string | undefined>(profileState?.pubkey);
|
||||||
const user = useUserProfile(id);
|
const [relays, setRelays] = useState<Array<string>>();
|
||||||
|
const user = useUserProfile(profileState ? undefined : id) || profileState;
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
const loginPubKey = login.publicKey;
|
const loginPubKey = login.publicKey;
|
||||||
const isMe = loginPubKey === id;
|
const isMe = loginPubKey === id;
|
||||||
@ -105,6 +110,7 @@ export default function ProfilePage({ id: propId }: ProfilePageProps) {
|
|||||||
const horizontalScroll = useHorizontalScroll();
|
const horizontalScroll = useHorizontalScroll();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!id) {
|
||||||
const resolvedId = propId || params.id;
|
const resolvedId = propId || params.id;
|
||||||
if (resolvedId?.match(EmailRegex)) {
|
if (resolvedId?.match(EmailRegex)) {
|
||||||
getNip05PubKey(resolvedId).then(a => {
|
getNip05PubKey(resolvedId).then(a => {
|
||||||
@ -113,14 +119,15 @@ export default function ProfilePage({ id: propId }: ProfilePageProps) {
|
|||||||
} else {
|
} else {
|
||||||
const nav = tryParseNostrLink(resolvedId ?? "");
|
const nav = tryParseNostrLink(resolvedId ?? "");
|
||||||
if (nav?.type === NostrPrefix.PublicKey || nav?.type === NostrPrefix.Profile) {
|
if (nav?.type === NostrPrefix.PublicKey || nav?.type === NostrPrefix.Profile) {
|
||||||
// todo: use relays if any for nprofile
|
|
||||||
setId(nav.id);
|
setId(nav.id);
|
||||||
|
setRelays(nav.relays);
|
||||||
} else {
|
} else {
|
||||||
setId(parseId(resolvedId ?? ""));
|
setId(parseId(resolvedId ?? ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
setTab(ProfileTab.Notes);
|
setTab(ProfileTab.Notes);
|
||||||
}, [propId, params]);
|
}, [id, propId, params]);
|
||||||
|
|
||||||
function musicStatus() {
|
function musicStatus() {
|
||||||
if (!status.music) return;
|
if (!status.music) return;
|
||||||
@ -145,20 +152,11 @@ export default function ProfilePage({ id: propId }: ProfilePageProps) {
|
|||||||
return inner();
|
return inner();
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (user?.nip05 && user?.isNostrAddressValid) {
|
|
||||||
if (user.nip05.endsWith(`@${CONFIG.nip05Domain}`)) {
|
|
||||||
const username = user.nip05?.replace(`@${CONFIG.nip05Domain}`, "");
|
|
||||||
navigate(`/${username}`, { replace: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [user?.isNostrAddressValid, user?.nip05]);
|
|
||||||
|
|
||||||
function username() {
|
function username() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex-column g4">
|
<div className="flex flex-col g4">
|
||||||
<h2 className="flex g4">
|
<h2 className="flex items-center g4">
|
||||||
<DisplayName user={user} pubkey={user?.pubkey ?? ""} />
|
<DisplayName user={user} pubkey={user?.pubkey ?? ""} />
|
||||||
<FollowsYou followsMe={follows.includes(loginPubKey ?? "")} />
|
<FollowsYou followsMe={follows.includes(loginPubKey ?? "")} />
|
||||||
</h2>
|
</h2>
|
||||||
@ -251,6 +249,7 @@ export default function ProfilePage({ id: propId }: ProfilePageProps) {
|
|||||||
type: "pubkey",
|
type: "pubkey",
|
||||||
items: [id],
|
items: [id],
|
||||||
discriminator: id.slice(0, 12),
|
discriminator: id.slice(0, 12),
|
||||||
|
relay: relays,
|
||||||
}}
|
}}
|
||||||
postsOnly={false}
|
postsOnly={false}
|
||||||
method={"LIMIT_UNTIL"}
|
method={"LIMIT_UNTIL"}
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
import useZapsFeed from "../../Feed/ZapsFeed";
|
import { FormattedMessage } from "react-intl";
|
||||||
import FormattedMessage from "../../Element/FormattedMessage";
|
|
||||||
import messages from "../messages";
|
|
||||||
import { formatShort } from "../../Number";
|
|
||||||
import useFollowersFeed from "../../Feed/FollowersFeed";
|
|
||||||
import FollowsList from "../../Element/User/FollowListBase";
|
|
||||||
import useFollowsFeed from "../../Feed/FollowsFeed";
|
|
||||||
import useRelaysFeed from "../../Feed/RelaysFeed";
|
|
||||||
import RelaysMetadata from "../../Element/Relay/RelaysMetadata";
|
|
||||||
import useBookmarkFeed from "../../Feed/BookmarkFeed";
|
|
||||||
import Bookmarks from "../../Element/Bookmarks";
|
|
||||||
import Icon from "../../Icons/Icon";
|
|
||||||
import { Tab } from "../../Element/Tabs";
|
|
||||||
import { EventKind, HexKey, NostrLink, NostrPrefix } from "@snort/system";
|
import { EventKind, HexKey, NostrLink, NostrPrefix } from "@snort/system";
|
||||||
|
|
||||||
|
import useZapsFeed from "Feed/ZapsFeed";
|
||||||
|
import { formatShort } from "Number";
|
||||||
|
import useFollowersFeed from "Feed/FollowersFeed";
|
||||||
|
import FollowsList from "Element/User/FollowListBase";
|
||||||
|
import useFollowsFeed from "Feed/FollowsFeed";
|
||||||
|
import useRelaysFeed from "Feed/RelaysFeed";
|
||||||
|
import RelaysMetadata from "Element/Relay/RelaysMetadata";
|
||||||
|
import useBookmarkFeed from "Feed/BookmarkFeed";
|
||||||
|
import Bookmarks from "Element/Bookmarks";
|
||||||
|
import Icon from "Icons/Icon";
|
||||||
|
import { Tab } from "Element/Tabs";
|
||||||
import { default as ZapElement } from "Element/Event/Zap";
|
import { default as ZapElement } from "Element/Event/Zap";
|
||||||
|
|
||||||
|
import messages from "../messages";
|
||||||
|
|
||||||
export enum ProfileTabType {
|
export enum ProfileTabType {
|
||||||
NOTES = 0,
|
NOTES = 0,
|
||||||
REACTIONS = 1,
|
REACTIONS = 1,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useContext, useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import { Link, Outlet, RouteObject, useParams } from "react-router-dom";
|
import { Link, Outlet, RouteObject, useParams } from "react-router-dom";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { unixNow } from "@snort/shared";
|
import { unixNow } from "@snort/shared";
|
||||||
import { NostrLink } from "@snort/system";
|
import { NostrLink } from "@snort/system";
|
||||||
import { SnortContext } from "@snort/system-react";
|
import { SnortContext } from "@snort/system-react";
|
||||||
@ -68,7 +68,7 @@ export const GlobalTab = () => {
|
|||||||
const subject: TimelineSubject = {
|
const subject: TimelineSubject = {
|
||||||
type: "global",
|
type: "global",
|
||||||
items: [],
|
items: [],
|
||||||
relay: relay?.url,
|
relay: relay?.url ? [relay.url] : undefined,
|
||||||
discriminator: `all-${sha256(relay?.url ?? "").slice(0, 12)}`,
|
discriminator: `all-${sha256(relay?.url ?? "").slice(0, 12)}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ export const GlobalTab = () => {
|
|||||||
const paidRelays = allRelays.filter(a => a.paid);
|
const paidRelays = allRelays.filter(a => a.paid);
|
||||||
const publicRelays = allRelays.filter(a => !a.paid);
|
const publicRelays = allRelays.filter(a => !a.paid);
|
||||||
return (
|
return (
|
||||||
<div className="flex mb10 f-end nowrap">
|
<div className="flex items-center mb10 justify-end nowrap">
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Read global from"
|
defaultMessage="Read global from"
|
||||||
description="Label for reading global feed from specific relays"
|
description="Label for reading global feed from specific relays"
|
||||||
|
@ -72,7 +72,7 @@ const SearchPage = () => {
|
|||||||
function sortOptions() {
|
function sortOptions() {
|
||||||
if (tab.value != PROFILES) return null;
|
if (tab.value != PROFILES) return null;
|
||||||
return (
|
return (
|
||||||
<div className="flex mb10 f-end">
|
<div className="flex mb10 justify-end">
|
||||||
<FormattedMessage defaultMessage="Sort" description="Label for sorting options for people search" />
|
<FormattedMessage defaultMessage="Sort" description="Label for sorting options for people search" />
|
||||||
|
|
||||||
<select onChange={e => setSortPopular(e.target.value == "true")} value={sortPopular ? "true" : "false"}>
|
<select onChange={e => setSortPopular(e.target.value == "true")} value={sortPopular ? "true" : "false"}>
|
||||||
@ -99,7 +99,7 @@ const SearchPage = () => {
|
|||||||
<div className="flex mb10">
|
<div className="flex mb10">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="f-grow mr10"
|
className="grow mr10"
|
||||||
placeholder={formatMessage({ defaultMessage: "Search..." })}
|
placeholder={formatMessage({ defaultMessage: "Search..." })}
|
||||||
value={search}
|
value={search}
|
||||||
onChange={e => setSearch(e.target.value)}
|
onChange={e => setSearch(e.target.value)}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { Outlet, RouteObject, useNavigate } from "react-router-dom";
|
import { Outlet, RouteObject, useNavigate } from "react-router-dom";
|
||||||
import SettingsIndex from "Pages/settings/Root";
|
import SettingsIndex from "Pages/settings/Root";
|
||||||
import Profile from "Pages/settings/Profile";
|
import Profile from "Pages/settings/Profile";
|
||||||
|
@ -86,7 +86,7 @@ export default function WalletPage() {
|
|||||||
<FormattedMessage defaultMessage="Enter wallet password" />
|
<FormattedMessage defaultMessage="Enter wallet password" />
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex w-max">
|
<div className="flex w-max">
|
||||||
<div className="f-grow mr10">
|
<div className="grow mr10">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
placeholder={formatMessage({
|
placeholder={formatMessage({
|
||||||
@ -140,7 +140,7 @@ export default function WalletPage() {
|
|||||||
</h3>
|
</h3>
|
||||||
{history?.map(a => (
|
{history?.map(a => (
|
||||||
<div className="card flex wallet-history-item" key={a.timestamp}>
|
<div className="card flex wallet-history-item" key={a.timestamp}>
|
||||||
<div className="f-grow f-col">
|
<div className="grow flex-col">
|
||||||
<NoteTime from={a.timestamp * 1000} fallback={formatMessage({ defaultMessage: "now" })} />
|
<NoteTime from={a.timestamp * 1000} fallback={formatMessage({ defaultMessage: "now" })} />
|
||||||
<div>{(a.memo ?? "").length === 0 ? <> </> : a.memo}</div>
|
<div>{(a.memo ?? "").length === 0 ? <> </> : a.memo}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ export default function ImportFollows() {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={formatMessage(messages.TwitterPlaceholder)}
|
placeholder={formatMessage(messages.TwitterPlaceholder)}
|
||||||
className="f-grow mr10"
|
className="grow mr10"
|
||||||
value={twitterUsername}
|
value={twitterUsername}
|
||||||
onChange={e => setTwitterUsername(e.target.value)}
|
onChange={e => setTwitterUsername(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import Logo from "Element/Logo";
|
import Logo from "Element/Logo";
|
||||||
@ -102,7 +102,7 @@ export default function NewUserFlow() {
|
|||||||
<h1>
|
<h1>
|
||||||
<FormattedMessage {...messages.SaveKeys} />
|
<FormattedMessage {...messages.SaveKeys} />
|
||||||
</h1>
|
</h1>
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<FormattedMessage defaultMessage="Language" />
|
<FormattedMessage defaultMessage="Language" />
|
||||||
<select
|
<select
|
||||||
value={login.preferences.language || DefaultPreferences.language}
|
value={login.preferences.language || DefaultPreferences.language}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import ProfilePreview from "Element/User/ProfilePreview";
|
import ProfilePreview from "Element/User/ProfilePreview";
|
||||||
@ -10,7 +10,7 @@ export default function AccountsPage() {
|
|||||||
const sub = getActiveSubscriptions(LoginStore.allSubscriptions());
|
const sub = getActiveSubscriptions(LoginStore.allSubscriptions());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-column g12">
|
<div className="flex flex-col g12">
|
||||||
<h3>
|
<h3>
|
||||||
<FormattedMessage defaultMessage="Logins" />
|
<FormattedMessage defaultMessage="Logins" />
|
||||||
</h3>
|
</h3>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
.mnemonic-grid .word > div:nth-of-type(1) {
|
.mnemonic-grid .word > div:nth-of-type(1) {
|
||||||
background-color: var(--gray);
|
background-color: var(--gray);
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
min-width: 1.5em;
|
min-width: 2em;
|
||||||
font-variant-numeric: ordinal;
|
font-variant-numeric: ordinal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import "./Keys.css";
|
import "./Keys.css";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { encodeTLV, KeyStorage, NostrPrefix } from "@snort/system";
|
import { encodeTLV, KeyStorage, NostrPrefix } from "@snort/system";
|
||||||
|
|
||||||
import Copy from "Element/Copy";
|
import Copy from "Element/Copy";
|
||||||
@ -10,7 +10,7 @@ import { hexToBech32 } from "SnortUtils";
|
|||||||
export default function ExportKeys() {
|
export default function ExportKeys() {
|
||||||
const { publicKey, privateKeyData, generatedEntropy } = useLogin();
|
const { publicKey, privateKeyData, generatedEntropy } = useLogin();
|
||||||
return (
|
return (
|
||||||
<div className="flex-column g12">
|
<div className="flex flex-col g12">
|
||||||
<h2>
|
<h2>
|
||||||
<FormattedMessage defaultMessage="Public Key" />
|
<FormattedMessage defaultMessage="Public Key" />
|
||||||
</h2>
|
</h2>
|
||||||
@ -33,7 +33,7 @@ export default function ExportKeys() {
|
|||||||
{hexToMnemonic(generatedEntropy ?? "")
|
{hexToMnemonic(generatedEntropy ?? "")
|
||||||
.split(" ")
|
.split(" ")
|
||||||
.map((a, i) => (
|
.map((a, i) => (
|
||||||
<div className="flex word">
|
<div className="flex items-center word">
|
||||||
<div>{i + 1}</div>
|
<div>{i + 1}</div>
|
||||||
<div>{a}</div>
|
<div>{a}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,7 +3,7 @@ import useLogin from "Hooks/useLogin";
|
|||||||
import { setAppData } from "Login";
|
import { setAppData } from "Login";
|
||||||
import { appendDedupe } from "SnortUtils";
|
import { appendDedupe } from "SnortUtils";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
export function ModerationSettings() {
|
export function ModerationSettings() {
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
@ -26,28 +26,41 @@ export function ModerationSettings() {
|
|||||||
);
|
);
|
||||||
setMuteWord("");
|
setMuteWord("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeMutedWord(word: string) {
|
||||||
|
setAppData(
|
||||||
|
login,
|
||||||
|
{
|
||||||
|
...login.appData.item,
|
||||||
|
mutedWords: login.appData.item.mutedWords.filter(a => a !== word),
|
||||||
|
},
|
||||||
|
unixNowMs(),
|
||||||
|
);
|
||||||
|
setMuteWord("");
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h2>
|
<h2>
|
||||||
<FormattedMessage defaultMessage="Muted Words" />
|
<FormattedMessage defaultMessage="Muted Words" />
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex-column g12">
|
<div className="flex flex-col g12">
|
||||||
<div className="flex g8">
|
<div className="flex g8">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="eg. crypto"
|
placeholder="eg. crypto"
|
||||||
className="w-max"
|
className="w-max"
|
||||||
value={muteWord}
|
value={muteWord}
|
||||||
onChange={e => setMuteWord(e.target.value)}
|
onChange={e => setMuteWord(e.target.value.toLowerCase())}
|
||||||
/>
|
/>
|
||||||
<button type="button" onClick={addMutedWord}>
|
<button type="button" onClick={addMutedWord}>
|
||||||
<FormattedMessage defaultMessage="Add" />
|
<FormattedMessage defaultMessage="Add" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{login.appData.item.mutedWords.map(v => (
|
{login.appData.item.mutedWords.map(v => (
|
||||||
<div className="p br b flex f-space">
|
<div className="p br b flex items-center justify-between">
|
||||||
<div>{v}</div>
|
<div>{v}</div>
|
||||||
<button type="button">
|
<button type="button" onClick={() => removeMutedWord(v)}>
|
||||||
<FormattedMessage defaultMessage="Delete" />
|
<FormattedMessage defaultMessage="Delete" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import "./Preferences.css";
|
import "./Preferences.css";
|
||||||
|
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import { DefaultPreferences, updatePreferences, UserPreferences } from "Login";
|
import { DefaultPreferences, updatePreferences, UserPreferences } from "Login";
|
||||||
import { DefaultImgProxy } from "Const";
|
import { DefaultImgProxy } from "Const";
|
||||||
import { unwrap } from "SnortUtils";
|
import { unwrap } from "SnortUtils";
|
||||||
import searchEmoji from "emoji-search";
|
|
||||||
|
|
||||||
import messages from "./messages";
|
import messages from "./messages";
|
||||||
|
|
||||||
@ -37,24 +35,15 @@ export const AllLanguageCodes = [
|
|||||||
const PreferencesPage = () => {
|
const PreferencesPage = () => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
console.debug(login);
|
|
||||||
const perf = login.preferences;
|
const perf = login.preferences;
|
||||||
const [emoji, setEmoji] = useState<Array<{ name: string; char: string }>>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
(async () => {
|
|
||||||
const allEmoji = await searchEmoji("");
|
|
||||||
setEmoji(allEmoji.map(a => ({ name: a.name, char: a.char })));
|
|
||||||
})();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="preferences flex-column g24">
|
<div className="preferences flex flex-col g24">
|
||||||
<h3>
|
<h3>
|
||||||
<FormattedMessage {...messages.Preferences} />
|
<FormattedMessage {...messages.Preferences} />
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div className="flex f-space w-max">
|
<div className="flex justify-between w-max">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Language" />
|
<FormattedMessage defaultMessage="Language" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -78,7 +67,7 @@ const PreferencesPage = () => {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex f-space w-max">
|
<div className="flex justify-between w-max">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage {...messages.Theme} />
|
<FormattedMessage {...messages.Theme} />
|
||||||
</h4>
|
</h4>
|
||||||
@ -103,7 +92,7 @@ const PreferencesPage = () => {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex f-space w-max">
|
<div className="flex justify-between w-max">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage {...messages.DefaultRootTab} />
|
<FormattedMessage {...messages.DefaultRootTab} />
|
||||||
</h4>
|
</h4>
|
||||||
@ -128,8 +117,8 @@ const PreferencesPage = () => {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex f-space w-max">
|
<div className="flex justify-between w-max">
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Send usage metrics" />
|
<FormattedMessage defaultMessage="Send usage metrics" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -146,7 +135,7 @@ const PreferencesPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-max">
|
<div className="flex w-max">
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage {...messages.AutoloadMedia} />
|
<FormattedMessage {...messages.AutoloadMedia} />
|
||||||
</h4>
|
</h4>
|
||||||
@ -176,8 +165,8 @@ const PreferencesPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex f-space w-max">
|
<div className="flex justify-between w-max">
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Check Signatures" />
|
<FormattedMessage defaultMessage="Check Signatures" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -193,8 +182,8 @@ const PreferencesPage = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex f-space w-max">
|
<div className="flex justify-between w-max">
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Proof of Work" />
|
<FormattedMessage defaultMessage="Proof of Work" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -211,7 +200,7 @@ const PreferencesPage = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex f-space w-max">
|
<div className="flex justify-between w-max">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Default Zap amount" />
|
<FormattedMessage defaultMessage="Default Zap amount" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -224,8 +213,8 @@ const PreferencesPage = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex f-space w-max">
|
<div className="flex justify-between w-max">
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Show Badges" />
|
<FormattedMessage defaultMessage="Show Badges" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -241,8 +230,8 @@ const PreferencesPage = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex f-space w-max">
|
<div className="flex justify-between w-max">
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Show Status" />
|
<FormattedMessage defaultMessage="Show Status" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -258,8 +247,8 @@ const PreferencesPage = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex f-space w-max">
|
<div className="flex justify-between w-max">
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Auto Zap" />
|
<FormattedMessage defaultMessage="Auto Zap" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -275,9 +264,9 @@ const PreferencesPage = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column">
|
<div className="flex flex-col">
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage {...messages.ImgProxy} />
|
<FormattedMessage {...messages.ImgProxy} />
|
||||||
</h4>
|
</h4>
|
||||||
@ -375,8 +364,8 @@ const PreferencesPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex f-space w-max">
|
<div className="flex justify-between w-max">
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage {...messages.EnableReactions} />
|
<FormattedMessage {...messages.EnableReactions} />
|
||||||
</h4>
|
</h4>
|
||||||
@ -392,36 +381,28 @@ const PreferencesPage = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage {...messages.ReactionEmoji} />
|
<FormattedMessage {...messages.ReactionEmoji} />
|
||||||
</h4>
|
</h4>
|
||||||
<small>
|
<small>
|
||||||
<FormattedMessage {...messages.ReactionEmojiHelp} />
|
<FormattedMessage {...messages.ReactionEmojiHelp} />
|
||||||
</small>
|
</small>
|
||||||
<select
|
<input
|
||||||
className="emoji-selector"
|
type="text"
|
||||||
value={perf.reactionEmoji}
|
value={perf.reactionEmoji}
|
||||||
onChange={e =>
|
onChange={e => {
|
||||||
|
const split = e.target.value.match(/[\p{L}\S]{1}/u);
|
||||||
|
console.debug(e.target.value, split);
|
||||||
updatePreferences(login, {
|
updatePreferences(login, {
|
||||||
...perf,
|
...perf,
|
||||||
reactionEmoji: e.target.value,
|
reactionEmoji: split?.[0] ?? "",
|
||||||
})
|
});
|
||||||
}>
|
}}
|
||||||
<option value="+">
|
/>
|
||||||
+ <FormattedMessage {...messages.Default} />
|
|
||||||
</option>
|
|
||||||
{emoji.map(({ name, char }) => {
|
|
||||||
return (
|
|
||||||
<option value={char}>
|
|
||||||
{name} {char}
|
|
||||||
</option>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage {...messages.ConfirmReposts} />
|
<FormattedMessage {...messages.ConfirmReposts} />
|
||||||
</h4>
|
</h4>
|
||||||
@ -437,8 +418,8 @@ const PreferencesPage = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage {...messages.ShowLatest} />
|
<FormattedMessage {...messages.ShowLatest} />
|
||||||
</h4>
|
</h4>
|
||||||
@ -454,7 +435,7 @@ const PreferencesPage = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage {...messages.FileUpload} />
|
<FormattedMessage {...messages.FileUpload} />
|
||||||
</h4>
|
</h4>
|
||||||
@ -476,8 +457,8 @@ const PreferencesPage = () => {
|
|||||||
<option value="nostrimg.com">nostrimg.com</option>
|
<option value="nostrimg.com">nostrimg.com</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex f-space">
|
<div className="flex justify-between">
|
||||||
<div className="flex-column g8">
|
<div className="flex flex-col g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage {...messages.DebugMenus} />
|
<FormattedMessage {...messages.DebugMenus} />
|
||||||
</h4>
|
</h4>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import "./Profile.css";
|
import "./Profile.css";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { mapEventToProfile } from "@snort/system";
|
import { mapEventToProfile } from "@snort/system";
|
||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
@ -13,6 +12,7 @@ import { UserCache } from "Cache";
|
|||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import Avatar from "Element/User/Avatar";
|
import Avatar from "Element/User/Avatar";
|
||||||
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
export interface ProfileSettingsProps {
|
export interface ProfileSettingsProps {
|
||||||
avatar?: boolean;
|
avatar?: boolean;
|
||||||
@ -107,8 +107,8 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
|
|||||||
|
|
||||||
function editor() {
|
function editor() {
|
||||||
return (
|
return (
|
||||||
<div className="flex-column g24">
|
<div className="flex flex-col g24">
|
||||||
<div className="flex-column w-max g8">
|
<div className="flex flex-col w-max g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Name" />
|
<FormattedMessage defaultMessage="Name" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -120,7 +120,7 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
|
|||||||
disabled={readonly}
|
disabled={readonly}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column w-max g8">
|
<div className="flex flex-col w-max g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="About" />
|
<FormattedMessage defaultMessage="About" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -130,7 +130,7 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
|
|||||||
value={about}
|
value={about}
|
||||||
disabled={readonly}></textarea>
|
disabled={readonly}></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column w-max g8">
|
<div className="flex flex-col w-max g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Website" />
|
<FormattedMessage defaultMessage="Website" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -142,11 +142,11 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
|
|||||||
disabled={readonly}
|
disabled={readonly}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column w-max g8">
|
<div className="flex flex-col w-max g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Nostr Address" />
|
<FormattedMessage defaultMessage="Nostr Address" />
|
||||||
</h4>
|
</h4>
|
||||||
<div className="flex-column g8 w-max">
|
<div className="flex flex-col g8 w-max">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="w-max"
|
className="w-max"
|
||||||
@ -158,16 +158,16 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
|
|||||||
<FormattedMessage defaultMessage="Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration." />
|
<FormattedMessage defaultMessage="Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration." />
|
||||||
</small>
|
</small>
|
||||||
<div className="flex g12">
|
<div className="flex g12">
|
||||||
<button className="flex f-center" type="button" onClick={() => navigate("/nostr-address")}>
|
<button className="flex items-center" type="button" onClick={() => navigate("/nostr-address")}>
|
||||||
<FormattedMessage defaultMessage="Buy nostr address" />
|
<FormattedMessage defaultMessage="Buy nostr address" />
|
||||||
</button>
|
</button>
|
||||||
<button className="flex f-center secondary" type="button" onClick={() => navigate("/free-nostr-address")}>
|
<button className="flex items-center secondary" type="button" onClick={() => navigate("/free-nostr-address")}>
|
||||||
<FormattedMessage defaultMessage="Get a free one" />
|
<FormattedMessage defaultMessage="Get a free one" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-column w-max g8">
|
<div className="flex flex-col w-max g8">
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Lightning Address" />
|
<FormattedMessage defaultMessage="Lightning Address" />
|
||||||
</h4>
|
</h4>
|
||||||
@ -207,7 +207,7 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
|
|||||||
<Avatar pubkey={id} user={user} image={picture} />
|
<Avatar pubkey={id} user={user} image={picture} />
|
||||||
<AsyncButton
|
<AsyncButton
|
||||||
type="button"
|
type="button"
|
||||||
className="circle flex f-center"
|
className="circle flex align-centerjustify-between"
|
||||||
onClick={() => setNewAvatar()}
|
onClick={() => setNewAvatar()}
|
||||||
disabled={readonly}>
|
disabled={readonly}>
|
||||||
<Icon name="upload-01" />
|
<Icon name="upload-01" />
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import ProfilePreview from "Element/User/ProfilePreview";
|
import ProfilePreview from "Element/User/ProfilePreview";
|
||||||
import useRelayState from "Feed/RelayState";
|
import useRelayState from "Feed/RelayState";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
@ -37,10 +37,10 @@ const RelayInfo = () => {
|
|||||||
)}
|
)}
|
||||||
{stats?.info?.software && (
|
{stats?.info?.software && (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<h4 className="f-grow">
|
<h4 className="grow">
|
||||||
<FormattedMessage {...messages.Software} />
|
<FormattedMessage {...messages.Software} />
|
||||||
</h4>
|
</h4>
|
||||||
<div className="flex f-col">
|
<div className="flex flex-col">
|
||||||
{stats.info.software.startsWith("http") ? (
|
{stats.info.software.startsWith("http") ? (
|
||||||
<a href={stats.info.software} target="_blank" rel="noreferrer">
|
<a href={stats.info.software} target="_blank" rel="noreferrer">
|
||||||
{stats.info.software}
|
{stats.info.software}
|
||||||
@ -57,7 +57,7 @@ const RelayInfo = () => {
|
|||||||
)}
|
)}
|
||||||
{stats?.info?.contact && (
|
{stats?.info?.contact && (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<h4 className="f-grow">
|
<h4 className="grow">
|
||||||
<FormattedMessage {...messages.Contact} />
|
<FormattedMessage {...messages.Contact} />
|
||||||
</h4>
|
</h4>
|
||||||
<a
|
<a
|
||||||
@ -73,7 +73,7 @@ const RelayInfo = () => {
|
|||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage {...messages.Supports} />
|
<FormattedMessage {...messages.Supports} />
|
||||||
</h4>
|
</h4>
|
||||||
<div className="f-grow">
|
<div className="grow">
|
||||||
{stats.info.supported_nips.map(a => (
|
{stats.info.supported_nips.map(a => (
|
||||||
<a target="_blank" rel="noreferrer" href={`https://nips.be/${a}`} className="pill">
|
<a target="_blank" rel="noreferrer" href={`https://nips.be/${a}`} className="pill">
|
||||||
NIP-{a.toString().padStart(2, "0")}
|
NIP-{a.toString().padStart(2, "0")}
|
||||||
@ -85,7 +85,7 @@ const RelayInfo = () => {
|
|||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Active Subscriptions" />
|
<FormattedMessage defaultMessage="Active Subscriptions" />
|
||||||
</h4>
|
</h4>
|
||||||
<div className="f-grow">
|
<div className="grow">
|
||||||
{stats?.activeRequests.map(a => (
|
{stats?.activeRequests.map(a => (
|
||||||
<span className="pill" key={a}>
|
<span className="pill" key={a}>
|
||||||
{a}
|
{a}
|
||||||
@ -95,14 +95,14 @@ const RelayInfo = () => {
|
|||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage defaultMessage="Pending Subscriptions" />
|
<FormattedMessage defaultMessage="Pending Subscriptions" />
|
||||||
</h4>
|
</h4>
|
||||||
<div className="f-grow">
|
<div className="grow">
|
||||||
{stats?.pendingRequests.map(a => (
|
{stats?.pendingRequests.map(a => (
|
||||||
<span className="pill" key={a}>
|
<span className="pill" key={a}>
|
||||||
{a}
|
{a}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex mt10 f-end">
|
<div className="flex mt10 justify-end">
|
||||||
<div
|
<div
|
||||||
className="btn error"
|
className="btn error"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { unixNowMs } from "@snort/shared";
|
import { unixNowMs } from "@snort/shared";
|
||||||
|
|
||||||
import { randomSample } from "SnortUtils";
|
import { randomSample } from "SnortUtils";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "./Root.css";
|
import "./Root.css";
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import { LoginStore, logout } from "Login";
|
import { LoginStore, logout } from "Login";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "./WalletSettings.css";
|
import "./WalletSettings.css";
|
||||||
import LndLogo from "lnd-logo.png";
|
import LndLogo from "lnd-logo.png";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { Link, RouteObject, useNavigate } from "react-router-dom";
|
import { Link, RouteObject, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import BlueWallet from "Icons/BlueWallet";
|
import BlueWallet from "Icons/BlueWallet";
|
||||||
|
@ -50,7 +50,7 @@ export default function LNForwardAddress({ handle }: { handle: ManageHandle }) {
|
|||||||
<FormattedMessage defaultMessage="Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address" />
|
<FormattedMessage defaultMessage="Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address" />
|
||||||
</p>
|
</p>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="f-grow">
|
<div className="grow">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="w-max mr10"
|
className="w-max mr10"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { ApiHost } from "Const";
|
import { ApiHost } from "Const";
|
||||||
@ -37,14 +37,14 @@ export default function ListHandles() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{handles.map(a => (
|
{handles.map(a => (
|
||||||
<div className="flex f-space" key={a.id}>
|
<div className="flex items-center justify-between" key={a.id}>
|
||||||
<h4 className="nip05">
|
<h4 className="nip05">
|
||||||
{a.handle}@
|
{a.handle}@
|
||||||
<span className="domain" data-domain={a.domain?.toLowerCase()}>
|
<span className="domain" data-domain={a.domain?.toLowerCase()}>
|
||||||
{a.domain}
|
{a.domain}
|
||||||
</span>
|
</span>
|
||||||
</h4>
|
</h4>
|
||||||
<button
|
<button type="button"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
navigate("manage", {
|
navigate("manage", {
|
||||||
state: a,
|
state: a,
|
||||||
@ -55,7 +55,7 @@ export default function ListHandles() {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{handles.length > 0 && (
|
{handles.length > 0 && (
|
||||||
<button onClick={() => navigate("/nostr-address")}>
|
<button type="button" onClick={() => navigate("/nostr-address")}>
|
||||||
<FormattedMessage defaultMessage="Buy Handle" />
|
<FormattedMessage defaultMessage="Buy Handle" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
@ -34,7 +34,7 @@ export default function TransferHandle({ handle }: { handle: ManageHandle }) {
|
|||||||
<FormattedMessage defaultMessage="Transfer to Pubkey" />
|
<FormattedMessage defaultMessage="Transfer to Pubkey" />
|
||||||
</h4>
|
</h4>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="f-grow">
|
<div className="grow">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="w-max mr10"
|
className="w-max mr10"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { Outlet, RouteObject, useNavigate } from "react-router-dom";
|
import { Outlet, RouteObject, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import ListHandles from "./ListHandles";
|
import ListHandles from "./ListHandles";
|
||||||
|
@ -51,7 +51,7 @@ const ConnectCashu = () => {
|
|||||||
<FormattedMessage defaultMessage="Enter mint URL" />
|
<FormattedMessage defaultMessage="Enter mint URL" />
|
||||||
</h4>
|
</h4>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="f-grow mr10">
|
<div className="grow mr10">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Mint URL"
|
placeholder="Mint URL"
|
||||||
|
@ -57,7 +57,7 @@ const ConnectLNC = () => {
|
|||||||
<FormattedMessage defaultMessage="Enter pairing phrase" />
|
<FormattedMessage defaultMessage="Enter pairing phrase" />
|
||||||
</h4>
|
</h4>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="f-grow mr10">
|
<div className="grow mr10">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={formatMessage({ defaultMessage: "Pairing phrase" })}
|
placeholder={formatMessage({ defaultMessage: "Pairing phrase" })}
|
||||||
@ -78,7 +78,7 @@ const ConnectLNC = () => {
|
|||||||
function flowSetPassword() {
|
function flowSetPassword() {
|
||||||
if (!connectedLNC) return null;
|
if (!connectedLNC) return null;
|
||||||
return (
|
return (
|
||||||
<div className="flex f-col">
|
<div className="flex flex-col">
|
||||||
<h3>
|
<h3>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Connected to: {node} 🎉"
|
defaultMessage="Connected to: {node} 🎉"
|
||||||
@ -91,7 +91,7 @@ const ConnectLNC = () => {
|
|||||||
<FormattedMessage defaultMessage="Enter password" />
|
<FormattedMessage defaultMessage="Enter password" />
|
||||||
</h4>
|
</h4>
|
||||||
<div className="flex w-max">
|
<div className="flex w-max">
|
||||||
<div className="f-grow mr10">
|
<div className="grow mr10">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
placeholder={formatMessage({ defaultMessage: "Wallet password" })}
|
placeholder={formatMessage({ defaultMessage: "Wallet password" })}
|
||||||
|
@ -49,7 +49,7 @@ const ConnectLNDHub = () => {
|
|||||||
<FormattedMessage defaultMessage="Enter LNDHub config" />
|
<FormattedMessage defaultMessage="Enter LNDHub config" />
|
||||||
</h4>
|
</h4>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="f-grow mr10">
|
<div className="grow mr10">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="lndhub://username:password@lndhub.io"
|
placeholder="lndhub://username:password@lndhub.io"
|
||||||
|
@ -49,7 +49,7 @@ const ConnectNostrWallet = () => {
|
|||||||
<FormattedMessage defaultMessage="Enter Nostr Wallet Connect config" />
|
<FormattedMessage defaultMessage="Enter Nostr Wallet Connect config" />
|
||||||
</h4>
|
</h4>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="f-grow mr10">
|
<div className="grow mr10">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="nostr+walletconnect:<pubkey>?relay=<relay>&secret=<secret>"
|
placeholder="nostr+walletconnect:<pubkey>?relay=<relay>&secret=<secret>"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import PageSpinner from "Element/PageSpinner";
|
import PageSpinner from "Element/PageSpinner";
|
||||||
@ -33,7 +33,7 @@ export default function ManageSubscriptionPage() {
|
|||||||
return <PageSpinner />;
|
return <PageSpinner />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="main-content p flex-column g16">
|
<div className="main-content p flex flex-col g16">
|
||||||
<h2>
|
<h2>
|
||||||
<FormattedMessage defaultMessage="Subscriptions" />
|
<FormattedMessage defaultMessage="Subscriptions" />
|
||||||
</h2>
|
</h2>
|
||||||
|
@ -120,7 +120,11 @@ export default function SubscriptionCard({ sub }: { sub: Subscription }) {
|
|||||||
<div className="flex flex-col g4">
|
<div className="flex flex-col g4">
|
||||||
<span> </span>
|
<span> </span>
|
||||||
<AsyncButton onClick={() => renew(sub.id, months)}>
|
<AsyncButton onClick={() => renew(sub.id, months)}>
|
||||||
{isExpired ? <FormattedMessage defaultMessage="Renew" /> : <FormattedMessage defaultMessage="Pay Now" />}
|
{isExpired ? (
|
||||||
|
<FormattedMessage defaultMessage="Renew" />
|
||||||
|
) : (
|
||||||
|
<FormattedMessage defaultMessage="Pay Now" />
|
||||||
|
)}
|
||||||
</AsyncButton>
|
</AsyncButton>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col g4">
|
<div className="flex flex-col g4">
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user