2022-11-27 14:29:45 -08:00
import { List as ImmutableList } from 'immutable' ;
2022-09-08 14:25:02 -07:00
import React from 'react' ;
import { defineMessages , FormattedMessage , useIntl } from 'react-intl' ;
2022-10-05 15:01:26 -07:00
import { Link , useHistory } from 'react-router-dom' ;
2022-09-08 14:25:02 -07:00
2022-10-05 15:01:26 -07:00
import { blockAccount } from 'soapbox/actions/accounts' ;
import { launchChat } from 'soapbox/actions/chats' ;
import { directCompose , mentionCompose , quoteCompose } from 'soapbox/actions/compose' ;
2022-09-25 14:18:11 -07:00
import { editEvent , fetchEventIcs } from 'soapbox/actions/events' ;
2022-10-05 15:01:26 -07:00
import { toggleBookmark , togglePin } from 'soapbox/actions/interactions' ;
2022-09-08 14:25:02 -07:00
import { openModal } from 'soapbox/actions/modals' ;
2022-09-21 14:27:53 -07:00
import { deleteStatusModal , toggleStatusSensitivityModal } from 'soapbox/actions/moderation' ;
2022-10-05 15:01:26 -07:00
import { initMuteModal } from 'soapbox/actions/mutes' ;
import { initReport } from 'soapbox/actions/reports' ;
import { deleteStatus } from 'soapbox/actions/statuses' ;
2022-09-08 14:25:02 -07:00
import Icon from 'soapbox/components/icon' ;
2022-11-26 11:25:48 -08:00
import StillImage from 'soapbox/components/still-image' ;
2022-09-21 14:27:53 -07:00
import { Button , HStack , IconButton , Menu , MenuButton , MenuDivider , MenuItem , MenuLink , MenuList , Stack , Text } from 'soapbox/components/ui' ;
2022-09-08 14:25:02 -07:00
import SvgIcon from 'soapbox/components/ui/icon/svg-icon' ;
2022-11-26 11:25:48 -08:00
import VerificationBadge from 'soapbox/components/verification-badge' ;
2022-10-05 15:01:26 -07:00
import { useAppDispatch , useFeatures , useOwnAccount } from 'soapbox/hooks' ;
2022-12-06 13:23:43 -08:00
import { isRemote } from 'soapbox/utils/accounts' ;
import copy from 'soapbox/utils/copy' ;
2022-09-08 14:25:02 -07:00
import { download } from 'soapbox/utils/download' ;
2022-09-21 14:27:53 -07:00
import { shortNumberFormat } from 'soapbox/utils/numbers' ;
2022-09-08 14:25:02 -07:00
2022-11-26 11:25:48 -08:00
import PlaceholderEventHeader from '../../placeholder/components/placeholder-event-header' ;
2022-09-08 14:25:02 -07:00
import EventActionButton from '../components/event-action-button' ;
import EventDate from '../components/event-date' ;
2022-11-26 11:25:48 -08:00
import type { Menu as MenuType } from 'soapbox/components/dropdown-menu' ;
2022-09-08 14:25:02 -07:00
import type { Account as AccountEntity , Status as StatusEntity } from 'soapbox/types/entities' ;
const messages = defineMessages ( {
bannerHeader : { id : 'event.banner' , defaultMessage : 'Event banner' } ,
2022-09-20 15:12:52 -07:00
exportIcs : { id : 'event.export_ics' , defaultMessage : 'Export to your calendar' } ,
copy : { id : 'event.copy' , defaultMessage : 'Copy link to event' } ,
2022-12-06 13:23:43 -08:00
external : { id : 'event.external' , defaultMessage : 'View event on {domain}' } ,
2022-09-21 14:27:53 -07:00
bookmark : { id : 'status.bookmark' , defaultMessage : 'Bookmark' } ,
unbookmark : { id : 'status.unbookmark' , defaultMessage : 'Remove bookmark' } ,
2022-10-05 15:01:26 -07:00
quotePost : { id : 'status.quote' , defaultMessage : 'Quote post' } ,
pin : { id : 'status.pin' , defaultMessage : 'Pin on profile' } ,
unpin : { id : 'status.unpin' , defaultMessage : 'Unpin from profile' } ,
reblog_private : { id : 'status.reblog_private' , defaultMessage : 'Repost to original audience' } ,
cancel_reblog_private : { id : 'status.cancel_reblog_private' , defaultMessage : 'Un-repost' } ,
delete : { id : 'status.delete' , defaultMessage : 'Delete' } ,
mention : { id : 'status.mention' , defaultMessage : 'Mention @{name}' } ,
chat : { id : 'status.chat' , defaultMessage : 'Chat with @{name}' } ,
direct : { id : 'status.direct' , defaultMessage : 'Direct message @{name}' } ,
mute : { id : 'account.mute' , defaultMessage : 'Mute @{name}' } ,
block : { id : 'account.block' , defaultMessage : 'Block @{name}' } ,
report : { id : 'status.report' , defaultMessage : 'Report @{name}' } ,
2022-09-21 14:27:53 -07:00
adminAccount : { id : 'status.admin_account' , defaultMessage : 'Moderate @{name}' } ,
adminStatus : { id : 'status.admin_status' , defaultMessage : 'Open this post in the moderation interface' } ,
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' } ,
deleteStatus : { id : 'admin.statuses.actions.delete_status' , defaultMessage : 'Delete post' } ,
2022-10-05 15:01:26 -07:00
blockConfirm : { id : 'confirmations.block.confirm' , defaultMessage : 'Block' } ,
blockAndReport : { id : 'confirmations.block.block_and_report' , defaultMessage : 'Block & Report' } ,
deleteConfirm : { id : 'confirmations.delete_event.confirm' , defaultMessage : 'Delete' } ,
deleteHeading : { id : 'confirmations.delete_event.heading' , defaultMessage : 'Delete event' } ,
deleteMessage : { id : 'confirmations.delete_event.message' , defaultMessage : 'Are you sure you want to delete this event?' } ,
2022-09-08 14:25:02 -07:00
} ) ;
interface IEventHeader {
status? : StatusEntity ,
}
const EventHeader : React.FC < IEventHeader > = ( { status } ) = > {
const intl = useIntl ( ) ;
const dispatch = useAppDispatch ( ) ;
2022-10-05 15:01:26 -07:00
const history = useHistory ( ) ;
2022-09-08 14:25:02 -07:00
2022-10-05 15:01:26 -07:00
const features = useFeatures ( ) ;
2022-09-21 14:27:53 -07:00
const ownAccount = useOwnAccount ( ) ;
const isStaff = ownAccount ? ownAccount.staff : false ;
const isAdmin = ownAccount ? ownAccount.admin : false ;
2022-09-08 14:25:02 -07:00
if ( ! status || ! status . event ) {
return (
< >
< div className = '-mt-4 -mx-4' >
2022-09-21 14:27:53 -07:00
< div className = 'relative h-32 w-full lg:h-48 md:rounded-t-xl bg-gray-200 dark:bg-gray-900/50' / >
2022-09-08 14:25:02 -07:00
< / div >
< PlaceholderEventHeader / >
< / >
) ;
}
const account = status . account as AccountEntity ;
const event = status . event ;
2022-11-27 14:29:45 -08:00
const banner = event . banner ;
2022-09-08 14:25:02 -07:00
2022-10-05 15:01:26 -07:00
const username = account . username ;
2022-09-08 14:25:02 -07:00
const handleHeaderClick : React.MouseEventHandler < HTMLAnchorElement > = ( e ) = > {
2022-09-21 14:27:53 -07:00
e . stopPropagation ( ) ;
2022-09-08 14:25:02 -07:00
2022-11-27 14:29:45 -08:00
dispatch ( openModal ( 'MEDIA' , { media : ImmutableList ( [ event . banner ] ) } ) ) ;
2022-09-08 14:25:02 -07:00
} ;
2022-09-21 14:27:53 -07:00
const handleExportClick = ( ) = > {
2022-09-08 14:25:02 -07:00
dispatch ( fetchEventIcs ( status . id ) ) . then ( ( response ) = > {
download ( response , 'calendar.ics' ) ;
} ) . catch ( ( ) = > { } ) ;
2022-09-21 14:27:53 -07:00
} ;
const handleCopy = ( ) = > {
const { uri } = status ;
2022-12-06 13:23:43 -08:00
copy ( uri ) ;
} ;
2022-09-21 14:27:53 -07:00
2022-12-06 13:23:43 -08:00
const handleExternalClick = ( ) = > {
window . open ( status . uri , '_blank' ) ;
2022-09-21 14:27:53 -07:00
} ;
2022-10-05 15:01:26 -07:00
const handleBookmarkClick = ( ) = > {
dispatch ( toggleBookmark ( status ) ) ;
} ;
const handleQuoteClick = ( ) = > {
dispatch ( quoteCompose ( status ) ) ;
} ;
const handlePinClick = ( ) = > {
dispatch ( togglePin ( status ) ) ;
} ;
const handleDeleteClick = ( ) = > {
dispatch ( openModal ( 'CONFIRM' , {
icon : require ( '@tabler/icons/trash.svg' ) ,
heading : intl.formatMessage ( messages . deleteHeading ) ,
message : intl.formatMessage ( messages . deleteMessage ) ,
confirm : intl.formatMessage ( messages . deleteConfirm ) ,
onConfirm : ( ) = > dispatch ( deleteStatus ( status . id ) ) ,
} ) ) ;
} ;
const handleMentionClick = ( ) = > {
dispatch ( mentionCompose ( account ) ) ;
} ;
const handleChatClick = ( ) = > {
dispatch ( launchChat ( account . id , history ) ) ;
} ;
const handleDirectClick = ( ) = > {
dispatch ( directCompose ( account ) ) ;
} ;
const handleMuteClick = ( ) = > {
dispatch ( initMuteModal ( account ) ) ;
} ;
const handleBlockClick = ( ) = > {
dispatch ( openModal ( 'CONFIRM' , {
icon : require ( '@tabler/icons/ban.svg' ) ,
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 > @ { account . acct } < / strong > } } / > ,
confirm : intl.formatMessage ( messages . blockConfirm ) ,
onConfirm : ( ) = > dispatch ( blockAccount ( account . id ) ) ,
secondary : intl.formatMessage ( messages . blockAndReport ) ,
onSecondary : ( ) = > {
dispatch ( blockAccount ( account . id ) ) ;
2022-12-06 10:21:25 -08:00
dispatch ( initReport ( account , { status } ) ) ;
2022-10-05 15:01:26 -07:00
} ,
} ) ) ;
} ;
const handleReport = ( ) = > {
2022-12-06 10:21:25 -08:00
dispatch ( initReport ( account , { status } ) ) ;
2022-10-05 15:01:26 -07:00
} ;
2022-09-21 14:27:53 -07:00
const handleModerate = ( ) = > {
dispatch ( openModal ( 'ACCOUNT_MODERATION' , { accountId : account.id } ) ) ;
} ;
const handleModerateStatus = ( ) = > {
window . open ( ` /pleroma/admin/#/statuses/ ${ status . id } / ` , '_blank' ) ;
} ;
const handleToggleStatusSensitivity = ( ) = > {
dispatch ( toggleStatusSensitivityModal ( intl , status . id , status . sensitive ) ) ;
} ;
const handleDeleteStatus = ( ) = > {
dispatch ( deleteStatusModal ( intl , status . id ) ) ;
2022-09-08 14:25:02 -07:00
} ;
2022-11-26 11:25:48 -08:00
const makeMenu = ( ) : MenuType = > {
2022-12-06 13:23:43 -08:00
const domain = account . fqn . split ( '@' ) [ 1 ] ;
2022-10-05 15:01:26 -07:00
const menu : MenuType = [
{
text : intl.formatMessage ( messages . exportIcs ) ,
action : handleExportClick ,
icon : require ( '@tabler/icons/calendar-plus.svg' ) ,
} ,
{
text : intl.formatMessage ( messages . copy ) ,
action : handleCopy ,
icon : require ( '@tabler/icons/link.svg' ) ,
} ,
] ;
2022-12-06 13:23:43 -08:00
if ( features . federating && isRemote ( account ) ) {
menu . push ( {
text : intl.formatMessage ( messages . external , { domain } ) ,
action : handleExternalClick ,
icon : require ( '@tabler/icons/external-link.svg' ) ,
} ) ;
}
2022-10-05 15:01:26 -07:00
if ( ! ownAccount ) return menu ;
if ( features . bookmarks ) {
menu . push ( {
text : intl.formatMessage ( status . bookmarked ? messages.unbookmark : messages.bookmark ) ,
action : handleBookmarkClick ,
icon : status.bookmarked ? require ( '@tabler/icons/bookmark-off.svg' ) : require ( '@tabler/icons/bookmark.svg' ) ,
} ) ;
}
2022-09-21 14:27:53 -07:00
2022-10-05 15:01:26 -07:00
if ( features . quotePosts ) {
2022-09-21 14:27:53 -07:00
menu . push ( {
2022-10-05 15:01:26 -07:00
text : intl.formatMessage ( messages . quotePost ) ,
action : handleQuoteClick ,
icon : require ( '@tabler/icons/quote.svg' ) ,
2022-09-21 14:27:53 -07:00
} ) ;
}
2022-10-05 15:01:26 -07:00
menu . push ( null ) ;
if ( ownAccount . id === account . id ) {
if ( [ 'public' , 'unlisted' ] . includes ( status . visibility ) ) {
menu . push ( {
text : intl.formatMessage ( status . pinned ? messages.unpin : messages.pin ) ,
action : handlePinClick ,
icon : status.pinned ? require ( '@tabler/icons/pinned-off.svg' ) : require ( '@tabler/icons/pin.svg' ) ,
} ) ;
}
2022-09-21 14:27:53 -07:00
menu . push ( {
2022-10-05 15:01:26 -07:00
text : intl.formatMessage ( messages . delete ) ,
action : handleDeleteClick ,
2022-09-21 14:27:53 -07:00
icon : require ( '@tabler/icons/trash.svg' ) ,
destructive : true ,
} ) ;
2022-10-05 15:01:26 -07:00
} else {
menu . push ( {
text : intl.formatMessage ( messages . mention , { name : username } ) ,
action : handleMentionClick ,
icon : require ( '@tabler/icons/at.svg' ) ,
} ) ;
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' ) ,
} ) ;
} else if ( features . privacyScopes ) {
menu . push ( {
text : intl.formatMessage ( messages . direct , { name : username } ) ,
action : handleDirectClick ,
icon : require ( '@tabler/icons/mail.svg' ) ,
} ) ;
}
menu . push ( null ) ;
menu . push ( {
text : intl.formatMessage ( messages . mute , { name : username } ) ,
action : handleMuteClick ,
icon : require ( '@tabler/icons/circle-x.svg' ) ,
} ) ;
menu . push ( {
text : intl.formatMessage ( messages . block , { name : username } ) ,
action : handleBlockClick ,
icon : require ( '@tabler/icons/ban.svg' ) ,
} ) ;
menu . push ( {
text : intl.formatMessage ( messages . report , { name : username } ) ,
action : handleReport ,
icon : require ( '@tabler/icons/flag.svg' ) ,
} ) ;
2022-09-21 14:27:53 -07:00
}
2022-10-05 15:01:26 -07:00
if ( isStaff ) {
menu . push ( null ) ;
menu . push ( {
text : intl.formatMessage ( messages . adminAccount , { name : account.username } ) ,
action : handleModerate ,
icon : require ( '@tabler/icons/gavel.svg' ) ,
} ) ;
if ( isAdmin ) {
menu . push ( {
text : intl.formatMessage ( messages . adminStatus ) ,
action : handleModerateStatus ,
icon : require ( '@tabler/icons/pencil.svg' ) ,
} ) ;
}
menu . push ( {
text : intl.formatMessage ( status . sensitive === false ? messages.markStatusSensitive : messages.markStatusNotSensitive ) ,
action : handleToggleStatusSensitivity ,
icon : require ( '@tabler/icons/alert-triangle.svg' ) ,
} ) ;
if ( account . id !== ownAccount ? . id ) {
menu . push ( {
text : intl.formatMessage ( messages . deleteStatus ) ,
action : handleDeleteStatus ,
icon : require ( '@tabler/icons/trash.svg' ) ,
destructive : true ,
} ) ;
}
}
return menu ;
} ;
2022-09-21 14:27:53 -07:00
const handleManageClick : React.MouseEventHandler = e = > {
e . stopPropagation ( ) ;
2022-09-25 14:18:11 -07:00
dispatch ( editEvent ( status . id ) ) ;
2022-09-21 14:27:53 -07:00
} ;
const handleParticipantsClick : React.MouseEventHandler = e = > {
2022-11-29 13:49:35 -08:00
e . preventDefault ( ) ;
2022-09-21 14:27:53 -07:00
e . stopPropagation ( ) ;
2022-12-06 13:17:23 -08:00
if ( ! ownAccount ) {
dispatch ( openModal ( 'UNAUTHORIZED' ) ) ;
} else {
dispatch ( openModal ( 'EVENT_PARTICIPANTS' , {
statusId : status.id ,
} ) ) ;
}
2022-09-21 14:27:53 -07:00
} ;
2022-09-08 14:25:02 -07:00
return (
< >
< div className = '-mt-4 -mx-4' >
2022-09-21 14:27:53 -07:00
< div className = 'relative h-32 w-full lg:h-48 md:rounded-t-xl bg-gray-200 dark:bg-gray-900/50' >
2022-09-08 14:25:02 -07:00
{ banner && (
< a href = { banner . url } onClick = { handleHeaderClick } target = '_blank' >
< StillImage
src = { banner . url }
alt = { intl . formatMessage ( messages . bannerHeader ) }
2022-11-26 13:15:58 -08:00
className = 'absolute inset-0 object-cover md:rounded-t-xl h-full'
2022-09-08 14:25:02 -07:00
/ >
< / a >
) }
< / div >
< / div >
< Stack space = { 2 } >
< HStack className = 'w-full' alignItems = 'start' space = { 2 } >
< Text className = 'flex-grow' size = 'lg' weight = 'bold' > { event . name } < / Text >
< Menu >
< MenuButton
as = { IconButton }
src = { require ( '@tabler/icons/dots.svg' ) }
theme = 'outlined'
className = 'px-2 h-[30px]'
iconClassName = 'w-4 h-4'
children = { null }
/ >
< MenuList >
2022-10-05 15:01:26 -07:00
{ makeMenu ( ) . map ( ( menuItem , idx ) = > {
2022-09-08 14:25:02 -07:00
if ( typeof menuItem ? . text === 'undefined' ) {
return < MenuDivider key = { idx } / > ;
} else {
const Comp = ( menuItem . action ? MenuItem : MenuLink ) as any ;
const itemProps = menuItem . action ? { onSelect : menuItem.action } : { to : menuItem.to , as : Link , target : menuItem.newTab ? '_blank' : '_self' } ;
return (
< Comp key = { idx } { ...itemProps } className = 'group' >
< div className = 'flex items-center' >
{ menuItem . icon && (
< SvgIcon src = { menuItem . icon } className = 'mr-3 h-5 w-5 text-gray-400 flex-none group-hover:text-gray-500' / >
) }
< div className = 'truncate' > { menuItem . text } < / div >
< / div >
< / Comp >
) ;
}
} ) }
< / MenuList >
< / Menu >
2022-09-21 14:27:53 -07:00
{ account . id === ownAccount ? . id ? (
< Button
size = 'sm'
theme = 'secondary'
onClick = { handleManageClick }
>
< FormattedMessage id = 'event.manage' defaultMessage = 'Manage' / >
< / Button >
) : < EventActionButton status = { status } / > }
2022-09-08 14:25:02 -07:00
< / HStack >
< Stack space = { 1 } >
< HStack alignItems = 'center' space = { 2 } >
2022-09-21 14:27:53 -07:00
< Icon src = { require ( '@tabler/icons/flag-3.svg' ) } / >
2022-09-08 14:25:02 -07:00
< span >
< FormattedMessage
id = 'event.organized_by'
defaultMessage = 'Organized by {name}'
values = { {
name : (
2022-11-29 13:49:35 -08:00
< Link className = 'mention inline-block' to = { ` /@ ${ account . acct } ` } >
2022-11-27 15:28:15 -08:00
< HStack space = { 1 } alignItems = 'center' grow >
< span dangerouslySetInnerHTML = { { __html : account.display_name_html } } / >
{ account . verified && < VerificationBadge / > }
< / HStack >
2022-09-08 14:25:02 -07:00
< / Link >
) ,
} }
/ >
< / span >
< / HStack >
2022-09-21 14:27:53 -07:00
< HStack alignItems = 'center' space = { 2 } >
< Icon src = { require ( '@tabler/icons/users.svg' ) } / >
< a href = '#' className = 'hover:underline' onClick = { handleParticipantsClick } >
< span >
< FormattedMessage
id = 'event.participants'
defaultMessage = '{count} {rawCount, plural, one {person} other {people}} going'
values = { {
rawCount : event.participants_count || 0 ,
count : shortNumberFormat ( event . participants_count || 0 ) ,
} }
/ >
< / span >
< / a >
< / HStack >
2022-09-08 14:25:02 -07:00
< EventDate status = { status } / >
{ event . location && (
< HStack alignItems = 'center' space = { 2 } >
< Icon src = { require ( '@tabler/icons/map-pin.svg' ) } / >
< span >
{ event . location . get ( 'name' ) }
< / span >
< / HStack >
) }
< / Stack >
< / Stack >
< / >
) ;
} ;
export default EventHeader ;