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