2022-01-10 14:17:52 -08:00
import { List as ImmutableList } from 'immutable' ;
2020-03-27 13:59:38 -07:00
import React from 'react' ;
2022-08-09 12:34:08 -07:00
import { defineMessages , FormattedMessage , useIntl } from 'react-intl' ;
2022-08-09 11:46:11 -07:00
import { useHistory } from 'react-router-dom' ;
2022-01-10 14:25:06 -08:00
2022-08-09 12:34:08 -07:00
import { blockAccount } from 'soapbox/actions/accounts' ;
import { launchChat } from 'soapbox/actions/chats' ;
import { directCompose , mentionCompose , quoteCompose , replyCompose } from 'soapbox/actions/compose' ;
2022-10-01 02:41:50 -07:00
import { editEvent } from 'soapbox/actions/events' ;
2023-03-25 04:14:53 -07:00
import { toggleBookmark , toggleDislike , toggleFavourite , togglePin , toggleReblog } from 'soapbox/actions/interactions' ;
2022-06-07 13:21:18 -07:00
import { openModal } from 'soapbox/actions/modals' ;
2022-09-11 12:36:58 -07:00
import { deleteStatusModal , toggleStatusSensitivityModal } from 'soapbox/actions/moderation' ;
2022-08-09 12:34:08 -07:00
import { initMuteModal } from 'soapbox/actions/mutes' ;
2023-03-22 10:56:32 -07:00
import { initReport , ReportableEntities } from 'soapbox/actions/reports' ;
2022-08-09 15:05:16 -07:00
import { deleteStatus , editStatus , toggleMuteStatus } from 'soapbox/actions/statuses' ;
2023-05-02 13:15:14 -07:00
import { deleteFromTimelines } from 'soapbox/actions/timelines' ;
import { useDeleteGroupStatus } from 'soapbox/api/hooks/groups/useDeleteGroupStatus' ;
2023-02-09 09:42:46 -08:00
import DropdownMenu from 'soapbox/components/dropdown-menu' ;
2022-04-02 16:43:34 -07:00
import StatusActionButton from 'soapbox/components/status-action-button' ;
2023-02-08 09:58:01 -08:00
import StatusReactionWrapper from 'soapbox/components/status-reaction-wrapper' ;
2022-11-20 10:33:53 -08:00
import { HStack } from 'soapbox/components/ui' ;
2022-08-09 12:34:08 -07:00
import { useAppDispatch , useAppSelector , useFeatures , useOwnAccount , useSettings , useSoapboxConfig } from 'soapbox/hooks' ;
2023-05-02 13:15:14 -07:00
import { GroupRoles } from 'soapbox/schemas/group-member' ;
2022-12-20 09:45:46 -08:00
import toast from 'soapbox/toast' ;
2022-11-25 14:44:42 -08:00
import { isLocal , isRemote } from 'soapbox/utils/accounts' ;
2022-12-06 13:23:43 -08:00
import copy from 'soapbox/utils/copy' ;
2022-11-15 12:46:23 -08:00
import { getReactForStatus , reduceEmoji } from 'soapbox/utils/emoji-reacts' ;
2022-01-10 14:25:06 -08:00
2023-03-28 12:37:35 -07:00
import GroupPopover from './groups/popover/group-popover' ;
2022-11-15 06:10:14 -08:00
import type { Menu } from 'soapbox/components/dropdown-menu' ;
2022-12-18 09:43:40 -08:00
import type { Account , Group , Status } from 'soapbox/types/entities' ;
2020-03-27 13:59:38 -07:00
const messages = defineMessages ( {
delete : { id : 'status.delete' , defaultMessage : 'Delete' } ,
redraft : { id : 'status.redraft' , defaultMessage : 'Delete & re-draft' } ,
2022-04-27 13:50:35 -07:00
edit : { id : 'status.edit' , defaultMessage : 'Edit' } ,
2020-03-27 13:59:38 -07:00
direct : { id : 'status.direct' , defaultMessage : 'Direct message @{name}' } ,
2021-10-13 11:55:02 -07:00
chat : { id : 'status.chat' , defaultMessage : 'Chat with @{name}' } ,
2020-03-27 13:59:38 -07:00
mention : { id : 'status.mention' , defaultMessage : 'Mention @{name}' } ,
mute : { id : 'account.mute' , defaultMessage : 'Mute @{name}' } ,
block : { id : 'account.block' , defaultMessage : 'Block @{name}' } ,
reply : { id : 'status.reply' , defaultMessage : 'Reply' } ,
share : { id : 'status.share' , defaultMessage : 'Share' } ,
more : { id : 'status.more' , defaultMessage : 'More' } ,
replyAll : { id : 'status.replyAll' , defaultMessage : 'Reply to thread' } ,
reblog : { id : 'status.reblog' , defaultMessage : 'Repost' } ,
reblog_private : { id : 'status.reblog_private' , defaultMessage : 'Repost to original audience' } ,
cancel_reblog_private : { id : 'status.cancel_reblog_private' , defaultMessage : 'Un-repost' } ,
cannot_reblog : { id : 'status.cannot_reblog' , defaultMessage : 'This post cannot be reposted' } ,
2021-06-30 13:56:23 -07:00
favourite : { id : 'status.favourite' , defaultMessage : 'Like' } ,
2023-03-25 04:14:53 -07:00
disfavourite : { id : 'status.disfavourite' , defaultMessage : 'Disike' } ,
2020-03-27 13:59:38 -07:00
open : { id : 'status.open' , defaultMessage : 'Expand this post' } ,
2020-07-29 14:08:36 -07:00
bookmark : { id : 'status.bookmark' , defaultMessage : 'Bookmark' } ,
unbookmark : { id : 'status.unbookmark' , defaultMessage : 'Remove bookmark' } ,
2020-03-27 13:59:38 -07:00
report : { id : 'status.report' , defaultMessage : 'Report @{name}' } ,
muteConversation : { id : 'status.mute_conversation' , defaultMessage : 'Mute conversation' } ,
unmuteConversation : { id : 'status.unmute_conversation' , defaultMessage : 'Unmute conversation' } ,
pin : { id : 'status.pin' , defaultMessage : 'Pin on profile' } ,
unpin : { id : 'status.unpin' , defaultMessage : 'Unpin from profile' } ,
embed : { id : 'status.embed' , defaultMessage : 'Embed' } ,
2022-09-11 12:36:58 -07:00
adminAccount : { id : 'status.admin_account' , defaultMessage : 'Moderate @{name}' } ,
2020-03-27 13:59:38 -07:00
admin_status : { id : 'status.admin_status' , defaultMessage : 'Open this post in the moderation interface' } ,
copy : { id : 'status.copy' , defaultMessage : 'Copy link to post' } ,
group_remove_account : { id : 'status.remove_account_from_group' , defaultMessage : 'Remove account from group' } ,
group_remove_post : { id : 'status.remove_post_from_group' , defaultMessage : 'Remove post from group' } ,
2022-11-25 14:44:42 -08:00
external : { id : 'status.external' , defaultMessage : 'View post on {domain}' } ,
2021-01-18 16:25:36 -08:00
deactivateUser : { id : 'admin.users.actions.deactivate_user' , defaultMessage : 'Deactivate @{name}' } ,
deleteUser : { id : 'admin.users.actions.delete_user' , defaultMessage : 'Delete @{name}' } ,
2021-01-18 13:57:20 -08:00
deleteStatus : { id : 'admin.statuses.actions.delete_status' , defaultMessage : 'Delete post' } ,
2021-01-18 18:59:07 -08:00
markStatusSensitive : { id : 'admin.statuses.actions.mark_status_sensitive' , defaultMessage : 'Mark post sensitive' } ,
markStatusNotSensitive : { id : 'admin.statuses.actions.mark_status_not_sensitive' , defaultMessage : 'Mark post not sensitive' } ,
2021-06-30 02:33:42 -07:00
reactionLike : { id : 'status.reactions.like' , defaultMessage : 'Like' } ,
reactionHeart : { id : 'status.reactions.heart' , defaultMessage : 'Love' } ,
reactionLaughing : { id : 'status.reactions.laughing' , defaultMessage : 'Haha' } ,
reactionOpenMouth : { id : 'status.reactions.open_mouth' , defaultMessage : 'Wow' } ,
reactionCry : { id : 'status.reactions.cry' , defaultMessage : 'Sad' } ,
reactionWeary : { id : 'status.reactions.weary' , defaultMessage : 'Weary' } ,
2022-01-23 13:15:25 -08:00
quotePost : { id : 'status.quote' , defaultMessage : 'Quote post' } ,
2022-08-09 12:34:08 -07:00
deleteConfirm : { id : 'confirmations.delete.confirm' , defaultMessage : 'Delete' } ,
deleteHeading : { id : 'confirmations.delete.heading' , defaultMessage : 'Delete post' } ,
deleteMessage : { id : 'confirmations.delete.message' , defaultMessage : 'Are you sure you want to delete this post?' } ,
redraftConfirm : { id : 'confirmations.redraft.confirm' , defaultMessage : 'Delete & redraft' } ,
redraftMessage : { id : 'confirmations.redraft.message' , defaultMessage : 'Are you sure you want to delete this post and re-draft it? Favorites and reposts will be lost, and replies to the original post will be orphaned.' } ,
blockConfirm : { id : 'confirmations.block.confirm' , defaultMessage : 'Block' } ,
replyConfirm : { id : 'confirmations.reply.confirm' , defaultMessage : 'Reply' } ,
redraftHeading : { id : 'confirmations.redraft.heading' , defaultMessage : 'Delete & redraft' } ,
replyMessage : { id : 'confirmations.reply.message' , defaultMessage : 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' } ,
blockAndReport : { id : 'confirmations.block.block_and_report' , defaultMessage : 'Block & Report' } ,
2022-12-18 09:43:40 -08:00
replies_disabled_group : { id : 'status.disabled_replies.group_membership' , defaultMessage : 'Only group members can reply' } ,
groupModDelete : { id : 'status.group_mod_delete' , defaultMessage : 'Delete post from group' } ,
deleteFromGroupMessage : { id : 'confirmations.delete_from_group.message' , defaultMessage : 'Are you sure you want to delete @{name}\'s post?' } ,
2020-03-27 13:59:38 -07:00
} ) ;
2022-08-09 11:46:11 -07:00
interface IStatusActionBar {
2023-02-15 13:26:27 -08:00
status : Status
withLabels? : boolean
expandable? : boolean
space ? : 'expand' | 'compact'
2022-04-01 16:39:27 -07:00
}
2022-08-09 16:40:33 -07:00
const StatusActionBar : React.FC < IStatusActionBar > = ( {
status ,
withLabels = false ,
expandable = true ,
2022-08-09 16:46:16 -07:00
space = 'compact' ,
2022-08-09 16:40:33 -07:00
} ) = > {
2022-08-09 11:46:11 -07:00
const intl = useIntl ( ) ;
const history = useHistory ( ) ;
2022-08-09 12:34:08 -07:00
const dispatch = useAppDispatch ( ) ;
2022-08-09 11:46:11 -07:00
const me = useAppSelector ( state = > state . me ) ;
2022-12-18 09:43:40 -08:00
const groupRelationship = useAppSelector ( state = > status . group ? state . group_relationships . get ( ( status . group as Group ) . id ) : null ) ;
2022-08-09 11:46:11 -07:00
const features = useFeatures ( ) ;
2022-08-09 12:34:08 -07:00
const settings = useSettings ( ) ;
const soapboxConfig = useSoapboxConfig ( ) ;
2023-05-02 13:15:14 -07:00
const deleteGroupStatus = useDeleteGroupStatus ( status ? . group as Group , status . id ) ;
2022-08-09 12:34:08 -07:00
const { allowedEmoji } = soapboxConfig ;
2022-08-09 11:46:11 -07:00
const account = useOwnAccount ( ) ;
const isStaff = account ? account.staff : false ;
const isAdmin = account ? account.admin : false ;
2022-08-09 12:34:08 -07:00
if ( ! status ) {
return null ;
}
2022-08-09 11:46:11 -07:00
const onOpenUnauthorizedModal = ( action? : string ) = > {
dispatch ( openModal ( 'UNAUTHORIZED' , {
action ,
ap_id : status.url ,
} ) ) ;
} ;
2022-03-21 11:09:01 -07:00
2022-08-09 11:46:11 -07:00
const handleReplyClick : React.MouseEventHandler = ( e ) = > {
2020-03-27 13:59:38 -07:00
if ( me ) {
2022-09-10 14:52:06 -07:00
dispatch ( replyCompose ( status ) ) ;
2020-03-27 13:59:38 -07:00
} else {
2022-01-02 12:43:53 -08:00
onOpenUnauthorizedModal ( 'REPLY' ) ;
2020-03-27 13:59:38 -07:00
}
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2022-08-09 11:46:11 -07:00
const handleShareClick = ( ) = > {
2020-03-27 13:59:38 -07:00
navigator . share ( {
2022-08-09 11:46:11 -07:00
text : status.search_index ,
url : status.uri ,
2020-03-27 13:59:38 -07:00
} ) . catch ( ( e ) = > {
if ( e . name !== 'AbortError' ) console . error ( e ) ;
} ) ;
2022-08-09 11:46:11 -07:00
} ;
2020-05-22 19:15:07 -07:00
2022-08-09 11:46:11 -07:00
const handleFavouriteClick : React.EventHandler < React.MouseEvent > = ( e ) = > {
2020-03-27 13:59:38 -07:00
if ( me ) {
2022-08-09 16:51:01 -07:00
dispatch ( toggleFavourite ( status ) ) ;
2020-03-27 13:59:38 -07:00
} else {
2022-01-02 12:43:53 -08:00
onOpenUnauthorizedModal ( 'FAVOURITE' ) ;
2020-03-27 13:59:38 -07:00
}
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2023-03-25 04:14:53 -07:00
const handleDislikeClick : React.EventHandler < React.MouseEvent > = ( e ) = > {
if ( me ) {
dispatch ( toggleDislike ( status ) ) ;
} else {
onOpenUnauthorizedModal ( 'DISLIKE' ) ;
}
} ;
2022-08-09 11:46:11 -07:00
const handleBookmarkClick : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-08-09 15:05:16 -07:00
dispatch ( toggleBookmark ( status ) ) ;
2022-08-09 12:34:08 -07:00
} ;
2022-11-25 14:44:42 -08:00
const handleExternalClick = ( ) = > {
window . open ( status . uri , '_blank' ) ;
} ;
2022-08-09 11:46:11 -07:00
const handleReblogClick : React.EventHandler < React.MouseEvent > = e = > {
2020-03-27 13:59:38 -07:00
if ( me ) {
2022-08-09 15:45:01 -07:00
const modalReblog = ( ) = > dispatch ( toggleReblog ( status ) ) ;
2022-08-09 12:34:08 -07:00
const boostModal = settings . get ( 'boostModal' ) ;
if ( ( e && e . shiftKey ) || ! boostModal ) {
modalReblog ( ) ;
} else {
dispatch ( openModal ( 'BOOST' , { status , onReblog : modalReblog } ) ) ;
}
2020-03-27 13:59:38 -07:00
} else {
2022-01-02 12:43:53 -08:00
onOpenUnauthorizedModal ( 'REBLOG' ) ;
2020-03-27 13:59:38 -07:00
}
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2022-08-09 11:46:11 -07:00
const handleQuoteClick : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-01-23 09:44:17 -08:00
if ( me ) {
2022-09-10 14:52:06 -07:00
dispatch ( quoteCompose ( status ) ) ;
2022-01-23 09:44:17 -08:00
} else {
onOpenUnauthorizedModal ( 'REBLOG' ) ;
}
2022-08-09 11:46:11 -07:00
} ;
2022-01-23 09:44:17 -08:00
2022-08-09 12:34:08 -07:00
const doDeleteStatus = ( withRedraft = false ) = > {
dispatch ( ( _ , getState ) = > {
const deleteModal = settings . get ( 'deleteModal' ) ;
if ( ! deleteModal ) {
dispatch ( deleteStatus ( status . id , withRedraft ) ) ;
} else {
dispatch ( openModal ( 'CONFIRM' , {
icon : withRedraft ? require ( '@tabler/icons/edit.svg' ) : require ( '@tabler/icons/trash.svg' ) ,
heading : intl.formatMessage ( withRedraft ? messages.redraftHeading : messages.deleteHeading ) ,
message : intl.formatMessage ( withRedraft ? messages.redraftMessage : messages.deleteMessage ) ,
confirm : intl.formatMessage ( withRedraft ? messages.redraftConfirm : messages.deleteConfirm ) ,
onConfirm : ( ) = > dispatch ( deleteStatus ( status . id , withRedraft ) ) ,
} ) ) ;
}
} ) ;
} ;
2022-08-09 11:46:11 -07:00
const handleDeleteClick : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-08-09 12:34:08 -07:00
doDeleteStatus ( ) ;
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2022-08-09 11:46:11 -07:00
const handleRedraftClick : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-08-09 12:34:08 -07:00
doDeleteStatus ( true ) ;
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2022-08-09 11:46:11 -07:00
const handleEditClick : React.EventHandler < React.MouseEvent > = ( ) = > {
2022-10-01 02:41:50 -07:00
if ( status . event ) dispatch ( editEvent ( status . id ) ) ;
else dispatch ( editStatus ( status . id ) ) ;
2022-08-09 11:46:11 -07:00
} ;
2022-04-27 13:50:35 -07:00
2022-08-09 11:46:11 -07:00
const handlePinClick : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-08-09 15:05:16 -07:00
dispatch ( togglePin ( status ) ) ;
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2022-08-09 11:46:11 -07:00
const handleMentionClick : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-08-09 12:34:08 -07:00
dispatch ( mentionCompose ( status . account as Account ) ) ;
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2022-08-09 11:46:11 -07:00
const handleDirectClick : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-08-09 12:34:08 -07:00
dispatch ( directCompose ( status . account as Account ) ) ;
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2022-08-09 11:46:11 -07:00
const handleChatClick : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-08-09 12:34:08 -07:00
const account = status . account as Account ;
dispatch ( launchChat ( account . id , history ) ) ;
2022-08-09 11:46:11 -07:00
} ;
2021-10-13 11:55:02 -07:00
2022-08-09 11:46:11 -07:00
const handleMuteClick : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-08-09 12:34:08 -07:00
dispatch ( initMuteModal ( status . account as Account ) ) ;
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2022-08-09 11:46:11 -07:00
const handleBlockClick : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-08-09 12:34:08 -07:00
const account = status . get ( 'account' ) as Account ;
2022-11-20 10:33:53 -08:00
2022-08-09 12:34:08 -07:00
dispatch ( openModal ( 'CONFIRM' , {
icon : require ( '@tabler/icons/ban.svg' ) ,
2023-05-02 11:28:42 -07:00
heading : < FormattedMessage id = 'confirmations.block.heading' defaultMessage = 'Block @{name}' values = { { name : account.acct } } / > ,
message : < FormattedMessage id = 'confirmations.block.message' defaultMessage = 'Are you sure you want to block {name}?' values = { { name : < strong className = 'break-words' > @ { account . acct } < / strong > } } / > ,
2022-08-09 12:34:08 -07:00
confirm : intl.formatMessage ( messages . blockConfirm ) ,
onConfirm : ( ) = > dispatch ( blockAccount ( account . id ) ) ,
secondary : intl.formatMessage ( messages . blockAndReport ) ,
onSecondary : ( ) = > {
dispatch ( blockAccount ( account . id ) ) ;
2023-03-22 10:56:32 -07:00
dispatch ( initReport ( ReportableEntities . STATUS , account , { status } ) ) ;
2022-08-09 12:34:08 -07:00
} ,
} ) ) ;
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2022-08-09 11:46:11 -07:00
const handleOpen : React.EventHandler < React.MouseEvent > = ( e ) = > {
history . push ( ` /@ ${ status . getIn ( [ 'account' , 'acct' ] ) } /posts/ ${ status . id } ` ) ;
} ;
2020-03-27 13:59:38 -07:00
2022-08-09 11:46:11 -07:00
const handleEmbed = ( ) = > {
2022-08-09 12:34:08 -07:00
dispatch ( openModal ( 'EMBED' , {
2022-08-21 08:21:25 -07:00
url : status.get ( 'url' ) ,
2022-12-20 09:45:46 -08:00
onError : ( error : any ) = > toast . showAlertForError ( error ) ,
2022-08-09 12:34:08 -07:00
} ) ) ;
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2022-08-09 11:46:11 -07:00
const handleReport : React.EventHandler < React.MouseEvent > = ( e ) = > {
2023-03-22 10:56:32 -07:00
dispatch ( initReport ( ReportableEntities . STATUS , status . account as Account , { status } ) ) ;
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2022-08-09 11:46:11 -07:00
const handleConversationMuteClick : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-08-09 15:05:16 -07:00
dispatch ( toggleMuteStatus ( status ) ) ;
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2022-08-09 11:46:11 -07:00
const handleCopy : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-11-09 11:23:33 -08:00
const { uri } = status ;
2020-03-27 13:59:38 -07:00
2022-12-06 13:23:43 -08:00
copy ( uri ) ;
2022-08-09 11:46:11 -07:00
} ;
2020-03-27 13:59:38 -07:00
2022-09-11 12:36:58 -07:00
const onModerate : React.MouseEventHandler = ( e ) = > {
const account = status . account as Account ;
dispatch ( openModal ( 'ACCOUNT_MODERATION' , { accountId : account.id } ) ) ;
2022-08-09 11:46:11 -07:00
} ;
2021-01-18 13:27:35 -08:00
2022-08-09 11:46:11 -07:00
const handleDeleteStatus : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-08-09 12:34:08 -07:00
dispatch ( deleteStatusModal ( intl , status . id ) ) ;
2022-08-09 11:46:11 -07:00
} ;
2021-01-18 13:57:20 -08:00
2022-08-09 11:46:11 -07:00
const handleToggleStatusSensitivity : React.EventHandler < React.MouseEvent > = ( e ) = > {
2022-08-09 12:34:08 -07:00
dispatch ( toggleStatusSensitivityModal ( intl , status . id , status . sensitive ) ) ;
2022-08-09 11:46:11 -07:00
} ;
2022-01-06 11:55:06 -08:00
2022-12-18 09:43:40 -08:00
const handleDeleteFromGroup : React.EventHandler < React.MouseEvent > = ( ) = > {
const account = status . account as Account ;
dispatch ( openModal ( 'CONFIRM' , {
heading : intl.formatMessage ( messages . deleteHeading ) ,
2023-05-02 11:28:42 -07:00
message : intl.formatMessage ( messages . deleteFromGroupMessage , { name : < strong className = 'break-words' > { account . username } < / strong > } ) ,
2022-12-18 09:43:40 -08:00
confirm : intl.formatMessage ( messages . deleteConfirm ) ,
2023-05-02 13:15:14 -07:00
onConfirm : ( ) = > {
deleteGroupStatus . mutate ( status . id , {
onSuccess() {
dispatch ( deleteFromTimelines ( status . id ) ) ;
} ,
} ) ;
} ,
2022-12-18 09:43:40 -08:00
} ) ) ;
} ;
2022-08-09 11:46:11 -07:00
const _makeMenu = ( publicStatus : boolean ) = > {
2022-04-02 11:07:12 -07:00
const mutingConversation = status . muted ;
2021-07-15 09:25:32 -07:00
const ownAccount = status . getIn ( [ 'account' , 'id' ] ) === me ;
2022-04-01 16:39:27 -07:00
const username = String ( status . getIn ( [ 'account' , 'username' ] ) ) ;
2022-11-25 14:44:42 -08:00
const account = status . account as Account ;
const domain = account . fqn . split ( '@' ) [ 1 ] ;
2020-03-27 13:59:38 -07:00
2022-04-02 11:03:12 -07:00
const menu : Menu = [ ] ;
2020-03-27 13:59:38 -07:00
2022-08-09 16:40:33 -07:00
if ( expandable ) {
menu . push ( {
text : intl.formatMessage ( messages . open ) ,
action : handleOpen ,
icon : require ( '@tabler/icons/arrows-vertical.svg' ) ,
} ) ;
}
2020-03-27 13:59:38 -07:00
if ( publicStatus ) {
2021-11-04 11:16:04 -07:00
menu . push ( {
text : intl.formatMessage ( messages . copy ) ,
2022-08-09 11:46:11 -07:00
action : handleCopy ,
2023-05-02 13:15:14 -07:00
icon : require ( '@tabler/icons/clipboard-copy.svg' ) ,
2021-11-04 11:16:04 -07:00
} ) ;
2022-05-03 14:23:26 -07:00
2022-11-25 14:44:42 -08:00
if ( features . embeds && isLocal ( account ) ) {
2022-08-21 08:21:25 -07:00
menu . push ( {
text : intl.formatMessage ( messages . embed ) ,
action : handleEmbed ,
icon : require ( '@tabler/icons/share.svg' ) ,
} ) ;
}
2020-03-27 13:59:38 -07:00
}
2022-02-09 14:11:45 -08:00
if ( ! me ) {
return menu ;
}
2021-09-18 13:03:38 -07:00
if ( features . bookmarks ) {
2021-11-04 11:16:04 -07:00
menu . push ( {
2022-04-02 11:07:12 -07:00
text : intl.formatMessage ( status . bookmarked ? messages.unbookmark : messages.bookmark ) ,
2022-08-09 11:46:11 -07:00
action : handleBookmarkClick ,
2022-07-09 09:22:38 -07:00
icon : status.bookmarked ? require ( '@tabler/icons/bookmark-off.svg' ) : require ( '@tabler/icons/bookmark.svg' ) ,
2021-11-04 11:16:04 -07:00
} ) ;
2021-09-18 13:03:38 -07:00
}
2020-07-29 14:08:36 -07:00
2022-11-25 14:44:42 -08:00
if ( features . federating && isRemote ( account ) ) {
menu . push ( {
text : intl.formatMessage ( messages . external , { domain } ) ,
action : handleExternalClick ,
icon : require ( '@tabler/icons/external-link.svg' ) ,
} ) ;
}
2020-03-27 13:59:38 -07:00
menu . push ( null ) ;
2023-02-02 09:13:23 -08:00
menu . push ( {
text : intl.formatMessage ( mutingConversation ? messages.unmuteConversation : messages.muteConversation ) ,
action : handleConversationMuteClick ,
icon : mutingConversation ? require ( '@tabler/icons/bell.svg' ) : require ( '@tabler/icons/bell-off.svg' ) ,
} ) ;
menu . push ( null ) ;
2020-03-27 13:59:38 -07:00
2021-07-15 09:25:32 -07:00
if ( ownAccount ) {
2020-03-27 13:59:38 -07:00
if ( publicStatus ) {
2021-11-04 11:16:04 -07:00
menu . push ( {
2022-04-02 11:07:12 -07:00
text : intl.formatMessage ( status . pinned ? messages.unpin : messages.pin ) ,
2022-08-09 11:46:11 -07:00
action : handlePinClick ,
2022-10-05 15:02:32 -07:00
icon : status.pinned ? require ( '@tabler/icons/pinned-off.svg' ) : require ( '@tabler/icons/pin.svg' ) ,
2021-11-04 11:16:04 -07:00
} ) ;
2020-03-27 13:59:38 -07:00
} else {
2022-04-02 11:07:12 -07:00
if ( status . visibility === 'private' ) {
2021-11-04 11:16:04 -07:00
menu . push ( {
2022-04-02 11:07:12 -07:00
text : intl.formatMessage ( status . reblogged ? messages.cancel_reblog_private : messages.reblog_private ) ,
2022-08-09 11:46:11 -07:00
action : handleReblogClick ,
2022-07-09 09:20:02 -07:00
icon : require ( '@tabler/icons/repeat.svg' ) ,
2021-11-04 11:16:04 -07:00
} ) ;
2020-03-27 13:59:38 -07:00
}
}
2021-11-04 11:16:04 -07:00
menu . push ( {
text : intl.formatMessage ( messages . delete ) ,
2022-08-09 11:46:11 -07:00
action : handleDeleteClick ,
2022-07-09 09:20:02 -07:00
icon : require ( '@tabler/icons/trash.svg' ) ,
2021-11-08 08:21:33 -08:00
destructive : true ,
2021-11-04 11:16:04 -07:00
} ) ;
2022-04-27 13:50:35 -07:00
if ( features . editStatuses ) {
menu . push ( {
text : intl.formatMessage ( messages . edit ) ,
2022-08-09 11:46:11 -07:00
action : handleEditClick ,
2022-07-09 09:20:02 -07:00
icon : require ( '@tabler/icons/edit.svg' ) ,
2022-04-27 13:50:35 -07:00
} ) ;
} else {
menu . push ( {
text : intl.formatMessage ( messages . redraft ) ,
2022-08-09 11:46:11 -07:00
action : handleRedraftClick ,
2022-07-09 09:20:02 -07:00
icon : require ( '@tabler/icons/edit.svg' ) ,
2022-04-27 13:50:35 -07:00
destructive : true ,
} ) ;
}
2020-03-27 13:59:38 -07:00
} else {
2021-11-04 11:16:04 -07:00
menu . push ( {
2022-04-01 16:39:27 -07:00
text : intl.formatMessage ( messages . mention , { name : username } ) ,
2022-08-09 11:46:11 -07:00
action : handleMentionClick ,
2022-07-09 09:20:02 -07:00
icon : require ( '@tabler/icons/at.svg' ) ,
2021-11-04 11:16:04 -07:00
} ) ;
2021-10-13 11:55:02 -07:00
2022-08-09 11:46:11 -07:00
if ( status . getIn ( [ 'account' , 'pleroma' , 'accepts_chat_messages' ] ) === true ) {
menu . push ( {
text : intl.formatMessage ( messages . chat , { name : username } ) ,
action : handleChatClick ,
icon : require ( '@tabler/icons/messages.svg' ) ,
} ) ;
2022-08-13 08:16:13 -07:00
} else if ( features . privacyScopes ) {
2022-08-09 11:46:11 -07:00
menu . push ( {
text : intl.formatMessage ( messages . direct , { name : username } ) ,
action : handleDirectClick ,
icon : require ( '@tabler/icons/mail.svg' ) ,
} ) ;
}
2021-10-13 11:55:02 -07:00
2020-03-27 13:59:38 -07:00
menu . push ( null ) ;
2021-11-04 11:16:04 -07:00
menu . push ( {
2022-04-01 16:39:27 -07:00
text : intl.formatMessage ( messages . mute , { name : username } ) ,
2022-08-09 11:46:11 -07:00
action : handleMuteClick ,
2023-05-02 13:15:14 -07:00
icon : require ( '@tabler/icons/volume-3.svg' ) ,
2021-11-04 11:16:04 -07:00
} ) ;
menu . push ( {
2022-04-01 16:39:27 -07:00
text : intl.formatMessage ( messages . block , { name : username } ) ,
2022-08-09 11:46:11 -07:00
action : handleBlockClick ,
2022-07-09 09:20:02 -07:00
icon : require ( '@tabler/icons/ban.svg' ) ,
2021-11-04 11:16:04 -07:00
} ) ;
menu . push ( {
2022-04-01 16:39:27 -07:00
text : intl.formatMessage ( messages . report , { name : username } ) ,
2022-08-09 11:46:11 -07:00
action : handleReport ,
2022-07-09 09:20:02 -07:00
icon : require ( '@tabler/icons/flag.svg' ) ,
2021-11-04 11:16:04 -07:00
} ) ;
2021-07-15 09:20:15 -07:00
}
2020-03-27 13:59:38 -07:00
2023-05-02 13:15:14 -07:00
if ( status . group &&
groupRelationship ? . role &&
[ GroupRoles . OWNER ] . includes ( groupRelationship . role ) &&
! ownAccount
) {
2022-12-18 09:43:40 -08:00
menu . push ( null ) ;
menu . push ( {
text : intl.formatMessage ( messages . groupModDelete ) ,
action : handleDeleteFromGroup ,
icon : require ( '@tabler/icons/trash.svg' ) ,
2023-05-02 13:15:14 -07:00
destructive : true ,
2022-12-18 09:43:40 -08:00
} ) ;
}
2021-07-15 09:20:15 -07:00
if ( isStaff ) {
menu . push ( null ) ;
2022-09-11 12:46:01 -07:00
menu . push ( {
text : intl.formatMessage ( messages . adminAccount , { name : username } ) ,
action : onModerate ,
icon : require ( '@tabler/icons/gavel.svg' ) ,
} ) ;
2021-07-15 09:20:15 -07:00
if ( isAdmin ) {
2021-11-04 11:16:04 -07:00
menu . push ( {
text : intl.formatMessage ( messages . admin_status ) ,
2022-04-02 11:07:12 -07:00
href : ` /pleroma/admin/#/statuses/ ${ status . id } / ` ,
2022-07-09 09:20:02 -07:00
icon : require ( '@tabler/icons/pencil.svg' ) ,
2021-11-04 11:16:04 -07:00
} ) ;
2021-07-15 09:20:15 -07:00
}
2021-11-04 11:16:04 -07:00
menu . push ( {
2022-04-02 11:07:12 -07:00
text : intl.formatMessage ( status . sensitive === false ? messages.markStatusSensitive : messages.markStatusNotSensitive ) ,
2022-08-09 11:46:11 -07:00
action : handleToggleStatusSensitivity ,
2022-07-09 09:20:02 -07:00
icon : require ( '@tabler/icons/alert-triangle.svg' ) ,
2021-11-04 11:16:04 -07:00
} ) ;
2021-07-15 09:20:15 -07:00
2021-07-15 09:25:32 -07:00
if ( ! ownAccount ) {
2021-11-04 11:16:04 -07:00
menu . push ( {
text : intl.formatMessage ( messages . deleteStatus ) ,
2022-08-09 11:46:11 -07:00
action : handleDeleteStatus ,
2022-07-09 09:20:02 -07:00
icon : require ( '@tabler/icons/trash.svg' ) ,
2021-11-08 08:21:33 -08:00
destructive : true ,
2021-11-04 11:16:04 -07:00
} ) ;
2020-03-27 13:59:38 -07:00
}
2021-07-15 09:20:15 -07:00
}
2020-03-27 13:59:38 -07:00
return menu ;
2022-08-09 11:46:11 -07:00
} ;
2022-01-23 15:17:32 -08:00
2023-04-14 05:06:53 -07:00
const publicStatus = [ 'public' , 'unlisted' , 'group' ] . includes ( status . visibility ) ;
2022-08-09 11:46:11 -07:00
const replyCount = status . replies_count ;
const reblogCount = status . reblogs_count ;
const favouriteCount = status . favourites_count ;
const emojiReactCount = reduceEmoji (
( status . pleroma . get ( 'emoji_reactions' ) || ImmutableList ( ) ) as ImmutableList < any > ,
favouriteCount ,
status . favourited ,
allowedEmoji ,
) . reduce ( ( acc , cur ) = > acc + cur . get ( 'count' ) , 0 ) ;
2023-03-17 16:07:18 -07:00
const meEmojiReact = getReactForStatus ( status , allowedEmoji ) ;
const meEmojiName = meEmojiReact ? . get ( 'name' ) as keyof typeof reactMessages | undefined ;
2022-08-09 11:46:11 -07:00
const reactMessages = {
'👍' : messages . reactionLike ,
'❤️' : messages . reactionHeart ,
'😆' : messages . reactionLaughing ,
'😮' : messages . reactionOpenMouth ,
'😢' : messages . reactionCry ,
'😩' : messages . reactionWeary ,
'' : messages . favourite ,
} ;
2022-01-23 15:17:32 -08:00
2023-03-17 16:07:18 -07:00
const meEmojiTitle = intl . formatMessage ( reactMessages [ meEmojiName || '' ] || messages . favourite ) ;
2022-08-09 11:46:11 -07:00
const menu = _makeMenu ( publicStatus ) ;
let reblogIcon = require ( '@tabler/icons/repeat.svg' ) ;
let replyTitle ;
2022-12-18 09:43:40 -08:00
let replyDisabled = false ;
2022-08-09 11:46:11 -07:00
if ( status . visibility === 'direct' ) {
reblogIcon = require ( '@tabler/icons/mail.svg' ) ;
} else if ( status . visibility === 'private' ) {
reblogIcon = require ( '@tabler/icons/lock.svg' ) ;
}
2022-12-18 09:43:40 -08:00
if ( ( status . group as Group ) ? . membership_required && ! groupRelationship ? . member ) {
replyDisabled = true ;
replyTitle = intl . formatMessage ( messages . replies_disabled_group ) ;
}
2022-08-09 11:46:11 -07:00
const reblogMenu = [ {
text : intl.formatMessage ( status . reblogged ? messages.cancel_reblog_private : messages.reblog ) ,
action : handleReblogClick ,
icon : require ( '@tabler/icons/repeat.svg' ) ,
} , {
text : intl.formatMessage ( messages . quotePost ) ,
action : handleQuoteClick ,
icon : require ( '@tabler/icons/quote.svg' ) ,
} ] ;
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 }
count = { reblogCount }
2022-08-09 16:40:33 -07:00
text = { withLabels ? intl . formatMessage ( messages . reblog ) : undefined }
2022-08-09 11:46:11 -07:00
/ >
) ;
if ( ! status . in_reply_to_id ) {
replyTitle = intl . formatMessage ( messages . reply ) ;
} else {
replyTitle = intl . formatMessage ( messages . replyAll ) ;
}
2023-04-03 12:46:58 -07:00
const canShare = ( 'share' in navigator ) && ( status . visibility === 'public' || status . visibility === 'group' ) ;
2022-08-09 11:46:11 -07:00
return (
2022-11-20 10:33:53 -08:00
< HStack data-testid = 'status-action-bar' >
< HStack
justifyContent = { space === 'expand' ? 'between' : undefined }
space = { space === 'compact' ? 2 : undefined }
grow = { space === 'expand' }
onClick = { e = > e . stopPropagation ( ) }
>
2023-03-28 12:37:35 -07:00
< GroupPopover
group = { status . group as any }
isEnabled = { replyDisabled }
>
< StatusActionButton
title = { replyTitle }
icon = { require ( '@tabler/icons/message-circle-2.svg' ) }
onClick = { handleReplyClick }
count = { replyCount }
text = { withLabels ? intl . formatMessage ( messages . reply ) : undefined }
disabled = { replyDisabled }
/ >
< / GroupPopover >
2022-03-21 11:09:01 -07:00
2022-11-20 10:33:53 -08:00
{ ( features . quotePosts && me ) ? (
2023-02-09 09:42:46 -08:00
< DropdownMenu
2022-11-20 10:33:53 -08:00
items = { reblogMenu }
disabled = { ! publicStatus }
onShiftClick = { handleReblogClick }
>
{ reblogButton }
2023-02-09 09:42:46 -08:00
< / DropdownMenu >
2022-11-20 10:33:53 -08:00
) : (
reblogButton
) }
{ features . emojiReacts ? (
2023-02-08 09:58:01 -08:00
< StatusReactionWrapper statusId = { status . id } >
2022-11-20 10:33:53 -08:00
< StatusActionButton
title = { meEmojiTitle }
icon = { require ( '@tabler/icons/heart.svg' ) }
filled
color = 'accent'
2023-03-17 16:07:18 -07:00
active = { Boolean ( meEmojiName ) }
2022-11-20 10:33:53 -08:00
count = { emojiReactCount }
emoji = { meEmojiReact }
text = { withLabels ? meEmojiTitle : undefined }
/ >
2023-02-08 09:58:01 -08:00
< / StatusReactionWrapper >
2022-11-20 10:33:53 -08:00
) : (
2022-04-01 15:38:36 -07:00
< StatusActionButton
2022-11-20 10:33:53 -08:00
title = { intl . formatMessage ( messages . favourite ) }
2023-03-25 04:14:53 -07:00
icon = { features . dislikes ? require ( '@tabler/icons/thumb-up.svg' ) : require ( '@tabler/icons/heart.svg' ) }
2022-08-09 11:46:11 -07:00
color = 'accent'
2022-11-20 10:33:53 -08:00
filled
onClick = { handleFavouriteClick }
2023-03-17 16:07:18 -07:00
active = { Boolean ( meEmojiName ) }
2022-11-20 10:33:53 -08:00
count = { favouriteCount }
2022-08-09 16:40:33 -07:00
text = { withLabels ? meEmojiTitle : undefined }
2022-04-01 16:39:27 -07:00
/ >
2022-11-20 10:33:53 -08:00
) }
2020-03-27 13:59:38 -07:00
2023-03-25 04:14:53 -07:00
{ features . dislikes && (
< StatusActionButton
2023-03-26 12:58:46 -07:00
title = { intl . formatMessage ( messages . disfavourite ) }
icon = { require ( '@tabler/icons/thumb-down.svg' ) }
color = 'accent'
filled
onClick = { handleDislikeClick }
active = { status . disliked }
count = { status . dislikes_count }
text = { withLabels ? intl . formatMessage ( messages . disfavourite ) : undefined }
2023-03-25 04:14:53 -07:00
/ >
) }
2022-11-20 10:33:53 -08:00
{ canShare && (
< StatusActionButton
title = { intl . formatMessage ( messages . share ) }
icon = { require ( '@tabler/icons/upload.svg' ) }
onClick = { handleShareClick }
/ >
) }
2021-07-13 08:41:31 -07:00
2023-02-09 09:42:46 -08:00
< DropdownMenu items = { menu } status = { status } >
2022-11-20 10:33:53 -08:00
< StatusActionButton
title = { intl . formatMessage ( messages . more ) }
icon = { require ( '@tabler/icons/dots.svg' ) }
/ >
2023-02-09 09:42:46 -08:00
< / DropdownMenu >
2022-11-20 10:33:53 -08:00
< / HStack >
< / HStack >
2022-08-09 11:46:11 -07:00
) ;
2020-04-01 19:20:47 -07:00
} ;
2022-08-09 11:46:11 -07:00
export default StatusActionBar ;