Mention repost author when replying to a reposted status
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
775d272254
commit
9c5e3a98e0
6 changed files with 29 additions and 18 deletions
|
@ -152,9 +152,10 @@ interface ComposeReplyAction {
|
||||||
account: Account;
|
account: Account;
|
||||||
explicitAddressing: boolean;
|
explicitAddressing: boolean;
|
||||||
preserveSpoilers: boolean;
|
preserveSpoilers: boolean;
|
||||||
|
rebloggedBy?: Account;
|
||||||
}
|
}
|
||||||
|
|
||||||
const replyCompose = (status: Status) =>
|
const replyCompose = (status: Status, rebloggedBy?: Account) =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const instance = state.instance;
|
const instance = state.instance;
|
||||||
|
@ -171,6 +172,7 @@ const replyCompose = (status: Status) =>
|
||||||
account,
|
account,
|
||||||
explicitAddressing,
|
explicitAddressing,
|
||||||
preserveSpoilers,
|
preserveSpoilers,
|
||||||
|
rebloggedBy: rebloggedBy,
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatch(action);
|
dispatch(action);
|
||||||
|
|
|
@ -28,7 +28,7 @@ import { getReactForStatus, reduceEmoji } from 'soapbox/utils/emoji-reacts';
|
||||||
import GroupPopover from './groups/popover/group-popover';
|
import GroupPopover from './groups/popover/group-popover';
|
||||||
|
|
||||||
import type { Menu } from 'soapbox/components/dropdown-menu';
|
import type { Menu } from 'soapbox/components/dropdown-menu';
|
||||||
import type { Group, Status } from 'soapbox/types/entities';
|
import type { Account, Group, Status } from 'soapbox/types/entities';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
adminAccount: { id: 'status.admin_account', defaultMessage: 'Moderate @{name}' },
|
adminAccount: { id: 'status.admin_account', defaultMessage: 'Moderate @{name}' },
|
||||||
|
@ -99,6 +99,7 @@ const messages = defineMessages({
|
||||||
|
|
||||||
interface IStatusActionBar {
|
interface IStatusActionBar {
|
||||||
status: Status;
|
status: Status;
|
||||||
|
rebloggedBy?: Account;
|
||||||
withLabels?: boolean;
|
withLabels?: boolean;
|
||||||
expandable?: boolean;
|
expandable?: boolean;
|
||||||
space?: 'sm' | 'md' | 'lg';
|
space?: 'sm' | 'md' | 'lg';
|
||||||
|
@ -113,6 +114,7 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
space = 'sm',
|
space = 'sm',
|
||||||
statusActionButtonTheme = 'default',
|
statusActionButtonTheme = 'default',
|
||||||
fromBookmarks = false,
|
fromBookmarks = false,
|
||||||
|
rebloggedBy,
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
@ -148,7 +150,7 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
|
|
||||||
const handleReplyClick: React.MouseEventHandler = (e) => {
|
const handleReplyClick: React.MouseEventHandler = (e) => {
|
||||||
if (me) {
|
if (me) {
|
||||||
dispatch(replyCompose(status));
|
dispatch(replyCompose(status, rebloggedBy));
|
||||||
} else {
|
} else {
|
||||||
onOpenUnauthorizedModal('REPLY');
|
onOpenUnauthorizedModal('REPLY');
|
||||||
}
|
}
|
||||||
|
@ -211,7 +213,7 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const doDeleteStatus = (withRedraft = false) => {
|
const doDeleteStatus = (withRedraft = false) => {
|
||||||
dispatch((_, getState) => {
|
dispatch((_) => {
|
||||||
if (!deleteModal) {
|
if (!deleteModal) {
|
||||||
dispatch(deleteStatus(status.id, withRedraft));
|
dispatch(deleteStatus(status.id, withRedraft));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -138,7 +138,7 @@ const Status: React.FC<IStatus> = (props) => {
|
||||||
|
|
||||||
const handleHotkeyReply = (e?: KeyboardEvent): void => {
|
const handleHotkeyReply = (e?: KeyboardEvent): void => {
|
||||||
e?.preventDefault();
|
e?.preventDefault();
|
||||||
dispatch(replyCompose(actualStatus));
|
dispatch(replyCompose(actualStatus, status.reblog && typeof status.reblog === 'object' ? status.account : undefined));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleHotkeyFavourite = (): void => {
|
const handleHotkeyFavourite = (): void => {
|
||||||
|
@ -458,7 +458,7 @@ const Status: React.FC<IStatus> = (props) => {
|
||||||
|
|
||||||
{!hideActionBar && (
|
{!hideActionBar && (
|
||||||
<div className='pt-4'>
|
<div className='pt-4'>
|
||||||
<StatusActionBar status={actualStatus} fromBookmarks={fromBookmarks} />
|
<StatusActionBar status={actualStatus} rebloggedBy={isReblog ? status.account : undefined} fromBookmarks={fromBookmarks} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,8 +2,7 @@ import React, { useCallback } from 'react';
|
||||||
import { FormattedList, FormattedMessage } from 'react-intl';
|
import { FormattedList, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
import { useAppDispatch, useAppSelector, useCompose, useFeatures, useOwnAccount } from 'soapbox/hooks';
|
import { useAppDispatch, useAppSelector, useCompose, useFeatures } from 'soapbox/hooks';
|
||||||
import { statusToMentionsAccountIdsArray } from 'soapbox/reducers/compose';
|
|
||||||
import { makeGetStatus } from 'soapbox/selectors';
|
import { makeGetStatus } from 'soapbox/selectors';
|
||||||
|
|
||||||
import type { Status as StatusEntity } from 'soapbox/types/entities';
|
import type { Status as StatusEntity } from 'soapbox/types/entities';
|
||||||
|
@ -20,14 +19,11 @@ const ReplyMentions: React.FC<IReplyMentions> = ({ composeId }) => {
|
||||||
const getStatus = useCallback(makeGetStatus(), []);
|
const getStatus = useCallback(makeGetStatus(), []);
|
||||||
const status = useAppSelector<StatusEntity | null>(state => getStatus(state, { id: compose.in_reply_to! }));
|
const status = useAppSelector<StatusEntity | null>(state => getStatus(state, { id: compose.in_reply_to! }));
|
||||||
const to = compose.to;
|
const to = compose.to;
|
||||||
const { account } = useOwnAccount();
|
|
||||||
|
|
||||||
if (!features.explicitAddressing || !status || !to) {
|
if (!features.explicitAddressing || !status || !to) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentTo = status && statusToMentionsAccountIdsArray(status, account!);
|
|
||||||
|
|
||||||
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
@ -36,7 +32,7 @@ const ReplyMentions: React.FC<IReplyMentions> = ({ composeId }) => {
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!parentTo || (parentTo.size === 0)) {
|
if (!compose.parent_reblogged_by && to.size === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ const ReplyMentionsModal: React.FC<IReplyMentionsModal> = ({ composeId, onClose
|
||||||
const status = useAppSelector<StatusEntity | null>(state => getStatus(state, { id: compose.in_reply_to! }));
|
const status = useAppSelector<StatusEntity | null>(state => getStatus(state, { id: compose.in_reply_to! }));
|
||||||
const { account } = useOwnAccount();
|
const { account } = useOwnAccount();
|
||||||
|
|
||||||
const mentions = statusToMentionsAccountIdsArray(status!, account!);
|
const mentions = statusToMentionsAccountIdsArray(status!, account!, compose.parent_reblogged_by);
|
||||||
const author = (status?.account as AccountEntity).id;
|
const author = (status?.account as AccountEntity).id;
|
||||||
|
|
||||||
const onClickClose = () => {
|
const onClickClose = () => {
|
||||||
|
|
|
@ -108,6 +108,7 @@ export const ReducerCompose = ImmutableRecord({
|
||||||
tagHistory: ImmutableList<string>(),
|
tagHistory: ImmutableList<string>(),
|
||||||
text: '',
|
text: '',
|
||||||
to: ImmutableOrderedSet<string>(),
|
to: ImmutableOrderedSet<string>(),
|
||||||
|
parent_reblogged_by: null as string | null,
|
||||||
});
|
});
|
||||||
|
|
||||||
type State = ImmutableMap<string, Compose>;
|
type State = ImmutableMap<string, Compose>;
|
||||||
|
@ -125,19 +126,21 @@ const statusToTextMentions = (status: Status, account: Account) => {
|
||||||
.join('');
|
.join('');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const statusToMentionsArray = (status: Status, account: Account) => {
|
export const statusToMentionsArray = (status: Status, account: Account, rebloggedBy?: Account) => {
|
||||||
const author = status.getIn(['account', 'acct']) as string;
|
const author = status.getIn(['account', 'acct']) as string;
|
||||||
const mentions = status.get('mentions')?.map((m) => m.acct) || [];
|
const mentions = status.get('mentions')?.map((m) => m.acct) || [];
|
||||||
|
|
||||||
return ImmutableOrderedSet<string>([author])
|
return ImmutableOrderedSet<string>([author])
|
||||||
|
.concat(rebloggedBy ? [rebloggedBy.acct] : [])
|
||||||
.concat(mentions)
|
.concat(mentions)
|
||||||
.delete(account.acct) as ImmutableOrderedSet<string>;
|
.delete(account.acct) as ImmutableOrderedSet<string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const statusToMentionsAccountIdsArray = (status: StatusEntity, account: Account) => {
|
export const statusToMentionsAccountIdsArray = (status: StatusEntity, account: Account, parentRebloggedBy?: string | null) => {
|
||||||
const mentions = status.mentions.map((m) => m.id);
|
const mentions = status.mentions.map((m) => m.id);
|
||||||
|
|
||||||
return ImmutableOrderedSet<string>([account.id])
|
return ImmutableOrderedSet<string>([status.account.id])
|
||||||
|
.concat(parentRebloggedBy ? [parentRebloggedBy] : [])
|
||||||
.concat(mentions)
|
.concat(mentions)
|
||||||
.delete(account.id);
|
.delete(account.id);
|
||||||
};
|
};
|
||||||
|
@ -306,9 +309,14 @@ export default function compose(state = initialState, action: ComposeAction | Ev
|
||||||
return updateCompose(state, action.id, compose => compose.withMutations(map => {
|
return updateCompose(state, action.id, compose => compose.withMutations(map => {
|
||||||
const defaultCompose = state.get('default')!;
|
const defaultCompose = state.get('default')!;
|
||||||
|
|
||||||
|
const to = action.explicitAddressing
|
||||||
|
? statusToMentionsArray(action.status, action.account, action.rebloggedBy)
|
||||||
|
: ImmutableOrderedSet<string>();
|
||||||
|
|
||||||
map.set('group_id', action.status.getIn(['group', 'id']) as string);
|
map.set('group_id', action.status.getIn(['group', 'id']) as string);
|
||||||
map.set('in_reply_to', action.status.get('id'));
|
map.set('in_reply_to', action.status.get('id'));
|
||||||
map.set('to', action.explicitAddressing ? statusToMentionsArray(action.status, action.account) : ImmutableOrderedSet<string>());
|
map.set('to', to);
|
||||||
|
map.set('parent_reblogged_by', action.rebloggedBy?.id || null);
|
||||||
map.set('text', !action.explicitAddressing ? statusToTextMentions(action.status, action.account) : '');
|
map.set('text', !action.explicitAddressing ? statusToTextMentions(action.status, action.account) : '');
|
||||||
map.set('privacy', privacyPreference(action.status.visibility, defaultCompose.privacy));
|
map.set('privacy', privacyPreference(action.status.visibility, defaultCompose.privacy));
|
||||||
map.set('focusDate', new Date());
|
map.set('focusDate', new Date());
|
||||||
|
@ -334,6 +342,7 @@ export default function compose(state = initialState, action: ComposeAction | Ev
|
||||||
|
|
||||||
map.set('quote', action.status.get('id'));
|
map.set('quote', action.status.get('id'));
|
||||||
map.set('to', ImmutableOrderedSet<string>([author]));
|
map.set('to', ImmutableOrderedSet<string>([author]));
|
||||||
|
map.set('parent_reblogged_by', null);
|
||||||
map.set('text', '');
|
map.set('text', '');
|
||||||
map.set('privacy', privacyPreference(action.status.visibility, defaultCompose.privacy));
|
map.set('privacy', privacyPreference(action.status.visibility, defaultCompose.privacy));
|
||||||
map.set('focusDate', new Date());
|
map.set('focusDate', new Date());
|
||||||
|
@ -435,11 +444,13 @@ export default function compose(state = initialState, action: ComposeAction | Ev
|
||||||
})));
|
})));
|
||||||
case COMPOSE_SET_STATUS:
|
case COMPOSE_SET_STATUS:
|
||||||
return updateCompose(state, 'compose-modal', compose => compose.withMutations(map => {
|
return updateCompose(state, 'compose-modal', compose => compose.withMutations(map => {
|
||||||
|
const to = action.explicitAddressing ? getExplicitMentions(action.status.account.id, action.status) : ImmutableOrderedSet<string>();
|
||||||
if (!action.withRedraft && !action.draftId) {
|
if (!action.withRedraft && !action.draftId) {
|
||||||
map.set('id', action.status.id);
|
map.set('id', action.status.id);
|
||||||
}
|
}
|
||||||
map.set('text', action.rawText || unescapeHTML(expandMentions(action.status)));
|
map.set('text', action.rawText || unescapeHTML(expandMentions(action.status)));
|
||||||
map.set('to', action.explicitAddressing ? getExplicitMentions(action.status.account.id, action.status) : ImmutableOrderedSet<string>());
|
map.set('to', to);
|
||||||
|
map.set('parent_reblogged_by', null);
|
||||||
map.set('in_reply_to', action.status.get('in_reply_to_id'));
|
map.set('in_reply_to', action.status.get('in_reply_to_id'));
|
||||||
map.set('privacy', action.status.get('visibility'));
|
map.set('privacy', action.status.get('visibility'));
|
||||||
map.set('focusDate', new Date());
|
map.set('focusDate', new Date());
|
||||||
|
|
Loading…
Reference in a new issue