Better POW UX

This commit is contained in:
Kieran 2023-08-18 19:01:34 +01:00
parent 1c5e61e020
commit a5be4da2e8
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
4 changed files with 78 additions and 28 deletions

View File

@ -36,6 +36,7 @@ import { LoginStore } from "Login";
import { getCurrentSubscription } from "Subscription";
import useLogin from "Hooks/useLogin";
import { System } from "index";
import AsyncButton from "Element/AsyncButton";
interface NotePreviewProps {
note: TaggedNostrEvent;
@ -174,9 +175,9 @@ export function NoteCreator() {
dispatch(reset());
}
function onSubmit(ev: React.MouseEvent<HTMLButtonElement>) {
async function onSubmit(ev: React.MouseEvent<HTMLButtonElement>) {
ev.stopPropagation();
sendNote().catch(console.warn);
await sendNote();
}
async function loadPreview() {
@ -374,9 +375,9 @@ export function NoteCreator() {
<button className="secondary" onClick={cancel}>
<FormattedMessage {...messages.Cancel} />
</button>
<button onClick={onSubmit}>
<AsyncButton onClick={onSubmit}>
{replyTo ? <FormattedMessage {...messages.Reply} /> : <FormattedMessage {...messages.Send} />}
</button>
</AsyncButton>
</div>
{showAdvanced && (
<div>

View File

@ -1,8 +1,8 @@
import React, { useEffect, useState } from "react";
import React, { HTMLProps, useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { useIntl } from "react-intl";
import { useLongPress } from "use-long-press";
import { TaggedNostrEvent, HexKey, u256, ParsedZap } from "@snort/system";
import { TaggedNostrEvent, HexKey, u256, ParsedZap, countLeadingZeros } from "@snort/system";
import { LNURL } from "@snort/shared";
import { useUserProfile } from "@snort/system-react";
@ -11,7 +11,7 @@ import Spinner from "Icons/Spinner";
import { formatShort } from "Number";
import useEventPublisher from "Feed/EventPublisher";
import { delay, normalizeReaction, unwrap } from "SnortUtils";
import { delay, findTag, normalizeReaction, unwrap } from "SnortUtils";
import { NoteCreator } from "Element/NoteCreator";
import SendSats from "Element/SendSats";
import { ZapsSummary } from "Element/Zap";
@ -173,16 +173,26 @@ export default function NoteFooter(props: NoteFooterProps) {
}
}, [prefs.autoZap, author, zapping]);
function powIcon() {
const pow = findTag(ev, "nonce") ? countLeadingZeros(ev.id) : undefined;
if (pow) {
return (
<AsyncFooterIcon title={formatMessage({ defaultMessage: "Proof of Work" })} iconName="diamond" value={pow} />
);
}
}
function tipButton() {
const service = getLNURL();
if (service) {
return (
<>
<div className={`reaction-pill ${didZap ? "reacted" : ""}`} {...longPress()} onClick={e => fastZap(e)}>
{zapping ? <Spinner /> : wallet?.isReady() ? <Icon name="zapFast" /> : <Icon name="zap" />}
{zapTotal > 0 && <div className="reaction-pill-number">{formatShort(zapTotal)}</div>}
</div>
</>
<AsyncFooterIcon
className={didZap ? "reacted" : ""}
{...longPress()}
iconName={wallet?.isReady() ? "zapFast" : "zap"}
value={zapTotal}
onClick={e => fastZap(e)}
/>
);
}
return null;
@ -190,10 +200,12 @@ export default function NoteFooter(props: NoteFooterProps) {
function repostIcon() {
return (
<div className={`reaction-pill ${hasReposted() ? "reacted" : ""}`} onClick={() => repost()}>
<Icon name="repeat" size={18} />
{reposts.length > 0 && <div className="reaction-pill-number">{formatShort(reposts.length)}</div>}
</div>
<AsyncFooterIcon
className={hasReposted() ? "reacted" : ""}
iconName="repeat"
value={reposts.length}
onClick={() => repost()}
/>
);
}
@ -203,12 +215,12 @@ export default function NoteFooter(props: NoteFooterProps) {
}
const reacted = hasReacted("+");
return (
<>
<div className={`reaction-pill ${reacted ? "reacted" : ""} `} onClick={() => react(prefs.reactionEmoji)}>
<Icon name={reacted ? "heart-solid" : "heart"} size={18} />
<div className="reaction-pill-number">{formatShort(positive.length)}</div>
</div>
</>
<AsyncFooterIcon
className={reacted ? "reacted" : ""}
iconName={reacted ? "heart-solid" : "heart"}
value={positive.length}
onClick={() => react(prefs.reactionEmoji)}
/>
);
}
@ -228,9 +240,12 @@ export default function NoteFooter(props: NoteFooterProps) {
{tipButton()}
{reactionIcons()}
{repostIcon()}
<div className={`reaction-pill ${showNoteCreatorModal ? "reacted" : ""}`} onClick={handleReplyButtonClick}>
<Icon name="reply" size={17} />
</div>
<AsyncFooterIcon
className={showNoteCreatorModal ? "reacted" : ""}
iconName="reply"
onClick={async () => handleReplyButtonClick()}
/>
{powIcon()}
</div>
{willRenderNoteCreator && <NoteCreator />}
<SendSats
@ -247,3 +262,36 @@ export default function NoteFooter(props: NoteFooterProps) {
</>
);
}
interface AsyncFooterIconProps extends HTMLProps<HTMLDivElement> {
iconName: string;
value?: number;
loading?: boolean;
onClick?: (e: React.MouseEvent<HTMLDivElement>) => Promise<void>;
}
function AsyncFooterIcon(props: AsyncFooterIconProps) {
const [loading, setLoading] = useState(props.loading ?? false);
async function handleClick(e: React.MouseEvent<HTMLDivElement>) {
setLoading(true);
try {
if (props.onClick) {
await props.onClick(e);
}
} catch (ex) {
console.error(ex);
}
setLoading(false);
}
return (
<div
{...props}
className={`reaction-pill${props.className ? ` ${props.className}` : ""}`}
onClick={e => handleClick(e)}>
{loading ? <Spinner /> : <Icon name={props.iconName} size={18} />}
{props.value && <div className="reaction-pill-number">{formatShort(props.value)}</div>}
</div>
);
}

View File

@ -22,6 +22,7 @@ export * from "./zaps";
export * from "./signer";
export * from "./text";
export * from "./pow";
export * from "./pow-util";
export * from "./impl/nip4";
export * from "./impl/nip44";

View File

@ -30,7 +30,7 @@ export function minePow(e: NostrPowEvent, target: number) {
e.tags[nonceTagIdx][1] = (++ctr).toString();
e.id = createId(e);
} while (countLeadingZeroes(e.id) < target);
} while (countLeadingZeros(e.id) < target);
return e;
}
@ -40,7 +40,7 @@ function createId(e: NostrPowEvent) {
return bytesToHex(sha256(JSON.stringify(payload)));
}
function countLeadingZeroes(hex: string) {
export function countLeadingZeros(hex: string) {
let count = 0;
for (let i = 0; i < hex.length; i++) {