diff --git a/web/SWMansionIcon.react.js b/web/SWMansionIcon.react.js index d5f95d654..771685f35 100644 --- a/web/SWMansionIcon.react.js +++ b/web/SWMansionIcon.react.js @@ -1,97 +1,312 @@ // @flow import * as React from 'react'; import IcomoonReact from 'react-icomoon'; -import iconSet from './icons/selection.json'; +import iconSet from 'lib/shared/swmansion-icon-config.json'; /* To see all of the icons the application uses and what their names are: - Go to: https://icomoon.io/app/#/select - - Click the import project button, upload the web/icons/AppIcons.json file - and click the load button. + - Click the import project button, upload the + lib/shared/swmansion-icon-config.json file and click the load button. - All of the icons in the selected icons section are used in the app - To see the icon image mapped to the name go to https://icomoon.io/app/#/select/image after going through the steps above */ export type Icon = - | 'arrow-right-small' - | 'bell' - | 'logout' - | 'plus-circle' - | 'users' - | 'chevron-right-small' - | 'reply-arrow' - | 'right-angle-arrow' - | 'plus' - | 'settings' - | 'wrench' - | 'message-filled-round' - | 'bug' - | 'cloud' - | 'copy' - | 'smile' - | 'inbox' - | 'info-circle' - | 'message-circle-line' - | 'question-circle' - | 'search' - | 'key' - | 'chevron-left' + | 'air' + | 'alarm' + | 'arrow-circle-down' + | 'arrow-circle-left' + | 'arrow-circle-right' + | 'arrow-circle-up' + | 'arrow-down' | 'arrow-left' | 'arrow-right' - | 'cross' - | 'edit' - | 'filters' - | 'menu-horizontal' - | 'menu-vertical' - | 'message-square' - | 'message-square-lines' + | 'arrow-small-down' + | 'arrow-small-left' + | 'arrow-small-right' + | 'arrow-small-up' + | 'arrow-up' + | 'at-email' + | 'attachment' + | 'basket' + | 'basketball' | 'bell-disabled' - | 'chevron-right' - | 'send' + | 'bell' + | 'block-1' + | 'block-2' + | 'bolt' + | 'bone-broken' + | 'bone' + | 'bookmark' + | 'calendar-check' + | 'calendar-clock' + | 'calendar-cross' + | 'calendar-edit' + | 'calendar-link' + | 'calendar-lock' + | 'calendar-minus' + | 'calendar-plus' + | 'calendar-user' + | 'calendar-warning' | 'calendar' - | 'message-circle-lines' - | 'image' - | 'upload' - | 'user-circle' - | 'arrow-left-small' + | 'cam-disabled' + | 'cam' + | 'camera-disabled' + | 'camera' + | 'capsule' + | 'cardiology' + | 'cart-1' + | 'cart-2' + | 'cart-3' + | 'cart-4' + | 'cast' + | 'chart-vertical' + | 'chart' + | 'check-circle' + | 'check-small' + | 'check' + | 'chevron-circle-down' + | 'chevron-circle-left' + | 'chevron-circle-right' + | 'chevron-circle-up' + | 'chevron-down' + | 'chevron-left' + | 'chevron-right' + | 'chevron-small-down' + | 'chevron-small-left' + | 'chevron-small-right' + | 'chevron-small-up' + | 'chevron-up' + | 'circle' + | 'clock' + | 'cloud' + | 'coin' + | 'command' + | 'copy' + | 'creditcard' | 'cross-circle' + | 'cross-small' + | 'cross' + | 'crown-1' + | 'crown-2' + | 'cut' + | 'delete' + | 'dislike' + | 'dna' + | 'document-check' | 'document-clean' - | 'globe' + | 'document-cross' + | 'document-filled' + | 'document-minus' + | 'document-plus' + | 'download' + | 'edit-1' + | 'edit-2' + | 'edit-3' + | 'edit-4' + | 'emote-normal' + | 'emote-sad' + | 'emote-smile' + | 'explore' + | 'eye-closed' + | 'eye-open' + | 'faceid' + | 'female' + | 'filters-1' + | 'filters-2' + | 'filters-3' + | 'flag-1' + | 'flag-2' + | 'flag-3' + | 'forward' + | 'fullscreen' + | 'gift' + | 'globe-1' + | 'grid-2-horizontal' + | 'grid-2-vertical' + | 'grid-4' + | 'headphones' + | 'heart' + | 'home-1' + | 'home-2' + | 'home-hospital' + | 'horizontal' + | 'hourglass' + | 'ice' + | 'image-1' + | 'inbox' + | 'info-circle' + | 'info-small' + | 'info' + | 'key' + | 'laptop' + | 'like' | 'link' + | 'list-center' + | 'list-left' + | 'list-pointers' + | 'list-right' + | 'location-1' + | 'location-2' + | 'location-med-1' + | 'location-med-2' + | 'lock-off' | 'lock-on' + | 'login' + | 'logout' | 'mail' + | 'male' + | 'map' + | 'medkit' + | 'meds' + | 'menu-hamburger' + | 'menu-horizontal' + | 'menu-vertical' + | 'message-circle-dots' + | 'message-circle-lines' | 'message-circle' - | 'smart-phone' + | 'message-square-dots' + | 'message-square-lines' + | 'message-square' + | 'microphone-disabled' + | 'microphone' + | 'minpaper-plus' + | 'minus-circle' + | 'minus-small' + | 'minus' + | 'money' + | 'moon' + | 'music' + | 'navigation' + | 'newscreen' + | 'next' + | 'offer' + | 'package' + | 'pause' + | 'phone-call' + | 'phone-cross' + | 'phone-down' + | 'phone' + | 'pin-1' + | 'pin-2' + | 'pinpaper-check' + | 'pinpaper-cross' + | 'pinpaper-filled' + | 'pinpaper-minus' + | 'play' + | 'plus-circle' + | 'plus-small' + | 'plus' + | 'power' + | 'previous' + | 'print' + | 'question-circle' + | 'question-small' + | 'question' + | 'quote' + | 'redo-circle' + | 'redo-small' + | 'redo' + | 'refresh-circle' + | 'refresh-small' + | 'refresh' + | 'resize-circle-horizontal' + | 'resize-circle-vertical' + | 'resize-small-horizontal' + | 'resize-small-vertical' + | 'rewind' + | 'rotate-circle-left' + | 'rotate-circle-right' + | 'rotate-left' + | 'rotate-right' + | 'rotate-small-left' + | 'rotate-small-right' + | 'save' + | 'screen-disabled' + | 'screen-share' + | 'screen' + | 'search' + | 'send-1' + | 'send-2' + | 'settings' + | 'share-1' + | 'share-2' + | 'shield-check' + | 'shield-cross' + | 'shield-empty' + | 'shirt' + | 'smartphone' + | 'sound-0' + | 'sound-1' + | 'sound-2' + | 'speaker-0' + | 'speaker-1' + | 'speaker-2' + | 'speaker-cross' + | 'speaker-disabled' + | 'star-1' + | 'star-2' + | 'stop' + | 'stopwatch' + | 'suitcase' + | 'sun' + | 'syringe' + | 'tag' + | 'test-tube' + | 'tooth' + | 'trash-1' + | 'trash-2' + | 'trending-down' + | 'trending-up' + | 'trophy' + | 'umbrella-1' + | 'umbrella-2' + | 'undo-circle' + | 'undo-small' + | 'undo' + | 'upload' + | 'user-1' + | 'user-2' + | 'user-check' + | 'user-cross' + | 'user-info' + | 'user-minus' | 'user-plus' + | 'user-question' + | 'user-warning' + | 'users-more' + | 'users' + | 'vertical' + | 'virus' + | 'wallet' + | 'wand' | 'warning-circle' - | 'dino-avatar' - | 'laptop' - | 'list-filled' - | 'reply-chat-bubble' - | 'all-notifs' - | 'badge-notifs' - | 'muted-notifs' - | 'check' - | 'user-cross'; + | 'warning-small' + | 'warning' + | 'waterdrop' + | 'windows' + | 'zoom-in' + | 'zoom-out'; type SWMansionIconProps = { +icon: Icon, +size: number | string, +color?: string, +title?: string, +className?: string, +disableFill?: boolean, +removeInlineStyle?: boolean, +style?: $Shape, }; function SWMansionIcon(props: SWMansionIconProps): React.Node { return ; } export default SWMansionIcon; diff --git a/web/chat/chat-input-bar.react.js b/web/chat/chat-input-bar.react.js index 8f0b3fcb5..65e4dc195 100644 --- a/web/chat/chat-input-bar.react.js +++ b/web/chat/chat-input-bar.react.js @@ -1,483 +1,483 @@ // @flow import invariant from 'invariant'; import _difference from 'lodash/fp/difference'; import * as React from 'react'; import { joinThreadActionTypes, joinThread, newThreadActionTypes, } from 'lib/actions/thread-actions'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors'; import { localIDPrefix, trimMessage } from 'lib/shared/message-utils'; import { threadHasPermission, viewerIsMember, threadFrozenDueToViewerBlock, threadActualMembers, checkIfDefaultMembersAreVoiced, } from 'lib/shared/thread-utils'; import type { CalendarQuery } from 'lib/types/entry-types'; import type { LoadingStatus } from 'lib/types/loading-types'; import { messageTypes } from 'lib/types/message-types'; import { type ThreadInfo, threadPermissions, type ClientThreadJoinRequest, type ThreadJoinPayload, } from 'lib/types/thread-types'; import { type UserInfos } from 'lib/types/user-types'; import { type DispatchActionPromise, useServerCall, useDispatchActionPromise, } from 'lib/utils/action-utils'; import { type InputState, type PendingMultimediaUpload, } from '../input/input-state'; import LoadingIndicator from '../loading-indicator.react'; import { allowedMimeTypeString } from '../media/file-utils'; import Multimedia from '../media/multimedia.react'; import { useSelector } from '../redux/redux-utils'; import { nonThreadCalendarQuery } from '../selectors/nav-selectors'; import SWMansionIcon from '../SWMansionIcon.react'; import css from './chat-input-bar.css'; type BaseProps = { +threadInfo: ThreadInfo, +inputState: InputState, }; type Props = { ...BaseProps, // Redux state +viewerID: ?string, +joinThreadLoadingStatus: LoadingStatus, +threadCreationInProgress: boolean, +calendarQuery: () => CalendarQuery, +nextLocalID: number, +isThreadActive: boolean, +userInfos: UserInfos, // Redux dispatch functions +dispatchActionPromise: DispatchActionPromise, // async functions that hit server APIs +joinThread: (request: ClientThreadJoinRequest) => Promise, }; class ChatInputBar extends React.PureComponent { textarea: ?HTMLTextAreaElement; multimediaInput: ?HTMLInputElement; componentDidMount() { this.updateHeight(); if (this.props.isThreadActive) { this.addReplyListener(); } } componentWillUnmount() { if (this.props.isThreadActive) { this.removeReplyListener(); } } componentDidUpdate(prevProps: Props) { if (this.props.isThreadActive && !prevProps.isThreadActive) { this.addReplyListener(); } else if (!this.props.isThreadActive && prevProps.isThreadActive) { this.removeReplyListener(); } const { inputState } = this.props; const prevInputState = prevProps.inputState; if (inputState.draft !== prevInputState.draft) { this.updateHeight(); } const curUploadIDs = ChatInputBar.unassignedUploadIDs( inputState.pendingUploads, ); const prevUploadIDs = ChatInputBar.unassignedUploadIDs( prevInputState.pendingUploads, ); if ( this.multimediaInput && _difference(prevUploadIDs)(curUploadIDs).length > 0 ) { // Whenever a pending upload is removed, we reset the file // HTMLInputElement's value field, so that if the same upload occurs again // the onChange call doesn't get filtered this.multimediaInput.value = ''; } else if ( this.textarea && _difference(curUploadIDs)(prevUploadIDs).length > 0 ) { // Whenever a pending upload is added, we focus the textarea this.textarea.focus(); return; } if (this.props.threadInfo.id !== prevProps.threadInfo.id && this.textarea) { this.textarea.focus(); } } static unassignedUploadIDs( pendingUploads: $ReadOnlyArray, ) { return pendingUploads .filter( (pendingUpload: PendingMultimediaUpload) => !pendingUpload.messageID, ) .map((pendingUpload: PendingMultimediaUpload) => pendingUpload.localID); } updateHeight() { const textarea = this.textarea; if (textarea) { textarea.style.height = 'auto'; const newHeight = Math.min(textarea.scrollHeight, 150); textarea.style.height = `${newHeight}px`; } } addReplyListener() { invariant( this.props.inputState, 'inputState should be set in addReplyListener', ); this.props.inputState.addReplyListener(this.focusAndUpdateText); } removeReplyListener() { invariant( this.props.inputState, 'inputState should be set in removeReplyListener', ); this.props.inputState.removeReplyListener(this.focusAndUpdateText); } render() { const isMember = viewerIsMember(this.props.threadInfo); const canJoin = threadHasPermission( this.props.threadInfo, threadPermissions.JOIN_THREAD, ); let joinButton = null; if (!isMember && canJoin && !this.props.threadCreationInProgress) { let buttonContent; if (this.props.joinThreadLoadingStatus === 'loading') { buttonContent = ( ); } else { buttonContent = ( <>

Join Chat

); } joinButton = (
); } const { pendingUploads, cancelPendingUpload } = this.props.inputState; const multimediaPreviews = pendingUploads.map(pendingUpload => ( )); const previews = multimediaPreviews.length > 0 ? (
{multimediaPreviews}
) : null; let content; // If the thread is created by somebody else while the viewer is attempting // to create it, the threadInfo might be modified in-place and won't // list the viewer as a member, which will end up hiding the input. In // this case, we will assume that our creation action will get translated, // into a join and as long as members are voiced, we can show the input. const defaultMembersAreVoiced = checkIfDefaultMembersAreVoiced( this.props.threadInfo, ); let sendButton; if (this.props.inputState.draft.length) { sendButton = ( ); } if ( threadHasPermission(this.props.threadInfo, threadPermissions.VOICED) || (this.props.threadCreationInProgress && defaultMembersAreVoiced) ) { content = (