diff --git a/assets/images/avatars/deleted-room.svg b/assets/images/avatars/deleted-room.svg new file mode 100644 index 0000000000000..cfce5daab4404 --- /dev/null +++ b/assets/images/avatars/deleted-room.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/components/Avatar.js b/src/components/Avatar.js index 841900b2b1dd2..c3e64dbac2b2a 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -1,8 +1,8 @@ import React, {PureComponent} from 'react'; -import {Image, View, StyleSheet} from 'react-native'; +import {Image, View} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../styles/styles'; -import RoomAvatar from '../../assets/images/avatars/room.svg'; +import RoomAvatar from './RoomAvatar'; const propTypes = { /** Url source for the avatar */ @@ -19,6 +19,9 @@ const propTypes = { /** Whether this avatar is for a default room */ isDefaultChatRoom: PropTypes.bool, + + /** Whether this avatar is for an archived default room */ + isArchivedRoom: PropTypes.bool, }; const defaultProps = { @@ -27,6 +30,7 @@ const defaultProps = { containerStyles: [], size: 'default', isDefaultChatRoom: false, + isArchivedRoom: false, }; class Avatar extends PureComponent { @@ -42,7 +46,7 @@ class Avatar extends PureComponent { return ( {this.props.isDefaultChatRoom - ? + ? : } ); diff --git a/src/components/MultipleAvatars.js b/src/components/MultipleAvatars.js index 079ac370928b4..518cf507a641a 100644 --- a/src/components/MultipleAvatars.js +++ b/src/components/MultipleAvatars.js @@ -18,6 +18,9 @@ const propTypes = { /** Whether this avatar is for a default room */ isDefaultChatRoom: PropTypes.bool, + + /** Whether this avatar is for an archived room */ + isArchivedRoom: PropTypes.bool, }; const defaultProps = { @@ -25,10 +28,11 @@ const defaultProps = { size: 'default', secondAvatarStyle: [styles.secondAvatarHovered], isDefaultChatRoom: false, + isArchivedRoom: false, }; const MultipleAvatars = ({ - avatarImageURLs, size, secondAvatarStyle, isDefaultChatRoom, + avatarImageURLs, size, secondAvatarStyle, isDefaultChatRoom, isArchivedRoom, }) => { const avatarContainerStyles = size === 'small' ? styles.emptyAvatarSmall : styles.emptyAvatar; const singleAvatarStyles = size === 'small' ? styles.singleAvatarSmall : styles.singleAvatar; @@ -44,7 +48,12 @@ const MultipleAvatars = ({ if (avatarImageURLs.length === 1) { return ( - + ); } diff --git a/src/components/RoomAvatar.js b/src/components/RoomAvatar.js new file mode 100644 index 0000000000000..f9675418e542f --- /dev/null +++ b/src/components/RoomAvatar.js @@ -0,0 +1,31 @@ +import React, {PureComponent} from 'react'; +import {StyleSheet} from 'react-native'; +import PropTypes from 'prop-types'; +import ActiveRoomAvatar from '../../assets/images/avatars/room.svg'; +import DeletedRoomAvatar from '../../assets/images/avatars/deleted-room.svg'; + +const propTypes = { + /** Extra styles to pass to Image */ + avatarStyle: PropTypes.arrayOf(PropTypes.object), + + /** Whether the room this avatar is for is deleted or not */ + isArchived: PropTypes.bool, +}; + +const defaultProps = { + avatarStyle: [], + isArchived: false, +}; + +class RoomAvatar extends PureComponent { + render() { + return (this.props.isArchived + ? + : + ); + } +} + +RoomAvatar.defaultProps = defaultProps; +RoomAvatar.propTypes = propTypes; +export default RoomAvatar; diff --git a/src/languages/en.js b/src/languages/en.js index 2c13b708c4045..12e18aecbb5c9 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -30,6 +30,7 @@ export default { privacy: 'Privacy', privacyPolicy: 'Privacy Policy', delete: 'Delete', + deleted: 'deleted', contacts: 'Contacts', recents: 'Recents', close: 'Close', @@ -107,6 +108,7 @@ export default { blockedFromConcierge: 'Communication is barred', youAppearToBeOffline: 'You appear to be offline.', fileUploadFailed: 'Upload Failed. File is not supported.', + roomIsArchived: 'This chat room has been deleted', }, contextMenuItem: { copyToClipboard: 'Copy to Clipboard', diff --git a/src/languages/es.js b/src/languages/es.js index de5b415a1fdf3..6290af5dd0af9 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -26,6 +26,7 @@ export default { and: 'y', details: 'Detalles', delete: 'Eliminar', + deleted: 'eliminado', contacts: 'Contactos', recents: 'Recientes', close: 'Cerrar', @@ -102,6 +103,7 @@ export default { writeSomething: 'Escribe algo...', blockedFromConcierge: 'Comunicación no permitida', youAppearToBeOffline: 'Parece que estás desconectado.', + roomIsArchived: 'Esta sala de chat ha sido eliminada', }, reportActionContextMenu: { copyToClipboard: 'Copiar al Portapapeles', diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 56a2eaa54e9d1..a59adb074c058 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -6,7 +6,9 @@ import lodashOrderBy from 'lodash/orderBy'; import Str from 'expensify-common/lib/str'; import ONYXKEYS from '../ONYXKEYS'; import CONST from '../CONST'; -import {getReportParticipantsTitle, isDefaultRoom, getDefaultRoomSubtitle} from './reportUtils'; +import { + getReportParticipantsTitle, isDefaultRoom, getDefaultRoomSubtitle, isArchivedRoom, +} from './reportUtils'; import {translate} from './translate'; import Permissions from './Permissions'; import md5 from './md5'; @@ -250,6 +252,7 @@ function createOption(personalDetailList, report, draftComments, { isIOUReportOwner: lodashGet(iouReport, 'ownerEmail', '') === currentUserLogin, iouReportAmount: lodashGet(iouReport, 'total', 0), isDefaultChatRoom, + isArchivedRoom: isArchivedRoom(report), }; } diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 75c82c8a3eb5d..a5f583edb08a2 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -19,7 +19,7 @@ import * as API from '../API'; import CONST from '../../CONST'; import Log from '../Log'; import { - isConciergeChatReport, isDefaultRoom, isReportMessageAttachment, sortReportsByLastVisited, + isConciergeChatReport, isDefaultRoom, isReportMessageAttachment, sortReportsByLastVisited, isArchivedRoom, } from '../reportUtils'; import Timers from '../Timers'; import {dangerouslyGetReportActionsMaxSequenceNumber, isReportMissingActions} from './ReportActions'; @@ -138,7 +138,13 @@ function getParticipantEmailsFromReport({sharedReportList}) { */ function getChatReportName(fullReport, chatType) { if (isDefaultRoom({chatType})) { - return `#${fullReport.reportName}`; + return `#${fullReport.reportName}${(isArchivedRoom({ + chatType, + stateNum: fullReport.stateNum, + statusNum: fullReport.reportStatus, + }) + ? ` (${translateLocal('common.deleted')})` + : '')}`; } const {sharedReportList} = fullReport; @@ -181,6 +187,9 @@ function getSimplifiedReportObject(report) { ? lodashGet(report, ['reportNameValuePairs', 'notificationPreferences', currentUserAccountID], 'daily') : ''; + // Used for archived rooms, will store the policy name that the room used to belong to. + const oldPolicyName = lodashGet(report, ['reportNameValuePairs', 'oldPolicyName'], ''); + return { reportID: report.reportID, reportName, @@ -201,6 +210,9 @@ function getSimplifiedReportObject(report) { lastActorEmail, hasOutstandingIOU: false, notificationPreference, + stateNum: report.state, + statusNum: report.status, + oldPolicyName, }; } diff --git a/src/libs/reportUtils.js b/src/libs/reportUtils.js index 45eee67217ada..66f9c4dbae8f6 100644 --- a/src/libs/reportUtils.js +++ b/src/libs/reportUtils.js @@ -102,6 +102,21 @@ function isDefaultRoom(report) { ], lodashGet(report, ['chatType'], '')); } +/** + * Whether the provided report is an archived room + * @param {Object} report + * @param {Number} report.stateNum + * @param {Number} report.statusNum + * @returns {Boolean} + */ +function isArchivedRoom(report) { + if (!isDefaultRoom(report)) { + return false; + } + + return report.statusNum === 2 && report.stateNum === 2; +} + /** * Get either the policyName or domainName the chat is tied to * @param {Object} report @@ -116,6 +131,9 @@ function getDefaultRoomSubtitle(report, policiesMap) { // The domainAll rooms are just #domainName, so we ignore the prefix '#' to get the domainName return report.reportName.substring(1); } + if (isArchivedRoom(report)) { + return report.oldPolicyName; + } return lodashGet( policiesMap, [`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, 'name'], @@ -143,5 +161,6 @@ export { sortReportsByLastVisited, isDefaultRoom, getDefaultRoomSubtitle, + isArchivedRoom, isConciergeChatReport, }; diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 16327accd60a3..38229dc46bfb5 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -15,7 +15,7 @@ import HeaderWithCloseButton from '../components/HeaderWithCloseButton'; import styles from '../styles/styles'; import DisplayNames from '../components/DisplayNames'; import {getPersonalDetailsForLogins} from '../libs/OptionsListUtils'; -import {getDefaultRoomSubtitle, isDefaultRoom} from '../libs/reportUtils'; +import {getDefaultRoomSubtitle, isDefaultRoom, isArchivedRoom} from '../libs/reportUtils'; import {participantPropTypes} from './home/sidebar/optionPropTypes'; import Picker from '../components/Picker'; import {updateNotificationPreference} from '../libs/actions/Report'; @@ -88,14 +88,15 @@ class ReportDetailsPage extends Component { }, }; - this.menuItems = [ - { - translationKey: 'reportDetailsPage.members', - icon: Users, - subtitle: props.report.participants.length, - action: () => { Navigation.navigate(ROUTES.getReportParticipantsRoute(props.report.reportID)); }, - }, - ]; + this.menuItems = isArchivedRoom(this.props.report) ? [] + : [ + { + translationKey: 'reportDetailsPage.members', + icon: Users, + subtitle: props.report.participants.length, + action: () => { Navigation.navigate(ROUTES.getReportParticipantsRoute(props.report.reportID)); }, + }, + ]; } render() { @@ -128,6 +129,7 @@ class ReportDetailsPage extends Component { > - - - {this.props.translate('common.notifications')} - - - - - {this.props.translate('reportDetailsPage.notificationPreferencesDescription')} - - - { - updateNotificationPreference( - this.props.report.reportID, - notificationPreference, - ); - }} - items={Object.values(this.notificationPreferencesOptions)} - value={this.props.report.notificationPreference} - /> + {!isArchivedRoom(this.props.report) && ( + + + + {this.props.translate('common.notifications')} + + + + + {this.props.translate('reportDetailsPage.notificationPreferencesDescription')} + + + { + updateNotificationPreference( + this.props.report.reportID, + notificationPreference, + ); + }} + items={Object.values(this.notificationPreferencesOptions)} + value={this.props.report.notificationPreference} + /> + + - + )} {this.menuItems.map((item) => { const keyTitle = item.translationKey ? this.props.translate(item.translationKey) : item.title; diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index c995a411d9709..c1f4e7d63c006 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -23,7 +23,7 @@ import VideoChatButtonAndMenu from '../../components/VideoChatButtonAndMenu'; import IOUBadge from '../../components/IOUBadge'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import CONST from '../../CONST'; -import {getDefaultRoomSubtitle, isDefaultRoom} from '../../libs/reportUtils'; +import {getDefaultRoomSubtitle, isDefaultRoom, isArchivedRoom} from '../../libs/reportUtils'; import Text from '../../components/Text'; const propTypes = { @@ -122,6 +122,7 @@ const HeaderView = (props) => { avatarImageURLs={props.report.icons} secondAvatarStyle={[styles.secondAvatarHovered]} isDefaultChatRoom={isDefaultChatRoom} + isArchivedRoom={isArchivedRoom(props.report)} /> @@ -555,7 +574,7 @@ class ReportActionCompose extends React.Component { onPasteFile={file => displayFileInModal({file})} shouldClear={this.state.textInputShouldClear} onClear={() => this.setTextInputShouldClear(false)} - isDisabled={isComposeDisabled || isBlockedFromConcierge} + isDisabled={isComposeDisabled || isBlockedFromConcierge || isArchivedChatRoom} selection={this.state.selection} onSelectionChange={this.onSelectionChange} /> @@ -594,7 +613,7 @@ class ReportActionCompose extends React.Component { ref={el => this.emojiPopoverAnchor = el} onLayout={this.measureEmojiPopoverAnchorPosition} onPress={this.showEmojiPicker} - disabled={isBlockedFromConcierge} + disabled={isBlockedFromConcierge || isArchivedChatRoom} > {({hovered, pressed}) => ( diff --git a/src/pages/home/sidebar/OptionRow.js b/src/pages/home/sidebar/OptionRow.js index 08c8bc1e68497..245e646ad2fe1 100644 --- a/src/pages/home/sidebar/OptionRow.js +++ b/src/pages/home/sidebar/OptionRow.js @@ -184,6 +184,7 @@ const OptionRow = ({ : undefined, ]} isDefaultChatRoom={option.isDefaultChatRoom} + isArchivedRoom={option.isArchivedRoom} /> ) }