@ -14,7 +14,7 @@
|
||||
.write-dm {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
background-color: var(--gray-light);
|
||||
background-color: var(--gray);
|
||||
width: inherit;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
|
@ -7,24 +7,24 @@ import messages from "./messages";
|
||||
|
||||
import "./Verification.css";
|
||||
|
||||
export default function VerificationPage() {
|
||||
const services = [
|
||||
{
|
||||
name: "Snort",
|
||||
service: `${ApiHost}/api/v1/n5sp`,
|
||||
link: "https://snort.social/",
|
||||
supportLink: "https://snort.social/help",
|
||||
about: <FormattedMessage {...messages.SnortSocialNip} />,
|
||||
},
|
||||
{
|
||||
name: "Nostr Plebs",
|
||||
service: "https://nostrplebs.com/api/v1",
|
||||
link: "https://nostrplebs.com/",
|
||||
supportLink: "https://nostrplebs.com/manage",
|
||||
about: <FormattedMessage {...messages.NostrPlebsNip} />,
|
||||
},
|
||||
];
|
||||
export const services = [
|
||||
{
|
||||
name: "Snort",
|
||||
service: `${ApiHost}/api/v1/n5sp`,
|
||||
link: "https://snort.social/",
|
||||
supportLink: "https://snort.social/help",
|
||||
about: <FormattedMessage {...messages.SnortSocialNip} />,
|
||||
},
|
||||
{
|
||||
name: "Nostr Plebs",
|
||||
service: "https://nostrplebs.com/api/v1",
|
||||
link: "https://nostrplebs.com/",
|
||||
supportLink: "https://nostrplebs.com/manage",
|
||||
about: <FormattedMessage {...messages.NostrPlebsNip} />,
|
||||
},
|
||||
];
|
||||
|
||||
export default function VerificationPage() {
|
||||
return (
|
||||
<div className="main-content verification">
|
||||
<h2>
|
||||
|
@ -1,21 +1,32 @@
|
||||
import { useIntl, FormattedMessage } from "react-intl";
|
||||
import { Link } from "react-router-dom";
|
||||
import { RecommendedFollows } from "Const";
|
||||
import FollowListBase from "Element/FollowListBase";
|
||||
import { useMemo } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import messages from "./messages";
|
||||
|
||||
export default function DiscoverFollows() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const sortedRecommends = useMemo(() => {
|
||||
const { formatMessage } = useIntl();
|
||||
const sortedReccomends = useMemo(() => {
|
||||
return RecommendedFollows.sort(() => (Math.random() >= 0.5 ? -1 : 1));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Follow some popular accounts</h2>
|
||||
<button onClick={() => navigate("/")}>Skip</button>
|
||||
{sortedRecommends.length > 0 && <FollowListBase pubkeys={sortedRecommends} />}
|
||||
<button onClick={() => navigate("/")}>Done!</button>
|
||||
</>
|
||||
<div className="main-content new-user">
|
||||
<div className="progress-bar">
|
||||
<div className="progress"></div>
|
||||
</div>
|
||||
<h1>
|
||||
<FormattedMessage {...messages.Ready} />
|
||||
</h1>
|
||||
<p>
|
||||
<FormattedMessage {...messages.Share} values={{ link: <Link to="/">{formatMessage(messages.World)}</Link> }} />
|
||||
</p>
|
||||
<h3>
|
||||
<FormattedMessage {...messages.PopularAccounts} />
|
||||
</h3>
|
||||
{sortedReccomends.length > 0 && <FollowListBase pubkeys={sortedReccomends} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
109
src/Pages/new/GetVerified.tsx
Normal file
109
src/Pages/new/GetVerified.tsx
Normal file
@ -0,0 +1,109 @@
|
||||
import { useState } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import { services } from "Pages/Verification";
|
||||
import Nip5Service from "Element/Nip5Service";
|
||||
import ProfileImage from "Element/ProfileImage";
|
||||
import type { RootState } from "State/Store";
|
||||
import { useUserProfile } from "Feed/ProfileFeed";
|
||||
|
||||
import messages from "./messages";
|
||||
|
||||
export default function GetVerified() {
|
||||
const navigate = useNavigate();
|
||||
const { publicKey } = useSelector((s: RootState) => s.login);
|
||||
const user = useUserProfile(publicKey);
|
||||
const [isVerified, setIsVerified] = useState(false);
|
||||
const name = user?.name || "nostrich";
|
||||
const [nip05, setNip05] = useState(`${name}@snort.social`);
|
||||
|
||||
const onNext = async () => {
|
||||
navigate("/new/import");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="main-content new-user">
|
||||
<div className="progress-bar">
|
||||
<div className="progress progress-third"></div>
|
||||
</div>
|
||||
<h1>
|
||||
<FormattedMessage {...messages.Identifier} />
|
||||
</h1>
|
||||
<h4>
|
||||
<FormattedMessage {...messages.PreviewOnSnort} />
|
||||
</h4>
|
||||
<div className="profile-preview-nip">
|
||||
{publicKey && <ProfileImage pubkey={publicKey} defaultNip={nip05} verifyNip={false} />}
|
||||
</div>
|
||||
<p>
|
||||
<FormattedMessage {...messages.IdentifierHelp} />
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<FormattedMessage {...messages.PreventFakes} />
|
||||
</li>
|
||||
<li>
|
||||
<FormattedMessage {...messages.EasierToFind} />
|
||||
</li>
|
||||
<li>
|
||||
<FormattedMessage {...messages.Funding} />
|
||||
</li>
|
||||
</ul>
|
||||
<p className="warning">
|
||||
<FormattedMessage {...messages.NameSquatting} />
|
||||
</p>
|
||||
{!isVerified && (
|
||||
<>
|
||||
<h2>
|
||||
<FormattedMessage {...messages.GetSnortId} />
|
||||
</h2>
|
||||
<p>
|
||||
<FormattedMessage {...messages.GetSnortIdHelp} />
|
||||
</p>
|
||||
<div className="nip-container">
|
||||
<Nip5Service
|
||||
key="snort"
|
||||
{...services[0]}
|
||||
helpText={false}
|
||||
onChange={setNip05}
|
||||
onSuccess={() => setIsVerified(true)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{!isVerified && (
|
||||
<>
|
||||
<h2>
|
||||
<FormattedMessage {...messages.GetPartnerId} />
|
||||
</h2>
|
||||
<p>
|
||||
<FormattedMessage {...messages.GetPartnerIdHelp} />
|
||||
</p>
|
||||
<div className="nip-container">
|
||||
<Nip5Service
|
||||
key="nostrplebs"
|
||||
{...services[1]}
|
||||
helpText={false}
|
||||
onChange={setNip05}
|
||||
onSuccess={() => setIsVerified(true)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="next-actions">
|
||||
{!isVerified && (
|
||||
<button type="button" className="transparent" onClick={onNext}>
|
||||
<FormattedMessage {...messages.Skip} />
|
||||
</button>
|
||||
)}
|
||||
{isVerified && (
|
||||
<button type="button" onClick={onNext}>
|
||||
<FormattedMessage {...messages.Next} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useIntl, FormattedMessage } from "react-intl";
|
||||
|
||||
import { ApiHost } from "Const";
|
||||
import AsyncButton from "Element/AsyncButton";
|
||||
@ -8,11 +9,14 @@ import { RootState } from "State/Store";
|
||||
import { bech32ToHex } from "Util";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import messages from "./messages";
|
||||
|
||||
const TwitterFollowsApi = `${ApiHost}/api/v1/twitter/follows-for-nostr`;
|
||||
|
||||
export default function ImportFollows() {
|
||||
const navigate = useNavigate();
|
||||
const currentFollows = useSelector((s: RootState) => s.login.follows);
|
||||
const { formatMessage } = useIntl();
|
||||
const [twitterUsername, setTwitterUsername] = useState<string>("");
|
||||
const [follows, setFollows] = useState<string[]>([]);
|
||||
const [error, setError] = useState<string>("");
|
||||
@ -29,45 +33,76 @@ export default function ImportFollows() {
|
||||
const data = await rsp.json();
|
||||
if (rsp.ok) {
|
||||
if (Array.isArray(data) && data.length === 0) {
|
||||
setError(`No nostr users found for "${twitterUsername}"`);
|
||||
setError(formatMessage(messages.NoUsersFound, { twitterUsername }));
|
||||
} else {
|
||||
setFollows(data);
|
||||
}
|
||||
} else if ("error" in data) {
|
||||
setError(data.error);
|
||||
} else {
|
||||
setError("Failed to load follows, please try again later");
|
||||
setError(formatMessage(messages.FailedToLoad));
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
setError("Failed to load follows, please try again later");
|
||||
setError(formatMessage(messages.FailedToLoad));
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Import Twitter Follows</h2>
|
||||
<div className="main-content new-user">
|
||||
<div className="progress-bar">
|
||||
<div className="progress progress-last"></div>
|
||||
</div>
|
||||
<h1>
|
||||
<FormattedMessage {...messages.ImportTwitter} />
|
||||
</h1>
|
||||
<p>
|
||||
Find your twitter follows on nostr (Data provided by{" "}
|
||||
<a href="https://nostr.directory" target="_blank" rel="noreferrer">
|
||||
nostr.directory
|
||||
</a>
|
||||
)
|
||||
<FormattedMessage
|
||||
{...messages.FindYourFollows}
|
||||
values={{
|
||||
provider: (
|
||||
<a href="https://nostr.directory" target="_blank" rel="noreferrer">
|
||||
nostr.directory
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<h2>
|
||||
<FormattedMessage {...messages.TwitterUsername} />
|
||||
</h2>
|
||||
<div className="flex">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Twitter username.."
|
||||
placeholder={formatMessage(messages.TwitterPlaceholder)}
|
||||
className="f-grow mr10"
|
||||
value={twitterUsername}
|
||||
onChange={e => setTwitterUsername(e.target.value)}
|
||||
/>
|
||||
<AsyncButton onClick={loadFollows}>Check</AsyncButton>
|
||||
<AsyncButton type="button" className="secondary tall" onClick={loadFollows}>
|
||||
<FormattedMessage {...messages.Check} />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
{error.length > 0 && <b className="error">{error}</b>}
|
||||
{sortedTwitterFollows.length > 0 && <FollowListBase pubkeys={sortedTwitterFollows} />}
|
||||
{sortedTwitterFollows.length > 0 && (
|
||||
<FollowListBase
|
||||
title={
|
||||
<h2>
|
||||
<FormattedMessage {...messages.FollowsOnNostr} values={{ username: twitterUsername }} />
|
||||
</h2>
|
||||
}
|
||||
pubkeys={sortedTwitterFollows}
|
||||
/>
|
||||
)}
|
||||
|
||||
<button onClick={() => navigate("/new/discover")}>Next</button>
|
||||
</>
|
||||
<div className="next-actions">
|
||||
<button className="secondary" type="button" onClick={() => navigate("/new/discover")}>
|
||||
<FormattedMessage {...messages.Skip} />
|
||||
</button>
|
||||
<button type="button" onClick={() => navigate("/new/discover")}>
|
||||
<FormattedMessage {...messages.Next} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
import ProfileSettings from "Pages/settings/Profile";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
export default function NewUserProfile() {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<>
|
||||
<h1>Setup your Profile</h1>
|
||||
<ProfileSettings privateKey={false} banner={false} />
|
||||
<button onClick={() => navigate("/new/import")}>Next</button>
|
||||
</>
|
||||
);
|
||||
}
|
102
src/Pages/new/NewUserFlow.tsx
Normal file
102
src/Pages/new/NewUserFlow.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { CollapsedSection } from "Element/Collapsed";
|
||||
import Copy from "Element/Copy";
|
||||
import { RootState } from "State/Store";
|
||||
import { hexToBech32 } from "Util";
|
||||
|
||||
import messages from "./messages";
|
||||
|
||||
const WhatIsSnort = () => {
|
||||
return (
|
||||
<CollapsedSection title={<FormattedMessage {...messages.WhatIsSnort} />}>
|
||||
<p>
|
||||
<FormattedMessage {...messages.WhatIsSnortIntro} />
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage {...messages.WhatIsSnortNotes} />
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage {...messages.WhatIsSnortExperience} />
|
||||
</p>
|
||||
</CollapsedSection>
|
||||
);
|
||||
};
|
||||
|
||||
const HowDoKeysWork = () => {
|
||||
return (
|
||||
<CollapsedSection title={<FormattedMessage {...messages.HowKeysWork} />}>
|
||||
<p>
|
||||
<FormattedMessage {...messages.DigitalSignatures} />
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage {...messages.TamperProof} />
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage {...messages.Bitcoin} />
|
||||
</p>
|
||||
</CollapsedSection>
|
||||
);
|
||||
};
|
||||
|
||||
const Extensions = () => {
|
||||
return (
|
||||
<CollapsedSection title={<FormattedMessage {...messages.ImproveSecurity} />}>
|
||||
<p>
|
||||
<FormattedMessage {...messages.Extensions} />
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://getalby.com/" target="_blank" rel="noreferrer">
|
||||
Alby
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/fiatjaf/nos2x" target="_blank" rel="noreferrer">
|
||||
nos2x
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<FormattedMessage {...messages.ExtensionsNostr} />
|
||||
</p>
|
||||
</CollapsedSection>
|
||||
);
|
||||
};
|
||||
|
||||
export default function NewUserFlow() {
|
||||
const { publicKey, privateKey } = useSelector((s: RootState) => s.login);
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className="main-content new-user">
|
||||
<div className="progress-bar">
|
||||
<div className="progress progress-first"></div>
|
||||
</div>
|
||||
<h1>
|
||||
<FormattedMessage {...messages.SaveKeys} />
|
||||
</h1>
|
||||
<p>
|
||||
<FormattedMessage {...messages.SaveKeysHelp} />
|
||||
</p>
|
||||
<h2>
|
||||
<FormattedMessage {...messages.YourPubkey} />
|
||||
</h2>
|
||||
<Copy text={hexToBech32("npub", publicKey ?? "")} />
|
||||
<h2>
|
||||
<FormattedMessage {...messages.YourPrivkey} />
|
||||
</h2>
|
||||
<Copy text={hexToBech32("nsec", privateKey ?? "")} />
|
||||
<div className="next-actions">
|
||||
<button type="button" onClick={() => navigate("/new/username")}>
|
||||
<FormattedMessage {...messages.KeysSaved} />{" "}
|
||||
</button>
|
||||
</div>
|
||||
<WhatIsSnort />
|
||||
<HowDoKeysWork />
|
||||
<Extensions />
|
||||
</div>
|
||||
);
|
||||
}
|
55
src/Pages/new/NewUsername.tsx
Normal file
55
src/Pages/new/NewUsername.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import { useState } from "react";
|
||||
import { useIntl, FormattedMessage } from "react-intl";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
|
||||
import messages from "./messages";
|
||||
|
||||
export default function NewUserName() {
|
||||
const [username, setUsername] = useState("");
|
||||
const { formatMessage } = useIntl();
|
||||
const publisher = useEventPublisher();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onNext = async () => {
|
||||
if (username.length > 0) {
|
||||
const ev = await publisher.metadata({ name: username });
|
||||
console.debug(ev);
|
||||
publisher.broadcast(ev);
|
||||
}
|
||||
navigate("/new/verify");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="main-content new-user">
|
||||
<div className="progress-bar">
|
||||
<div className="progress progress-second"></div>
|
||||
</div>
|
||||
<h1>
|
||||
<FormattedMessage {...messages.PickUsername} />
|
||||
</h1>
|
||||
<p>
|
||||
<FormattedMessage {...messages.UsernameHelp} />
|
||||
</p>
|
||||
<h2>
|
||||
<FormattedMessage {...messages.Username} />
|
||||
</h2>
|
||||
<input
|
||||
className="username"
|
||||
placeholder={formatMessage(messages.UsernamePlaceholder)}
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={ev => setUsername(ev.target.value)}
|
||||
/>
|
||||
<div className="next-actions">
|
||||
<button type="button" className="transparent" onClick={() => navigate("/new/verify")}>
|
||||
<FormattedMessage {...messages.Skip} />
|
||||
</button>
|
||||
<button type="button" onClick={onNext}>
|
||||
<FormattedMessage {...messages.Next} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
151
src/Pages/new/index.css
Normal file
151
src/Pages/new/index.css
Normal file
@ -0,0 +1,151 @@
|
||||
.new-user p {
|
||||
color: var(--font-secondary-color);
|
||||
}
|
||||
.new-user li {
|
||||
color: var(--font-secondary-color);
|
||||
}
|
||||
|
||||
.new-user p > a {
|
||||
color: var(--highlight);
|
||||
}
|
||||
|
||||
.new-user li > a {
|
||||
color: var(--highlight);
|
||||
}
|
||||
|
||||
.new-user h1 {
|
||||
color: var(--font-color);
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
line-height: 39px;
|
||||
}
|
||||
|
||||
.new-user h2 {
|
||||
color: var(--font-color);
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
.new-user h3 {
|
||||
color: var(--font-color);
|
||||
font-weight: 700;
|
||||
font-size: 21px;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
.new-user h4 {
|
||||
color: var(--font-secondary-color);
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
line-height: 19px;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 7px;
|
||||
background: var(--gray-secondary);
|
||||
border-radius: 53px;
|
||||
}
|
||||
|
||||
.progress-bar .progress {
|
||||
height: 7px;
|
||||
background: var(--snort-gradient);
|
||||
border-radius: 53px;
|
||||
}
|
||||
|
||||
.progress.progress-first {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.progress.progress-second {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.progress.progress-third {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.progress.progress-last {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.new-user .next-actions {
|
||||
margin-top: 32px;
|
||||
margin-bottom: 64px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.new-user .next-actions button:not(:last-child) {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.new-user > .copy {
|
||||
padding: 12px 16px;
|
||||
border: 2px dashed #222222;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.light .new-user > .copy {
|
||||
border: 2px dashed #aaaaaa;
|
||||
}
|
||||
|
||||
.new-user > .copy .body {
|
||||
font-size: 16px;
|
||||
}
|
||||
@media (max-width: 520px) {
|
||||
.new-user > .copy .body {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.new-user > .copy .icon {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.new-user > .copy .icon svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.new-user input {
|
||||
width: 100%;
|
||||
max-width: 568px;
|
||||
background: #222;
|
||||
border: none;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.new-user input {
|
||||
width: calc(100vw - 40px);
|
||||
}
|
||||
}
|
||||
|
||||
.light .new-user input {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.new-user .warning {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
color: #fc6e1e;
|
||||
}
|
||||
|
||||
.profile-preview-nip {
|
||||
padding: 12px 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.light .profile-preview-nip {
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.new-user .nip-container input[type="text"] {
|
||||
width: 166px;
|
||||
}
|
@ -1,81 +1,36 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import { RouteObject, useNavigate } from "react-router-dom";
|
||||
import "./index.css";
|
||||
import { RouteObject } from "react-router-dom";
|
||||
|
||||
import Copy from "Element/Copy";
|
||||
import { RootState } from "State/Store";
|
||||
import { hexToBech32 } from "Util";
|
||||
import NewUserProfile from "Pages/new//NewProfile";
|
||||
import GetVerified from "Pages/new/GetVerified";
|
||||
import NewUserName from "Pages/new/NewUsername";
|
||||
import NewUserFlow from "Pages/new/NewUserFlow";
|
||||
import ImportFollows from "Pages/new/ImportFollows";
|
||||
import DiscoverFollows from "Pages/new/DiscoverFollows";
|
||||
|
||||
const USERNAME = "/new/username";
|
||||
const IMPORT = "/new/import";
|
||||
const DISCOVER = "/new/discover";
|
||||
const VERIFY = "/new/verify";
|
||||
|
||||
export const NewUserRoutes: RouteObject[] = [
|
||||
{
|
||||
path: "/new",
|
||||
element: <NewUserFlow />,
|
||||
},
|
||||
{
|
||||
path: "/new/profile",
|
||||
element: <NewUserProfile />,
|
||||
path: USERNAME,
|
||||
element: <NewUserName />,
|
||||
},
|
||||
{
|
||||
path: "/new/import",
|
||||
path: IMPORT,
|
||||
element: <ImportFollows />,
|
||||
},
|
||||
{
|
||||
path: "/new/discover",
|
||||
path: VERIFY,
|
||||
element: <GetVerified />,
|
||||
},
|
||||
{
|
||||
path: DISCOVER,
|
||||
element: <DiscoverFollows />,
|
||||
},
|
||||
];
|
||||
|
||||
export default function NewUserFlow() {
|
||||
const { privateKey } = useSelector((s: RootState) => s.login);
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Welcome to Snort!</h1>
|
||||
<p>Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing "notes".</p>
|
||||
<p>Notes hold text content, the most popular usage of these notes is to store "tweet like" messages.</p>
|
||||
<p>Snort is designed to have a similar experience to Twitter.</p>
|
||||
|
||||
<h2>Keys</h2>
|
||||
<p>
|
||||
Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many
|
||||
relays to provide redundant storage of your content.
|
||||
</p>
|
||||
<p>
|
||||
This means that nobody can modify notes which you have created and everybody can easily verify that the notes
|
||||
they are reading are created by you.
|
||||
</p>
|
||||
<p>This is the same technology which is used by Bitcoin and has been proven to be extremely secure.</p>
|
||||
|
||||
<h2>Your Key</h2>
|
||||
<p>
|
||||
When you want to author new notes you need to sign them with your private key, as with Bitcoin private keys
|
||||
these need to be kept secure.
|
||||
</p>
|
||||
<p>Please now copy your private key and save it somewhere secure:</p>
|
||||
<div className="card">
|
||||
<Copy text={hexToBech32("nsec", privateKey ?? "")} />
|
||||
</div>
|
||||
<p>
|
||||
It is also recommended to use one of the following browser extensions if you are on a desktop computer to secure
|
||||
your key:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://getalby.com/" target="_blank" rel="noreferrer">
|
||||
Alby
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/fiatjaf/nos2x" target="_blank" rel="noreferrer">
|
||||
nos2x
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p>You can also use these extensions to login to most Nostr sites.</p>
|
||||
<button onClick={() => navigate("/new/profile")}>Next</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
57
src/Pages/new/messages.js
Normal file
57
src/Pages/new/messages.js
Normal file
@ -0,0 +1,57 @@
|
||||
import { defineMessages } from "react-intl";
|
||||
|
||||
export default defineMessages({
|
||||
SaveKeys: "Save your keys!",
|
||||
SaveKeysHelp:
|
||||
"Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.",
|
||||
YourPubkey: "Your public key",
|
||||
YourPrivkey: "Your private key",
|
||||
KeysSaved: "I have saved my keys, continue",
|
||||
WhatIsSnort: "What is Snort and how does it work?",
|
||||
WhatIsSnortIntro: `Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing "notes".`,
|
||||
WhatIsSnortNotes: `Notes hold text content, the most popular usage of these notes is to store "tweet like" messages.`,
|
||||
|
||||
WhatIsSnortExperience: "Snort is designed to have a similar experience to Twitter.",
|
||||
HowKeysWork: "How do keys work?",
|
||||
DigitalSignatures: `Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.`,
|
||||
TamperProof: `This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.`,
|
||||
Bitcoin: `This is the same technology which is used by Bitcoin and has been proven to be extremely secure.`,
|
||||
Extensions: `It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:`,
|
||||
ExtensionsNostr: `You can also use these extensions to login to most Nostr sites.`,
|
||||
ImproveSecurity: "Improve login security with browser extensions",
|
||||
PickUsername: "Pick a username",
|
||||
UsernameHelp:
|
||||
"On Nostr, many people have the same username. User names and identity are separate things. You can get a unique identifier in the next step.",
|
||||
Username: "Username",
|
||||
UsernamePlaceholder: "e.g. Jack",
|
||||
PopularAccounts: "Follow some popular accounts",
|
||||
Skip: "Skip",
|
||||
Done: "Done!",
|
||||
ImportTwitter: "Import Twitter Follows (optional)",
|
||||
TwitterPlaceholder: "Twitter username...",
|
||||
FindYourFollows: "Find your twitter follows on nostr (Data provided by {provider})",
|
||||
TwitterUsername: "Twitter username",
|
||||
FollowsOnNostr: "{username}'s Follows on Nostr",
|
||||
NoUsersFound: "No nostr users found for {twitterUsername}",
|
||||
FailedToLoad: "Failed to load follows, please try again later",
|
||||
Check: "Check",
|
||||
Next: "Next",
|
||||
SetupProfile: "Setup your Profile",
|
||||
Identifier: "Get an identifier (optional)",
|
||||
IdentifierHelp:
|
||||
"Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.",
|
||||
PreventFakes: "Prevent fake accounts from imitating you",
|
||||
EasierToFind: "Make your profile easier to find and share",
|
||||
Funding: "Fund developers and platforms providing NIP-05 verification services",
|
||||
NameSquatting:
|
||||
"Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.",
|
||||
PreviewOnSnort: "Preview on snort",
|
||||
GetSnortId: "Get a Snort identifier",
|
||||
GetSnortIdHelp:
|
||||
"Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.",
|
||||
GetPartnerId: "Get a partner identifier",
|
||||
GetPartnerIdHelp: "We have also partnered with nostrplebs.com to give you more options",
|
||||
Ready: "You're ready!",
|
||||
Share: "Share your thoughts with {link}",
|
||||
World: "the world",
|
||||
});
|
Reference in New Issue
Block a user