feat: UserState
This commit is contained in:
@ -1,10 +1,10 @@
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
import { MixCloudRegex } from "@/Utils/Const";
|
||||
|
||||
const MixCloudEmbed = ({ link }: { link: string }) => {
|
||||
const feedPath = (MixCloudRegex.test(link) && RegExp.$1) + "%2F" + (MixCloudRegex.test(link) && RegExp.$2);
|
||||
|
||||
const theme = useLogin(s => s.appData.json.preferences.theme);
|
||||
const theme = usePreferences(s => s.theme);
|
||||
const lightParams = theme === "light" ? "light=1" : "light=0";
|
||||
return (
|
||||
<>
|
||||
|
@ -8,30 +8,31 @@ import AsyncButton from "@/Components/Button/AsyncButton";
|
||||
import { Toastore } from "@/Components/Toaster/Toaster";
|
||||
import FollowListBase from "@/Components/User/FollowListBase";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
import { dedupe, findTag, getDisplayName, hexToBech32 } from "@/Utils";
|
||||
import { useWallet } from "@/Wallet";
|
||||
|
||||
export default function PubkeyList({ ev, className }: { ev: NostrEvent; className?: string }) {
|
||||
const wallet = useWallet();
|
||||
const login = useLogin();
|
||||
const { publisher } = useEventPublisher();
|
||||
const defaultZapAmount = usePreferences(s => s.defaultZapAmount);
|
||||
const { publisher, system } = useEventPublisher();
|
||||
const ids = dedupe(ev.tags.filter(a => a[0] === "p").map(a => a[1]));
|
||||
|
||||
async function zapAll() {
|
||||
for (const pk of ids) {
|
||||
try {
|
||||
const profile = await UserCache.get(pk);
|
||||
const amtSend = login.appData.json.preferences.defaultZapAmount;
|
||||
const amtSend = defaultZapAmount;
|
||||
const lnurl = profile?.lud16 || profile?.lud06;
|
||||
if (lnurl) {
|
||||
const svc = new LNURL(lnurl);
|
||||
await svc.load();
|
||||
|
||||
const relays = await system.requestRouter?.forReplyTo(pk);
|
||||
const zap = await publisher?.zap(
|
||||
amtSend * 1000,
|
||||
pk,
|
||||
Object.keys(login.relays.item),
|
||||
relays ?? [],
|
||||
undefined,
|
||||
`Zap from ${hexToBech32("note", ev.id)}`,
|
||||
);
|
||||
@ -74,7 +75,7 @@ export default function PubkeyList({ ev, className }: { ev: NostrEvent; classNam
|
||||
defaultMessage="Zap all {n} sats"
|
||||
id="IVbtTS"
|
||||
values={{
|
||||
n: <FormattedNumber value={login.appData.json.preferences.defaultZapAmount * ids.length} />,
|
||||
n: <FormattedNumber value={defaultZapAmount * ids.length} />,
|
||||
}}
|
||||
/>
|
||||
</AsyncButton>
|
||||
|
@ -20,6 +20,8 @@ import { Toastore } from "@/Components/Toaster/Toaster";
|
||||
import ProfileImage from "@/Components/User/ProfileImage";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
import useRelays from "@/Hooks/useRelays";
|
||||
import { useNoteCreator } from "@/State/NoteCreator";
|
||||
import { openFile, trackEvent } from "@/Utils";
|
||||
import useFileUpload from "@/Utils/Upload";
|
||||
@ -56,11 +58,12 @@ const quoteNoteOptions = {
|
||||
export function NoteCreator() {
|
||||
const { formatMessage } = useIntl();
|
||||
const uploader = useFileUpload();
|
||||
const login = useLogin(s => ({ relays: s.relays, publicKey: s.publicKey, pow: s.appData.json.preferences.pow }));
|
||||
const publicKey = useLogin(s => s.publicKey);
|
||||
const pow = usePreferences(s => s.pow);
|
||||
const relays = useRelays();
|
||||
const { system, publisher: pub } = useEventPublisher();
|
||||
const publisher = login.pow ? pub?.pow(login.pow, GetPowWorker()) : pub;
|
||||
const publisher = pow ? pub?.pow(pow, GetPowWorker()) : pub;
|
||||
const note = useNoteCreator();
|
||||
const relays = login.relays;
|
||||
|
||||
useEffect(() => {
|
||||
const draft = localStorage.getItem("msgDraft");
|
||||
@ -367,8 +370,9 @@ export function NoteCreator() {
|
||||
function renderRelayCustomisation() {
|
||||
return (
|
||||
<div className="flex flex-col g8">
|
||||
{Object.keys(relays.item || {})
|
||||
.filter(el => relays.item[el].write)
|
||||
{Object.entries(relays)
|
||||
.filter(el => el[1].write)
|
||||
.map(a => a[0])
|
||||
.map((r, i, a) => (
|
||||
<div className="p flex justify-between note-creator-relay" key={r}>
|
||||
<div>{r}</div>
|
||||
@ -523,7 +527,7 @@ export function NoteCreator() {
|
||||
<div className="flex justify-between">
|
||||
<div className="flex items-center g8">
|
||||
<ProfileImage
|
||||
pubkey={login.publicKey ?? ""}
|
||||
pubkey={publicKey ?? ""}
|
||||
className="note-creator-icon"
|
||||
link=""
|
||||
showUsername={false}
|
||||
|
@ -8,21 +8,16 @@ import IconButton from "@/Components/Button/IconButton";
|
||||
import Icon from "@/Components/Icons/Icon";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import { saveRelays } from "@/Pages/settings/saveRelays";
|
||||
import { getRelayName } from "@/Utils";
|
||||
import { removeRelay } from "@/Utils/Login";
|
||||
|
||||
export function OkResponseRow({ rsp, close }: { rsp: OkResponse; close: () => void }) {
|
||||
const [r, setResult] = useState(rsp);
|
||||
const { formatMessage } = useIntl();
|
||||
const { publisher, system } = useEventPublisher();
|
||||
const { system } = useEventPublisher();
|
||||
const login = useLogin();
|
||||
|
||||
async function removeRelayFromResult(r: OkResponse) {
|
||||
if (publisher) {
|
||||
removeRelay(login, unwrap(sanitizeRelayUrl(r.relay)));
|
||||
await saveRelays(system, publisher, login.relays.item);
|
||||
}
|
||||
await login.state.removeRelay(unwrap(sanitizeRelayUrl(r.relay)), true);
|
||||
close();
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { useState } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
|
||||
const HiddenNote = ({ children }: { children: React.ReactNode }) => {
|
||||
const hideMutedNotes = useLogin(s => s.appData.json.preferences.hideMutedNotes);
|
||||
const hideMutedNotes = usePreferences(s => s.hideMutedNotes);
|
||||
const [show, setShow] = useState(false);
|
||||
if (hideMutedNotes) return;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { HexKey, NostrLink, NostrPrefix } from "@snort/system";
|
||||
import { EventKind, HexKey, NostrLink, NostrPrefix } from "@snort/system";
|
||||
import { Menu, MenuItem } from "@szhsin/react-menu";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
@ -10,7 +10,7 @@ import SnortApi from "@/External/SnortApi";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import useModeration from "@/Hooks/useModeration";
|
||||
import { setBookmarked, setPinned } from "@/Utils/Login";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
import { getCurrentSubscription, SubscriptionType } from "@/Utils/Subscription";
|
||||
|
||||
import { ReBroadcaster } from "../../ReBroadcaster";
|
||||
@ -18,7 +18,8 @@ import { ReBroadcaster } from "../../ReBroadcaster";
|
||||
export function NoteContextMenu({ ev, ...props }: NoteContextMenuProps) {
|
||||
const { formatMessage } = useIntl();
|
||||
const login = useLogin();
|
||||
const { mute, block } = useModeration();
|
||||
const autoTranslate = usePreferences(s => s.autoTranslate);
|
||||
const { mute } = useModeration();
|
||||
const { publisher, system } = useEventPublisher();
|
||||
const [showBroadcast, setShowBroadcast] = useState(false);
|
||||
const lang = window.navigator.language;
|
||||
@ -26,6 +27,7 @@ export function NoteContextMenu({ ev, ...props }: NoteContextMenuProps) {
|
||||
type: "language",
|
||||
});
|
||||
const isMine = ev.pubkey === login.publicKey;
|
||||
const link = NostrLink.fromEvent(ev);
|
||||
|
||||
async function deleteEvent() {
|
||||
if (window.confirm(formatMessage(messages.ConfirmDeletion, { id: ev.id.substring(0, 8) })) && publisher) {
|
||||
@ -78,7 +80,7 @@ export function NoteContextMenu({ ev, ...props }: NoteContextMenuProps) {
|
||||
|
||||
useEffect(() => {
|
||||
const sub = getCurrentSubscription(login.subscriptions);
|
||||
if (sub?.type === SubscriptionType.Premium && (login.appData.json.preferences.autoTranslate ?? true)) {
|
||||
if (sub?.type === SubscriptionType.Premium && (autoTranslate ?? true)) {
|
||||
translate();
|
||||
}
|
||||
}, []);
|
||||
@ -90,19 +92,13 @@ export function NoteContextMenu({ ev, ...props }: NoteContextMenuProps) {
|
||||
|
||||
async function pin(id: HexKey) {
|
||||
if (publisher) {
|
||||
const es = [...login.pinned.item, id];
|
||||
const ev = await publisher.pinned(es.map(a => new NostrLink(NostrPrefix.Note, a)));
|
||||
system.BroadcastEvent(ev);
|
||||
setPinned(login, es, ev.created_at * 1000);
|
||||
//todo: PIN note
|
||||
}
|
||||
}
|
||||
|
||||
async function bookmark(id: string) {
|
||||
if (publisher) {
|
||||
const es = [...login.bookmarked.item, id];
|
||||
const ev = await publisher.bookmarks(es.map(a => new NostrLink(NostrPrefix.Note, a)));
|
||||
system.BroadcastEvent(ev);
|
||||
setBookmarked(login, es, ev.created_at * 1000);
|
||||
//todo: bookmark note
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,13 +128,13 @@ export function NoteContextMenu({ ev, ...props }: NoteContextMenuProps) {
|
||||
<Icon name="share" />
|
||||
<FormattedMessage {...messages.Share} />
|
||||
</MenuItem>
|
||||
{!login.pinned.item.includes(ev.id) && !login.readonly && (
|
||||
{!login.state.isOnList(EventKind.PinList, link) && !login.readonly && (
|
||||
<MenuItem onClick={() => pin(ev.id)}>
|
||||
<Icon name="pin" />
|
||||
<FormattedMessage {...messages.Pin} />
|
||||
</MenuItem>
|
||||
)}
|
||||
{!login.bookmarked.item.includes(ev.id) && !login.readonly && (
|
||||
{!login.state.isOnList(EventKind.BookmarksList, link) && !login.readonly && (
|
||||
<MenuItem onClick={() => bookmark(ev.id)}>
|
||||
<Icon name="bookmark" />
|
||||
<FormattedMessage {...messages.Bookmark} />
|
||||
@ -158,12 +154,6 @@ export function NoteContextMenu({ ev, ...props }: NoteContextMenuProps) {
|
||||
<Icon name="relay" />
|
||||
<FormattedMessage defaultMessage="Broadcast Event" id="Gxcr08" />
|
||||
</MenuItem>
|
||||
{ev.pubkey !== login.publicKey && !login.readonly && (
|
||||
<MenuItem onClick={() => block(ev.pubkey)}>
|
||||
<Icon name="block" />
|
||||
<FormattedMessage {...messages.Block} />
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem onClick={() => translate()}>
|
||||
<Icon name="translate" />
|
||||
<FormattedMessage {...messages.TranslateTo} values={{ lang: langNames.of(lang.split("-")[0]) }} />
|
||||
|
@ -11,6 +11,7 @@ import { ZapsSummary } from "@/Components/Event/ZapsSummary";
|
||||
import ZapModal from "@/Components/ZapModal/ZapModal";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
import { getDisplayName } from "@/Utils";
|
||||
import { Zapper, ZapTarget } from "@/Utils/Zapper";
|
||||
import { ZapPoolController } from "@/Utils/ZapPoolController";
|
||||
@ -23,15 +24,11 @@ export interface ZapIconProps {
|
||||
}
|
||||
|
||||
export const FooterZapButton = ({ ev, zaps, onClickZappers }: ZapIconProps) => {
|
||||
const {
|
||||
publicKey,
|
||||
readonly,
|
||||
preferences: prefs,
|
||||
} = useLogin(s => ({
|
||||
const { publicKey, readonly } = useLogin(s => ({
|
||||
publicKey: s.publicKey,
|
||||
readonly: s.readonly,
|
||||
preferences: s.appData.json.preferences,
|
||||
}));
|
||||
const preferences = usePreferences(s => ({ autoZap: s.autoZap, defaultZapAmount: s.defaultZapAmount }));
|
||||
const walletState = useWallet();
|
||||
const wallet = walletState.wallet;
|
||||
const link = NostrLink.fromEvent(ev);
|
||||
@ -75,7 +72,7 @@ export const FooterZapButton = ({ ev, zaps, onClickZappers }: ZapIconProps) => {
|
||||
if (canFastZap && lnurl) {
|
||||
setZapping(true);
|
||||
try {
|
||||
await fastZapInner(lnurl, prefs.defaultZapAmount);
|
||||
await fastZapInner(lnurl, preferences.defaultZapAmount);
|
||||
} catch (e) {
|
||||
console.warn("Fast zap failed", e);
|
||||
if (!(e instanceof Error) || e.message !== "User rejected") {
|
||||
@ -110,13 +107,13 @@ export const FooterZapButton = ({ ev, zaps, onClickZappers }: ZapIconProps) => {
|
||||
const targets = getZapTarget();
|
||||
|
||||
useEffect(() => {
|
||||
if (prefs.autoZap && !didZap && !isMine && !zapping) {
|
||||
if (preferences.autoZap && !didZap && !isMine && !zapping) {
|
||||
const lnurl = getZapTarget();
|
||||
if (wallet?.isReady() && lnurl) {
|
||||
setZapping(true);
|
||||
queueMicrotask(async () => {
|
||||
try {
|
||||
await fastZapInner(lnurl, prefs.defaultZapAmount);
|
||||
await fastZapInner(lnurl, preferences.defaultZapAmount);
|
||||
} catch {
|
||||
// ignored
|
||||
} finally {
|
||||
@ -125,7 +122,7 @@ export const FooterZapButton = ({ ev, zaps, onClickZappers }: ZapIconProps) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [prefs.autoZap, author, zapping]);
|
||||
}, [preferences.autoZap, author, zapping]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -9,6 +9,7 @@ import { ReplyButton } from "@/Components/Event/Note/NoteFooter/ReplyButton";
|
||||
import { RepostButton } from "@/Components/Event/Note/NoteFooter/RepostButton";
|
||||
import ReactionsModal from "@/Components/Event/Note/ReactionsModal";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
|
||||
export interface NoteFooterProps {
|
||||
replyCount?: number;
|
||||
@ -25,17 +26,14 @@ export default function NoteFooter(props: NoteFooterProps) {
|
||||
const { replies, reactions, zaps, reposts } = useEventReactions(link, related);
|
||||
const { positive } = reactions;
|
||||
|
||||
const { preferences: prefs, readonly } = useLogin(s => ({
|
||||
preferences: s.appData.json.preferences,
|
||||
publicKey: s.publicKey,
|
||||
readonly: s.readonly,
|
||||
}));
|
||||
const readonly = useLogin(s => s.readonly);
|
||||
const enableReactions = usePreferences(s => s.enableReactions);
|
||||
|
||||
return (
|
||||
<div className="flex flex-row gap-4 overflow-hidden max-w-full h-6 items-center">
|
||||
<ReplyButton ev={ev} replyCount={props.replyCount ?? replies.length} readonly={readonly} />
|
||||
<RepostButton ev={ev} reposts={reposts} />
|
||||
{prefs.enableReactions && <LikeButton ev={ev} positiveReactions={positive} />}
|
||||
{enableReactions && <LikeButton ev={ev} positiveReactions={positive} />}
|
||||
{CONFIG.showPowIcon && <PowIcon ev={ev} />}
|
||||
<FooterZapButton ev={ev} zaps={zaps} onClickZappers={() => setShowReactions(true)} />
|
||||
{showReactions && <ReactionsModal initialTab={1} onClose={() => setShowReactions(false)} event={ev} />}
|
||||
|
@ -9,16 +9,15 @@ import Icon from "@/Components/Icons/Icon";
|
||||
import messages from "@/Components/messages";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
import { useNoteCreator } from "@/State/NoteCreator";
|
||||
|
||||
export const RepostButton = ({ ev, reposts }: { ev: TaggedNostrEvent; reposts: TaggedNostrEvent[] }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const navigate = useNavigate();
|
||||
const { publisher, system } = useEventPublisher();
|
||||
const { publicKey, preferences: prefs } = useLogin(s => ({
|
||||
preferences: s.appData.json.preferences,
|
||||
publicKey: s.publicKey,
|
||||
}));
|
||||
const publicKey = useLogin(s => s.publicKey);
|
||||
const confirmReposts = usePreferences(s => s.confirmReposts);
|
||||
const note = useNoteCreator(n => ({ show: n.show, replyTo: n.replyTo, update: n.update, quote: n.quote }));
|
||||
|
||||
const hasReposted = () => {
|
||||
@ -27,7 +26,7 @@ export const RepostButton = ({ ev, reposts }: { ev: TaggedNostrEvent; reposts: T
|
||||
|
||||
const repost = async () => {
|
||||
if (!hasReposted() && publisher) {
|
||||
if (!prefs.confirmReposts || window.confirm(formatMessage(messages.ConfirmRepost, { id: ev.id }))) {
|
||||
if (!confirmReposts || window.confirm(formatMessage(messages.ConfirmRepost, { id: ev.id }))) {
|
||||
const evRepost = await publisher.repost(ev);
|
||||
system.BroadcastEvent(evRepost);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { HexKey, NostrLink, NostrPrefix, TaggedNostrEvent } from "@snort/system";
|
||||
import { EventKind, NostrLink, TaggedNostrEvent } from "@snort/system";
|
||||
import React, { useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
@ -13,7 +13,6 @@ import messages from "@/Components/messages";
|
||||
import ProfileImage from "@/Components/User/ProfileImage";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import { setBookmarked, setPinned } from "@/Utils/Login";
|
||||
|
||||
export default function NoteHeader(props: {
|
||||
ev: TaggedNostrEvent;
|
||||
@ -24,28 +23,21 @@ export default function NoteHeader(props: {
|
||||
const [showReactions, setShowReactions] = useState(false);
|
||||
const { ev, options, setTranslated } = props;
|
||||
const { formatMessage } = useIntl();
|
||||
const { pinned, bookmarked } = useLogin();
|
||||
const { publisher, system } = useEventPublisher();
|
||||
const { publisher } = useEventPublisher();
|
||||
const login = useLogin();
|
||||
|
||||
async function unpin(id: HexKey) {
|
||||
async function unpin() {
|
||||
if (options.canUnpin && publisher) {
|
||||
if (window.confirm(formatMessage(messages.ConfirmUnpin))) {
|
||||
const es = pinned.item.filter(e => e !== id);
|
||||
const ev = await publisher.pinned(es.map(a => new NostrLink(NostrPrefix.Note, a)));
|
||||
system.BroadcastEvent(ev);
|
||||
setPinned(login, es, ev.created_at * 1000);
|
||||
await login.state.removeFromList(EventKind.PinList, NostrLink.fromEvent(ev));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function unbookmark(id: HexKey) {
|
||||
async function unbookmark() {
|
||||
if (options.canUnbookmark && publisher) {
|
||||
if (window.confirm(formatMessage(messages.ConfirmUnbookmark))) {
|
||||
const es = bookmarked.item.filter(e => e !== id);
|
||||
const ev = await publisher.pinned(es.map(a => new NostrLink(NostrPrefix.Note, a)));
|
||||
system.BroadcastEvent(ev);
|
||||
setBookmarked(login, es, ev.created_at * 1000);
|
||||
await login.state.removeFromList(EventKind.BookmarksList, NostrLink.fromEvent(ev));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,7 +58,7 @@ export default function NoteHeader(props: {
|
||||
{(options.showTime || options.showBookmarked) && (
|
||||
<>
|
||||
{options.showBookmarked && (
|
||||
<div className={`saved ${options.canUnbookmark ? "pointer" : ""}`} onClick={() => unbookmark(ev.id)}>
|
||||
<div className={`saved ${options.canUnbookmark ? "pointer" : ""}`} onClick={() => unbookmark()}>
|
||||
<Icon name="bookmark" /> <FormattedMessage {...messages.Bookmarked} />
|
||||
</div>
|
||||
)}
|
||||
@ -74,7 +66,7 @@ export default function NoteHeader(props: {
|
||||
</>
|
||||
)}
|
||||
{options.showPinned && (
|
||||
<div className={`pinned ${options.canUnpin ? "pointer" : ""}`} onClick={() => unpin(ev.id)}>
|
||||
<div className={`pinned ${options.canUnpin ? "pointer" : ""}`} onClick={() => unpin()}>
|
||||
<Icon name="pin" /> <FormattedMessage {...messages.Pinned} />
|
||||
</div>
|
||||
)}
|
||||
|
@ -6,14 +6,14 @@ import { NoteProps } from "@/Components/Event/EventComponent";
|
||||
import { NoteTranslation } from "@/Components/Event/Note/types";
|
||||
import Reveal from "@/Components/Event/Reveal";
|
||||
import Text from "@/Components/Text/Text";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
|
||||
const TEXT_TRUNCATE_LENGTH = 400;
|
||||
export const NoteText = memo(function InnerContent(
|
||||
props: NoteProps & { translated: NoteTranslation; showTranslation?: boolean },
|
||||
) {
|
||||
const { data: ev, options, translated, showTranslation } = props;
|
||||
const appData = useLogin(s => s.appData);
|
||||
const showContentWarningPosts = usePreferences(s => s.showContentWarningPosts);
|
||||
const [showMore, setShowMore] = useState(false);
|
||||
const body = translated && !translated.skipped && showTranslation ? translated.text : ev?.content ?? "";
|
||||
const id = translated && !translated.skipped && showTranslation ? `${ev.id}-translated` : ev.id;
|
||||
@ -53,7 +53,7 @@ export const NoteText = memo(function InnerContent(
|
||||
</>
|
||||
);
|
||||
|
||||
if (!appData.json.showContentWarningPosts) {
|
||||
if (!showContentWarningPosts) {
|
||||
const contentWarning = ev.tags.find(a => a[0] === "content-warning");
|
||||
if (contentWarning) {
|
||||
return (
|
||||
|
@ -8,6 +8,7 @@ import Spinner from "@/Components/Icons/Spinner";
|
||||
import ZapModal from "@/Components/ZapModal/ZapModal";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
import { unwrap } from "@/Utils";
|
||||
import { formatShort } from "@/Utils/Number";
|
||||
import { useWallet } from "@/Wallet";
|
||||
@ -21,13 +22,10 @@ type PollTally = "zaps" | "pubkeys";
|
||||
|
||||
export default function Poll(props: PollProps) {
|
||||
const { formatMessage } = useIntl();
|
||||
const { publisher } = useEventPublisher();
|
||||
const { publisher, system } = useEventPublisher();
|
||||
const { wallet } = useWallet();
|
||||
const {
|
||||
preferences: prefs,
|
||||
publicKey: myPubKey,
|
||||
relays,
|
||||
} = useLogin(s => ({ preferences: s.appData.json.preferences, publicKey: s.publicKey, relays: s.relays }));
|
||||
const defaultZapAmount = usePreferences(s => s.defaultZapAmount);
|
||||
const myPubKey = useLogin(s => s.publicKey);
|
||||
const pollerProfile = useUserProfile(props.ev.pubkey);
|
||||
const [tallyBy, setTallyBy] = useState<PollTally>("pubkeys");
|
||||
const [error, setError] = useState("");
|
||||
@ -45,7 +43,7 @@ export default function Poll(props: PollProps) {
|
||||
ev.stopPropagation();
|
||||
if (voting || !publisher) return;
|
||||
|
||||
const amount = prefs.defaultZapAmount;
|
||||
const amount = defaultZapAmount;
|
||||
try {
|
||||
if (amount <= 0) {
|
||||
throw new Error(
|
||||
@ -62,9 +60,14 @@ export default function Poll(props: PollProps) {
|
||||
}
|
||||
|
||||
setVoting(opt);
|
||||
const r = Object.keys(relays.item);
|
||||
const zap = await publisher.zap(amount * 1000, props.ev.pubkey, r, NostrLink.fromEvent(props.ev), undefined, eb =>
|
||||
eb.tag(["poll_option", opt.toString()]),
|
||||
const r = await system.requestRouter?.forReplyTo(props.ev.pubkey);
|
||||
const zap = await publisher.zap(
|
||||
amount * 1000,
|
||||
props.ev.pubkey,
|
||||
r ?? [],
|
||||
NostrLink.fromEvent(props.ev),
|
||||
undefined,
|
||||
eb => eb.tag(["poll_option", opt.toString()]),
|
||||
);
|
||||
|
||||
const lnurl = props.ev.tags.find(a => a[0] === "zap")?.[1] || pollerProfile?.lud16 || pollerProfile?.lud06;
|
||||
@ -121,7 +124,7 @@ export default function Poll(props: PollProps) {
|
||||
defaultMessage="You are voting with {amount} sats"
|
||||
id="3qnJlS"
|
||||
values={{
|
||||
amount: formatShort(prefs.defaultZapAmount),
|
||||
amount: formatShort(defaultZapAmount),
|
||||
}}
|
||||
/>
|
||||
</small>
|
||||
|
@ -6,6 +6,7 @@ import { MediaElement } from "@/Components/Embed/MediaElement";
|
||||
import Reveal from "@/Components/Event/Reveal";
|
||||
import useFollowsControls from "@/Hooks/useFollowControls";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
import { FileExtensionRegex } from "@/Utils/Const";
|
||||
|
||||
interface RevealMediaProps {
|
||||
@ -17,15 +18,13 @@ interface RevealMediaProps {
|
||||
}
|
||||
|
||||
export default function RevealMedia(props: RevealMediaProps) {
|
||||
const { preferences, publicKey } = useLogin(s => ({
|
||||
preferences: s.appData.json.preferences,
|
||||
publicKey: s.publicKey,
|
||||
}));
|
||||
const publicKey = useLogin(s => s.publicKey);
|
||||
const autoLoadMedia = usePreferences(s => s.autoLoadMedia);
|
||||
const { isFollowing } = useFollowsControls();
|
||||
|
||||
const hideNonFollows = preferences.autoLoadMedia === "follows-only" && !isFollowing(props.creator);
|
||||
const hideNonFollows = autoLoadMedia === "follows-only" && !isFollowing(props.creator);
|
||||
const isMine = props.creator === publicKey;
|
||||
const hideMedia = preferences.autoLoadMedia === "none" || (!isMine && hideNonFollows);
|
||||
const hideMedia = autoLoadMedia === "none" || (!isMine && hideNonFollows);
|
||||
const hostname = new URL(props.link).hostname;
|
||||
|
||||
const url = new URL(props.link);
|
||||
|
@ -123,8 +123,6 @@ export function Thread(props: { onBack?: () => void; disableSpotlight?: boolean
|
||||
<pre>{JSON.stringify(thread.root, undefined, " ")}</pre>
|
||||
<h1>Data</h1>
|
||||
<pre>{JSON.stringify(thread.data, undefined, " ")}</pre>
|
||||
<h1>Reactions</h1>
|
||||
<pre>{JSON.stringify(thread.reactions, undefined, " ")}</pre>
|
||||
</div>
|
||||
)}
|
||||
{parent && (
|
||||
|
@ -3,9 +3,8 @@ import { FormattedMessage } from "react-intl";
|
||||
|
||||
import Icon from "@/Components/Icons/Icon";
|
||||
import { RootTabRoutePath } from "@/Pages/Root/RootTabRoutes";
|
||||
import { Newest } from "@/Utils/Login";
|
||||
|
||||
export function rootTabItems(base: string, pubKey: string | undefined, tags: Newest<Array<string>>) {
|
||||
export function rootTabItems(base: string, pubKey: string | undefined, tags: Array<string>) {
|
||||
const menuItems = [
|
||||
{
|
||||
tab: "for-you",
|
||||
@ -98,7 +97,7 @@ export function rootTabItems(base: string, pubKey: string | undefined, tags: New
|
||||
{
|
||||
tab: "tags",
|
||||
path: `${base}/topics`,
|
||||
show: tags.item.length > 0,
|
||||
show: tags.length > 0,
|
||||
element: (
|
||||
<>
|
||||
<Icon name="hash" />
|
||||
|
@ -7,26 +7,26 @@ import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { rootTabItems } from "@/Components/Feed/RootTabItems";
|
||||
import Icon from "@/Components/Icons/Icon";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
import { RootTabRoutePath } from "@/Pages/Root/RootTabRoutes";
|
||||
import { EventKind } from "@snort/system";
|
||||
import { unwrap } from "@snort/shared";
|
||||
|
||||
export function RootTabs({ base = "/" }: { base: string }) {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const {
|
||||
publicKey: pubKey,
|
||||
tags,
|
||||
preferences,
|
||||
} = useLogin(s => ({
|
||||
const { publicKey: pubKey, tags } = useLogin(s => ({
|
||||
publicKey: s.publicKey,
|
||||
tags: s.tags,
|
||||
preferences: s.appData.json.preferences,
|
||||
tags: s.state.getList(EventKind.InterestSet),
|
||||
}));
|
||||
const defaultRootTab = usePreferences(s => s.defaultRootTab);
|
||||
|
||||
const menuItems = useMemo(() => rootTabItems(base, pubKey, tags), [base, pubKey, tags]);
|
||||
const hashTags = tags.filter(a => a.toEventTag()?.[0] === "t").map(a => unwrap(a.toEventTag())[1]);
|
||||
const menuItems = useMemo(() => rootTabItems(base, pubKey, hashTags), [base, pubKey, tags]);
|
||||
|
||||
let defaultTab: RootTabRoutePath;
|
||||
if (pubKey) {
|
||||
defaultTab = preferences.defaultRootTab;
|
||||
defaultTab = defaultRootTab;
|
||||
} else {
|
||||
defaultTab = `trending/notes`;
|
||||
}
|
||||
|
@ -26,7 +26,11 @@ export interface TimelineFollowsProps {
|
||||
* A list of notes by "subject"
|
||||
*/
|
||||
const TimelineFollows = (props: TimelineFollowsProps) => {
|
||||
const login = useLogin();
|
||||
const login = useLogin(s => ({
|
||||
publicKey: s.publicKey,
|
||||
feedDisplayAs: s.feedDisplayAs,
|
||||
tags: s.state.getList(EventKind.InterestSet),
|
||||
}));
|
||||
const displayAsInitial = props.displayAs ?? login.feedDisplayAs ?? "list";
|
||||
const [displayAs, setDisplayAs] = useState<DisplayAs>(displayAsInitial);
|
||||
const [openedAt] = useHistoryState(Math.floor(Date.now() / 1000), "openedAt");
|
||||
@ -38,12 +42,12 @@ const TimelineFollows = (props: TimelineFollowsProps) => {
|
||||
items: followList,
|
||||
discriminator: login.publicKey?.slice(0, 12),
|
||||
extra: rb => {
|
||||
if (login.tags.item.length > 0) {
|
||||
rb.withFilter().kinds([EventKind.TextNote, EventKind.Repost]).tag("t", login.tags.item);
|
||||
if (login.tags.length > 0) {
|
||||
rb.withFilter().kinds([EventKind.TextNote, EventKind.Repost]).tags(login.tags);
|
||||
}
|
||||
},
|
||||
}) as TimelineSubject,
|
||||
[followList, login.tags.item],
|
||||
[login.publicKey, followList, login.tags],
|
||||
);
|
||||
const feed = useTimelineFeed(subject, { method: "TIME_RANGE", now: openedAt } as TimelineFeedOptions);
|
||||
|
||||
|
@ -16,12 +16,12 @@ export default function UsersFeed({ keyword, sortPopular = true }: { keyword: st
|
||||
{ method: "LIMIT_UNTIL" },
|
||||
);
|
||||
|
||||
const { muted, isEventMuted } = useModeration();
|
||||
const { isEventMuted } = useModeration();
|
||||
const filterPosts = useCallback(
|
||||
(nts: readonly TaggedNostrEvent[]) => {
|
||||
return nts.filter(a => !isEventMuted(a));
|
||||
},
|
||||
[muted],
|
||||
[isEventMuted],
|
||||
);
|
||||
const usersFeed = useMemo(() => filterPosts(feed.main ?? []).map(p => p.pubkey), [feed, filterPosts]);
|
||||
|
||||
|
@ -2,10 +2,10 @@ import { useSyncExternalStore } from "react";
|
||||
|
||||
import { getLocale } from "@/Components/IntlProvider/IntlProviderUtils";
|
||||
import { LangOverride } from "@/Components/IntlProvider/langStore";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
|
||||
export function useLocale() {
|
||||
const { language } = useLogin(s => ({ language: s.appData.json.preferences.language }));
|
||||
const language = usePreferences(s => s.language);
|
||||
const loggedOutLang = useSyncExternalStore(
|
||||
c => LangOverride.hook(c),
|
||||
() => LangOverride.snapshot(),
|
||||
|
@ -7,6 +7,7 @@ import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
import { createPublisher, LoginStore, sessionNeedsPin } from "@/Utils/Login";
|
||||
import { GetPowWorker } from "@/Utils/wasm";
|
||||
|
||||
@ -97,6 +98,7 @@ export function PinPrompt({
|
||||
|
||||
export function LoginUnlock() {
|
||||
const login = useLogin();
|
||||
const pow = usePreferences(s => s.pow);
|
||||
const { publisher } = useEventPublisher();
|
||||
|
||||
async function encryptMigration(pin: string) {
|
||||
@ -104,8 +106,8 @@ export function LoginUnlock() {
|
||||
const newPin = await PinEncrypted.create(k, pin);
|
||||
|
||||
const pub = EventPublisher.privateKey(k);
|
||||
if (login.appData.json.preferences.pow) {
|
||||
pub.pow(login.appData.json.preferences.pow, GetPowWorker());
|
||||
if (pow) {
|
||||
pub.pow(pow, GetPowWorker());
|
||||
}
|
||||
LoginStore.setPublisher(login.id, pub);
|
||||
LoginStore.updateSession({
|
||||
@ -121,8 +123,8 @@ export function LoginUnlock() {
|
||||
await key.unlock(pin);
|
||||
const pub = createPublisher(login);
|
||||
if (pub) {
|
||||
if (login.appData.json.preferences.pow) {
|
||||
pub.pow(login.appData.json.preferences.pow, GetPowWorker());
|
||||
if (pow) {
|
||||
pub.pow(pow, GetPowWorker());
|
||||
}
|
||||
LoginStore.setPublisher(login.id, pub);
|
||||
LoginStore.updateSession({
|
||||
|
@ -4,7 +4,7 @@ import { useContext, useState } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import Modal from "@/Components/Modal/Modal";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import useRelays from "@/Hooks/useRelays";
|
||||
|
||||
import AsyncButton from "./Button/AsyncButton";
|
||||
import messages from "./messages";
|
||||
@ -12,7 +12,7 @@ import messages from "./messages";
|
||||
export function ReBroadcaster({ onClose, ev }: { onClose: () => void; ev: TaggedNostrEvent }) {
|
||||
const [selected, setSelected] = useState<Array<string>>();
|
||||
const system = useContext(SnortContext);
|
||||
const { relays } = useLogin(s => ({ relays: s.relays }));
|
||||
const relays = useRelays();
|
||||
|
||||
async function sendReBroadcast() {
|
||||
if (selected) {
|
||||
@ -25,8 +25,8 @@ export function ReBroadcaster({ onClose, ev }: { onClose: () => void; ev: Tagged
|
||||
function renderRelayCustomisation() {
|
||||
return (
|
||||
<div className="flex flex-col g8">
|
||||
{Object.keys(relays.item || {})
|
||||
.filter(el => relays.item[el].write)
|
||||
{Object.keys(relays)
|
||||
.filter(el => relays[el].write)
|
||||
.map((r, i, a) => (
|
||||
<div key={r} className="card flex justify-between">
|
||||
<div>{r}</div>
|
||||
@ -36,7 +36,7 @@ export function ReBroadcaster({ onClose, ev }: { onClose: () => void; ev: Tagged
|
||||
checked={!selected || selected.includes(r)}
|
||||
onChange={e =>
|
||||
setSelected(
|
||||
e.target.checked && selected && selected.length == a.length - 1
|
||||
e.target.checked && selected && selected.length === a.length - 1
|
||||
? undefined
|
||||
: a.filter(el => (el === r ? e.target.checked : !selected || selected.includes(el))),
|
||||
)
|
||||
|
@ -1,17 +1,14 @@
|
||||
import "./Relay.css";
|
||||
|
||||
import { unixNowMs } from "@snort/shared";
|
||||
import { RelaySettings } from "@snort/system";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
import classNames from "classnames";
|
||||
import { useContext, useMemo } from "react";
|
||||
import { useMemo } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { AsyncIcon } from "@/Components/Button/AsyncIcon";
|
||||
import useRelayState from "@/Feed/RelayState";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import { getRelayName, unwrap } from "@/Utils";
|
||||
import { removeRelay, setRelays } from "@/Utils/Login";
|
||||
import { getRelayName } from "@/Utils";
|
||||
|
||||
import { RelayFavicon } from "./RelaysMetadata";
|
||||
|
||||
@ -21,35 +18,29 @@ export interface RelayProps {
|
||||
|
||||
export default function Relay(props: RelayProps) {
|
||||
const navigate = useNavigate();
|
||||
const system = useContext(SnortContext);
|
||||
const login = useLogin();
|
||||
const state = useLogin(s => s.state);
|
||||
|
||||
const relaySettings = unwrap(login.relays.item[props.addr] ?? system.pool.getConnection(props.addr)?.Settings ?? {});
|
||||
const state = useRelayState(props.addr);
|
||||
const name = useMemo(() => getRelayName(props.addr), [props.addr]);
|
||||
const connection = useRelayState(props.addr);
|
||||
|
||||
function configure(o: RelaySettings) {
|
||||
setRelays(
|
||||
login,
|
||||
{
|
||||
...login.relays.item,
|
||||
[props.addr]: o,
|
||||
},
|
||||
unixNowMs(),
|
||||
);
|
||||
const relaySettings = state.relays?.find(a => a.url === props.addr)?.settings;
|
||||
if (!relaySettings || !connection) return;
|
||||
|
||||
async function configure(o: RelaySettings) {
|
||||
await state.updateRelay(props.addr, o);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relay bg-dark">
|
||||
<div className={classNames("flex items-center", state?.IsClosed === false ? "bg-success" : "bg-error")}>
|
||||
<div className={classNames("flex items-center", connection.isOpen ? "bg-success" : "bg-error")}>
|
||||
<RelayFavicon url={props.addr} />
|
||||
</div>
|
||||
<div className="flex flex-col g8">
|
||||
<div>
|
||||
<b>{name}</b>
|
||||
</div>
|
||||
{!state?.Ephemeral && (
|
||||
{!connection?.Ephemeral && (
|
||||
<div className="flex g8">
|
||||
<AsyncIcon
|
||||
iconName="write"
|
||||
@ -77,13 +68,13 @@ export default function Relay(props: RelayProps) {
|
||||
iconName="trash"
|
||||
iconSize={16}
|
||||
className="button-icon-sm transparent trash-icon"
|
||||
onClick={() => removeRelay(login, props.addr)}
|
||||
onClick={() => state.removeRelay(props.addr)}
|
||||
/>
|
||||
<AsyncIcon
|
||||
iconName="gear"
|
||||
iconSize={16}
|
||||
className="button-icon-sm transparent"
|
||||
onClick={() => navigate(state?.Id ?? "")}
|
||||
onClick={() => navigate(connection?.Id ?? "")}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
@ -17,15 +17,15 @@ enum Provider {
|
||||
}
|
||||
|
||||
export default function SuggestedProfiles() {
|
||||
const login = useLogin(s => ({ publicKey: s.publicKey, follows: s.contacts }));
|
||||
const publicKey = useLogin(s => s.publicKey);
|
||||
const [provider, setProvider] = useState(Provider.NostrBand);
|
||||
|
||||
const getUrlAndKey = () => {
|
||||
if (!login.publicKey) return { url: null, key: null };
|
||||
if (!publicKey) return { url: null, key: null };
|
||||
switch (provider) {
|
||||
case Provider.NostrBand: {
|
||||
const api = new NostrBandApi();
|
||||
const url = api.suggestedFollowsUrl(hexToBech32(NostrPrefix.PublicKey, login.publicKey));
|
||||
const url = api.suggestedFollowsUrl(hexToBech32(NostrPrefix.PublicKey, publicKey));
|
||||
return { url, key: `nostr-band-${url}` };
|
||||
}
|
||||
default:
|
||||
|
@ -33,7 +33,7 @@ export default function TrendingNotes({ count = Infinity, small = false }: { cou
|
||||
const ev = a.event;
|
||||
if (!System.optimizer.schnorrVerify(ev)) {
|
||||
console.error(`Event with invalid sig\n\n${ev}\n\nfrom ${trendingNotesUrl}`);
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
System.HandleEvent("*", ev as TaggedNostrEvent);
|
||||
return ev;
|
||||
|
@ -1,25 +0,0 @@
|
||||
import { HexKey } from "@snort/system";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import useModeration from "@/Hooks/useModeration";
|
||||
|
||||
import messages from "../messages";
|
||||
|
||||
interface BlockButtonProps {
|
||||
pubkey: HexKey;
|
||||
}
|
||||
|
||||
const BlockButton = ({ pubkey }: BlockButtonProps) => {
|
||||
const { block, unblock, isBlocked } = useModeration();
|
||||
return isBlocked(pubkey) ? (
|
||||
<button className="secondary" type="button" onClick={() => unblock(pubkey)}>
|
||||
<FormattedMessage {...messages.Unblock} />
|
||||
</button>
|
||||
) : (
|
||||
<button className="secondary" type="button" onClick={() => block(pubkey)}>
|
||||
<FormattedMessage {...messages.Block} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default BlockButton;
|
@ -1,15 +0,0 @@
|
||||
import BlockButton from "@/Components/User/BlockButton";
|
||||
import ProfilePreview from "@/Components/User/ProfilePreview";
|
||||
import useModeration from "@/Hooks/useModeration";
|
||||
|
||||
export default function BlockList() {
|
||||
const { blocked } = useModeration();
|
||||
|
||||
return (
|
||||
<div className="main-content p">
|
||||
{blocked.map(a => {
|
||||
return <ProfilePreview actions={<BlockButton pubkey={a} />} pubkey={a} options={{ about: false }} key={a} />;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { HexKey } from "@snort/system";
|
||||
import { HexKey, NostrPrefix } from "@snort/system";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import MuteButton from "@/Components/User/MuteButton";
|
||||
@ -11,26 +11,31 @@ export interface MutedListProps {
|
||||
pubkeys: HexKey[];
|
||||
}
|
||||
|
||||
export default function MutedList({ pubkeys }: MutedListProps) {
|
||||
const { isMuted, muteAll } = useModeration();
|
||||
const hasAllMuted = pubkeys.every(isMuted);
|
||||
export default function MutedList() {
|
||||
const { muteList } = useModeration();
|
||||
|
||||
return (
|
||||
<div className="p">
|
||||
<div className="flex justify-between">
|
||||
<div className="bold">
|
||||
<FormattedMessage {...messages.MuteCount} values={{ n: pubkeys?.length }} />
|
||||
<FormattedMessage {...messages.MuteCount} values={{ n: muteList?.length }} />
|
||||
</div>
|
||||
<button
|
||||
disabled={hasAllMuted || pubkeys.length === 0}
|
||||
className="transparent"
|
||||
type="button"
|
||||
onClick={() => muteAll(pubkeys)}>
|
||||
<FormattedMessage {...messages.MuteAll} />
|
||||
</button>
|
||||
</div>
|
||||
{pubkeys?.map(a => {
|
||||
return <ProfilePreview actions={<MuteButton pubkey={a} />} pubkey={a} options={{ about: false }} key={a} />;
|
||||
{muteList?.map(a => {
|
||||
switch (a.type) {
|
||||
case NostrPrefix.Profile:
|
||||
case NostrPrefix.PublicKey: {
|
||||
return (
|
||||
<ProfilePreview
|
||||
actions={<MuteButton pubkey={a.id} />}
|
||||
pubkey={a.id}
|
||||
options={{ about: false }}
|
||||
key={a.id}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
@ -7,6 +7,7 @@ import messages from "@/Components/messages";
|
||||
import { ZapType } from "@/Components/ZapModal/ZapType";
|
||||
import { ZapTypeSelector } from "@/Components/ZapModal/ZapTypeSelector";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import usePreferences from "@/Hooks/usePreferences";
|
||||
import { formatShort } from "@/Utils/Number";
|
||||
import { Zapper } from "@/Utils/Zapper";
|
||||
|
||||
@ -21,10 +22,8 @@ export function ZapModalInput(props: {
|
||||
onChange?: (v: SendSatsInputSelection) => void;
|
||||
onNextStage: (v: SendSatsInputSelection) => Promise<void>;
|
||||
}) {
|
||||
const { defaultZapAmount, readonly } = useLogin(s => ({
|
||||
defaultZapAmount: s.appData.json.preferences.defaultZapAmount,
|
||||
readonly: s.readonly,
|
||||
}));
|
||||
const defaultZapAmount = usePreferences(s => s.defaultZapAmount);
|
||||
const readonly = useLogin(s => s.readonly);
|
||||
const { formatMessage } = useIntl();
|
||||
const amounts: Record<string, string> = {
|
||||
[defaultZapAmount.toString()]: "",
|
||||
|
Reference in New Issue
Block a user