2024-03-11 16:02:21 +01:00
import PropTypes from 'prop-types' ;
2024-08-09 16:56:39 +02:00
import { useRef , useCallback , useEffect , useState } from 'react' ;
2024-03-11 16:02:21 +01:00
import { defineMessages , useIntl , FormattedMessage } from 'react-intl' ;
import { Helmet } from 'react-helmet' ;
import { useSelector , useDispatch } from 'react-redux' ;
2024-08-20 00:11:58 +02:00
import ArrowDropDownIcon from '@/material-icons/400-24px/arrow_drop_down.svg?react' ;
2024-03-19 16:39:26 +01:00
import InventoryIcon from '@/material-icons/400-24px/inventory_2.svg?react' ;
2024-08-09 16:56:39 +02:00
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react' ;
import { openModal } from 'mastodon/actions/modal' ;
import { fetchNotificationRequests , expandNotificationRequests , acceptNotificationRequests , dismissNotificationRequests } from 'mastodon/actions/notifications' ;
2024-08-02 16:59:37 +02:00
import { changeSetting } from 'mastodon/actions/settings' ;
2024-08-09 16:56:39 +02:00
import { CheckBox } from 'mastodon/components/check_box' ;
2024-03-11 16:02:21 +01:00
import Column from 'mastodon/components/column' ;
import ColumnHeader from 'mastodon/components/column_header' ;
2024-08-20 00:11:58 +02:00
import { Icon } from 'mastodon/components/icon' ;
2024-03-11 16:02:21 +01:00
import ScrollableList from 'mastodon/components/scrollable_list' ;
2024-08-09 16:56:39 +02:00
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container' ;
2024-03-11 16:02:21 +01:00
import { NotificationRequest } from './components/notification_request' ;
2024-08-02 16:59:37 +02:00
import { PolicyControls } from './components/policy_controls' ;
import SettingToggle from './components/setting_toggle' ;
2024-03-11 16:02:21 +01:00
const messages = defineMessages ( {
title : { id : 'notification_requests.title' , defaultMessage : 'Filtered notifications' } ,
2024-08-09 16:56:39 +02:00
maximize : { id : 'notification_requests.maximize' , defaultMessage : 'Maximize' } ,
more : { id : 'status.more' , defaultMessage : 'More' } ,
2024-08-20 00:11:58 +02:00
acceptMultiple : { id : 'notification_requests.accept_multiple' , defaultMessage : '{count, plural, one {Accept # request…} other {Accept # requests…}}' } ,
dismissMultiple : { id : 'notification_requests.dismiss_multiple' , defaultMessage : '{count, plural, one {Dismiss # request…} other {Dismiss # requests…}}' } ,
confirmAcceptMultipleTitle : { id : 'notification_requests.confirm_accept_multiple.title' , defaultMessage : 'Accept notification requests?' } ,
confirmAcceptMultipleMessage : { id : 'notification_requests.confirm_accept_multiple.message' , defaultMessage : 'You are about to accept {count, plural, one {one notification request} other {# notification requests}}. Are you sure you want to proceed?' } ,
confirmAcceptMultipleButton : { id : 'notification_requests.confirm_accept_multiple.button' , defaultMessage : '{count, plural, one {Accept request} other {Accept requests}}' } ,
confirmDismissMultipleTitle : { id : 'notification_requests.confirm_dismiss_multiple.title' , defaultMessage : 'Dismiss notification requests?' } ,
confirmDismissMultipleMessage : { id : 'notification_requests.confirm_dismiss_multiple.message' , defaultMessage : "You are about to dismiss {count, plural, one {one notification request} other {# notification requests}}. You won't be able to easily access {count, plural, one {it} other {them}} again. Are you sure you want to proceed?" } ,
confirmDismissMultipleButton : { id : 'notification_requests.confirm_dismiss_multiple.button' , defaultMessage : '{count, plural, one {Dismiss request} other {Dismiss requests}}' } ,
2024-03-11 16:02:21 +01:00
} ) ;
2024-08-02 16:59:37 +02:00
const ColumnSettings = ( ) => {
const dispatch = useDispatch ( ) ;
const settings = useSelector ( ( state ) => state . settings . get ( 'notifications' ) ) ;
const onChange = useCallback (
( key , checked ) => {
dispatch ( changeSetting ( [ 'notifications' , ... key ] , checked ) ) ;
} ,
[ dispatch ] ,
) ;
return (
< div className = 'column-settings' >
< section >
< div className = 'column-settings__row' >
< SettingToggle
prefix = 'notifications'
settings = { settings }
settingPath = { [ 'minimizeFilteredBanner' ] }
onChange = { onChange }
label = {
2024-08-02 21:55:13 +02:00
< FormattedMessage id = 'notification_requests.minimize_banner' defaultMessage = 'Minimize filtered notifications banner' / >
2024-08-02 16:59:37 +02:00
}
/ >
< / div >
< / section >
< PolicyControls / >
< / div >
) ;
} ;
2024-08-09 16:56:39 +02:00
const SelectRow = ( { selectAllChecked , toggleSelectAll , selectedItems , selectionMode , setSelectionMode } ) => {
const intl = useIntl ( ) ;
const dispatch = useDispatch ( ) ;
const selectedCount = selectedItems . length ;
2024-08-09 18:40:15 +02:00
const handleAcceptMultiple = useCallback ( ( ) => {
2024-08-09 16:56:39 +02:00
dispatch ( openModal ( {
modalType : 'CONFIRM' ,
modalProps : {
2024-08-20 00:11:58 +02:00
title : intl . formatMessage ( messages . confirmAcceptMultipleTitle ) ,
message : intl . formatMessage ( messages . confirmAcceptMultipleMessage , { count : selectedItems . length } ) ,
confirm : intl . formatMessage ( messages . confirmAcceptMultipleButton , { count : selectedItems . length } ) ,
2024-08-09 16:56:39 +02:00
onConfirm : ( ) =>
dispatch ( acceptNotificationRequests ( selectedItems ) ) ,
} ,
} ) ) ;
} , [ dispatch , intl , selectedItems ] ) ;
2024-08-09 18:40:15 +02:00
const handleDismissMultiple = useCallback ( ( ) => {
2024-08-09 16:56:39 +02:00
dispatch ( openModal ( {
modalType : 'CONFIRM' ,
modalProps : {
2024-08-20 00:11:58 +02:00
title : intl . formatMessage ( messages . confirmDismissMultipleTitle ) ,
message : intl . formatMessage ( messages . confirmDismissMultipleMessage , { count : selectedItems . length } ) ,
confirm : intl . formatMessage ( messages . confirmDismissMultipleButton , { count : selectedItems . length } ) ,
2024-08-09 16:56:39 +02:00
onConfirm : ( ) =>
dispatch ( dismissNotificationRequests ( selectedItems ) ) ,
} ,
} ) ) ;
} , [ dispatch , intl , selectedItems ] ) ;
const handleToggleSelectionMode = useCallback ( ( ) => {
setSelectionMode ( ( mode ) => ! mode ) ;
} , [ setSelectionMode ] ) ;
2024-08-20 00:11:58 +02:00
const menu = [
{ text : intl . formatMessage ( messages . acceptMultiple , { count : selectedCount } ) , action : handleAcceptMultiple } ,
{ text : intl . formatMessage ( messages . dismissMultiple , { count : selectedCount } ) , action : handleDismissMultiple } ,
] ;
const handleSelectAll = useCallback ( ( ) => {
setSelectionMode ( true ) ;
toggleSelectAll ( ) ;
} , [ setSelectionMode , toggleSelectAll ] ) ;
2024-08-09 16:56:39 +02:00
return (
< div className = 'column-header__select-row' >
2024-08-20 00:11:58 +02:00
< div className = 'column-header__select-row__checkbox' >
< CheckBox checked = { selectAllChecked } indeterminate = { selectedCount > 0 && ! selectAllChecked } onChange = { handleSelectAll } / >
< / div >
< DropdownMenuContainer
items = { menu }
icons = 'ellipsis-h'
iconComponent = { MoreHorizIcon }
direction = 'right'
title = { intl . formatMessage ( messages . more ) }
>
< button className = 'dropdown-button column-header__select-row__select-menu' disabled = { selectedItems . length === 0 } >
< span className = 'dropdown-button__label' >
{ selectedCount } selected
< / span >
< Icon id = 'down' icon = { ArrowDropDownIcon } / >
< / button >
< / DropdownMenuContainer >
< div className = 'column-header__select-row__mode-button' >
2024-08-09 16:56:39 +02:00
< button className = 'text-btn' tabIndex = { 0 } onClick = { handleToggleSelectionMode } >
{ selectionMode ? (
2024-08-20 00:11:58 +02:00
< FormattedMessage id = 'notification_requests.exit_selection' defaultMessage = 'Done' / >
2024-08-09 16:56:39 +02:00
) :
(
2024-08-20 00:11:58 +02:00
< FormattedMessage id = 'notification_requests.edit_selection' defaultMessage = 'Edit' / >
2024-08-09 16:56:39 +02:00
) }
< / button >
< / div >
< / div >
) ;
} ;
SelectRow . propTypes = {
selectAllChecked : PropTypes . func . isRequired ,
toggleSelectAll : PropTypes . func . isRequired ,
selectedItems : PropTypes . arrayOf ( PropTypes . string ) . isRequired ,
selectionMode : PropTypes . bool ,
setSelectionMode : PropTypes . func . isRequired ,
} ;
2024-03-11 16:02:21 +01:00
export const NotificationRequests = ( { multiColumn } ) => {
const columnRef = useRef ( ) ;
const intl = useIntl ( ) ;
const dispatch = useDispatch ( ) ;
const isLoading = useSelector ( state => state . getIn ( [ 'notificationRequests' , 'isLoading' ] ) ) ;
const notificationRequests = useSelector ( state => state . getIn ( [ 'notificationRequests' , 'items' ] ) ) ;
const hasMore = useSelector ( state => ! ! state . getIn ( [ 'notificationRequests' , 'next' ] ) ) ;
2024-08-09 16:56:39 +02:00
const [ selectionMode , setSelectionMode ] = useState ( false ) ;
const [ checkedRequestIds , setCheckedRequestIds ] = useState ( [ ] ) ;
const [ selectAllChecked , setSelectAllChecked ] = useState ( false ) ;
2024-03-11 16:02:21 +01:00
const handleHeaderClick = useCallback ( ( ) => {
columnRef . current ? . scrollTop ( ) ;
} , [ columnRef ] ) ;
2024-08-09 16:56:39 +02:00
const handleCheck = useCallback ( id => {
setCheckedRequestIds ( ids => {
const position = ids . indexOf ( id ) ;
if ( position > - 1 )
ids . splice ( position , 1 ) ;
else
ids . push ( id ) ;
setSelectAllChecked ( ids . length === notificationRequests . size ) ;
return [ ... ids ] ;
} ) ;
} , [ setCheckedRequestIds , notificationRequests ] ) ;
const toggleSelectAll = useCallback ( ( ) => {
setSelectAllChecked ( checked => {
if ( checked )
setCheckedRequestIds ( [ ] ) ;
else
setCheckedRequestIds ( notificationRequests . map ( request => request . get ( 'id' ) ) . toArray ( ) ) ;
return ! checked ;
} ) ;
} , [ notificationRequests ] ) ;
2024-03-11 16:02:21 +01:00
const handleLoadMore = useCallback ( ( ) => {
dispatch ( expandNotificationRequests ( ) ) ;
} , [ dispatch ] ) ;
useEffect ( ( ) => {
dispatch ( fetchNotificationRequests ( ) ) ;
} , [ dispatch ] ) ;
return (
< Column bindToDocument = { ! multiColumn } ref = { columnRef } label = { intl . formatMessage ( messages . title ) } >
< ColumnHeader
icon = 'archive'
2024-03-19 16:39:26 +01:00
iconComponent = { InventoryIcon }
2024-03-11 16:02:21 +01:00
title = { intl . formatMessage ( messages . title ) }
onClick = { handleHeaderClick }
multiColumn = { multiColumn }
showBackButton
2024-08-09 16:56:39 +02:00
appendContent = {
2024-08-22 15:42:02 +02:00
notificationRequests . size > 0 && (
< SelectRow selectionMode = { selectionMode } setSelectionMode = { setSelectionMode } selectAllChecked = { selectAllChecked } toggleSelectAll = { toggleSelectAll } selectedItems = { checkedRequestIds } / >
) }
2024-08-02 16:59:37 +02:00
>
< ColumnSettings / >
< / ColumnHeader >
2024-03-11 16:02:21 +01:00
< ScrollableList
scrollKey = 'notification_requests'
trackScroll = { ! multiColumn }
bindToDocument = { ! multiColumn }
isLoading = { isLoading }
showLoading = { isLoading && notificationRequests . size === 0 }
hasMore = { hasMore }
onLoadMore = { handleLoadMore }
emptyMessage = { < FormattedMessage id = 'empty_column.notification_requests' defaultMessage = 'All clear! There is nothing here. When you receive new notifications, they will appear here according to your settings.' / > }
>
{ notificationRequests . map ( request => (
< NotificationRequest
key = { request . get ( 'id' ) }
id = { request . get ( 'id' ) }
accountId = { request . get ( 'account' ) }
notificationsCount = { request . get ( 'notifications_count' ) }
2024-08-09 16:56:39 +02:00
showCheckbox = { selectionMode }
checked = { checkedRequestIds . includes ( request . get ( 'id' ) ) }
toggleCheck = { handleCheck }
2024-03-11 16:02:21 +01:00
/ >
) ) }
< / ScrollableList >
< Helmet >
< title > { intl . formatMessage ( messages . title ) } < / title >
< meta name = 'robots' content = 'noindex' / >
< / Helmet >
< / Column >
) ;
} ;
NotificationRequests . propTypes = {
multiColumn : PropTypes . bool ,
} ;
export default NotificationRequests ;