2017-11-18 03:11:18 +00:00
import PropTypes from 'prop-types' ;
2023-05-28 14:38:10 +00:00
2024-04-06 17:34:30 +00:00
import { injectIntl , FormattedMessage , defineMessages } from 'react-intl' ;
import classNames from 'classnames' ;
import { withRouter } from 'react-router-dom' ;
2016-11-20 18:39:18 +00:00
import ImmutablePropTypes from 'react-immutable-proptypes' ;
2017-05-03 00:04:16 +00:00
import ImmutablePureComponent from 'react-immutable-pure-component' ;
2016-11-20 18:39:18 +00:00
2024-04-06 17:34:30 +00:00
import { HotKeys } from 'react-hotkeys' ;
import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react' ;
2024-03-20 15:37:21 +00:00
import LinkOffIcon from '@/material-icons/400-24px/link_off.svg?react' ;
2024-04-06 17:34:30 +00:00
import PersonIcon from '@/material-icons/400-24px/person-fill.svg?react' ;
import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react' ;
import { Icon } from 'flavours/glitch/components/icon' ;
import { Permalink } from 'flavours/glitch/components/permalink' ;
import AccountContainer from 'flavours/glitch/containers/account_container' ;
2017-12-04 07:26:40 +00:00
import StatusContainer from 'flavours/glitch/containers/status_container' ;
2024-04-06 17:34:30 +00:00
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router' ;
import FollowRequestContainer from '../containers/follow_request_container' ;
import NotificationOverlayContainer from '../containers/overlay_container' ;
2024-03-20 15:37:21 +00:00
import RelationshipsSeveranceEvent from './relationships_severance_event' ;
2024-04-06 17:34:30 +00:00
import Report from './report' ;
const messages = defineMessages ( {
follow : { id : 'notification.follow' , defaultMessage : '{name} followed you' } ,
2024-03-20 15:37:21 +00:00
severedRelationships : { id : 'notification.severed_relationships' , defaultMessage : 'Relationships with {name} severed' } ,
2024-04-06 17:34:30 +00:00
adminSignUp : { id : 'notification.admin.sign_up' , defaultMessage : '{name} signed up' } ,
adminReport : { id : 'notification.admin.report' , defaultMessage : '{name} reported {target}' } ,
} ) ;
2023-05-28 14:38:10 +00:00
2024-04-06 17:34:30 +00:00
const notificationForScreenReader = ( intl , message , timestamp ) => {
const output = [ message ] ;
2023-05-28 14:38:10 +00:00
2024-04-06 17:34:30 +00:00
output . push ( intl . formatDate ( timestamp , { hour : '2-digit' , minute : '2-digit' , month : 'short' , day : 'numeric' } ) ) ;
2023-05-28 14:38:10 +00:00
2024-04-06 17:34:30 +00:00
return output . join ( ', ' ) ;
} ;
2016-11-20 18:39:18 +00:00
2024-04-06 17:34:30 +00:00
class Notification extends ImmutablePureComponent {
2017-05-12 12:44:10 +00:00
static propTypes = {
2017-05-20 15:31:47 +00:00
notification : ImmutablePropTypes . map . isRequired ,
2017-11-18 03:11:18 +00:00
hidden : PropTypes . bool ,
onMoveUp : PropTypes . func . isRequired ,
onMoveDown : PropTypes . func . isRequired ,
onMention : PropTypes . func . isRequired ,
2024-04-06 17:34:30 +00:00
onFavourite : PropTypes . func . isRequired ,
onReblog : PropTypes . func . isRequired ,
onToggleHidden : PropTypes . func . isRequired ,
status : ImmutablePropTypes . map ,
intl : PropTypes . object . isRequired ,
2018-04-15 19:29:03 +00:00
getScrollPosition : PropTypes . func ,
updateScrollBottom : PropTypes . func ,
2019-02-09 19:54:11 +00:00
cacheMediaWidth : PropTypes . func ,
cachedMediaWidth : PropTypes . number ,
onUnmount : PropTypes . func ,
2020-09-19 11:53:24 +00:00
unread : PropTypes . bool ,
2024-04-06 17:34:30 +00:00
... WithRouterPropTypes ,
} ;
handleMoveUp = ( ) => {
const { notification , onMoveUp } = this . props ;
onMoveUp ( notification . get ( 'id' ) ) ;
} ;
handleMoveDown = ( ) => {
const { notification , onMoveDown } = this . props ;
onMoveDown ( notification . get ( 'id' ) ) ;
} ;
handleOpen = ( ) => {
const { notification } = this . props ;
if ( notification . get ( 'status' ) ) {
this . props . history . push ( ` /@ ${ notification . getIn ( [ 'status' , 'account' , 'acct' ] ) } / ${ notification . get ( 'status' ) } ` ) ;
} else {
this . handleOpenProfile ( ) ;
}
} ;
handleOpenProfile = ( ) => {
const { notification } = this . props ;
this . props . history . push ( ` /@ ${ notification . getIn ( [ 'account' , 'acct' ] ) } ` ) ;
} ;
handleMention = e => {
e . preventDefault ( ) ;
const { notification , onMention } = this . props ;
onMention ( notification . get ( 'account' ) , this . props . history ) ;
} ;
handleHotkeyFavourite = ( ) => {
const { status } = this . props ;
if ( status ) this . props . onFavourite ( status ) ;
2017-05-12 12:44:10 +00:00
} ;
2024-04-06 17:34:30 +00:00
handleHotkeyBoost = e => {
const { status } = this . props ;
if ( status ) this . props . onReblog ( status , e ) ;
} ;
getHandlers ( ) {
return {
reply : this . handleMention ,
favourite : this . handleHotkeyFavourite ,
boost : this . handleHotkeyBoost ,
mention : this . handleMention ,
open : this . handleOpen ,
openProfile : this . handleOpenProfile ,
moveUp : this . handleMoveUp ,
moveDown : this . handleMoveDown ,
} ;
}
renderFollow ( notification , account , link ) {
const { intl , unread } = this . props ;
return (
< HotKeys handlers = { this . getHandlers ( ) } >
< div className = { classNames ( 'notification notification-follow focusable' , { unread } ) } tabIndex = { 0 } aria - label = { notificationForScreenReader ( intl , intl . formatMessage ( messages . follow , { name : account . get ( 'acct' ) } ) , notification . get ( 'created_at' ) ) } >
< div className = 'notification__message' >
< Icon id = 'user-plus' icon = { PersonAddIcon } / >
< span title = { notification . get ( 'created_at' ) } >
< FormattedMessage id = 'notification.follow' defaultMessage = '{name} followed you' values = { { name : link } } / >
< / span >
< / div >
< AccountContainer id = { account . get ( 'id' ) } hidden = { this . props . hidden } / >
< NotificationOverlayContainer notification = { notification } / >
< / div >
< / HotKeys >
) ;
}
renderFollowRequest ( notification , account , link ) {
const { intl , unread } = this . props ;
return (
< HotKeys handlers = { this . getHandlers ( ) } >
< div className = { classNames ( 'notification notification-follow-request focusable' , { unread } ) } tabIndex = { 0 } aria - label = { notificationForScreenReader ( intl , intl . formatMessage ( { id : 'notification.follow_request' , defaultMessage : '{name} has requested to follow you' } , { name : account . get ( 'acct' ) } ) , notification . get ( 'created_at' ) ) } >
< div className = 'notification__message' >
< Icon id = 'user' icon = { PersonIcon } / >
< span title = { notification . get ( 'created_at' ) } >
< FormattedMessage id = 'notification.follow_request' defaultMessage = '{name} has requested to follow you' values = { { name : link } } / >
< / span >
< / div >
< FollowRequestContainer id = { account . get ( 'id' ) } withNote = { false } hidden = { this . props . hidden } / >
< NotificationOverlayContainer notification = { notification } / >
< / div >
< / HotKeys >
) ;
}
renderMention ( notification ) {
return (
< StatusContainer
id = { notification . get ( 'status' ) }
containerId = { notification . get ( 'id' ) }
withDismiss
hidden = { this . props . hidden }
onMoveDown = { this . handleMoveDown }
onMoveUp = { this . handleMoveUp }
onMention = { this . props . onMention }
contextType = 'notifications'
getScrollPosition = { this . props . getScrollPosition }
updateScrollBottom = { this . props . updateScrollBottom }
notification = { notification }
cachedMediaWidth = { this . props . cachedMediaWidth }
cacheMediaWidth = { this . props . cacheMediaWidth }
unread = { this . props . unread }
onUnmount = { this . props . onUnmount }
/ >
) ;
}
renderFavourite ( notification ) {
return (
< StatusContainer
containerId = { notification . get ( 'id' ) }
hidden = { ! ! this . props . hidden }
id = { notification . get ( 'status' ) }
account = { notification . get ( 'account' ) }
prepend = 'favourite'
muted
withDismiss
notification = { notification }
onMoveDown = { this . handleMoveDown }
onMoveUp = { this . handleMoveUp }
onMention = { this . props . onMention }
contextType = 'notifications'
getScrollPosition = { this . props . getScrollPosition }
updateScrollBottom = { this . props . updateScrollBottom }
cachedMediaWidth = { this . props . cachedMediaWidth }
cacheMediaWidth = { this . props . cacheMediaWidth }
onUnmount = { this . props . onUnmount }
unread = { this . props . unread }
/ >
) ;
}
renderReblog ( notification ) {
return (
< StatusContainer
containerId = { notification . get ( 'id' ) }
hidden = { ! ! this . props . hidden }
id = { notification . get ( 'status' ) }
account = { notification . get ( 'account' ) }
prepend = 'reblog'
muted
notification = { notification }
onMoveDown = { this . handleMoveDown }
onMoveUp = { this . handleMoveUp }
onMention = { this . props . onMention }
contextType = 'notifications'
getScrollPosition = { this . props . getScrollPosition }
updateScrollBottom = { this . props . updateScrollBottom }
cachedMediaWidth = { this . props . cachedMediaWidth }
cacheMediaWidth = { this . props . cacheMediaWidth }
onUnmount = { this . props . onUnmount }
withDismiss
unread = { this . props . unread }
/ >
) ;
}
renderStatus ( notification ) {
return (
< StatusContainer
containerId = { notification . get ( 'id' ) }
hidden = { ! ! this . props . hidden }
id = { notification . get ( 'status' ) }
account = { notification . get ( 'account' ) }
prepend = 'status'
muted
notification = { notification }
onMoveDown = { this . handleMoveDown }
onMoveUp = { this . handleMoveUp }
onMention = { this . props . onMention }
contextType = 'notifications'
getScrollPosition = { this . props . getScrollPosition }
updateScrollBottom = { this . props . updateScrollBottom }
cachedMediaWidth = { this . props . cachedMediaWidth }
cacheMediaWidth = { this . props . cacheMediaWidth }
onUnmount = { this . props . onUnmount }
withDismiss
unread = { this . props . unread }
/ >
) ;
}
renderUpdate ( notification ) {
return (
< StatusContainer
containerId = { notification . get ( 'id' ) }
hidden = { ! ! this . props . hidden }
id = { notification . get ( 'status' ) }
account = { notification . get ( 'account' ) }
prepend = 'update'
muted
notification = { notification }
onMoveDown = { this . handleMoveDown }
onMoveUp = { this . handleMoveUp }
onMention = { this . props . onMention }
contextType = 'notifications'
getScrollPosition = { this . props . getScrollPosition }
updateScrollBottom = { this . props . updateScrollBottom }
cachedMediaWidth = { this . props . cachedMediaWidth }
cacheMediaWidth = { this . props . cacheMediaWidth }
onUnmount = { this . props . onUnmount }
withDismiss
unread = { this . props . unread }
/ >
) ;
}
renderPoll ( notification ) {
return (
< StatusContainer
containerId = { notification . get ( 'id' ) }
hidden = { ! ! this . props . hidden }
id = { notification . get ( 'status' ) }
account = { notification . get ( 'account' ) }
prepend = 'poll'
muted
notification = { notification }
onMoveDown = { this . handleMoveDown }
onMoveUp = { this . handleMoveUp }
onMention = { this . props . onMention }
contextType = 'notifications'
getScrollPosition = { this . props . getScrollPosition }
updateScrollBottom = { this . props . updateScrollBottom }
cachedMediaWidth = { this . props . cachedMediaWidth }
cacheMediaWidth = { this . props . cacheMediaWidth }
onUnmount = { this . props . onUnmount }
withDismiss
unread = { this . props . unread }
/ >
) ;
}
2024-03-20 15:37:21 +00:00
renderRelationshipsSevered ( notification ) {
const { intl , unread } = this . props ;
if ( ! notification . get ( 'event' ) ) {
return null ;
}
return (
< HotKeys handlers = { this . getHandlers ( ) } >
< div className = { classNames ( 'notification notification-severed-relationships focusable' , { unread } ) } tabIndex = { 0 } aria - label = { notificationForScreenReader ( intl , intl . formatMessage ( messages . adminReport , { name : notification . getIn ( [ 'event' , 'target_name' ] ) } ) , notification . get ( 'created_at' ) ) } >
< div className = 'notification__message' >
< Icon id = 'unlink' icon = { LinkOffIcon } / >
< span title = { notification . get ( 'created_at' ) } >
< FormattedMessage id = 'notification.severedRelationships' defaultMessage = 'Relationships with {name} severed' values = { { name : notification . getIn ( [ 'event' , 'target_name' ] ) } } / >
< / span >
< / div >
< RelationshipsSeveranceEvent event = { notification . get ( 'event' ) } / >
< / div >
< / HotKeys >
) ;
}
2024-04-06 17:34:30 +00:00
renderAdminSignUp ( notification , account , link ) {
const { intl , unread } = this . props ;
return (
< HotKeys handlers = { this . getHandlers ( ) } >
< div className = { classNames ( 'notification notification-admin-sign-up focusable' , { unread } ) } tabIndex = { 0 } aria - label = { notificationForScreenReader ( intl , intl . formatMessage ( messages . adminSignUp , { name : account . get ( 'acct' ) } ) , notification . get ( 'created_at' ) ) } >
< div className = 'notification__message' >
< Icon id = 'user-plus' icon = { PersonAddIcon } / >
< span title = { notification . get ( 'created_at' ) } >
< FormattedMessage id = 'notification.admin.sign_up' defaultMessage = '{name} signed up' values = { { name : link } } / >
< / span >
< / div >
< AccountContainer id = { account . get ( 'id' ) } hidden = { this . props . hidden } / >
< NotificationOverlayContainer notification = { notification } / >
< / div >
< / HotKeys >
) ;
}
renderAdminReport ( notification , account , link ) {
const { intl , unread , report } = this . props ;
if ( ! report ) {
return null ;
}
const targetAccount = report . get ( 'target_account' ) ;
const targetDisplayNameHtml = { _ _html : targetAccount . get ( 'display_name_html' ) } ;
const targetLink = (
< bdi >
< Permalink
className = 'notification__display-name'
href = { account . get ( 'url' ) }
title = { targetAccount . get ( 'acct' ) }
to = { ` /@ ${ targetAccount . get ( 'acct' ) } ` }
dangerouslySetInnerHTML = { targetDisplayNameHtml }
/ >
< / bdi >
) ;
return (
< HotKeys handlers = { this . getHandlers ( ) } >
< div className = { classNames ( 'notification notification-admin-report focusable' , { unread } ) } tabIndex = { 0 } aria - label = { notificationForScreenReader ( intl , intl . formatMessage ( messages . adminReport , { name : account . get ( 'acct' ) , target : notification . getIn ( [ 'report' , 'target_account' , 'acct' ] ) } ) , notification . get ( 'created_at' ) ) } >
< div className = 'notification__message' >
< Icon id = 'flag' icon = { FlagIcon } / >
< span title = { notification . get ( 'created_at' ) } >
< FormattedMessage id = 'notification.admin.report' defaultMessage = '{name} reported {target}' values = { { name : link , target : targetLink } } / >
< / span >
< / div >
< Report account = { account } report = { notification . get ( 'report' ) } hidden = { this . props . hidden } / >
< NotificationOverlayContainer notification = { notification } / >
< / div >
< / HotKeys >
) ;
}
2017-06-11 08:42:42 +00:00
render ( ) {
2024-04-06 17:34:30 +00:00
const { notification } = this . props ;
const account = notification . get ( 'account' ) ;
const displayNameHtml = { _ _html : account . get ( 'display_name_html' ) } ;
const link = (
< bdi >
< Permalink
className = 'notification__display-name'
href = { ` /@ ${ account . get ( 'acct' ) } ` }
title = { account . get ( 'acct' ) }
to = { ` /@ ${ account . get ( 'acct' ) } ` }
dangerouslySetInnerHTML = { displayNameHtml }
/ >
< / bdi >
) ;
2017-11-27 21:17:12 +00:00
2016-11-20 18:39:18 +00:00
switch ( notification . get ( 'type' ) ) {
2017-04-11 20:53:58 +00:00
case 'follow' :
2024-04-06 17:34:30 +00:00
return this . renderFollow ( notification , account , link ) ;
2019-12-01 16:25:29 +00:00
case 'follow_request' :
2024-04-06 17:34:30 +00:00
return this . renderFollowRequest ( notification , account , link ) ;
2017-04-11 20:53:58 +00:00
case 'mention' :
2024-04-06 17:34:30 +00:00
return this . renderMention ( notification ) ;
2017-04-11 20:53:58 +00:00
case 'favourite' :
2024-04-06 17:34:30 +00:00
return this . renderFavourite ( notification ) ;
2017-04-11 20:53:58 +00:00
case 'reblog' :
2024-04-06 17:34:30 +00:00
return this . renderReblog ( notification ) ;
case 'status' :
return this . renderStatus ( notification ) ;
2022-02-11 21:20:19 +00:00
case 'update' :
2024-04-06 17:34:30 +00:00
return this . renderUpdate ( notification ) ;
case 'poll' :
return this . renderPoll ( notification ) ;
2024-03-20 15:37:21 +00:00
case 'severed_relationships' :
return this . renderRelationshipsSevered ( notification ) ;
2024-04-06 17:34:30 +00:00
case 'admin.sign_up' :
return this . renderAdminSignUp ( notification , account , link ) ;
case 'admin.report' :
return this . renderAdminReport ( notification , account , link ) ;
2016-11-20 18:39:18 +00:00
}
2024-04-06 17:34:30 +00:00
return null ;
2016-11-20 18:39:18 +00:00
}
2017-04-21 18:05:35 +00:00
}
2024-04-06 17:34:30 +00:00
export default withRouter ( injectIntl ( Notification ) ) ;