Various
This commit is contained in:
@ -70,7 +70,7 @@ export function SnortDeckLayout() {
|
||||
</div>
|
||||
{deckScope.thread && (
|
||||
<>
|
||||
<Modal onClose={() => deckScope.setThread(undefined)} className="thread-overlay">
|
||||
<Modal id="thread-overlay" onClose={() => deckScope.setThread(undefined)} className="thread-overlay">
|
||||
<ThreadContextWrapper link={deckScope.thread}>
|
||||
<SpotlightFromThread onClose={() => deckScope.setThread(undefined)} />
|
||||
<div>
|
||||
|
@ -3,7 +3,7 @@ import { useParams } from "react-router-dom";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import Timeline from "Element/Timeline";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
import useEventPublisher from "Hooks/useEventPublisher";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
import { setTags } from "Login";
|
||||
import { System } from "index";
|
||||
|
@ -1,6 +1,5 @@
|
||||
import "./Layout.css";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Link, Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
@ -9,8 +8,6 @@ import { NostrLink, NostrPrefix, tryParseNostrLink } from "@snort/system";
|
||||
import messages from "./messages";
|
||||
|
||||
import Icon from "Icons/Icon";
|
||||
import { RootState } from "State/Store";
|
||||
import { setShow, reset } from "State/NoteCreator";
|
||||
import useLoginFeed from "Feed/LoginFeed";
|
||||
import { NoteCreator } from "Element/NoteCreator";
|
||||
import { mapPlanName } from "./subscribe";
|
||||
@ -23,29 +20,19 @@ import Spinner from "Icons/Spinner";
|
||||
import { fetchNip05Pubkey } from "Nip05/Verifier";
|
||||
import { useTheme } from "Hooks/useTheme";
|
||||
import { useLoginRelays } from "Hooks/useLoginRelays";
|
||||
import { useNoteCreator } from "State/NoteCreator";
|
||||
import { LoginUnlock } from "Element/PinPrompt";
|
||||
|
||||
export default function Layout() {
|
||||
const location = useLocation();
|
||||
const replyTo = useSelector((s: RootState) => s.noteCreator.replyTo);
|
||||
const isNoteCreatorShowing = useSelector((s: RootState) => s.noteCreator.show);
|
||||
const isReplyNoteCreatorShowing = replyTo && isNoteCreatorShowing;
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const { publicKey, subscriptions } = useLogin();
|
||||
const currentSubscription = getCurrentSubscription(subscriptions);
|
||||
const note = useNoteCreator();
|
||||
const isReplyNoteCreatorShowing = note.replyTo && note.show;
|
||||
const [pageClass, setPageClass] = useState("page");
|
||||
|
||||
useLoginFeed();
|
||||
useTheme();
|
||||
useLoginRelays();
|
||||
|
||||
const handleNoteCreatorButtonClick = () => {
|
||||
if (replyTo) {
|
||||
dispatch(reset());
|
||||
}
|
||||
dispatch(setShow(true));
|
||||
};
|
||||
|
||||
const shouldHideNoteCreator = useMemo(() => {
|
||||
const hideOn = ["/settings", "/messages", "/new", "/login", "/donate", "/e", "/subscribe"];
|
||||
return isReplyNoteCreatorShowing || hideOn.some(a => location.pathname.startsWith(a));
|
||||
@ -66,34 +53,22 @@ export default function Layout() {
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
return (
|
||||
return (<>
|
||||
<div className={pageClass}>
|
||||
{!shouldHideHeader && (
|
||||
<header className="main-content">
|
||||
<Link to="/" className="logo">
|
||||
<h1>Snort</h1>
|
||||
{currentSubscription && (
|
||||
<small className="flex">
|
||||
<Icon name="diamond" size={10} className="mr5" />
|
||||
{mapPlanName(currentSubscription.type)}
|
||||
</small>
|
||||
)}
|
||||
</Link>
|
||||
|
||||
{publicKey ? (
|
||||
<AccountHeader />
|
||||
) : (
|
||||
<button type="button" onClick={() => navigate("/login")}>
|
||||
<FormattedMessage {...messages.Login} />
|
||||
</button>
|
||||
)}
|
||||
<LogoHeader />
|
||||
<AccountHeader />
|
||||
</header>
|
||||
)}
|
||||
<Outlet />
|
||||
|
||||
{!shouldHideNoteCreator && (
|
||||
<>
|
||||
<button className="primary note-create-button" onClick={handleNoteCreatorButtonClick}>
|
||||
<button className="primary note-create-button" onClick={() => note.update(v => {
|
||||
v.replyTo = undefined;
|
||||
v.show = true
|
||||
})}>
|
||||
<Icon name="plus" size={16} />
|
||||
</button>
|
||||
<NoteCreator />
|
||||
@ -101,6 +76,8 @@ export default function Layout() {
|
||||
)}
|
||||
<Toaster />
|
||||
</div>
|
||||
<LoginUnlock />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -156,6 +133,13 @@ const AccountHeader = () => {
|
||||
}
|
||||
}
|
||||
|
||||
if (!publicKey) {
|
||||
return (
|
||||
<button type="button" onClick={() => navigate("/login")}>
|
||||
<FormattedMessage {...messages.Login} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className="header-actions">
|
||||
{!location.pathname.startsWith("/search") && (
|
||||
@ -199,3 +183,20 @@ const AccountHeader = () => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function LogoHeader() {
|
||||
const { subscriptions } = useLogin();
|
||||
const currentSubscription = getCurrentSubscription(subscriptions);
|
||||
|
||||
return (
|
||||
<Link to="/" className="logo">
|
||||
<h1>Snort</h1>
|
||||
{currentSubscription && (
|
||||
<small className="flex">
|
||||
<Icon name="diamond" size={10} className="mr5" />
|
||||
{mapPlanName(currentSubscription.type)}
|
||||
</small>
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
}
|
@ -12,13 +12,14 @@ import Icon from "Icons/Icon";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
import { generateNewLogin, LoginSessionType, LoginStore } from "Login";
|
||||
import AsyncButton from "Element/AsyncButton";
|
||||
import useLoginHandler from "Hooks/useLoginHandler";
|
||||
import useLoginHandler, { PinRequiredError } from "Hooks/useLoginHandler";
|
||||
import { secp256k1 } from "@noble/curves/secp256k1";
|
||||
import { bytesToHex } from "@noble/curves/abstract/utils";
|
||||
import Modal from "Element/Modal";
|
||||
import QrCode from "Element/QrCode";
|
||||
import Copy from "Element/Copy";
|
||||
import { delay } from "SnortUtils";
|
||||
import { PinPrompt } from "Element/PinPrompt";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@ -78,6 +79,7 @@ export default function LoginPage() {
|
||||
const login = useLogin();
|
||||
const [key, setKey] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const [pin, setPin] = useState(false);
|
||||
const [art, setArt] = useState<ArtworkEntry>();
|
||||
const [isMasking, setMasking] = useState(true);
|
||||
const { formatMessage } = useIntl();
|
||||
@ -99,10 +101,13 @@ export default function LoginPage() {
|
||||
setArt({ ...ret, link: url });
|
||||
}, []);
|
||||
|
||||
async function doLogin() {
|
||||
async function doLogin(pin?: string) {
|
||||
try {
|
||||
await loginHandler.doLogin(key);
|
||||
await loginHandler.doLogin(key, pin);
|
||||
} catch (e) {
|
||||
if (e instanceof PinRequiredError) {
|
||||
setPin(true);
|
||||
}
|
||||
if (e instanceof Error) {
|
||||
setError(e.message);
|
||||
} else {
|
||||
@ -116,10 +121,16 @@ export default function LoginPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function makeRandomKey() {
|
||||
await generateNewLogin();
|
||||
window.plausible?.("Generate Account");
|
||||
navigate("/new");
|
||||
async function makeRandomKey(pin: string) {
|
||||
try {
|
||||
await generateNewLogin(pin);
|
||||
window.plausible?.("Generate Account");
|
||||
navigate("/new");
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
setError(e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function doNip07Login() {
|
||||
@ -157,7 +168,7 @@ export default function LoginPage() {
|
||||
<FormattedMessage defaultMessage="Nostr Connect (NIP-46)" description="Login button for NIP-46 signer app" />
|
||||
</AsyncButton>
|
||||
{nostrConnect && (
|
||||
<Modal onClose={() => setNostrConnect("")}>
|
||||
<Modal id="nostr-connect" onClose={() => setNostrConnect("")}>
|
||||
<div className="flex f-col">
|
||||
<QrCode data={nostrConnect} />
|
||||
<Copy text={nostrConnect} />
|
||||
@ -283,12 +294,19 @@ export default function LoginPage() {
|
||||
/>
|
||||
</p>
|
||||
<div dir="auto" className="login-actions">
|
||||
<AsyncButton type="button" onClick={doLogin}>
|
||||
<AsyncButton type="button" onClick={() => doLogin()}>
|
||||
<FormattedMessage defaultMessage="Login" description="Login button" />
|
||||
</AsyncButton>
|
||||
<AsyncButton onClick={() => makeRandomKey()}>
|
||||
<AsyncButton onClick={() => setPin(true)}>
|
||||
<FormattedMessage defaultMessage="Create Account" />
|
||||
</AsyncButton>
|
||||
{pin && <PinPrompt onResult={pin => {
|
||||
if (key) {
|
||||
doLogin(pin);
|
||||
} else {
|
||||
makeRandomKey(pin);
|
||||
}
|
||||
}} onCancel={() => setPin(false)} />}
|
||||
{altLogins()}
|
||||
</div>
|
||||
{installExtension()}
|
||||
|
@ -210,7 +210,7 @@ function NewChatWindow() {
|
||||
<Icon name="plus" size={16} />
|
||||
</button>
|
||||
{show && (
|
||||
<Modal onClose={() => setShow(false)} className="new-chat-modal">
|
||||
<Modal id="new-chat" onClose={() => setShow(false)} className="new-chat-modal">
|
||||
<div className="flex-column g16">
|
||||
<div className="flex f-space">
|
||||
<h2>
|
||||
|
@ -326,14 +326,14 @@ export default function ProfilePage() {
|
||||
targets={
|
||||
lnurl?.lnurl && id
|
||||
? [
|
||||
{
|
||||
type: "lnurl",
|
||||
value: lnurl?.lnurl,
|
||||
weight: 1,
|
||||
name: user?.display_name || user?.name,
|
||||
zap: { pubkey: id },
|
||||
} as ZapTarget,
|
||||
]
|
||||
{
|
||||
type: "lnurl",
|
||||
value: lnurl?.lnurl,
|
||||
weight: 1,
|
||||
name: user?.display_name || user?.name,
|
||||
zap: { pubkey: id },
|
||||
} as ZapTarget,
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
show={showLnQr}
|
||||
@ -447,7 +447,7 @@ export default function ProfilePage() {
|
||||
<Icon name="qr" size={16} />
|
||||
</IconButton>
|
||||
{showProfileQr && (
|
||||
<Modal className="qr-modal" onClose={() => setShowProfileQr(false)}>
|
||||
<Modal id="profile-qr" className="qr-modal" onClose={() => setShowProfileQr(false)}>
|
||||
<ProfileImage pubkey={id} />
|
||||
<QrCode data={link} className="m10 align-center" />
|
||||
<Copy text={link} className="align-center" />
|
||||
|
@ -5,7 +5,7 @@ import { mapEventToProfile } from "@snort/system";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
|
||||
import Logo from "Element/Logo";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
import useEventPublisher from "Hooks/useEventPublisher";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
import { UserCache } from "Cache";
|
||||
import AvatarEditor from "Element/AvatarEditor";
|
||||
|
@ -6,7 +6,7 @@ import { mapEventToProfile } from "@snort/system";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
|
||||
import { System } from "index";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
import useEventPublisher from "Hooks/useEventPublisher";
|
||||
import { openFile } from "SnortUtils";
|
||||
import useFileUpload from "Upload";
|
||||
import AsyncButton from "Element/AsyncButton";
|
||||
|
@ -4,7 +4,7 @@ import { unixNowMs } from "@snort/shared";
|
||||
|
||||
import { randomSample } from "SnortUtils";
|
||||
import Relay from "Element/Relay";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
import useEventPublisher from "Hooks/useEventPublisher";
|
||||
import { System } from "index";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
import { setRelays } from "Login";
|
||||
|
@ -5,7 +5,6 @@ import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
import Icon from "Icons/Icon";
|
||||
import { LoginStore, logout } from "Login";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
import { unwrap } from "SnortUtils";
|
||||
import { getCurrentSubscription } from "Subscription";
|
||||
|
||||
import messages from "./messages";
|
||||
@ -19,7 +18,7 @@ const SettingsIndex = () => {
|
||||
const sub = getCurrentSubscription(LoginStore.allSubscriptions());
|
||||
|
||||
function handleLogout() {
|
||||
logout(unwrap(login.publicKey));
|
||||
logout(login.id);
|
||||
navigate("/");
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ import { LNURL } from "@snort/shared";
|
||||
|
||||
import { ApiHost } from "Const";
|
||||
import AsyncButton from "Element/AsyncButton";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
import useEventPublisher from "Hooks/useEventPublisher";
|
||||
import SnortServiceProvider, { ManageHandle } from "Nip05/SnortServiceProvider";
|
||||
|
||||
export default function LNForwardAddress({ handle }: { handle: ManageHandle }) {
|
||||
|
@ -3,7 +3,7 @@ import { FormattedMessage } from "react-intl";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
|
||||
import { ApiHost } from "Const";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
import useEventPublisher from "Hooks/useEventPublisher";
|
||||
import SnortServiceProvider, { ManageHandle } from "Nip05/SnortServiceProvider";
|
||||
|
||||
export default function ListHandles() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ApiHost } from "Const";
|
||||
import AsyncButton from "Element/AsyncButton";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
import useEventPublisher from "Hooks/useEventPublisher";
|
||||
import { ServiceError } from "Nip05/ServiceProvider";
|
||||
import SnortServiceProvider, { ManageHandle } from "Nip05/SnortServiceProvider";
|
||||
import { useState } from "react";
|
||||
|
@ -3,7 +3,7 @@ import { FormattedMessage } from "react-intl";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
|
||||
import PageSpinner from "Element/PageSpinner";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
import useEventPublisher from "Hooks/useEventPublisher";
|
||||
import SnortApi, { Subscription, SubscriptionError } from "SnortApi";
|
||||
import { mapSubscriptionErrorCode } from ".";
|
||||
import SubscriptionCard from "./SubscriptionCard";
|
||||
|
@ -5,7 +5,7 @@ import SnortApi, { Subscription, SubscriptionError } from "SnortApi";
|
||||
import { mapPlanName, mapSubscriptionErrorCode } from ".";
|
||||
import AsyncButton from "Element/AsyncButton";
|
||||
import Icon from "Icons/Icon";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
import useEventPublisher from "Hooks/useEventPublisher";
|
||||
import SendSats from "Element/SendSats";
|
||||
import Nip5Service from "Element/Nip5Service";
|
||||
import { SnortNostrAddressService } from "Pages/NostrAddressPage";
|
||||
|
@ -8,7 +8,7 @@ import { formatShort } from "Number";
|
||||
import { LockedFeatures, Plans, SubscriptionType } from "Subscription";
|
||||
import ManageSubscriptionPage from "Pages/subscribe/ManageSubscription";
|
||||
import AsyncButton from "Element/AsyncButton";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
import useEventPublisher from "Hooks/useEventPublisher";
|
||||
import SnortApi, { SubscriptionError, SubscriptionErrorCode } from "SnortApi";
|
||||
import SendSats from "Element/SendSats";
|
||||
|
||||
|
Reference in New Issue
Block a user