From 20ecc0586c43704a2c78055f1cb72bd5e32abc92 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Tue, 4 Jun 2024 13:46:42 -0300 Subject: [PATCH 1/4] feat: zap request in new format - pass account_id in /api/v1/ditto/zap request body - get invoice from body instead of header --- src/actions/interactions.ts | 14 +++++++------- .../zap/components/zap-pay-request-form.tsx | 18 ++++++++---------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/actions/interactions.ts b/src/actions/interactions.ts index a8960e607..0520c33ef 100644 --- a/src/actions/interactions.ts +++ b/src/actions/interactions.ts @@ -12,7 +12,7 @@ import { openModal } from './modals'; import { expandGroupFeaturedTimeline } from './timelines'; import type { AppDispatch, RootState } from 'soapbox/store'; -import type { APIEntity, Group, Status as StatusEntity } from 'soapbox/types/entities'; +import type { Account as AccountEntity, APIEntity, Group, Status as StatusEntity } from 'soapbox/types/entities'; const REBLOG_REQUEST = 'REBLOG_REQUEST'; const REBLOG_SUCCESS = 'REBLOG_SUCCESS'; @@ -314,26 +314,26 @@ const undislikeFail = (status: StatusEntity, error: unknown) => ({ skipLoading: true, }); -const zap = (status: StatusEntity, amount: number, comment: string) => (dispatch: AppDispatch, getState: () => RootState) => { +const zap = (account: AccountEntity, status: StatusEntity|undefined, amount: number, comment: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; - dispatch(zapRequest(status)); + if (status) dispatch(zapRequest(status)); - return api(getState).post(`/api/v1/statuses/${status.id}/zap`, { amount, comment: comment ?? '' }).then(async function(response) { - const invoice = response.headers['ln-invoice']; + return api(getState).post('/api/v1/ditto/zap', { amount, comment: comment ?? '', account_id: account.id, status_id: status?.id ?? '' }).then(async function(response) { + const { invoice } = response.data; if (!invoice) throw Error('Could not generate invoice'); if (!window.webln) return invoice; try { await window.webln?.enable(); await window.webln?.sendPayment(invoice); - dispatch(zapSuccess(status)); + if (status) dispatch(zapSuccess(status)); return undefined; } catch (e) { // In case it fails we just return the invoice so the QR code can be created return invoice; } }).catch(function(e) { - dispatch(zapFail(status, e)); + if (status) dispatch(zapFail(status, e)); }); }; diff --git a/src/features/zap/components/zap-pay-request-form.tsx b/src/features/zap/components/zap-pay-request-form.tsx index 9b34da7f6..0668c762f 100644 --- a/src/features/zap/components/zap-pay-request-form.tsx +++ b/src/features/zap/components/zap-pay-request-form.tsx @@ -27,17 +27,15 @@ const ZapPayRequestForm = ({ account, status }: IZapPayRequestForm) => { const handleSubmit = async (e?: React.FormEvent) => { e?.preventDefault(); - if (status) { - const invoice = await dispatch(zap(status, zapAmount * 1000, zapComment)); - // If invoice is undefined it means the user has paid through his extension - // In this case, we simply close the modal - if (!invoice) { - dispatch(closeModal('ZAP_PAY_REQUEST')); - return; - } - // open QR code modal - dispatch(openModal('ZAP_INVOICE', { invoice, account })); + const invoice = await dispatch(zap(account, status, zapAmount * 1000, zapComment)); + // If invoice is undefined it means the user has paid through his extension + // In this case, we simply close the modal + if (!invoice) { + dispatch(closeModal('ZAP_PAY_REQUEST')); + return; } + // open QR code modal + dispatch(openModal('ZAP_INVOICE', { invoice, account })); }; const zapOptions = () => { From f1d6d32f92d3c2d0dce78811dab90a8f066ce939 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Tue, 4 Jun 2024 16:55:32 -0300 Subject: [PATCH 2/4] feat: zap account directly --- src/features/account/components/header.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/features/account/components/header.tsx b/src/features/account/components/header.tsx index cd95d5945..be74a5b65 100644 --- a/src/features/account/components/header.tsx +++ b/src/features/account/components/header.tsx @@ -75,6 +75,7 @@ const messages = defineMessages({ profileExternal: { id: 'account.profile_external', defaultMessage: 'View profile on {domain}' }, header: { id: 'account.header.alt', defaultMessage: 'Profile header' }, subscribeFeed: { id: 'account.rss_feed', defaultMessage: 'Subscribe to RSS feed' }, + zap: { id: 'zap.send_to', defaultMessage: 'Send zaps to {target}' }, }); interface IHeader { @@ -282,6 +283,10 @@ const Header: React.FC = ({ account }) => { copy(nip19.npubEncode(account.nostr.pubkey!)); }; + const handleZapAccount: React.EventHandler = (e) => { + dispatch(openModal('ZAP_PAY_REQUEST', { account })); + }; + const makeMenu = () => { const menu: Menu = []; @@ -621,8 +626,22 @@ const Header: React.FC = ({ account }) => { ); }; + const renderZapAccount = () => { + return ( + + ); + }; + const info = makeInfo(); const menu = makeMenu(); + const acceptsZaps = account.ditto.accepts_zaps === true; return (
@@ -664,6 +683,7 @@ const Header: React.FC = ({ account }) => { {renderMessageButton()} {renderShareButton()} + {acceptsZaps && renderZapAccount()} {menu.length > 0 && ( From 673425463055c4a05ed3ad0c714de9bab2296c84 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 4 Jun 2024 22:57:44 +0000 Subject: [PATCH 3/4] Add spaces around union pipe --- src/actions/interactions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/actions/interactions.ts b/src/actions/interactions.ts index 0520c33ef..5c8460742 100644 --- a/src/actions/interactions.ts +++ b/src/actions/interactions.ts @@ -314,7 +314,7 @@ const undislikeFail = (status: StatusEntity, error: unknown) => ({ skipLoading: true, }); -const zap = (account: AccountEntity, status: StatusEntity|undefined, amount: number, comment: string) => (dispatch: AppDispatch, getState: () => RootState) => { +const zap = (account: AccountEntity, status: StatusEntity | undefined, amount: number, comment: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; if (status) dispatch(zapRequest(status)); From 3b419b1097b5201dba9f54101f41fe1f0faef79e Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 4 Jun 2024 22:59:20 +0000 Subject: [PATCH 4/4] Don't pass empty strings to the API for no reason? --- src/actions/interactions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/actions/interactions.ts b/src/actions/interactions.ts index 5c8460742..cabcbb391 100644 --- a/src/actions/interactions.ts +++ b/src/actions/interactions.ts @@ -319,7 +319,7 @@ const zap = (account: AccountEntity, status: StatusEntity | undefined, amount: n if (status) dispatch(zapRequest(status)); - return api(getState).post('/api/v1/ditto/zap', { amount, comment: comment ?? '', account_id: account.id, status_id: status?.id ?? '' }).then(async function(response) { + return api(getState).post('/api/v1/ditto/zap', { amount, comment, account_id: account.id, status_id: status?.id }).then(async function(response) { const { invoice } = response.data; if (!invoice) throw Error('Could not generate invoice'); if (!window.webln) return invoice;