268 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			268 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| import PropTypes from 'prop-types';
 | |
| import { useRef, useCallback, useEffect, useState } from 'react';
 | |
| 
 | |
| import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
 | |
| 
 | |
| import { Helmet } from 'react-helmet';
 | |
| 
 | |
| import { useSelector, useDispatch } from 'react-redux';
 | |
| 
 | |
| import ArrowDropDownIcon from '@/material-icons/400-24px/arrow_drop_down.svg?react';
 | |
| import InventoryIcon from '@/material-icons/400-24px/inventory_2.svg?react';
 | |
| import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
 | |
| import { openModal } from 'flavours/glitch/actions/modal';
 | |
| import {
 | |
|   fetchNotificationRequests,
 | |
|   expandNotificationRequests,
 | |
|   acceptNotificationRequests,
 | |
|   dismissNotificationRequests,
 | |
| } from 'flavours/glitch/actions/notification_requests';
 | |
| import { changeSetting } from 'flavours/glitch/actions/settings';
 | |
| import { CheckBox } from 'flavours/glitch/components/check_box';
 | |
| import Column from 'flavours/glitch/components/column';
 | |
| import ColumnHeader from 'flavours/glitch/components/column_header';
 | |
| import { Icon } from 'flavours/glitch/components/icon';
 | |
| import ScrollableList from 'flavours/glitch/components/scrollable_list';
 | |
| import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
 | |
| 
 | |
| import { NotificationRequest } from './components/notification_request';
 | |
| import { PolicyControls } from './components/policy_controls';
 | |
| import SettingToggle from './components/setting_toggle';
 | |
| 
 | |
| const messages = defineMessages({
 | |
|   title: { id: 'notification_requests.title', defaultMessage: 'Filtered notifications' },
 | |
|   maximize: { id: 'notification_requests.maximize', defaultMessage: 'Maximize' },
 | |
|   more: { id: 'status.more', defaultMessage: 'More' },
 | |
|   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}}' },
 | |
| });
 | |
| 
 | |
| 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={
 | |
|               <FormattedMessage id='notification_requests.minimize_banner' defaultMessage='Minimize filtered notifications banner' />
 | |
|             }
 | |
|           />
 | |
|         </div>
 | |
|       </section>
 | |
| 
 | |
|       <PolicyControls />
 | |
|     </div>
 | |
|   );
 | |
| };
 | |
| 
 | |
| const SelectRow = ({selectAllChecked, toggleSelectAll, selectedItems, selectionMode, setSelectionMode}) => {
 | |
|   const intl = useIntl();
 | |
|   const dispatch = useDispatch();
 | |
| 
 | |
|   const selectedCount = selectedItems.length;
 | |
| 
 | |
|   const handleAcceptMultiple = useCallback(() => {
 | |
|     dispatch(openModal({
 | |
|       modalType: 'CONFIRM',
 | |
|       modalProps: {
 | |
|         title: intl.formatMessage(messages.confirmAcceptMultipleTitle),
 | |
|         message: intl.formatMessage(messages.confirmAcceptMultipleMessage, { count: selectedItems.length }),
 | |
|         confirm: intl.formatMessage(messages.confirmAcceptMultipleButton, { count: selectedItems.length}),
 | |
|         onConfirm: () =>
 | |
|           dispatch(acceptNotificationRequests({ ids: selectedItems })),
 | |
|       },
 | |
|     }));
 | |
|   }, [dispatch, intl, selectedItems]);
 | |
| 
 | |
|   const handleDismissMultiple = useCallback(() => {
 | |
|     dispatch(openModal({
 | |
|       modalType: 'CONFIRM',
 | |
|       modalProps: {
 | |
|         title: intl.formatMessage(messages.confirmDismissMultipleTitle),
 | |
|         message: intl.formatMessage(messages.confirmDismissMultipleMessage, { count: selectedItems.length }),
 | |
|         confirm: intl.formatMessage(messages.confirmDismissMultipleButton, { count: selectedItems.length}),
 | |
|         onConfirm: () =>
 | |
|           dispatch(dismissNotificationRequests({ ids: selectedItems })),
 | |
|       },
 | |
|     }));
 | |
|   }, [dispatch, intl, selectedItems]);
 | |
| 
 | |
|   const handleToggleSelectionMode = useCallback(() => {
 | |
|     setSelectionMode((mode) => !mode);
 | |
|   }, [setSelectionMode]);
 | |
| 
 | |
|   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]);
 | |
| 
 | |
|   return (
 | |
|     <div className='column-header__select-row'>
 | |
|       <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'>
 | |
|         <button className='text-btn' tabIndex={0} onClick={handleToggleSelectionMode}>
 | |
|           {selectionMode ? (
 | |
|             <FormattedMessage id='notification_requests.exit_selection' defaultMessage='Done' />
 | |
|           ) :
 | |
|             (
 | |
|               <FormattedMessage id='notification_requests.edit_selection' defaultMessage='Edit' />
 | |
|             )}
 | |
|         </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,
 | |
| };
 | |
| 
 | |
| export const NotificationRequests = ({ multiColumn }) => {
 | |
|   const columnRef = useRef();
 | |
|   const intl = useIntl();
 | |
|   const dispatch = useDispatch();
 | |
|   const isLoading = useSelector(state => state.notificationRequests.isLoading);
 | |
|   const notificationRequests = useSelector(state => state.notificationRequests.items);
 | |
|   const hasMore = useSelector(state => !!state.notificationRequests.next);
 | |
| 
 | |
|   const [selectionMode, setSelectionMode] = useState(false);
 | |
|   const [checkedRequestIds, setCheckedRequestIds] = useState([]);
 | |
|   const [selectAllChecked, setSelectAllChecked] = useState(false);
 | |
| 
 | |
|   const handleHeaderClick = useCallback(() => {
 | |
|     columnRef.current?.scrollTop();
 | |
|   }, [columnRef]);
 | |
| 
 | |
|   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.length);
 | |
| 
 | |
|       return [...ids];
 | |
|     });
 | |
|   }, [setCheckedRequestIds, notificationRequests]);
 | |
| 
 | |
|   const toggleSelectAll = useCallback(() => {
 | |
|     setSelectAllChecked(checked => {
 | |
|       if(checked)
 | |
|         setCheckedRequestIds([]);
 | |
|       else
 | |
|         setCheckedRequestIds(notificationRequests.map(request => request.id));
 | |
| 
 | |
|       return !checked;
 | |
|     });
 | |
|   }, [notificationRequests]);
 | |
| 
 | |
|   const handleLoadMore = useCallback(() => {
 | |
|     dispatch(expandNotificationRequests());
 | |
|   }, [dispatch]);
 | |
| 
 | |
|   useEffect(() => {
 | |
|     dispatch(fetchNotificationRequests());
 | |
|   }, [dispatch]);
 | |
| 
 | |
|   return (
 | |
|     <Column bindToDocument={!multiColumn} ref={columnRef} label={intl.formatMessage(messages.title)}>
 | |
|       <ColumnHeader
 | |
|         icon='archive'
 | |
|         iconComponent={InventoryIcon}
 | |
|         title={intl.formatMessage(messages.title)}
 | |
|         onClick={handleHeaderClick}
 | |
|         multiColumn={multiColumn}
 | |
|         showBackButton
 | |
|         appendContent={
 | |
|           notificationRequests.length > 0 && (
 | |
|             <SelectRow selectionMode={selectionMode} setSelectionMode={setSelectionMode} selectAllChecked={selectAllChecked} toggleSelectAll={toggleSelectAll} selectedItems={checkedRequestIds} />
 | |
|           )}
 | |
|       >
 | |
|         <ColumnSettings />
 | |
|       </ColumnHeader>
 | |
| 
 | |
|       <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.id}
 | |
|             id={request.id}
 | |
|             accountId={request.account_id}
 | |
|             notificationsCount={request.notifications_count}
 | |
|             showCheckbox={selectionMode}
 | |
|             checked={checkedRequestIds.includes(request.id)}
 | |
|             toggleCheck={handleCheck}
 | |
|           />
 | |
|         ))}
 | |
|       </ScrollableList>
 | |
| 
 | |
|       <Helmet>
 | |
|         <title>{intl.formatMessage(messages.title)}</title>
 | |
|         <meta name='robots' content='noindex' />
 | |
|       </Helmet>
 | |
|     </Column>
 | |
|   );
 | |
| };
 | |
| 
 | |
| NotificationRequests.propTypes = {
 | |
|   multiColumn: PropTypes.bool,
 | |
| };
 | |
| 
 | |
| export default NotificationRequests;
 |