Nostr Zaps
This commit is contained in:
parent
e281fbade9
commit
9cad54c97c
5 changed files with 67 additions and 2 deletions
|
@ -78,6 +78,10 @@ const FAVOURITES_EXPAND_FAIL = 'FAVOURITES_EXPAND_FAIL';
|
||||||
const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS';
|
const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS';
|
||||||
const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL';
|
const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL';
|
||||||
|
|
||||||
|
const ZAP_REQUEST = 'ZAP_REQUEST';
|
||||||
|
const ZAP_SUCCESS = 'ZAP_SUCCESS';
|
||||||
|
const ZAP_FAIL = 'ZAP_FAIL';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
bookmarkAdded: { id: 'status.bookmarked', defaultMessage: 'Bookmark added.' },
|
bookmarkAdded: { id: 'status.bookmarked', defaultMessage: 'Bookmark added.' },
|
||||||
bookmarkRemoved: { id: 'status.unbookmarked', defaultMessage: 'Bookmark removed.' },
|
bookmarkRemoved: { id: 'status.unbookmarked', defaultMessage: 'Bookmark removed.' },
|
||||||
|
@ -306,6 +310,38 @@ const undislikeFail = (status: StatusEntity, error: unknown) => ({
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const zap = (status: StatusEntity, amount: number) =>
|
||||||
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
|
if (!isLoggedIn(getState)) return;
|
||||||
|
|
||||||
|
dispatch(zapRequest(status));
|
||||||
|
|
||||||
|
api(getState).post(`/api/v1/statuses/${status.id}/zap`, { amount }).then(function(response) {
|
||||||
|
dispatch(zapSuccess(status));
|
||||||
|
}).catch(function(error) {
|
||||||
|
dispatch(zapFail(status, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const zapRequest = (status: StatusEntity) => ({
|
||||||
|
type: ZAP_REQUEST,
|
||||||
|
status: status,
|
||||||
|
skipLoading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const zapSuccess = (status: StatusEntity) => ({
|
||||||
|
type: ZAP_SUCCESS,
|
||||||
|
status: status,
|
||||||
|
skipLoading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const zapFail = (status: StatusEntity, error: unknown) => ({
|
||||||
|
type: ZAP_FAIL,
|
||||||
|
status: status,
|
||||||
|
error: error,
|
||||||
|
skipLoading: true,
|
||||||
|
});
|
||||||
|
|
||||||
const bookmark = (status: StatusEntity) =>
|
const bookmark = (status: StatusEntity) =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch(bookmarkRequest(status));
|
dispatch(bookmarkRequest(status));
|
||||||
|
@ -801,4 +837,5 @@ export {
|
||||||
remoteInteractionRequest,
|
remoteInteractionRequest,
|
||||||
remoteInteractionSuccess,
|
remoteInteractionSuccess,
|
||||||
remoteInteractionFail,
|
remoteInteractionFail,
|
||||||
|
zap,
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { blockAccount } from 'soapbox/actions/accounts';
|
||||||
import { launchChat } from 'soapbox/actions/chats';
|
import { launchChat } from 'soapbox/actions/chats';
|
||||||
import { directCompose, mentionCompose, quoteCompose, replyCompose } from 'soapbox/actions/compose';
|
import { directCompose, mentionCompose, quoteCompose, replyCompose } from 'soapbox/actions/compose';
|
||||||
import { editEvent } from 'soapbox/actions/events';
|
import { editEvent } from 'soapbox/actions/events';
|
||||||
import { pinToGroup, toggleBookmark, toggleDislike, toggleFavourite, togglePin, toggleReblog, unpinFromGroup } from 'soapbox/actions/interactions';
|
import { pinToGroup, toggleBookmark, toggleDislike, toggleFavourite, togglePin, toggleReblog, unpinFromGroup, zap } from 'soapbox/actions/interactions';
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
import { deleteStatusModal, toggleStatusSensitivityModal } from 'soapbox/actions/moderation';
|
import { deleteStatusModal, toggleStatusSensitivityModal } from 'soapbox/actions/moderation';
|
||||||
import { initMuteModal } from 'soapbox/actions/mutes';
|
import { initMuteModal } from 'soapbox/actions/mutes';
|
||||||
|
@ -103,6 +103,7 @@ const messages = defineMessages({
|
||||||
unmuteSuccess: { id: 'group.unmute.success', defaultMessage: 'Unmuted the group' },
|
unmuteSuccess: { id: 'group.unmute.success', defaultMessage: 'Unmuted the group' },
|
||||||
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
||||||
unpinFromGroup: { id: 'status.unpin_to_group', defaultMessage: 'Unpin from Group' },
|
unpinFromGroup: { id: 'status.unpin_to_group', defaultMessage: 'Unpin from Group' },
|
||||||
|
zap: { id: 'status.zap', defaultMessage: 'Zap' },
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IStatusActionBar {
|
interface IStatusActionBar {
|
||||||
|
@ -188,6 +189,14 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleZapClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||||
|
if (me) {
|
||||||
|
dispatch(zap(status, 1337));
|
||||||
|
} else {
|
||||||
|
onOpenUnauthorizedModal('ZAP');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleBookmarkClick: React.EventHandler<React.MouseEvent> = (e) => {
|
const handleBookmarkClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||||
dispatch(toggleBookmark(status));
|
dispatch(toggleBookmark(status));
|
||||||
};
|
};
|
||||||
|
@ -694,6 +703,7 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const canShare = ('share' in navigator) && (status.visibility === 'public' || status.visibility === 'group');
|
const canShare = ('share' in navigator) && (status.visibility === 'public' || status.visibility === 'group');
|
||||||
|
const acceptsZaps = status.account.ditto.accepts_zaps === true;
|
||||||
|
|
||||||
const spacing: {
|
const spacing: {
|
||||||
[key: string]: React.ComponentProps<typeof HStack>['space'];
|
[key: string]: React.ComponentProps<typeof HStack>['space'];
|
||||||
|
@ -781,6 +791,19 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{acceptsZaps && (
|
||||||
|
<StatusActionButton
|
||||||
|
title={intl.formatMessage(messages.zap)}
|
||||||
|
icon={require('@tabler/icons/bolt.svg')}
|
||||||
|
color='accent'
|
||||||
|
filled
|
||||||
|
onClick={handleZapClick}
|
||||||
|
active={status.zapped}
|
||||||
|
text={withLabels ? intl.formatMessage(messages.zap) : undefined}
|
||||||
|
theme={statusActionButtonTheme}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{canShare && (
|
{canShare && (
|
||||||
<StatusActionButton
|
<StatusActionButton
|
||||||
title={intl.formatMessage(messages.share)}
|
title={intl.formatMessage(messages.share)}
|
||||||
|
|
|
@ -82,6 +82,7 @@ export const StatusRecord = ImmutableRecord({
|
||||||
uri: '',
|
uri: '',
|
||||||
url: '',
|
url: '',
|
||||||
visibility: 'public' as StatusVisibility,
|
visibility: 'public' as StatusVisibility,
|
||||||
|
zapped: false,
|
||||||
event: null as ReturnType<typeof EventRecord> | null,
|
event: null as ReturnType<typeof EventRecord> | null,
|
||||||
|
|
||||||
// Internal fields
|
// Internal fields
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { unescapeHTML } from 'soapbox/utils/html';
|
||||||
|
|
||||||
import { customEmojiSchema } from './custom-emoji';
|
import { customEmojiSchema } from './custom-emoji';
|
||||||
import { relationshipSchema } from './relationship';
|
import { relationshipSchema } from './relationship';
|
||||||
import { contentSchema, filteredArray, makeCustomEmojiMap } from './utils';
|
import { coerceObject, contentSchema, filteredArray, makeCustomEmojiMap } from './utils';
|
||||||
|
|
||||||
import type { Resolve } from 'soapbox/utils/types';
|
import type { Resolve } from 'soapbox/utils/types';
|
||||||
|
|
||||||
|
@ -29,6 +29,9 @@ const baseAccountSchema = z.object({
|
||||||
created_at: z.string().datetime().catch(new Date().toUTCString()),
|
created_at: z.string().datetime().catch(new Date().toUTCString()),
|
||||||
discoverable: z.boolean().catch(false),
|
discoverable: z.boolean().catch(false),
|
||||||
display_name: z.string().catch(''),
|
display_name: z.string().catch(''),
|
||||||
|
ditto: coerceObject({
|
||||||
|
accepts_zaps: z.boolean().catch(false),
|
||||||
|
}),
|
||||||
emojis: filteredArray(customEmojiSchema),
|
emojis: filteredArray(customEmojiSchema),
|
||||||
fields: filteredArray(fieldSchema),
|
fields: filteredArray(fieldSchema),
|
||||||
followers_count: z.number().catch(0),
|
followers_count: z.number().catch(0),
|
||||||
|
|
|
@ -67,6 +67,7 @@ const baseStatusSchema = z.object({
|
||||||
uri: z.string().url().catch(''),
|
uri: z.string().url().catch(''),
|
||||||
url: z.string().url().catch(''),
|
url: z.string().url().catch(''),
|
||||||
visibility: z.string().catch('public'),
|
visibility: z.string().catch('public'),
|
||||||
|
zapped: z.coerce.boolean(),
|
||||||
});
|
});
|
||||||
|
|
||||||
type BaseStatus = z.infer<typeof baseStatusSchema>;
|
type BaseStatus = z.infer<typeof baseStatusSchema>;
|
||||||
|
|
Loading…
Reference in a new issue