2024-03-11 11:02:21 -04:00
import PropTypes from 'prop-types' ;
import { useRef , useCallback , useEffect } from 'react' ;
2024-08-08 05:16:33 -04:00
import { defineMessages , useIntl , FormattedMessage } from 'react-intl' ;
2024-03-11 11:02:21 -04:00
import { Helmet } from 'react-helmet' ;
import { useSelector , useDispatch } from 'react-redux' ;
2024-07-31 03:52:59 -04:00
import DeleteIcon from '@/material-icons/400-24px/delete.svg?react' ;
2024-03-11 11:02:21 -04:00
import DoneIcon from '@/material-icons/400-24px/done.svg?react' ;
2024-03-19 11:39:26 -04:00
import InventoryIcon from '@/material-icons/400-24px/inventory_2.svg?react' ;
2024-03-11 11:02:21 -04:00
import { fetchNotificationRequest , fetchNotificationsForRequest , expandNotificationsForRequest , acceptNotificationRequest , dismissNotificationRequest } from 'mastodon/actions/notifications' ;
import Column from 'mastodon/components/column' ;
import ColumnHeader from 'mastodon/components/column_header' ;
import { IconButton } from 'mastodon/components/icon_button' ;
import ScrollableList from 'mastodon/components/scrollable_list' ;
2024-03-13 12:47:48 -04:00
import { SensitiveMediaContextProvider } from 'mastodon/features/ui/util/sensitive_media_context' ;
2024-03-11 11:02:21 -04:00
import NotificationContainer from './containers/notification_container' ;
const messages = defineMessages ( {
title : { id : 'notification_requests.notifications_from' , defaultMessage : 'Notifications from {name}' } ,
accept : { id : 'notification_requests.accept' , defaultMessage : 'Accept' } ,
dismiss : { id : 'notification_requests.dismiss' , defaultMessage : 'Dismiss' } ,
} ) ;
const selectChild = ( ref , index , alignTop ) => {
const container = ref . current . node ;
const element = container . querySelector ( ` article:nth-of-type( ${ index + 1 } ) .focusable ` ) ;
if ( element ) {
if ( alignTop && container . scrollTop > element . offsetTop ) {
element . scrollIntoView ( true ) ;
} else if ( ! alignTop && container . scrollTop + container . clientHeight < element . offsetTop + element . offsetHeight ) {
element . scrollIntoView ( false ) ;
}
element . focus ( ) ;
}
} ;
export const NotificationRequest = ( { multiColumn , params : { id } } ) => {
const columnRef = useRef ( ) ;
const intl = useIntl ( ) ;
const dispatch = useDispatch ( ) ;
const notificationRequest = useSelector ( state => state . getIn ( [ 'notificationRequests' , 'current' , 'item' , 'id' ] ) === id ? state . getIn ( [ 'notificationRequests' , 'current' , 'item' ] ) : null ) ;
const accountId = notificationRequest ? . get ( 'account' ) ;
const account = useSelector ( state => state . getIn ( [ 'accounts' , accountId ] ) ) ;
const notifications = useSelector ( state => state . getIn ( [ 'notificationRequests' , 'current' , 'notifications' , 'items' ] ) ) ;
const isLoading = useSelector ( state => state . getIn ( [ 'notificationRequests' , 'current' , 'notifications' , 'isLoading' ] ) ) ;
const hasMore = useSelector ( state => ! ! state . getIn ( [ 'notificationRequests' , 'current' , 'notifications' , 'next' ] ) ) ;
const removed = useSelector ( state => state . getIn ( [ 'notificationRequests' , 'current' , 'removed' ] ) ) ;
const handleHeaderClick = useCallback ( ( ) => {
columnRef . current ? . scrollTop ( ) ;
} , [ columnRef ] ) ;
const handleLoadMore = useCallback ( ( ) => {
dispatch ( expandNotificationsForRequest ( ) ) ;
} , [ dispatch ] ) ;
const handleDismiss = useCallback ( ( ) => {
dispatch ( dismissNotificationRequest ( id ) ) ;
} , [ dispatch , id ] ) ;
const handleAccept = useCallback ( ( ) => {
dispatch ( acceptNotificationRequest ( id ) ) ;
} , [ dispatch , id ] ) ;
const handleMoveUp = useCallback ( id => {
const elementIndex = notifications . findIndex ( item => item !== null && item . get ( 'id' ) === id ) - 1 ;
selectChild ( columnRef , elementIndex , true ) ;
} , [ columnRef , notifications ] ) ;
const handleMoveDown = useCallback ( id => {
const elementIndex = notifications . findIndex ( item => item !== null && item . get ( 'id' ) === id ) + 1 ;
selectChild ( columnRef , elementIndex , false ) ;
} , [ columnRef , notifications ] ) ;
useEffect ( ( ) => {
dispatch ( fetchNotificationRequest ( id ) ) ;
} , [ dispatch , id ] ) ;
useEffect ( ( ) => {
if ( accountId ) {
dispatch ( fetchNotificationsForRequest ( accountId ) ) ;
}
} , [ dispatch , accountId ] ) ;
2024-03-14 04:58:44 -04:00
const columnTitle = intl . formatMessage ( messages . title , { name : account ? . get ( 'display_name' ) || account ? . get ( 'username' ) } ) ;
2024-03-11 11:02:21 -04:00
2024-08-08 05:16:33 -04:00
let explainer = null ;
if ( account ? . limited ) {
const isLocal = account . acct . indexOf ( '@' ) === - 1 ;
explainer = (
< div className = 'dismissable-banner' >
< div className = 'dismissable-banner__message' >
{ isLocal ? (
< FormattedMessage id = 'notification_requests.explainer_for_limited_account' defaultMessage = 'Notifications from this account have been filtered because the account has been limited by a moderator.' / >
) : (
< FormattedMessage id = 'notification_requests.explainer_for_limited_remote_account' defaultMessage = 'Notifications from this account have been filtered because the account or its server has been limited by a moderator.' / >
) }
< / div >
< / div >
) ;
}
2024-03-11 11:02:21 -04:00
return (
< Column bindToDocument = { ! multiColumn } ref = { columnRef } label = { columnTitle } >
< ColumnHeader
icon = 'archive'
2024-03-19 11:39:26 -04:00
iconComponent = { InventoryIcon }
2024-03-11 11:02:21 -04:00
title = { columnTitle }
onClick = { handleHeaderClick }
multiColumn = { multiColumn }
showBackButton
extraButton = { ! removed && (
< >
2024-07-31 03:52:59 -04:00
< IconButton className = 'column-header__button' iconComponent = { DeleteIcon } onClick = { handleDismiss } title = { intl . formatMessage ( messages . dismiss ) } / >
2024-03-11 11:02:21 -04:00
< IconButton className = 'column-header__button' iconComponent = { DoneIcon } onClick = { handleAccept } title = { intl . formatMessage ( messages . accept ) } / >
< / >
) }
/ >
2024-03-13 12:47:48 -04:00
< SensitiveMediaContextProvider hideMediaByDefault >
< ScrollableList
2024-08-08 05:16:33 -04:00
prepend = { explainer }
2024-03-13 12:47:48 -04:00
scrollKey = { ` notification_requests/ ${ id } ` }
trackScroll = { ! multiColumn }
bindToDocument = { ! multiColumn }
isLoading = { isLoading }
showLoading = { isLoading && notifications . size === 0 }
hasMore = { hasMore }
onLoadMore = { handleLoadMore }
>
{ notifications . map ( item => (
item && < NotificationContainer
key = { item . get ( 'id' ) }
notification = { item }
accountId = { item . get ( 'account' ) }
onMoveUp = { handleMoveUp }
onMoveDown = { handleMoveDown }
/ >
) ) }
< / ScrollableList >
< / SensitiveMediaContextProvider >
2024-03-11 11:02:21 -04:00
< Helmet >
< title > { columnTitle } < / title >
< meta name = 'robots' content = 'noindex' / >
< / Helmet >
< / Column >
) ;
} ;
NotificationRequest . propTypes = {
multiColumn : PropTypes . bool ,
params : PropTypes . shape ( {
id : PropTypes . string . isRequired ,
} ) ,
} ;
export default NotificationRequest ;