mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-09-30 00:41:09 +00:00
More subs stuff
This commit is contained in:
parent
fcb3926e67
commit
bd09ed7668
@ -4,7 +4,7 @@
|
||||
gap: 8px;
|
||||
border-radius: 8px;
|
||||
background: var(--background-header-input);
|
||||
width: 268px;
|
||||
width: 300px;
|
||||
padding: 16px;
|
||||
|
||||
.userInfo {
|
||||
@ -49,8 +49,9 @@
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
>button {
|
||||
color: var(--text-primary);
|
||||
|
@ -3,12 +3,15 @@ import { batch, Component, createEffect, createSignal, For, JSXElement, onMount,
|
||||
import { createStore } from 'solid-js/store';
|
||||
import { Portal } from 'solid-js/web';
|
||||
import { APP_ID } from '../../App';
|
||||
import { Kind } from '../../constants';
|
||||
import { useAccountContext } from '../../contexts/AccountContext';
|
||||
import { CustomZapInfo, useAppContext } from '../../contexts/AppContext';
|
||||
import { useThreadContext } from '../../contexts/ThreadContext';
|
||||
import { fetchUserProfile } from '../../handleNotes';
|
||||
import { date, shortDate } from '../../lib/dates';
|
||||
import { hookForDev } from '../../lib/devTools';
|
||||
import { sendEvent } from '../../lib/notes';
|
||||
import { zapSubscription } from '../../lib/zap';
|
||||
import { userName } from '../../stores/profile';
|
||||
import { PrimalArticle, PrimalUser, ZapOption } from '../../types/primal';
|
||||
import { uuidv4 } from '../../utils';
|
||||
@ -22,6 +25,7 @@ import ArticleFooter from '../Note/NoteFooter/ArticleFooter';
|
||||
import NoteFooter from '../Note/NoteFooter/NoteFooter';
|
||||
import NoteTopZaps from '../Note/NoteTopZaps';
|
||||
import NoteTopZapsCompact from '../Note/NoteTopZapsCompact';
|
||||
import { Tier } from '../SubscribeToAuthorModal/SubscribeToAuthorModal';
|
||||
import VerificationCheck from '../VerificationCheck/VerificationCheck';
|
||||
|
||||
import styles from './AuthorSubscribe.module.scss';
|
||||
@ -55,8 +59,34 @@ const AuthoreSubscribe: Component<{
|
||||
getAuthorData();
|
||||
});
|
||||
|
||||
const doSubscription = async (tier: Tier) => {
|
||||
const a = author();
|
||||
|
||||
if (!a || !account) return;
|
||||
|
||||
const subEvent = {
|
||||
kind: Kind.Subscribe,
|
||||
content: '',
|
||||
created_at: Math.floor((new Date()).getTime() / 1_000),
|
||||
tags: [
|
||||
['p', a.pubkey],
|
||||
['e', tier.id],
|
||||
['amount', tier.costs[0].amount, tier.costs[0].unit, tier.costs[0].duration],
|
||||
['event', JSON.stringify(tier.event)],
|
||||
// Copy any zap splits
|
||||
...(tier.event.tags?.filter(t => t[0] === 'zap') || []),
|
||||
],
|
||||
}
|
||||
|
||||
const { success, note } = await sendEvent(subEvent, account.relays, account.relaySettings);
|
||||
|
||||
if (success && note) {
|
||||
await zapSubscription(note, a, account.publicKey, account.relays);
|
||||
}
|
||||
}
|
||||
|
||||
const openSubscribe = () => {
|
||||
app?.actions.openAuthorSubscribeModal(author());
|
||||
app?.actions.openAuthorSubscribeModal(author(), doSubscription);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -82,7 +112,10 @@ const AuthoreSubscribe: Component<{
|
||||
{author()?.about || ''}
|
||||
</div>
|
||||
<div class={styles.actions}>
|
||||
<ButtonSecondary onClick={() => navigate(`/p/${author()?.npub}`)}>
|
||||
<ButtonSecondary
|
||||
light={true}
|
||||
onClick={() => navigate(`/p/${author()?.npub}`)}
|
||||
>
|
||||
view profile
|
||||
</ButtonSecondary>
|
||||
|
||||
|
@ -77,6 +77,8 @@
|
||||
|
||||
|
||||
.readsSidebar {
|
||||
margin-left: -8px;
|
||||
|
||||
.section {
|
||||
margin-bottom: 28px;
|
||||
|
||||
|
@ -113,6 +113,9 @@ const ReadsSidebar: Component< { id?: string } > = (props) => {
|
||||
// const author = 'a8eb6e07bf408713b0979f337a3cd978f622e0d41709f3b74b48fff43dbfcd2b';
|
||||
// setFeautredAuthor(() => author);
|
||||
|
||||
// const author = '88cc134b1a65f54ef48acc1df3665063d3ea45f04eab8af4646e561c5ae99079';
|
||||
// setFeautredAuthor(() => author);
|
||||
|
||||
setFeautredAuthor(() => authors[Math.floor(Math.random() * authors.length)]);
|
||||
},
|
||||
onEose: () => {
|
||||
@ -181,7 +184,9 @@ const ReadsSidebar: Component< { id?: string } > = (props) => {
|
||||
<Loader />
|
||||
}
|
||||
>
|
||||
<div class={styles.section}>
|
||||
<AuthorSubscribe pubkey={featuredAuthor()} />
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
|
||||
|
@ -196,7 +196,7 @@ const Layout: Component = () => {
|
||||
<SubscribeToAuthorModal
|
||||
author={app?.subscribeToAuthor}
|
||||
onClose={app?.actions.closeAuthorSubscribeModal}
|
||||
onSubscribe={() => {}}
|
||||
onSubscribe={app?.subscribeToTier}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
|
@ -90,6 +90,7 @@
|
||||
background: none;
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
width: 400px;
|
||||
|
||||
&.selected {
|
||||
border: 1px solid var(--accent);
|
||||
|
@ -44,7 +44,7 @@ const SubscribeToAuthorModal: Component<{
|
||||
id?: string,
|
||||
author: PrimalUser | undefined,
|
||||
onClose: () => void,
|
||||
onSubscribe: () => void,
|
||||
onSubscribe: (tier: Tier) => void,
|
||||
}> = (props) => {
|
||||
|
||||
const [store, updateStore] = createStore<TierStore>({
|
||||
@ -107,6 +107,15 @@ const SubscribeToAuthorModal: Component<{
|
||||
|
||||
const isSelectedTier = (tier: Tier) => tier.id === store.selectedTier?.id;
|
||||
|
||||
|
||||
// const costForTier = (tier: Tier) => {
|
||||
// const costs = tier.costs.filter(c => payUnits.includes(c.unit));
|
||||
|
||||
// costs.reduce((acc, c) => {
|
||||
// return
|
||||
// }, [])
|
||||
// }
|
||||
|
||||
return (
|
||||
<Modal open={props.author !== undefined} onClose={props.onClose}>
|
||||
<div id={props.id} class={styles.subscribeToAuthor}>
|
||||
@ -176,7 +185,9 @@ const SubscribeToAuthorModal: Component<{
|
||||
</div>
|
||||
|
||||
<div class={styles.payAction}>
|
||||
<ButtonPrimary onClick={props.onSubscribe}>
|
||||
<ButtonPrimary
|
||||
onClick={() => store.selectedTier && props.onSubscribe(store.selectedTier)}
|
||||
>
|
||||
subscribe
|
||||
</ButtonPrimary>
|
||||
</div>
|
||||
|
@ -9,6 +9,9 @@ import {
|
||||
} from "solid-js";
|
||||
import { PrimalArticle, PrimalNote, PrimalUser, ZapOption } from "../types/primal";
|
||||
import { CashuMint } from "@cashu/cashu-ts";
|
||||
import { Tier } from "../components/SubscribeToAuthorModal/SubscribeToAuthorModal";
|
||||
import { Kind } from "../constants";
|
||||
import { sendEvent } from "../lib/notes";
|
||||
|
||||
|
||||
export type ReactionStats = {
|
||||
@ -67,6 +70,7 @@ export type AppContextStore = {
|
||||
confirmInfo: ConfirmInfo | undefined,
|
||||
cashuMints: Map<string, CashuMint>,
|
||||
subscribeToAuthor: PrimalUser | undefined,
|
||||
subscribeToTier: (tier: Tier) => void,
|
||||
actions: {
|
||||
openReactionModal: (noteId: string, stats: ReactionStats) => void,
|
||||
closeReactionModal: () => void,
|
||||
@ -82,7 +86,7 @@ export type AppContextStore = {
|
||||
openConfirmModal: (confirmInfo: ConfirmInfo) => void,
|
||||
closeConfirmModal: () => void,
|
||||
getCashuMint: (url: string) => CashuMint | undefined,
|
||||
openAuthorSubscribeModal: (author: PrimalUser | undefined) => void,
|
||||
openAuthorSubscribeModal: (author: PrimalUser | undefined, subscribeTo: (tier: Tier) => void) => void,
|
||||
closeAuthorSubscribeModal: () => void,
|
||||
},
|
||||
}
|
||||
@ -110,6 +114,7 @@ const initialData: Omit<AppContextStore, 'actions'> = {
|
||||
confirmInfo: undefined,
|
||||
cashuMints: new Map(),
|
||||
subscribeToAuthor: undefined,
|
||||
subscribeToTier: () => {},
|
||||
};
|
||||
|
||||
export const AppContext = createContext<AppContextStore>();
|
||||
@ -225,10 +230,11 @@ export const AppProvider = (props: { children: JSXElement }) => {
|
||||
return store.cashuMints.get(formatted);
|
||||
};
|
||||
|
||||
const openAuthorSubscribeModal = (author: PrimalUser | undefined, subscribeTo: (tier: Tier) => void) => {
|
||||
if (!author) return;
|
||||
|
||||
const openAuthorSubscribeModal = (author: PrimalUser | undefined) => {
|
||||
console.log('OPEN: ', author)
|
||||
author && updateStore('subscribeToAuthor', () => ({ ...author }));
|
||||
updateStore('subscribeToAuthor', () => ({ ...author }));
|
||||
updateStore('subscribeToTier', () => subscribeTo);
|
||||
};
|
||||
|
||||
const closeAuthorSubscribeModal = () => {
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { bech32 } from "@scure/base";
|
||||
// @ts-ignore Bad types in nostr-tools
|
||||
import { nip57, Relay, utils } from "nostr-tools";
|
||||
import { PrimalArticle, PrimalNote, PrimalUser } from "../types/primal";
|
||||
import { Tier } from "../components/SubscribeToAuthorModal/SubscribeToAuthorModal";
|
||||
import { Kind } from "../constants";
|
||||
import { NostrRelaySignedEvent, PrimalArticle, PrimalNote, PrimalUser } from "../types/primal";
|
||||
import { logError } from "./logger";
|
||||
import { enableWebLn, sendPayment, signEvent } from "./nostrAPI";
|
||||
|
||||
@ -138,6 +140,62 @@ export const zapProfile = async (profile: PrimalUser, sender: string | undefined
|
||||
}
|
||||
}
|
||||
|
||||
export const zapSubscription = async (subEvent: NostrRelaySignedEvent, recipient: PrimalUser, sender: string | undefined, relays: Relay[]) => {
|
||||
if (!sender || !recipient) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const callback = await getZapEndpoint(recipient);
|
||||
|
||||
if (!callback) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const costTag = subEvent.tags.find(t => t [0] === 'amount');
|
||||
if (!costTag) return false;
|
||||
|
||||
let sats = 0;
|
||||
|
||||
if (costTag[2] === 'sats') {
|
||||
sats = parseInt(costTag[1]) * 1_000;
|
||||
}
|
||||
|
||||
if (costTag[2] === 'msat') {
|
||||
sats = parseInt(costTag[1]);
|
||||
}
|
||||
|
||||
let payload = {
|
||||
profile: recipient.pubkey,
|
||||
event: subEvent.id,
|
||||
amount: sats,
|
||||
relays: relays.map(r => r.url)
|
||||
};
|
||||
|
||||
if (subEvent.content.length > 0) {
|
||||
// @ts-ignore
|
||||
payload.comment = comment;
|
||||
}
|
||||
|
||||
const zapReq = nip57.makeZapRequest(payload);
|
||||
|
||||
try {
|
||||
const signedEvent = await signEvent(zapReq);
|
||||
|
||||
const event = encodeURIComponent(JSON.stringify(signedEvent));
|
||||
|
||||
const r2 = await (await fetch(`${callback}?amount=${sats}&nostr=${event}`)).json();
|
||||
const pr = r2.pr;
|
||||
|
||||
await enableWebLn();
|
||||
await sendPayment(pr);
|
||||
|
||||
return true;
|
||||
} catch (reason) {
|
||||
console.error('Failed to zap: ', reason);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const getZapEndpoint = async (user: PrimalUser): Promise<string | null> => {
|
||||
try {
|
||||
let lnurl: string = ''
|
||||
|
@ -6,13 +6,13 @@ import { APP_ID } from "../App";
|
||||
import { Kind } from "../constants";
|
||||
import { useAccountContext } from "../contexts/AccountContext";
|
||||
import { decodeIdentifier } from "../lib/keys";
|
||||
import { getParametrizedEvent, setLinkPreviews } from "../lib/notes";
|
||||
import { subscribeTo } from "../sockets";
|
||||
import { getParametrizedEvent, sendEvent, setLinkPreviews } from "../lib/notes";
|
||||
import { subscribeTo, subsTo } from "../sockets";
|
||||
import { SolidMarkdown } from "solid-markdown";
|
||||
|
||||
import styles from './Longform.module.scss';
|
||||
import Loader from "../components/Loader/Loader";
|
||||
import { FeedPage, NostrEventContent, NostrMentionContent, NostrNoteActionsContent, NostrNoteContent, NostrStatsContent, NostrUserContent, NoteActions, PrimalArticle, PrimalNote, PrimalUser, SendNoteResult, TopZap, ZapOption } from "../types/primal";
|
||||
import { FeedPage, NostrEventContent, NostrMentionContent, NostrNoteActionsContent, NostrNoteContent, NostrStatsContent, NostrTier, NostrUserContent, NoteActions, PrimalArticle, PrimalNote, PrimalUser, SendNoteResult, TopZap, ZapOption } from "../types/primal";
|
||||
import { getUserProfileInfo, getUserProfiles } from "../lib/profile";
|
||||
import { convertToUser, nip05Verification, userName } from "../stores/profile";
|
||||
import Avatar from "../components/Avatar/Avatar";
|
||||
@ -28,7 +28,7 @@ import NoteTopZaps from "../components/Note/NoteTopZaps";
|
||||
import { parseBolt11, uuidv4 } from "../utils";
|
||||
import Note, { NoteReactionsState } from "../components/Note/Note";
|
||||
import NoteFooter from "../components/Note/NoteFooter/NoteFooter";
|
||||
import { getArticleThread, getThread } from "../lib/feed";
|
||||
import { getArticleThread, getAuthorSubscriptionTiers, getThread } from "../lib/feed";
|
||||
import PhotoSwipeLightbox from "photoswipe/lightbox";
|
||||
import NoteImage from "../components/NoteImage/NoteImage";
|
||||
import { nip19 } from "nostr-tools";
|
||||
@ -48,6 +48,9 @@ import ArticleSidebar from "../components/HomeSidebar/ArticleSidebar";
|
||||
import ReplyToNote from "../components/ReplyToNote/ReplyToNote";
|
||||
import { sanitize } from "dompurify";
|
||||
import { fetchNotes } from "../handleNotes";
|
||||
import { Tier } from "../components/SubscribeToAuthorModal/SubscribeToAuthorModal";
|
||||
import ButtonPrimary from "../components/Buttons/ButtonPrimary";
|
||||
import { zapSubscription } from "../lib/zap";
|
||||
|
||||
export type LongFormData = {
|
||||
title: string,
|
||||
@ -69,6 +72,7 @@ export type LongformThreadStore = {
|
||||
users: PrimalUser[],
|
||||
isFetching: boolean,
|
||||
lastReply: PrimalNote | undefined,
|
||||
hasTiers: boolean,
|
||||
}
|
||||
|
||||
const emptyArticle = {
|
||||
@ -99,6 +103,7 @@ const emptyStore: LongformThreadStore = {
|
||||
users: [],
|
||||
isFetching: false,
|
||||
lastReply: undefined,
|
||||
hasTiers: false,
|
||||
}
|
||||
|
||||
const test = `
|
||||
@ -354,6 +359,68 @@ const Longform: Component< { naddr: string } > = (props) => {
|
||||
fetchArticle();
|
||||
});
|
||||
|
||||
|
||||
createEffect(() => {
|
||||
if (store.article?.user) {
|
||||
getTiers(store.article.user);
|
||||
}
|
||||
});
|
||||
|
||||
const getTiers = (author: PrimalUser) => {
|
||||
if (!author) return;
|
||||
|
||||
const subId = `article_tiers_${APP_ID}`;
|
||||
|
||||
const unsub = subsTo(subId, {
|
||||
onEvent: (_, content) => {
|
||||
if (content.kind === Kind.TierList) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.kind === Kind.Tier) {
|
||||
updateStore('hasTiers', () => true);
|
||||
|
||||
return;
|
||||
}
|
||||
},
|
||||
onEose: () => {
|
||||
unsub();
|
||||
},
|
||||
})
|
||||
|
||||
getAuthorSubscriptionTiers(author.pubkey, subId)
|
||||
}
|
||||
|
||||
const doSubscription = async (tier: Tier) => {
|
||||
const a = store.article?.user;
|
||||
|
||||
if (!a || !account) return;
|
||||
|
||||
const subEvent = {
|
||||
kind: Kind.Subscribe,
|
||||
content: '',
|
||||
created_at: Math.floor((new Date()).getTime() / 1_000),
|
||||
tags: [
|
||||
['p', a.pubkey],
|
||||
['e', tier.id],
|
||||
['amount', tier.costs[0].amount, tier.costs[0].unit, tier.costs[0].duration],
|
||||
['event', JSON.stringify(tier.event)],
|
||||
// Copy any zap splits
|
||||
...(tier.event.tags?.filter(t => t[0] === 'zap') || []),
|
||||
],
|
||||
}
|
||||
|
||||
const { success, note } = await sendEvent(subEvent, account.relays, account.relaySettings);
|
||||
|
||||
if (success && note) {
|
||||
await zapSubscription(note, a, account.publicKey, account.relays);
|
||||
}
|
||||
}
|
||||
|
||||
const openSubscribe = () => {
|
||||
app?.actions.openAuthorSubscribeModal(store.article?.user, doSubscription);
|
||||
};
|
||||
|
||||
const onConfirmZap = (zapOption: ZapOption) => {
|
||||
app?.actions.closeCustomZapModal();
|
||||
batch(() => {
|
||||
@ -823,6 +890,14 @@ const Longform: Component< { naddr: string } > = (props) => {
|
||||
</Show>
|
||||
</div>
|
||||
</A>
|
||||
|
||||
<Show when={store.hasTiers}>
|
||||
<ButtonPrimary
|
||||
onClick={openSubscribe}
|
||||
>
|
||||
subscribe
|
||||
</ButtonPrimary>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<div class={styles.topBar}>
|
||||
|
@ -19,13 +19,13 @@ import { useProfileContext } from '../contexts/ProfileContext';
|
||||
import { useAccountContext } from '../contexts/AccountContext';
|
||||
import Wormhole from '../components/Wormhole/Wormhole';
|
||||
import { useIntl } from '@cookbook/solid-intl';
|
||||
import { sanitize } from '../lib/notes';
|
||||
import { sanitize, sendEvent } from '../lib/notes';
|
||||
import { shortDate } from '../lib/dates';
|
||||
|
||||
import styles from './Profile.module.scss';
|
||||
import StickySidebar from '../components/StickySidebar/StickySidebar';
|
||||
import ProfileSidebar from '../components/ProfileSidebar/ProfileSidebar';
|
||||
import { MenuItem, VanityProfiles, ZapOption } from '../types/primal';
|
||||
import { MenuItem, PrimalUser, VanityProfiles, ZapOption } from '../types/primal';
|
||||
import PageTitle from '../components/PageTitle/PageTitle';
|
||||
import FollowButton from '../components/FollowButton/FollowButton';
|
||||
import Search from '../components/Search/Search';
|
||||
@ -44,6 +44,13 @@ import NoteImage from '../components/NoteImage/NoteImage';
|
||||
import ProfileQrCodeModal from '../components/ProfileQrCodeModal/ProfileQrCodeModal';
|
||||
import { CustomZapInfo, useAppContext } from '../contexts/AppContext';
|
||||
import ProfileAbout from '../components/ProfileAbout/ProfileAbout';
|
||||
import ButtonPrimary from '../components/Buttons/ButtonPrimary';
|
||||
import { Tier } from '../components/SubscribeToAuthorModal/SubscribeToAuthorModal';
|
||||
import { Kind } from '../constants';
|
||||
import { getAuthorSubscriptionTiers } from '../lib/feed';
|
||||
import { zapSubscription } from '../lib/zap';
|
||||
import { updateStore, store } from '../services/StoreService';
|
||||
import { subsTo } from '../sockets';
|
||||
|
||||
const Profile: Component = () => {
|
||||
|
||||
@ -66,6 +73,8 @@ const Profile: Component = () => {
|
||||
const [confirmMuteUser, setConfirmMuteUser] = createSignal(false);
|
||||
const [openQr, setOpenQr] = createSignal(false);
|
||||
|
||||
const [hasTiers, setHasTiers] = createSignal(false);
|
||||
|
||||
const lightbox = new PhotoSwipeLightbox({
|
||||
gallery: '#central_header',
|
||||
children: 'a.profile_image',
|
||||
@ -124,6 +133,7 @@ const Profile: Component = () => {
|
||||
profile?.actions.clearContacts();
|
||||
profile?.actions.clearZaps();
|
||||
profile?.actions.clearFilterReason();
|
||||
setHasTiers(() => false);
|
||||
}
|
||||
|
||||
let keyIsDone = false
|
||||
@ -504,6 +514,69 @@ const Profile: Component = () => {
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
createEffect(() => {
|
||||
if (profile?.userProfile) {
|
||||
getTiers(profile.userProfile);
|
||||
}
|
||||
});
|
||||
|
||||
const getTiers = (author: PrimalUser) => {
|
||||
if (!author) return;
|
||||
|
||||
const subId = `article_tiers_${APP_ID}`;
|
||||
|
||||
const unsub = subsTo(subId, {
|
||||
onEvent: (_, content) => {
|
||||
if (content.kind === Kind.TierList) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.kind === Kind.Tier) {
|
||||
setHasTiers(() => true);
|
||||
|
||||
return;
|
||||
}
|
||||
},
|
||||
onEose: () => {
|
||||
unsub();
|
||||
},
|
||||
})
|
||||
|
||||
getAuthorSubscriptionTiers(author.pubkey, subId)
|
||||
}
|
||||
|
||||
const doSubscription = async (tier: Tier) => {
|
||||
const a = profile?.userProfile;
|
||||
|
||||
if (!a || !account) return;
|
||||
|
||||
const subEvent = {
|
||||
kind: Kind.Subscribe,
|
||||
content: '',
|
||||
created_at: Math.floor((new Date()).getTime() / 1_000),
|
||||
tags: [
|
||||
['p', a.pubkey],
|
||||
['e', tier.id],
|
||||
['amount', tier.costs[0].amount, tier.costs[0].unit, tier.costs[0].duration],
|
||||
['event', JSON.stringify(tier.event)],
|
||||
// Copy any zap splits
|
||||
...(tier.event.tags?.filter(t => t[0] === 'zap') || []),
|
||||
],
|
||||
}
|
||||
|
||||
const { success, note } = await sendEvent(subEvent, account.relays, account.relaySettings);
|
||||
|
||||
if (success && note) {
|
||||
await zapSubscription(note, a, account.publicKey, account.relays);
|
||||
}
|
||||
}
|
||||
|
||||
const openSubscribe = () => {
|
||||
app?.actions.openAuthorSubscribeModal(profile?.userProfile, doSubscription);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle title={
|
||||
@ -600,6 +673,14 @@ const Profile: Component = () => {
|
||||
<FollowButton person={profile?.userProfile} large={true} />
|
||||
</Show>
|
||||
|
||||
<Show when={hasTiers()}>
|
||||
<ButtonPrimary
|
||||
onClick={openSubscribe}
|
||||
>
|
||||
subscribe
|
||||
</ButtonPrimary>
|
||||
</Show>
|
||||
|
||||
<Show when={isCurrentUser()}>
|
||||
<div class={styles.editProfileButton}>
|
||||
<ButtonSecondary
|
||||
|
Loading…
Reference in New Issue
Block a user