Iris nip-05 registration #638

Merged
Kieran merged 7 commits from mmalmi/snort:main into main 2023-10-04 10:44:26 +00:00
8 changed files with 94 additions and 17 deletions
Showing only changes of commit 2efef99c95 - Show all commits

View File

@ -1,14 +1,15 @@
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import FormattedMessage from "Element/FormattedMessage";
export default function AccountName({ name = "", link = true }) { export default function AccountName({ name = "", link = true }) {
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
<> <>
<div> <div>
Kieran marked this conversation as resolved
Review

All the strings need to be <FormattedMessage/>

All the strings need to be `<FormattedMessage/>`
Username: <b>{name}</b> <FormattedMessage defaultMessage="Username" />: <b>{name}</b>
</div> </div>
<div> <div>
Short link:{" "} <FormattedMessage defaultMessage="Short link" />:{" "}
{link ? ( {link ? (
<a <a
href={`https://iris.to/${name}`} href={`https://iris.to/${name}`}
@ -23,7 +24,7 @@ export default function AccountName({ name = "", link = true }) {
)} )}
</div> </div>
<div> <div>
Nostr address (nip05): <b>{name}@iris.to</b> <FormattedMessage defaultMessage="Nostr address (nip05)" />: <b>{name}@iris.to</b>
</div> </div>
</> </>
); );

View File

@ -5,6 +5,7 @@ import { System } from "../../index";
import { UserCache } from "../../Cache"; import { UserCache } from "../../Cache";
import useEventPublisher from "../../Hooks/useEventPublisher"; import useEventPublisher from "../../Hooks/useEventPublisher";
import { mapEventToProfile } from "@snort/system"; import { mapEventToProfile } from "@snort/system";
import FormattedMessage from "Element/FormattedMessage";
export default function ActiveAccount({ name = "", setAsPrimary = () => {} }) { export default function ActiveAccount({ name = "", setAsPrimary = () => {} }) {
const { publicKey, readonly } = useLogin(s => ({ const { publicKey, readonly } = useLogin(s => ({
@ -60,12 +61,12 @@ export default function ActiveAccount({ name = "", setAsPrimary = () => {} }) {
return ( return (
<div> <div>
<div className="negative"> <div className="negative">
You have an active iris.to account: <FormattedMessage defaultMessage="You have an active iris.to account" />:
<AccountName name={name} /> <AccountName name={name} />
</div> </div>
<p> <p>
<button type="button" onClick={onClick}> <button type="button" onClick={onClick}>
Set as primary Nostr address (nip05) <FormattedMessage defaultMessage="Set as primary Nostr address (nip05)" />
</button> </button>
</p> </p>
</div> </div>

View File

@ -6,6 +6,9 @@ import AccountName from "./AccountName";
import ActiveAccount from "./ActiveAccount"; import ActiveAccount from "./ActiveAccount";
import ReservedAccount from "./ReservedAccount"; import ReservedAccount from "./ReservedAccount";
import { ProfileLoader } from "../../index"; import { ProfileLoader } from "../../index";
import FormattedMessage from "Element/FormattedMessage";
import { injectIntl } from "react-intl";
import messages from "Element/messages";
declare global { declare global {
interface Window { interface Window {
@ -13,8 +16,12 @@ declare global {
} }
} }
type Props = {
intl: any;
};
// TODO split into smaller components // TODO split into smaller components
export default class IrisAccount extends Component { class IrisAccount extends Component<Props> {
state = { state = {
irisToActive: false, irisToActive: false,
existing: null as any, existing: null as any,
@ -63,7 +70,9 @@ export default class IrisAccount extends Component {
} else { } else {
view = ( view = (
<div> <div>
<p>Register an Iris username (iris.to/username)</p> <p>
<FormattedMessage defaultMessage="Register an Iris username" /> (iris.to/username)
</p>
<form onSubmit={e => this.showChallenge(e)}> <form onSubmit={e => this.showChallenge(e)}>
<div className="flex gap-2"> <div className="flex gap-2">
<input <input
@ -73,12 +82,16 @@ export default class IrisAccount extends Component {
value={this.state.newUserName} value={this.state.newUserName}
onInput={e => this.onNewUserNameChange(e)} onInput={e => this.onNewUserNameChange(e)}
/> />
<button type="submit">Register</button> <button type="submit">
<FormattedMessage defaultMessage="Register" />
</button>
</div> </div>
<div> <div>
{this.state.newUserNameValid ? ( {this.state.newUserNameValid ? (
<> <>
<span className="text-iris-green">Username is available</span> <span className="text-iris-green">
<FormattedMessage defaultMessage="Username is available" />
</span>
<AccountName name={this.state.newUserName} link={false} /> <AccountName name={this.state.newUserName} link={false} />
</> </>
) : ( ) : (
@ -92,7 +105,9 @@ export default class IrisAccount extends Component {
return ( return (
<> <>
<h3>Iris.to account</h3> <h3>
<FormattedMessage defaultMessage="Iris.to account" />
</h3>
{view} {view}
<p> <p>
<a href="https://github.com/irislib/faq#iris-username">FAQ</a> <a href="https://github.com/irislib/faq#iris-username">FAQ</a>
@ -111,11 +126,12 @@ export default class IrisAccount extends Component {
}); });
return; return;
} }
if (newUserName.length < 8 || newUserName.length > 15) { if (newUserName.length < 8 || newUserName.length > 15) {
this.setState({ this.setState({
newUserName, newUserName,
newUserNameValid: false, newUserNameValid: false,
invalidUsernameMessage: "Username must be between 8 and 15 characters", invalidUsernameMessage: this.props.intl.formatMessage(messages.IrisUserNameLengthError),
}); });
return; return;
} }
@ -123,7 +139,7 @@ export default class IrisAccount extends Component {
this.setState({ this.setState({
newUserName, newUserName,
newUserNameValid: false, newUserNameValid: false,
invalidUsernameMessage: "Username must only contain lowercase letters and numbers", invalidUsernameMessage: this.props.intl.formatMessage(messages.IrisUserNameFormatError),
}); });
return; return;
} }
@ -244,7 +260,6 @@ export default class IrisAccount extends Component {
const login = LoginStore.snapshot(); const login = LoginStore.snapshot();
const publisher = LoginStore.getPublisher(login.id); const publisher = LoginStore.getPublisher(login.id);
const event = await publisher?.note(`decline iris.to/${this.state.newUserName}`); const event = await publisher?.note(`decline iris.to/${this.state.newUserName}`);
// post signed event as request body to https://api.iris.to/user/confirm_user
const res = await fetch("https://api.iris.to/user/decline_user", { const res = await fetch("https://api.iris.to/user/decline_user", {
method: "POST", method: "POST",
headers: { headers: {
@ -288,3 +303,5 @@ export default class IrisAccount extends Component {
} }
} }
} }
export default injectIntl(IrisAccount);

View File

@ -1,20 +1,24 @@
import AccountName from "./AccountName"; import AccountName from "./AccountName";
import FormattedMessage from "Element/FormattedMessage";
export default function ReservedAccount({ name = "", enableReserved = () => {}, declineReserved = () => {} }) { export default function ReservedAccount({ name = "", enableReserved = () => {}, declineReserved = () => {} }) {
return ( return (
<div> <div>
<p className="text-iris-green"> <p className="text-iris-green">
Username iris.to/<b>{name}</b> is reserved for you! <FormattedMessage
defaultMessage="Username iris.to/<b>{name}</b> is reserved for you!"
values={{ name, b: s => <b>{s}</b> }}
/>
</p> </p>
<AccountName name={name} link={false} /> <AccountName name={name} link={false} />
<p> <p>
<button className="btn btn-sm btn-primary" onClick={() => enableReserved()}> <button className="btn btn-sm btn-primary" onClick={() => enableReserved()}>
Yes please <FormattedMessage defaultMessage="Yes please" />
</button> </button>
</p> </p>
<p> <p>
<button className="btn btn-sm btn-neutral" onClick={() => declineReserved()}> <button className="btn btn-sm btn-neutral" onClick={() => declineReserved()}>
No thanks <FormattedMessage defaultMessage="No thanks" />
</button> </button>
</p> </p>
</div> </div>

View File

@ -98,4 +98,6 @@ export default defineMessages({
ConfirmUnpin: { defaultMessage: "Are you sure you want to unpin this note?" }, ConfirmUnpin: { defaultMessage: "Are you sure you want to unpin this note?" },
ReactionsLink: { defaultMessage: "{n} Reactions" }, ReactionsLink: { defaultMessage: "{n} Reactions" },
ReBroadcast: { defaultMessage: "Broadcast Again" }, ReBroadcast: { defaultMessage: "Broadcast Again" },
IrisUserNameLengthError: { defaultMessage: "Name must be between 1 and 32 characters" },
IrisUserNameFormatError: { defaultMessage: "Username must only contain lowercase letters and numbers" },
}); });

View File

@ -157,6 +157,9 @@
"4L2vUY": { "4L2vUY": {
"defaultMessage": "Your new NIP-05 handle is:" "defaultMessage": "Your new NIP-05 handle is:"
}, },
"4MBtMa": {
"defaultMessage": "Name must be between 1 and 32 characters"
},
"4OB335": { "4OB335": {
"defaultMessage": "Dislike" "defaultMessage": "Dislike"
}, },
@ -329,6 +332,9 @@
"BcGMo+": { "BcGMo+": {
"defaultMessage": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages." "defaultMessage": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages."
}, },
"BjNwZW": {
"defaultMessage": "Nostr address (nip05)"
},
"C1LjMx": { "C1LjMx": {
"defaultMessage": "Lightning Donation" "defaultMessage": "Lightning Donation"
}, },
@ -401,6 +407,9 @@
"EcZF24": { "EcZF24": {
"defaultMessage": "Custom Relays" "defaultMessage": "Custom Relays"
}, },
"EcfIwB": {
"defaultMessage": "Username is available"
},
"EcglP9": { "EcglP9": {
"defaultMessage": "Key" "defaultMessage": "Key"
}, },
@ -617,6 +626,9 @@
"MWTx65": { "MWTx65": {
"defaultMessage": "Default Page" "defaultMessage": "Default Page"
}, },
"MiMipu": {
"defaultMessage": "Set as primary Nostr address (nip05)"
},
"Mrpkot": { "Mrpkot": {
"defaultMessage": "Pay for subscription" "defaultMessage": "Pay for subscription"
}, },
@ -626,6 +638,9 @@
"MzRYWH": { "MzRYWH": {
"defaultMessage": "Buying {item}" "defaultMessage": "Buying {item}"
}, },
"Mzizei": {
"defaultMessage": "Iris.to account"
},
"N2IrpM": { "N2IrpM": {
"defaultMessage": "Confirm" "defaultMessage": "Confirm"
}, },
@ -720,6 +735,9 @@
"RDZVQL": { "RDZVQL": {
"defaultMessage": "Check" "defaultMessage": "Check"
}, },
"RSr2uB": {
"defaultMessage": "Username must only contain lowercase letters and numbers"
},
"RahCRH": { "RahCRH": {
"defaultMessage": "Expired" "defaultMessage": "Expired"
}, },
@ -792,6 +810,9 @@
"Up5U7K": { "Up5U7K": {
"defaultMessage": "Block" "defaultMessage": "Block"
}, },
"UrKTqQ": {
"defaultMessage": "You have an active iris.to account"
},
"VBadwB": { "VBadwB": {
"defaultMessage": "Hmm, can't find a key manager extension.. try reloading the page." "defaultMessage": "Hmm, can't find a key manager extension.. try reloading the page."
}, },
@ -804,6 +825,9 @@
"VR5eHw": { "VR5eHw": {
"defaultMessage": "Public key (npub/nprofile)" "defaultMessage": "Public key (npub/nprofile)"
}, },
"VcwrfF": {
"defaultMessage": "Yes please"
},
"VlJkSk": { "VlJkSk": {
"defaultMessage": "{n} muted" "defaultMessage": "{n} muted"
}, },
@ -880,6 +904,9 @@
"ZS+jRE": { "ZS+jRE": {
"defaultMessage": "Send zap splits to" "defaultMessage": "Send zap splits to"
}, },
"Zff6lu": {
"defaultMessage": "Username iris.to/<b>{name}</b> is reserved for you!"
},
"Zr5TMx": { "Zr5TMx": {
"defaultMessage": "Setup profile" "defaultMessage": "Setup profile"
}, },
@ -916,6 +943,9 @@
"bxv59V": { "bxv59V": {
"defaultMessage": "Just now" "defaultMessage": "Just now"
}, },
"c+JYNI": {
"defaultMessage": "No thanks"
},
"c+oiJe": { "c+oiJe": {
"defaultMessage": "Install Extension", "defaultMessage": "Install Extension",
"description": "Heading for install key manager extension" "description": "Heading for install key manager extension"
@ -964,6 +994,9 @@
"dOQCL8": { "dOQCL8": {
"defaultMessage": "Display name" "defaultMessage": "Display name"
}, },
"deEeEI": {
"defaultMessage": "Register"
},
"e61Jf3": { "e61Jf3": {
"defaultMessage": "Coming soon" "defaultMessage": "Coming soon"
}, },
@ -1112,6 +1145,9 @@
"k7sKNy": { "k7sKNy": {
"defaultMessage": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!" "defaultMessage": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!"
}, },
"kEZUR8": {
"defaultMessage": "Register an Iris username"
},
"kJYo0u": { "kJYo0u": {
"defaultMessage": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}" "defaultMessage": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}"
}, },
@ -1295,6 +1331,9 @@
"rudscU": { "rudscU": {
"defaultMessage": "Failed to load follows, please try again later" "defaultMessage": "Failed to load follows, please try again later"
}, },
"rx1i0i": {
"defaultMessage": "Short link"
},
"sKDn4e": { "sKDn4e": {
"defaultMessage": "Show Badges" "defaultMessage": "Show Badges"
}, },

View File

@ -51,6 +51,7 @@
"47FYwb": "Cancel", "47FYwb": "Cancel",
"4IPzdn": "Primary Developers", "4IPzdn": "Primary Developers",
"4L2vUY": "Your new NIP-05 handle is:", "4L2vUY": "Your new NIP-05 handle is:",
"4MBtMa": "Name must be between 1 and 32 characters",
"4OB335": "Dislike", "4OB335": "Dislike",
"4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices",
"4Z3t5i": "Use imgproxy to compress images", "4Z3t5i": "Use imgproxy to compress images",
@ -107,6 +108,7 @@
"BOr9z/": "Snort is an open source project built by passionate people in their free time", "BOr9z/": "Snort is an open source project built by passionate people in their free time",
"BWpuKl": "Update", "BWpuKl": "Update",
"BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.",
"BjNwZW": "Nostr address (nip05)",
"C1LjMx": "Lightning Donation", "C1LjMx": "Lightning Donation",
"C5xzTC": "Premium", "C5xzTC": "Premium",
"C81/uG": "Logout", "C81/uG": "Logout",
@ -131,6 +133,7 @@
"EWyQH5": "Global", "EWyQH5": "Global",
"Ebl/B2": "Translate to {lang}", "Ebl/B2": "Translate to {lang}",
"EcZF24": "Custom Relays", "EcZF24": "Custom Relays",
"EcfIwB": "Username is available",
"EcglP9": "Key", "EcglP9": "Key",
"EjFyoR": "On-chain Donation Address", "EjFyoR": "On-chain Donation Address",
"EnCOBJ": "Buy", "EnCOBJ": "Buy",
@ -202,9 +205,11 @@
"MP54GY": "Wallet password", "MP54GY": "Wallet password",
"MRp6Ly": "Twitter username", "MRp6Ly": "Twitter username",
"MWTx65": "Default Page", "MWTx65": "Default Page",
"MiMipu": "Set as primary Nostr address (nip05)",
"Mrpkot": "Pay for subscription", "Mrpkot": "Pay for subscription",
"MuVeKe": "Buy nostr address", "MuVeKe": "Buy nostr address",
"MzRYWH": "Buying {item}", "MzRYWH": "Buying {item}",
"Mzizei": "Iris.to account",
"N2IrpM": "Confirm", "N2IrpM": "Confirm",
"NAidKb": "Notifications", "NAidKb": "Notifications",
"NAuFNH": "You already have a subscription of this type, please renew or pay", "NAuFNH": "You already have a subscription of this type, please renew or pay",
@ -236,6 +241,7 @@
"R/6nsx": "Subscription", "R/6nsx": "Subscription",
"R81upa": "People you follow", "R81upa": "People you follow",
"RDZVQL": "Check", "RDZVQL": "Check",
"RSr2uB": "Username must only contain lowercase letters and numbers",
"RahCRH": "Expired", "RahCRH": "Expired",
"RfhLwC": "By: {author}", "RfhLwC": "By: {author}",
"RhDAoS": "Are you sure you want to delete {id}", "RhDAoS": "Are you sure you want to delete {id}",
@ -259,10 +265,12 @@
"UT7Nkj": "New Chat", "UT7Nkj": "New Chat",
"UUPFlt": "Users must accept the content warning to show the content of your note.", "UUPFlt": "Users must accept the content warning to show the content of your note.",
"Up5U7K": "Block", "Up5U7K": "Block",
"UrKTqQ": "You have an active iris.to account",
"VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.",
"VN0+Fz": "Balance: {amount} sats", "VN0+Fz": "Balance: {amount} sats",
"VOjC1i": "Pick which upload service you want to upload attachments to", "VOjC1i": "Pick which upload service you want to upload attachments to",
"VR5eHw": "Public key (npub/nprofile)", "VR5eHw": "Public key (npub/nprofile)",
"VcwrfF": "Yes please",
"VlJkSk": "{n} muted", "VlJkSk": "{n} muted",
"VnXp8Z": "Avatar", "VnXp8Z": "Avatar",
"VvaJst": "View Wallets", "VvaJst": "View Wallets",
@ -288,6 +296,7 @@
"ZKORll": "Activate Now", "ZKORll": "Activate Now",
"ZLmyG9": "Contributors", "ZLmyG9": "Contributors",
"ZS+jRE": "Send zap splits to", "ZS+jRE": "Send zap splits to",
"Zff6lu": "Username iris.to/<b>{name}</b> is reserved for you!",
"Zr5TMx": "Setup profile", "Zr5TMx": "Setup profile",
"a5UPxh": "Fund developers and platforms providing NIP-05 verification services", "a5UPxh": "Fund developers and platforms providing NIP-05 verification services",
"a7TDNm": "Notes will stream in real time into global and notes tab", "a7TDNm": "Notes will stream in real time into global and notes tab",
@ -300,6 +309,7 @@
"bfvyfs": "Anon", "bfvyfs": "Anon",
"brAXSu": "Pick a username", "brAXSu": "Pick a username",
"bxv59V": "Just now", "bxv59V": "Just now",
"c+JYNI": "No thanks",
"c+oiJe": "Install Extension", "c+oiJe": "Install Extension",
"c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.",
"c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}",
@ -315,6 +325,7 @@
"d6CyG5": "History", "d6CyG5": "History",
"d7d0/x": "LN Address", "d7d0/x": "LN Address",
"dOQCL8": "Display name", "dOQCL8": "Display name",
"deEeEI": "Register",
"e61Jf3": "Coming soon", "e61Jf3": "Coming soon",
"e7VmYP": "Enter pin to unlock your private key", "e7VmYP": "Enter pin to unlock your private key",
"e7qqly": "Mark All Read", "e7qqly": "Mark All Read",
@ -364,6 +375,7 @@
"jzgQ2z": "{n} Reactions", "jzgQ2z": "{n} Reactions",
"k2veDA": "Write", "k2veDA": "Write",
"k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!",
"kEZUR8": "Register an Iris username",
"kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}",
"kaaf1E": "now", "kaaf1E": "now",
"kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}",
@ -424,6 +436,7 @@
"rmdsT4": "{n} days", "rmdsT4": "{n} days",
"rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.",
"rudscU": "Failed to load follows, please try again later", "rudscU": "Failed to load follows, please try again later",
"rx1i0i": "Short link",
"sKDn4e": "Show Badges", "sKDn4e": "Show Badges",
"sUNhQE": "user", "sUNhQE": "user",
"sWnYKw": "Snort is designed to have a similar experience to Twitter.", "sWnYKw": "Snort is designed to have a similar experience to Twitter.",

View File

@ -12,7 +12,7 @@ export interface KeyedHookFilter {
/** /**
* Dexie backed generic hookable store * Dexie backed generic hookable store
*/ */
export abstract class FeedCache<TCached> { export abstract class FeedCache<TCached> {
#name: string; #name: string;
#hooks: Array<KeyedHookFilter> = []; #hooks: Array<KeyedHookFilter> = [];
#snapshot: Array<TCached> = []; #snapshot: Array<TCached> = [];