pl-fe: Split StatusActionBar into separate components
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
618f5f5f25
commit
492a4214a5
1 changed files with 449 additions and 247 deletions
|
@ -43,6 +43,7 @@ import type { UnauthorizedModalAction } from 'pl-fe/features/ui/components/modal
|
||||||
import type { Account } from 'pl-fe/normalizers/account';
|
import type { Account } from 'pl-fe/normalizers/account';
|
||||||
import type { Group } from 'pl-fe/normalizers/group';
|
import type { Group } from 'pl-fe/normalizers/group';
|
||||||
import type { SelectedStatus } from 'pl-fe/selectors';
|
import type { SelectedStatus } from 'pl-fe/selectors';
|
||||||
|
import type { Me } from 'pl-fe/types/pl-fe';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
adminAccount: { id: 'status.admin_account', defaultMessage: 'Moderate @{name}' },
|
adminAccount: { id: 'status.admin_account', defaultMessage: 'Moderate @{name}' },
|
||||||
|
@ -110,29 +111,358 @@ const messages = defineMessages({
|
||||||
hideTranslation: { id: 'status.hide_translation', defaultMessage: 'Hide translation' },
|
hideTranslation: { id: 'status.hide_translation', defaultMessage: 'Hide translation' },
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IStatusActionBar {
|
interface IActionButton extends Pick<IStatusActionBar, 'status' | 'statusActionButtonTheme' | 'withLabels'> {
|
||||||
status: SelectedStatus;
|
me: Me;
|
||||||
|
onOpenUnauthorizedModal: (action?: UnauthorizedModalAction) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IReplyButton extends IActionButton {
|
||||||
rebloggedBy?: Account;
|
rebloggedBy?: Account;
|
||||||
withLabels?: boolean;
|
}
|
||||||
|
|
||||||
|
const ReplyButton: React.FC<IReplyButton> = ({
|
||||||
|
status,
|
||||||
|
statusActionButtonTheme,
|
||||||
|
withLabels,
|
||||||
|
me,
|
||||||
|
onOpenUnauthorizedModal,
|
||||||
|
rebloggedBy,
|
||||||
|
}) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const { groupRelationship } = useGroupRelationship(status.group_id || undefined);
|
||||||
|
|
||||||
|
let replyTitle;
|
||||||
|
let replyDisabled = false;
|
||||||
|
const replyCount = status.replies_count;
|
||||||
|
|
||||||
|
if ((status.group as Group)?.membership_required && !groupRelationship?.member) {
|
||||||
|
replyDisabled = true;
|
||||||
|
replyTitle = intl.formatMessage(messages.replies_disabled_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!status.in_reply_to_id) {
|
||||||
|
replyTitle = intl.formatMessage(messages.reply);
|
||||||
|
} else {
|
||||||
|
replyTitle = intl.formatMessage(messages.replyAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReplyClick: React.MouseEventHandler = (e) => {
|
||||||
|
if (me) {
|
||||||
|
dispatch(replyCompose(status, rebloggedBy));
|
||||||
|
} else {
|
||||||
|
onOpenUnauthorizedModal('REPLY');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const replyButton = (
|
||||||
|
<StatusActionButton
|
||||||
|
title={replyTitle}
|
||||||
|
icon={require('@tabler/icons/outline/message-circle.svg')}
|
||||||
|
onClick={handleReplyClick}
|
||||||
|
count={replyCount}
|
||||||
|
text={withLabels ? intl.formatMessage(messages.reply) : undefined}
|
||||||
|
disabled={replyDisabled}
|
||||||
|
theme={statusActionButtonTheme}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return status.group ? (
|
||||||
|
<GroupPopover
|
||||||
|
group={status.group}
|
||||||
|
isEnabled={replyDisabled}
|
||||||
|
>
|
||||||
|
{replyButton}
|
||||||
|
</GroupPopover>
|
||||||
|
) : replyButton;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IReblogButton extends IActionButton {
|
||||||
|
publicStatus: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReblogButton: React.FC<IReblogButton> = ({
|
||||||
|
status,
|
||||||
|
statusActionButtonTheme,
|
||||||
|
withLabels,
|
||||||
|
me,
|
||||||
|
onOpenUnauthorizedModal,
|
||||||
|
publicStatus,
|
||||||
|
}) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const features = useFeatures();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const { boostModal } = useSettings();
|
||||||
|
const { openModal } = useModalsStore();
|
||||||
|
|
||||||
|
let reblogIcon = require('@tabler/icons/outline/repeat.svg');
|
||||||
|
|
||||||
|
if (status.visibility === 'direct') {
|
||||||
|
reblogIcon = require('@tabler/icons/outline/mail.svg');
|
||||||
|
} else if (status.visibility === 'private' || status.visibility === 'mutuals_only') {
|
||||||
|
reblogIcon = require('@tabler/icons/outline/lock.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReblogClick: React.EventHandler<React.MouseEvent> = e => {
|
||||||
|
if (me) {
|
||||||
|
const modalReblog = () => dispatch(toggleReblog(status));
|
||||||
|
if ((e && e.shiftKey) || !boostModal) {
|
||||||
|
modalReblog();
|
||||||
|
} else {
|
||||||
|
openModal('BOOST', { statusId: status.id, onReblog: modalReblog });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onOpenUnauthorizedModal('REBLOG');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReblogLongPress = status.reblogs_count ? () => {
|
||||||
|
openModal('REBLOGS', { statusId: status.id });
|
||||||
|
} : undefined;
|
||||||
|
|
||||||
|
const reblogButton = (
|
||||||
|
<StatusActionButton
|
||||||
|
icon={reblogIcon}
|
||||||
|
color='success'
|
||||||
|
disabled={!publicStatus}
|
||||||
|
title={!publicStatus ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)}
|
||||||
|
active={status.reblogged}
|
||||||
|
onClick={handleReblogClick}
|
||||||
|
onLongPress={handleReblogLongPress}
|
||||||
|
count={status.reblogs_count + status.quotes_count}
|
||||||
|
text={withLabels ? intl.formatMessage(messages.reblog) : undefined}
|
||||||
|
theme={statusActionButtonTheme}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!features.quotePosts || !me) return reblogButton;
|
||||||
|
|
||||||
|
const handleQuoteClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||||
|
if (me) {
|
||||||
|
dispatch(quoteCompose(status));
|
||||||
|
} else {
|
||||||
|
onOpenUnauthorizedModal('REBLOG');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const reblogMenu = [{
|
||||||
|
text: intl.formatMessage(status.reblogged ? messages.cancel_reblog_private : messages.reblog),
|
||||||
|
action: handleReblogClick,
|
||||||
|
icon: require('@tabler/icons/outline/repeat.svg'),
|
||||||
|
}, {
|
||||||
|
text: intl.formatMessage(messages.quotePost),
|
||||||
|
action: handleQuoteClick,
|
||||||
|
icon: require('@tabler/icons/outline/quote.svg'),
|
||||||
|
}];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu
|
||||||
|
items={reblogMenu}
|
||||||
|
disabled={!publicStatus}
|
||||||
|
onShiftClick={handleReblogClick}
|
||||||
|
>
|
||||||
|
{reblogButton}
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const FavouriteButton: React.FC<IActionButton> = ({
|
||||||
|
status,
|
||||||
|
statusActionButtonTheme,
|
||||||
|
me,
|
||||||
|
withLabels,
|
||||||
|
onOpenUnauthorizedModal,
|
||||||
|
}) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const features = useFeatures();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const { openModal } = useModalsStore();
|
||||||
|
|
||||||
|
const handleFavouriteClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||||
|
if (me) {
|
||||||
|
dispatch(toggleFavourite(status));
|
||||||
|
} else {
|
||||||
|
onOpenUnauthorizedModal('FAVOURITE');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFavouriteLongPress = status.favourites_count ? () => {
|
||||||
|
openModal('FAVOURITES', { statusId: status.id });
|
||||||
|
} : undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StatusActionButton
|
||||||
|
title={intl.formatMessage(messages.favourite)}
|
||||||
|
icon={features.statusDislikes ? require('@tabler/icons/outline/thumb-up.svg') : require('@tabler/icons/outline/heart.svg')}
|
||||||
|
color='accent'
|
||||||
|
filled
|
||||||
|
onClick={handleFavouriteClick}
|
||||||
|
onLongPress={handleFavouriteLongPress}
|
||||||
|
active={status.favourited}
|
||||||
|
count={status.favourites_count}
|
||||||
|
text={withLabels ? intl.formatMessage(messages.favourite) : undefined}
|
||||||
|
theme={statusActionButtonTheme}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const DislikeButton: React.FC<IActionButton> = ({
|
||||||
|
status,
|
||||||
|
statusActionButtonTheme,
|
||||||
|
withLabels,
|
||||||
|
me,
|
||||||
|
onOpenUnauthorizedModal,
|
||||||
|
}) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const features = useFeatures();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const { openModal } = useModalsStore();
|
||||||
|
|
||||||
|
if (!features.statusDislikes) return;
|
||||||
|
|
||||||
|
const handleDislikeClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||||
|
if (me) {
|
||||||
|
dispatch(toggleDislike(status));
|
||||||
|
} else {
|
||||||
|
onOpenUnauthorizedModal('DISLIKE');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDislikeLongPress = status.dislikes_count ? () => {
|
||||||
|
openModal('DISLIKES', { statusId: status.id });
|
||||||
|
} : undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StatusActionButton
|
||||||
|
title={intl.formatMessage(messages.disfavourite)}
|
||||||
|
icon={require('@tabler/icons/outline/thumb-down.svg')}
|
||||||
|
color='accent'
|
||||||
|
filled
|
||||||
|
onClick={handleDislikeClick}
|
||||||
|
onLongPress={handleDislikeLongPress}
|
||||||
|
active={status.disliked}
|
||||||
|
count={status.dislikes_count}
|
||||||
|
text={withLabels ? intl.formatMessage(messages.disfavourite) : undefined}
|
||||||
|
theme={statusActionButtonTheme}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const WrenchButton: React.FC<IActionButton> = ({
|
||||||
|
status,
|
||||||
|
statusActionButtonTheme,
|
||||||
|
withLabels,
|
||||||
|
me,
|
||||||
|
}) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const intl = useIntl();
|
||||||
|
const features = useFeatures();
|
||||||
|
|
||||||
|
const { openModal } = useModalsStore();
|
||||||
|
const { showWrenchButton } = useSettings();
|
||||||
|
|
||||||
|
if (!me || withLabels || !features.emojiReacts || !showWrenchButton) return;
|
||||||
|
|
||||||
|
const wrenches = showWrenchButton && status.emoji_reactions.find(emoji => emoji.name === '🔧') || undefined;
|
||||||
|
|
||||||
|
const handleWrenchClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||||
|
if (wrenches?.me) {
|
||||||
|
dispatch(unEmojiReact(status, '🔧'));
|
||||||
|
} else {
|
||||||
|
dispatch(emojiReact(status, '🔧'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleWrenchLongPress = wrenches?.count ? () => {
|
||||||
|
openModal('REACTIONS', { statusId: status.id, reaction: wrenches.name });
|
||||||
|
} : undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StatusActionButton
|
||||||
|
title={intl.formatMessage(messages.wrench)}
|
||||||
|
icon={require('@tabler/icons/outline/tool.svg')}
|
||||||
|
color='accent'
|
||||||
|
filled
|
||||||
|
onClick={handleWrenchClick}
|
||||||
|
onLongPress={handleWrenchLongPress}
|
||||||
|
active={wrenches?.me}
|
||||||
|
count={wrenches?.count || undefined}
|
||||||
|
theme={statusActionButtonTheme}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const EmojiPickerButton: React.FC<Omit<IActionButton, 'onOpenUnauthorizedModal'>> = ({
|
||||||
|
status,
|
||||||
|
statusActionButtonTheme,
|
||||||
|
withLabels,
|
||||||
|
me,
|
||||||
|
}) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const features = useFeatures();
|
||||||
|
|
||||||
|
const handlePickEmoji = (emoji: EmojiType) => {
|
||||||
|
dispatch(emojiReact(status, emoji.custom ? emoji.id : emoji.native, emoji.custom ? emoji.imageUrl : undefined));
|
||||||
|
};
|
||||||
|
|
||||||
|
return me && !withLabels && features.emojiReacts && (
|
||||||
|
<EmojiPickerDropdown
|
||||||
|
onPickEmoji={handlePickEmoji}
|
||||||
|
theme={statusActionButtonTheme}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ShareButton: React.FC<Pick<IActionButton, 'status' | 'statusActionButtonTheme'>> = ({
|
||||||
|
status,
|
||||||
|
statusActionButtonTheme,
|
||||||
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const handleShareClick = () => {
|
||||||
|
navigator.share({
|
||||||
|
text: status.search_index,
|
||||||
|
url: status.uri,
|
||||||
|
}).catch((e) => {
|
||||||
|
if (e.name !== 'AbortError') console.error(e);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const canShare = ('share' in navigator) && (status.visibility === 'public' || status.visibility === 'group');
|
||||||
|
|
||||||
|
return canShare && (
|
||||||
|
<StatusActionButton
|
||||||
|
title={intl.formatMessage(messages.share)}
|
||||||
|
icon={require('@tabler/icons/outline/upload.svg')}
|
||||||
|
onClick={handleShareClick}
|
||||||
|
theme={statusActionButtonTheme}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IMenuButton extends IActionButton {
|
||||||
expandable?: boolean;
|
expandable?: boolean;
|
||||||
space?: 'sm' | 'md' | 'lg';
|
|
||||||
statusActionButtonTheme?: 'default' | 'inverse';
|
|
||||||
fromBookmarks?: boolean;
|
fromBookmarks?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StatusActionBar: React.FC<IStatusActionBar> = ({
|
const MenuButton: React.FC<IMenuButton> = ({
|
||||||
status,
|
status,
|
||||||
withLabels = false,
|
statusActionButtonTheme,
|
||||||
|
withLabels,
|
||||||
|
me,
|
||||||
expandable,
|
expandable,
|
||||||
space = 'sm',
|
fromBookmarks,
|
||||||
statusActionButtonTheme = 'default',
|
|
||||||
fromBookmarks = false,
|
|
||||||
rebloggedBy,
|
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const match = useRouteMatch<{ groupId: string }>('/groups/:groupId');
|
const match = useRouteMatch<{ groupId: string }>('/groups/:groupId');
|
||||||
|
const { boostModal } = useSettings();
|
||||||
|
|
||||||
const { openModal } = useModalsStore();
|
const { openModal } = useModalsStore();
|
||||||
const { group } = useGroup((status.group as Group)?.id as string);
|
const { group } = useGroup((status.group as Group)?.id as string);
|
||||||
|
@ -140,13 +470,10 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
const blockGroupMember = useBlockGroupMember(group as Group, status.account);
|
const blockGroupMember = useBlockGroupMember(group as Group, status.account);
|
||||||
const { getOrCreateChatByAccountId } = useChats();
|
const { getOrCreateChatByAccountId } = useChats();
|
||||||
|
|
||||||
const me = useAppSelector(state => state.me);
|
|
||||||
const { groupRelationship } = useGroupRelationship(status.group_id || undefined);
|
const { groupRelationship } = useGroupRelationship(status.group_id || undefined);
|
||||||
const features = useFeatures();
|
const features = useFeatures();
|
||||||
const instance = useInstance();
|
const instance = useInstance();
|
||||||
const { autoTranslate, boostModal, deleteModal, knownLanguages, showWrenchButton } = useSettings();
|
const { autoTranslate, deleteModal, knownLanguages } = useSettings();
|
||||||
|
|
||||||
const wrenches = showWrenchButton && status.emoji_reactions.find(emoji => emoji.name === '🔧') || undefined;
|
|
||||||
|
|
||||||
const { translationLanguages } = useTranslationLanguages();
|
const { translationLanguages } = useTranslationLanguages();
|
||||||
|
|
||||||
|
@ -166,76 +493,6 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
const isStaff = account ? account.is_admin || account.is_moderator : false;
|
const isStaff = account ? account.is_admin || account.is_moderator : false;
|
||||||
const isAdmin = account ? account.is_admin : false;
|
const isAdmin = account ? account.is_admin : false;
|
||||||
|
|
||||||
if (!status) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const onOpenUnauthorizedModal = (action?: UnauthorizedModalAction) => {
|
|
||||||
openModal('UNAUTHORIZED', {
|
|
||||||
action,
|
|
||||||
ap_id: status.url,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReplyClick: React.MouseEventHandler = (e) => {
|
|
||||||
if (me) {
|
|
||||||
dispatch(replyCompose(status, rebloggedBy));
|
|
||||||
} else {
|
|
||||||
onOpenUnauthorizedModal('REPLY');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleShareClick = () => {
|
|
||||||
navigator.share({
|
|
||||||
text: status.search_index,
|
|
||||||
url: status.uri,
|
|
||||||
}).catch((e) => {
|
|
||||||
if (e.name !== 'AbortError') console.error(e);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFavouriteClick: React.EventHandler<React.MouseEvent> = (e) => {
|
|
||||||
if (me) {
|
|
||||||
dispatch(toggleFavourite(status));
|
|
||||||
} else {
|
|
||||||
onOpenUnauthorizedModal('FAVOURITE');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFavouriteLongPress = status.favourites_count ? () => {
|
|
||||||
openModal('FAVOURITES', { statusId: status.id });
|
|
||||||
} : undefined;
|
|
||||||
|
|
||||||
const handleDislikeClick: React.EventHandler<React.MouseEvent> = (e) => {
|
|
||||||
if (me) {
|
|
||||||
dispatch(toggleDislike(status));
|
|
||||||
} else {
|
|
||||||
onOpenUnauthorizedModal('DISLIKE');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleWrenchClick: React.EventHandler<React.MouseEvent> = (e) => {
|
|
||||||
if (!me) {
|
|
||||||
onOpenUnauthorizedModal('DISLIKE');
|
|
||||||
} else if (wrenches?.me) {
|
|
||||||
dispatch(unEmojiReact(status, '🔧'));
|
|
||||||
} else {
|
|
||||||
dispatch(emojiReact(status, '🔧'));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDislikeLongPress = status.dislikes_count ? () => {
|
|
||||||
openModal('DISLIKES', { statusId: status.id });
|
|
||||||
} : undefined;
|
|
||||||
|
|
||||||
const handleWrenchLongPress = wrenches?.count ? () => {
|
|
||||||
openModal('REACTIONS', { statusId: status.id, reaction: wrenches.name });
|
|
||||||
} : undefined;
|
|
||||||
|
|
||||||
const handlePickEmoji = (emoji: EmojiType) => {
|
|
||||||
dispatch(emojiReact(status, emoji.custom ? emoji.id : emoji.native, emoji.custom ? emoji.imageUrl : undefined));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBookmarkClick: React.EventHandler<React.MouseEvent> = (e) => {
|
const handleBookmarkClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||||
dispatch(toggleBookmark(status));
|
dispatch(toggleBookmark(status));
|
||||||
};
|
};
|
||||||
|
@ -246,31 +503,6 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReblogClick: React.EventHandler<React.MouseEvent> = e => {
|
|
||||||
if (me) {
|
|
||||||
const modalReblog = () => dispatch(toggleReblog(status));
|
|
||||||
if ((e && e.shiftKey) || !boostModal) {
|
|
||||||
modalReblog();
|
|
||||||
} else {
|
|
||||||
openModal('BOOST', { statusId: status.id, onReblog: modalReblog });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
onOpenUnauthorizedModal('REBLOG');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReblogLongPress = status.reblogs_count ? () => {
|
|
||||||
openModal('REBLOGS', { statusId: status.id });
|
|
||||||
} : undefined;
|
|
||||||
|
|
||||||
const handleQuoteClick: React.EventHandler<React.MouseEvent> = (e) => {
|
|
||||||
if (me) {
|
|
||||||
dispatch(quoteCompose(status));
|
|
||||||
} else {
|
|
||||||
onOpenUnauthorizedModal('REBLOG');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const doDeleteStatus = (withRedraft = false) => {
|
const doDeleteStatus = (withRedraft = false) => {
|
||||||
if (!deleteModal) {
|
if (!deleteModal) {
|
||||||
dispatch(deleteStatus(status.id, withRedraft));
|
dispatch(deleteStatus(status.id, withRedraft));
|
||||||
|
@ -301,6 +533,15 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
dispatch(togglePin(status));
|
dispatch(togglePin(status));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleReblogClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||||
|
const modalReblog = () => dispatch(toggleReblog(status));
|
||||||
|
if ((e && e.shiftKey) || !boostModal) {
|
||||||
|
modalReblog();
|
||||||
|
} else {
|
||||||
|
openModal('BOOST', { statusId: status.id, onReblog: modalReblog });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleMentionClick: React.EventHandler<React.MouseEvent> = (e) => {
|
const handleMentionClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||||
dispatch(mentionCompose(status.account));
|
dispatch(mentionCompose(status.account));
|
||||||
};
|
};
|
||||||
|
@ -669,71 +910,55 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
|
|
||||||
const publicStatus = ['public', 'unlisted', 'group'].includes(status.visibility);
|
const publicStatus = ['public', 'unlisted', 'group'].includes(status.visibility);
|
||||||
|
|
||||||
const replyCount = status.replies_count;
|
|
||||||
const reblogCount = status.reblogs_count;
|
|
||||||
const quoteCount = status.quotes_count;
|
|
||||||
const favouriteCount = status.favourites_count;
|
|
||||||
|
|
||||||
const menu = _makeMenu(publicStatus);
|
const menu = _makeMenu(publicStatus);
|
||||||
let reblogIcon = require('@tabler/icons/outline/repeat.svg');
|
|
||||||
let replyTitle;
|
|
||||||
let replyDisabled = false;
|
|
||||||
|
|
||||||
if (status.visibility === 'direct') {
|
return (
|
||||||
reblogIcon = require('@tabler/icons/outline/mail.svg');
|
<DropdownMenu items={menu}>
|
||||||
} else if (status.visibility === 'private' || status.visibility === 'mutuals_only') {
|
<StatusActionButton
|
||||||
reblogIcon = require('@tabler/icons/outline/lock.svg');
|
title={intl.formatMessage(messages.more)}
|
||||||
}
|
icon={require('@tabler/icons/outline/dots.svg')}
|
||||||
|
theme={statusActionButtonTheme}
|
||||||
if ((status.group as Group)?.membership_required && !groupRelationship?.member) {
|
/>
|
||||||
replyDisabled = true;
|
</DropdownMenu>
|
||||||
replyTitle = intl.formatMessage(messages.replies_disabled_group);
|
|
||||||
}
|
|
||||||
|
|
||||||
const replyButton = (
|
|
||||||
<StatusActionButton
|
|
||||||
title={replyTitle}
|
|
||||||
icon={require('@tabler/icons/outline/message-circle.svg')}
|
|
||||||
onClick={handleReplyClick}
|
|
||||||
count={replyCount}
|
|
||||||
text={withLabels ? intl.formatMessage(messages.reply) : undefined}
|
|
||||||
disabled={replyDisabled}
|
|
||||||
theme={statusActionButtonTheme}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const reblogMenu = [{
|
interface IStatusActionBar {
|
||||||
text: intl.formatMessage(status.reblogged ? messages.cancel_reblog_private : messages.reblog),
|
status: SelectedStatus;
|
||||||
action: handleReblogClick,
|
rebloggedBy?: Account;
|
||||||
icon: require('@tabler/icons/outline/repeat.svg'),
|
withLabels?: boolean;
|
||||||
}, {
|
expandable?: boolean;
|
||||||
text: intl.formatMessage(messages.quotePost),
|
space?: 'sm' | 'md' | 'lg';
|
||||||
action: handleQuoteClick,
|
statusActionButtonTheme?: 'default' | 'inverse';
|
||||||
icon: require('@tabler/icons/outline/quote.svg'),
|
fromBookmarks?: boolean;
|
||||||
}];
|
}
|
||||||
|
|
||||||
const reblogButton = (
|
const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
<StatusActionButton
|
status,
|
||||||
icon={reblogIcon}
|
withLabels = false,
|
||||||
color='success'
|
expandable,
|
||||||
disabled={!publicStatus}
|
space = 'sm',
|
||||||
title={!publicStatus ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)}
|
statusActionButtonTheme = 'default',
|
||||||
active={status.reblogged}
|
fromBookmarks = false,
|
||||||
onClick={handleReblogClick}
|
rebloggedBy,
|
||||||
onLongPress={handleReblogLongPress}
|
}) => {
|
||||||
count={reblogCount + quoteCount}
|
|
||||||
text={withLabels ? intl.formatMessage(messages.reblog) : undefined}
|
|
||||||
theme={statusActionButtonTheme}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!status.in_reply_to_id) {
|
const { openModal } = useModalsStore();
|
||||||
replyTitle = intl.formatMessage(messages.reply);
|
|
||||||
} else {
|
const me = useAppSelector(state => state.me);
|
||||||
replyTitle = intl.formatMessage(messages.replyAll);
|
|
||||||
|
if (!status) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const canShare = ('share' in navigator) && (status.visibility === 'public' || status.visibility === 'group');
|
const onOpenUnauthorizedModal = (action?: UnauthorizedModalAction) => {
|
||||||
|
openModal('UNAUTHORIZED', {
|
||||||
|
action,
|
||||||
|
ap_id: status.url,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const publicStatus = ['public', 'unlisted', 'group'].includes(status.visibility);
|
||||||
|
|
||||||
const spacing: {
|
const spacing: {
|
||||||
[key: string]: React.ComponentProps<typeof HStack>['space'];
|
[key: string]: React.ComponentProps<typeof HStack>['space'];
|
||||||
|
@ -752,92 +977,69 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
||||||
onClick={e => e.stopPropagation()}
|
onClick={e => e.stopPropagation()}
|
||||||
alignItems='center'
|
alignItems='center'
|
||||||
>
|
>
|
||||||
{status.group ? (
|
<ReplyButton
|
||||||
<GroupPopover
|
status={status}
|
||||||
group={status.group}
|
statusActionButtonTheme={statusActionButtonTheme}
|
||||||
isEnabled={replyDisabled}
|
withLabels={withLabels}
|
||||||
>
|
me={me}
|
||||||
{replyButton}
|
onOpenUnauthorizedModal={onOpenUnauthorizedModal}
|
||||||
</GroupPopover>
|
rebloggedBy={rebloggedBy}
|
||||||
) : replyButton}
|
|
||||||
|
|
||||||
{(features.quotePosts && me) ? (
|
|
||||||
<DropdownMenu
|
|
||||||
items={reblogMenu}
|
|
||||||
disabled={!publicStatus}
|
|
||||||
onShiftClick={handleReblogClick}
|
|
||||||
>
|
|
||||||
{reblogButton}
|
|
||||||
</DropdownMenu>
|
|
||||||
) : (
|
|
||||||
reblogButton
|
|
||||||
)}
|
|
||||||
|
|
||||||
<StatusActionButton
|
|
||||||
title={intl.formatMessage(messages.favourite)}
|
|
||||||
icon={features.statusDislikes ? require('@tabler/icons/outline/thumb-up.svg') : require('@tabler/icons/outline/heart.svg')}
|
|
||||||
color='accent'
|
|
||||||
filled
|
|
||||||
onClick={handleFavouriteClick}
|
|
||||||
onLongPress={handleFavouriteLongPress}
|
|
||||||
active={status.favourited}
|
|
||||||
count={favouriteCount}
|
|
||||||
text={withLabels ? intl.formatMessage(messages.favourite) : undefined}
|
|
||||||
theme={statusActionButtonTheme}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{features.statusDislikes && (
|
<ReblogButton
|
||||||
<StatusActionButton
|
status={status}
|
||||||
title={intl.formatMessage(messages.disfavourite)}
|
statusActionButtonTheme={statusActionButtonTheme}
|
||||||
icon={require('@tabler/icons/outline/thumb-down.svg')}
|
withLabels={withLabels}
|
||||||
color='accent'
|
me={me}
|
||||||
filled
|
onOpenUnauthorizedModal={onOpenUnauthorizedModal}
|
||||||
onClick={handleDislikeClick}
|
publicStatus={publicStatus}
|
||||||
onLongPress={handleDislikeLongPress}
|
/>
|
||||||
active={status.disliked}
|
|
||||||
count={status.dislikes_count}
|
|
||||||
text={withLabels ? intl.formatMessage(messages.disfavourite) : undefined}
|
|
||||||
theme={statusActionButtonTheme}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{me && !withLabels && features.emojiReacts && showWrenchButton && (
|
<FavouriteButton
|
||||||
<StatusActionButton
|
status={status}
|
||||||
title={intl.formatMessage(messages.wrench)}
|
statusActionButtonTheme={statusActionButtonTheme}
|
||||||
icon={require('@tabler/icons/outline/tool.svg')}
|
withLabels={withLabels}
|
||||||
color='accent'
|
me={me}
|
||||||
filled
|
onOpenUnauthorizedModal={onOpenUnauthorizedModal}
|
||||||
onClick={handleWrenchClick}
|
/>
|
||||||
onLongPress={handleWrenchLongPress}
|
|
||||||
active={wrenches?.me}
|
|
||||||
count={wrenches?.count || undefined}
|
|
||||||
theme={statusActionButtonTheme}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{me && !withLabels && features.emojiReacts && (
|
<DislikeButton
|
||||||
<EmojiPickerDropdown
|
status={status}
|
||||||
onPickEmoji={handlePickEmoji}
|
statusActionButtonTheme={statusActionButtonTheme}
|
||||||
theme={statusActionButtonTheme}
|
withLabels={withLabels}
|
||||||
/>
|
me={me}
|
||||||
)}
|
onOpenUnauthorizedModal={onOpenUnauthorizedModal}
|
||||||
|
/>
|
||||||
|
|
||||||
{canShare && (
|
<WrenchButton
|
||||||
<StatusActionButton
|
status={status}
|
||||||
title={intl.formatMessage(messages.share)}
|
statusActionButtonTheme={statusActionButtonTheme}
|
||||||
icon={require('@tabler/icons/outline/upload.svg')}
|
withLabels={withLabels}
|
||||||
onClick={handleShareClick}
|
me={me}
|
||||||
theme={statusActionButtonTheme}
|
onOpenUnauthorizedModal={onOpenUnauthorizedModal}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
|
|
||||||
<DropdownMenu items={menu}>
|
<EmojiPickerButton
|
||||||
<StatusActionButton
|
status={status}
|
||||||
title={intl.formatMessage(messages.more)}
|
statusActionButtonTheme={statusActionButtonTheme}
|
||||||
icon={require('@tabler/icons/outline/dots.svg')}
|
withLabels={withLabels}
|
||||||
theme={statusActionButtonTheme}
|
me={me}
|
||||||
/>
|
/>
|
||||||
</DropdownMenu>
|
|
||||||
|
<ShareButton
|
||||||
|
status={status}
|
||||||
|
statusActionButtonTheme={statusActionButtonTheme}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MenuButton
|
||||||
|
status={status}
|
||||||
|
statusActionButtonTheme={statusActionButtonTheme}
|
||||||
|
withLabels={withLabels}
|
||||||
|
me={me}
|
||||||
|
onOpenUnauthorizedModal={onOpenUnauthorizedModal}
|
||||||
|
expandable={expandable}
|
||||||
|
fromBookmarks={fromBookmarks}
|
||||||
|
/>
|
||||||
</HStack>
|
</HStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue