pl-fe: required approval notifications
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
0c84346c3a
commit
3b0cd07e1d
7 changed files with 50 additions and 25 deletions
|
@ -167,11 +167,13 @@ interface ComposeReplyAction {
|
|||
explicitAddressing: boolean;
|
||||
preserveSpoilers: boolean;
|
||||
rebloggedBy?: Pick<Account, 'acct' | 'id'>;
|
||||
approvalRequired?: boolean;
|
||||
}
|
||||
|
||||
const replyCompose = (
|
||||
status: ComposeReplyAction['status'],
|
||||
rebloggedBy?: ComposeReplyAction['rebloggedBy'],
|
||||
approvalRequired?: ComposeReplyAction['approvalRequired'],
|
||||
) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
|
@ -190,6 +192,7 @@ const replyCompose = (
|
|||
explicitAddressing,
|
||||
preserveSpoilers,
|
||||
rebloggedBy,
|
||||
approvalRequired,
|
||||
});
|
||||
useModalsStore.getState().openModal('COMPOSE');
|
||||
};
|
||||
|
|
|
@ -78,6 +78,8 @@ const FAVOURITES_EXPAND_FAIL = 'FAVOURITES_EXPAND_FAIL' as const;
|
|||
const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS' as const;
|
||||
const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL' as const;
|
||||
|
||||
const noOp = () => new Promise(f => f(undefined));
|
||||
|
||||
type AccountListLink = () => Promise<PaginatedResponse<Account>>;
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -90,7 +92,7 @@ const messages = defineMessages({
|
|||
|
||||
const reblog = (status: Pick<Status, 'id'>) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
if (!isLoggedIn(getState)) return noOp();
|
||||
|
||||
dispatch(reblogRequest(status.id));
|
||||
|
||||
|
@ -106,7 +108,7 @@ const reblog = (status: Pick<Status, 'id'>) =>
|
|||
|
||||
const unreblog = (status: Pick<Status, 'id'>) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
if (!isLoggedIn(getState)) return noOp();
|
||||
|
||||
dispatch(unreblogRequest(status.id));
|
||||
|
||||
|
@ -117,14 +119,13 @@ const unreblog = (status: Pick<Status, 'id'>) =>
|
|||
});
|
||||
};
|
||||
|
||||
const toggleReblog = (status: Pick<Status, 'id' | 'reblogged'>) =>
|
||||
(dispatch: AppDispatch) => {
|
||||
if (status.reblogged) {
|
||||
dispatch(unreblog(status));
|
||||
} else {
|
||||
dispatch(reblog(status));
|
||||
}
|
||||
};
|
||||
const toggleReblog = (status: Pick<Status, 'id' | 'reblogged'>) => {
|
||||
if (status.reblogged) {
|
||||
return unreblog(status);
|
||||
} else {
|
||||
return reblog(status);
|
||||
}
|
||||
};
|
||||
|
||||
const reblogRequest = (statusId: string) => ({
|
||||
type: REBLOG_REQUEST,
|
||||
|
@ -162,7 +163,7 @@ const unreblogFail = (statusId: string, error: unknown) => ({
|
|||
|
||||
const favourite = (status: Pick<Status, 'id'>) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
if (!isLoggedIn(getState)) return noOp();
|
||||
|
||||
dispatch(favouriteRequest(status.id));
|
||||
|
||||
|
@ -175,7 +176,7 @@ const favourite = (status: Pick<Status, 'id'>) =>
|
|||
|
||||
const unfavourite = (status: Pick<Status, 'id'>) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
if (!isLoggedIn(getState)) return noOp();
|
||||
|
||||
dispatch(unfavouriteRequest(status.id));
|
||||
|
||||
|
@ -186,14 +187,13 @@ const unfavourite = (status: Pick<Status, 'id'>) =>
|
|||
});
|
||||
};
|
||||
|
||||
const toggleFavourite = (status: Pick<Status, 'id' | 'favourited'>) =>
|
||||
(dispatch: AppDispatch) => {
|
||||
if (status.favourited) {
|
||||
dispatch(unfavourite(status));
|
||||
} else {
|
||||
dispatch(favourite(status));
|
||||
}
|
||||
};
|
||||
const toggleFavourite = (status: Pick<Status, 'id' | 'favourited'>) => {
|
||||
if (status.favourited) {
|
||||
return unfavourite(status);
|
||||
} else {
|
||||
return favourite(status);
|
||||
}
|
||||
};
|
||||
|
||||
const favouriteRequest = (statusId: string) => ({
|
||||
type: FAVOURITE_REQUEST,
|
||||
|
|
|
@ -134,6 +134,9 @@ const messages = defineMessages({
|
|||
replyInteractionPolicyFollowing: { id: 'status.interaction_policy.reply.following_only', defaultMessage: 'Only users followed by the author can reply.' },
|
||||
replyInteractionPolicyMutuals: { id: 'status.interaction_policy.reply.mutuals_only', defaultMessage: 'Only users mutually following the author can reply.' },
|
||||
replyInteractionPolicyMentioned: { id: 'status.interaction_policy.reply.mentioned_only', defaultMessage: 'Only users mentioned by the author can reply.' },
|
||||
|
||||
favouriteApprovalRequired: { id: 'status.interaction_policy.favourite.approval_required', defaultMessage: 'The author needs to approve your like.' },
|
||||
reblogApprovalRequired: { id: 'status.interaction_policy.reblog.approval_required', defaultMessage: 'The author needs to approve your repost.' },
|
||||
});
|
||||
|
||||
interface IInteractionPopover {
|
||||
|
@ -225,7 +228,7 @@ const ReplyButton: React.FC<IReplyButton> = ({
|
|||
|
||||
const handleReplyClick: React.MouseEventHandler = (e) => {
|
||||
if (me) {
|
||||
dispatch(replyCompose(status, rebloggedBy));
|
||||
dispatch(replyCompose(status, rebloggedBy, canReply.approvalRequired || false));
|
||||
} else {
|
||||
onOpenUnauthorizedModal('REPLY');
|
||||
}
|
||||
|
@ -292,7 +295,9 @@ const ReblogButton: React.FC<IReblogButton> = ({
|
|||
|
||||
const handleReblogClick: React.EventHandler<React.MouseEvent> = e => {
|
||||
if (me) {
|
||||
const modalReblog = () => dispatch(toggleReblog(status));
|
||||
const modalReblog = () => dispatch(toggleReblog(status)).then(() => {
|
||||
if (canReblog.approvalRequired) toast.info(messages.reblogApprovalRequired);
|
||||
});
|
||||
if ((e && e.shiftKey) || !boostModal) {
|
||||
modalReblog();
|
||||
} else {
|
||||
|
@ -378,7 +383,9 @@ const FavouriteButton: React.FC<IActionButton> = ({
|
|||
|
||||
const handleFavouriteClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||
if (me) {
|
||||
dispatch(toggleFavourite(status));
|
||||
dispatch(toggleFavourite(status)).then(() => {
|
||||
if (canFavourite.approvalRequired) toast.info(messages.favouriteApprovalRequired);
|
||||
}).catch(() => {});
|
||||
} else {
|
||||
onOpenUnauthorizedModal('FAVOURITE');
|
||||
}
|
||||
|
@ -411,6 +418,7 @@ const FavouriteButton: React.FC<IActionButton> = ({
|
|||
{favouriteButton}
|
||||
</Popover>
|
||||
);
|
||||
return favouriteButton;
|
||||
};
|
||||
|
||||
const DislikeButton: React.FC<IActionButton> = ({
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import clsx from 'clsx';
|
||||
import { CLEAR_EDITOR_COMMAND, TextNode, type LexicalEditor } from 'lexical';
|
||||
import React, { Suspense, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { length } from 'stringz';
|
||||
|
||||
|
@ -45,6 +45,7 @@ import SpoilerInput from './spoiler-input';
|
|||
import TextCharacterCounter from './text-character-counter';
|
||||
import UploadForm from './upload-form';
|
||||
import VisualCharacterCounter from './visual-character-counter';
|
||||
import Warning from './warning';
|
||||
|
||||
import type { AutoSuggestion } from 'pl-fe/components/autosuggest-input';
|
||||
import type { Emoji } from 'pl-fe/features/emoji';
|
||||
|
@ -227,6 +228,14 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
|
||||
return (
|
||||
<Stack className='w-full' space={4} ref={formRef} onClick={handleClick} element='form' onSubmit={handleSubmit}>
|
||||
{!!compose.in_reply_to && compose.approvalRequired && (
|
||||
<Warning
|
||||
message={(
|
||||
<FormattedMessage id='compose_form.approval_required' defaultMessage='The reply needs to be approved by the post author.' />
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<WarningContainer composeId={id} />
|
||||
|
||||
{!shouldCondense && !event && !group && groupId && <ReplyGroupIndicator composeId={id} />}
|
||||
|
|
|
@ -11,7 +11,7 @@ interface IWarning {
|
|||
const Warning: React.FC<IWarning> = ({ message }) => (
|
||||
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
<div className='mb-2.5 rounded border border-solid border-gray-400 bg-transparent px-2.5 py-2 text-xs text-gray-900 dark:border-gray-800 dark:text-white' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
|
||||
<div className='rounded border border-solid border-gray-400 bg-transparent px-2.5 py-2 text-xs text-gray-900 dark:border-gray-800 dark:text-white' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
|
||||
{message}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -455,6 +455,7 @@
|
|||
"compose_event.tabs.pending": "Manage requests",
|
||||
"compose_event.update": "Update",
|
||||
"compose_event.upload_banner": "Upload event banner",
|
||||
"compose_form.approval_required": "The reply needs to be approved by the post author.",
|
||||
"compose_form.content_type.change": "Change content type",
|
||||
"compose_form.direct_message_warning": "This post will only be sent to the mentioned users.",
|
||||
"compose_form.event_placeholder": "Post to this event",
|
||||
|
@ -1459,11 +1460,13 @@
|
|||
"status.group": "Posted in {group}",
|
||||
"status.group_mod_delete": "Delete post from group",
|
||||
"status.hide_translation": "Hide translation",
|
||||
"status.interaction_policy.favourite.approval_required": "The author needs to approve your like.",
|
||||
"status.interaction_policy.favourite.followers_only": "Only users following the author can like.",
|
||||
"status.interaction_policy.favourite.following_only": "Only users followed by the author can like.",
|
||||
"status.interaction_policy.favourite.header": "The author limits who can like this post.",
|
||||
"status.interaction_policy.favourite.mentioned_only": "Only users mentioned by the author can like.",
|
||||
"status.interaction_policy.favourite.mutuals_only": "Only users mutually following the author can like.",
|
||||
"status.interaction_policy.reblog.approval_required": "The author needs to approve your repost.",
|
||||
"status.interaction_policy.reblog.followers_only": "Only users following the author can repost.",
|
||||
"status.interaction_policy.reblog.following_only": "Only users followed by the author can repost.",
|
||||
"status.interaction_policy.reblog.header": "The author limits who can repost this post.",
|
||||
|
|
|
@ -120,6 +120,7 @@ const ReducerCompose = ImmutableRecord({
|
|||
modified_language: null as Language | null,
|
||||
suggested_language: null as string | null,
|
||||
federated: true,
|
||||
approvalRequired: false,
|
||||
});
|
||||
|
||||
type State = ImmutableMap<string, Compose>;
|
||||
|
@ -345,6 +346,7 @@ const compose = (state = initialState, action: ComposeAction | EventsAction | In
|
|||
map.set('caretPosition', null);
|
||||
map.set('idempotencyKey', crypto.randomUUID());
|
||||
map.set('content_type', defaultCompose.content_type);
|
||||
map.set('approvalRequired', action.approvalRequired || false);
|
||||
if (action.preserveSpoilers && action.status.spoiler_text) {
|
||||
map.set('sensitive', true);
|
||||
map.set('spoiler_text', action.status.spoiler_text);
|
||||
|
|
Loading…
Reference in a new issue