Default disappearing message timeout fixes
This commit is contained in:
		
					parent
					
						
							
								c9415dcf67
							
						
					
				
			
			
				commit
				
					
						cd28e71bc6
					
				
			
		
					 25 changed files with 456 additions and 164 deletions
				
			
		|  | @ -1805,7 +1805,7 @@ | ||||||
|   }, |   }, | ||||||
|   "disappearingMessages": { |   "disappearingMessages": { | ||||||
|     "message": "Disappearing messages", |     "message": "Disappearing messages", | ||||||
|     "description": "Conversation menu option to enable disappearing messages. Title of the settings section for Disappearing Messages" |     "description": "Conversation menu option to enable disappearing messages. Title of the settings section for Disappearing Messages. Label of the disappearing timer select in group creation flow" | ||||||
|   }, |   }, | ||||||
|   "disappearingMessagesDisabled": { |   "disappearingMessagesDisabled": { | ||||||
|     "message": "Disappearing messages disabled", |     "message": "Disappearing messages disabled", | ||||||
|  |  | ||||||
|  | @ -56,7 +56,7 @@ const { | ||||||
| } = require('../../ts/components/conversation/StagedLinkPreview'); | } = require('../../ts/components/conversation/StagedLinkPreview'); | ||||||
| const { | const { | ||||||
|   DisappearingTimeDialog, |   DisappearingTimeDialog, | ||||||
| } = require('../../ts/components/conversation/DisappearingTimeDialog'); | } = require('../../ts/components/DisappearingTimeDialog'); | ||||||
| 
 | 
 | ||||||
| // State
 | // State
 | ||||||
| const { createTimeline } = require('../../ts/state/roots/createTimeline'); | const { createTimeline } = require('../../ts/state/roots/createTimeline'); | ||||||
|  |  | ||||||
|  | @ -2366,64 +2366,9 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05', | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Module: Timer Notification | // Module: Timer Notification | ||||||
| 
 |  | ||||||
| .module-timer-notification { |  | ||||||
|   text-align: center; |  | ||||||
| 
 |  | ||||||
|   @include light-theme { |  | ||||||
|     color: $color-gray-60; |  | ||||||
|   } |  | ||||||
|   @include dark-theme { |  | ||||||
|     color: $color-gray-05; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .module-timer-notification__icon-container { |  | ||||||
|   margin-left: auto; |  | ||||||
|   margin-right: auto; |  | ||||||
|   display: inline-flex; |  | ||||||
|   flex-direction: row; |  | ||||||
|   align-items: center; |  | ||||||
|   margin-bottom: 4px; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .module-timer-notification__icon { |  | ||||||
|   height: 20px; |  | ||||||
|   width: 20px; |  | ||||||
|   display: inline-block; |  | ||||||
| 
 |  | ||||||
|   @include light-theme { |  | ||||||
|     @include color-svg('../images/icons/v2/timer-24.svg', $color-gray-60); |  | ||||||
|   } |  | ||||||
|   @include dark-theme { |  | ||||||
|     @include color-svg('../images/icons/v2/timer-24.svg', $color-gray-05); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .module-timer-notification__icon--disabled { |  | ||||||
|   @include light-theme { |  | ||||||
|     @include color-svg( |  | ||||||
|       '../images/icons/v2/timer-disabled-24.svg', |  | ||||||
|       $color-gray-60 |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|   @include dark-theme { |  | ||||||
|     @include color-svg( |  | ||||||
|       '../images/icons/v2/timer-disabled-24.svg', |  | ||||||
|       $color-gray-05 |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .module-timer-notification__icon-label { |  | ||||||
|   margin-left: 6px; |  | ||||||
| 
 |  | ||||||
|   // Didn't seem centered otherwise |  | ||||||
|   margin-top: 1px; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Module: Universal Timer Notification | // Module: Universal Timer Notification | ||||||
| 
 | 
 | ||||||
|  | .module-timer-notification, | ||||||
| .module-universal-timer-notification { | .module-universal-timer-notification { | ||||||
|   text-align: center; |   text-align: center; | ||||||
| 
 | 
 | ||||||
|  | @ -2433,6 +2378,53 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05', | ||||||
|   @include dark-theme { |   @include dark-theme { | ||||||
|     color: $color-gray-05; |     color: $color-gray-05; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   &__icon-container { | ||||||
|  |     margin-left: auto; | ||||||
|  |     margin-right: auto; | ||||||
|  |     display: inline-flex; | ||||||
|  |     flex-direction: row; | ||||||
|  |     align-items: center; | ||||||
|  |     margin-bottom: 8px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &__icon { | ||||||
|  |     height: 20px; | ||||||
|  |     width: 20px; | ||||||
|  |     display: inline-block; | ||||||
|  |     opacity: 0.6; | ||||||
|  | 
 | ||||||
|  |     @include light-theme { | ||||||
|  |       @include color-svg('../images/icons/v2/timer-24.svg', $color-gray-60); | ||||||
|  |     } | ||||||
|  |     @include dark-theme { | ||||||
|  |       @include color-svg('../images/icons/v2/timer-24.svg', $color-gray-05); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &--disabled { | ||||||
|  |       @include light-theme { | ||||||
|  |         @include color-svg( | ||||||
|  |           '../images/icons/v2/timer-disabled-24.svg', | ||||||
|  |           $color-gray-60 | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |       @include dark-theme { | ||||||
|  |         @include color-svg( | ||||||
|  |           '../images/icons/v2/timer-disabled-24.svg', | ||||||
|  |           $color-gray-05 | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &__icon-label { | ||||||
|  |     margin-left: 4px; | ||||||
|  | 
 | ||||||
|  |     // Didn't seem centered otherwise | ||||||
|  |     margin-top: 1px; | ||||||
|  | 
 | ||||||
|  |     opacity: 0.8; | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .module-notification--with-click-handler { | .module-notification--with-click-handler { | ||||||
|  | @ -3070,8 +3062,7 @@ button.module-conversation-details__action-button { | ||||||
|       margin-right: 12px; |       margin-right: 12px; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     &__info, |     &__info { | ||||||
|     &__right-info { |  | ||||||
|       @include font-body-2; |       @include font-body-2; | ||||||
|       margin-top: 4px; |       margin-top: 4px; | ||||||
| 
 | 
 | ||||||
|  | @ -3090,14 +3081,6 @@ button.module-conversation-details__action-button { | ||||||
|       min-width: 143px; |       min-width: 143px; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     &__right-info { |  | ||||||
|       position: absolute; |  | ||||||
| 
 |  | ||||||
|       @include font-subtitle; |  | ||||||
| 
 |  | ||||||
|       padding-left: 14px; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     &__actions { |     &__actions { | ||||||
|       margin-left: 12px; |       margin-left: 12px; | ||||||
|       overflow: hidden; |       overflow: hidden; | ||||||
|  | @ -7005,6 +6988,21 @@ button.module-image__border-overlay:focus { | ||||||
|   &__form { |   &__form { | ||||||
|     display: flex; |     display: flex; | ||||||
|     flex-direction: column; |     flex-direction: column; | ||||||
|  | 
 | ||||||
|  |     &__expire-timer { | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: row; | ||||||
|  | 
 | ||||||
|  |       margin: 0 16px 16px 16px; | ||||||
|  | 
 | ||||||
|  |       &__label { | ||||||
|  |         margin-right: 12px; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .module-disappearing-timer-select { | ||||||
|  |         width: 144px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										23
									
								
								stylesheets/components/DisappearingTimerSelect.scss
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								stylesheets/components/DisappearingTimerSelect.scss
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | ||||||
|  | // Copyright 2021 Signal Messenger, LLC | ||||||
|  | // SPDX-License-Identifier: AGPL-3.0-only | ||||||
|  | 
 | ||||||
|  | .module-disappearing-timer-select { | ||||||
|  |   position: relative; | ||||||
|  | 
 | ||||||
|  |   &__info { | ||||||
|  |     position: absolute; | ||||||
|  | 
 | ||||||
|  |     margin-top: 4px; | ||||||
|  |     padding-left: 14px; | ||||||
|  | 
 | ||||||
|  |     @include font-subtitle; | ||||||
|  | 
 | ||||||
|  |     @include light-theme { | ||||||
|  |       color: $color-gray-60; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @include dark-theme { | ||||||
|  |       color: $color-gray-25; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -43,6 +43,7 @@ | ||||||
| @import './components/ConversationHeader.scss'; | @import './components/ConversationHeader.scss'; | ||||||
| @import './components/CustomColorEditor.scss'; | @import './components/CustomColorEditor.scss'; | ||||||
| @import './components/DisappearingTimeDialog.scss'; | @import './components/DisappearingTimeDialog.scss'; | ||||||
|  | @import './components/DisappearingTimerSelect.scss'; | ||||||
| @import './components/EditConversationAttributesModal.scss'; | @import './components/EditConversationAttributesModal.scss'; | ||||||
| @import './components/ForwardMessageModal.scss'; | @import './components/ForwardMessageModal.scss'; | ||||||
| @import './components/GradientDial.scss'; | @import './components/GradientDial.scss'; | ||||||
|  |  | ||||||
|  | @ -6,10 +6,10 @@ import { action } from '@storybook/addon-actions'; | ||||||
| import { storiesOf } from '@storybook/react'; | import { storiesOf } from '@storybook/react'; | ||||||
| 
 | 
 | ||||||
| import { DisappearingTimeDialog } from './DisappearingTimeDialog'; | import { DisappearingTimeDialog } from './DisappearingTimeDialog'; | ||||||
| import { setup as setupI18n } from '../../../js/modules/i18n'; | import { setup as setupI18n } from '../../js/modules/i18n'; | ||||||
| import enMessages from '../../../_locales/en/messages.json'; | import enMessages from '../../_locales/en/messages.json'; | ||||||
| 
 | 
 | ||||||
| import { EXPIRE_TIMERS } from '../../test-both/util/expireTimers'; | import { EXPIRE_TIMERS } from '../test-both/util/expireTimers'; | ||||||
| 
 | 
 | ||||||
| const story = storiesOf('Components/DisappearingTimeDialog', module); | const story = storiesOf('Components/DisappearingTimeDialog', module); | ||||||
| 
 | 
 | ||||||
|  | @ -4,10 +4,10 @@ | ||||||
| 
 | 
 | ||||||
| import React, { useState } from 'react'; | import React, { useState } from 'react'; | ||||||
| 
 | 
 | ||||||
| import { ConfirmationDialog } from '../ConfirmationDialog'; | import { ConfirmationDialog } from './ConfirmationDialog'; | ||||||
| import { Select } from '../Select'; | import { Select } from './Select'; | ||||||
| import { LocalizerType } from '../../types/Util'; | import { LocalizerType } from '../types/Util'; | ||||||
| import { Theme } from '../../util/theme'; | import { Theme } from '../util/theme'; | ||||||
| 
 | 
 | ||||||
| const CSS_MODULE = 'module-disappearing-time-dialog'; | const CSS_MODULE = 'module-disappearing-time-dialog'; | ||||||
| 
 | 
 | ||||||
							
								
								
									
										37
									
								
								ts/components/DisappearingTimerSelect.stories.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								ts/components/DisappearingTimerSelect.stories.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | // Copyright 2021 Signal Messenger, LLC
 | ||||||
|  | // SPDX-License-Identifier: AGPL-3.0-only
 | ||||||
|  | 
 | ||||||
|  | import React, { useState } from 'react'; | ||||||
|  | import { storiesOf } from '@storybook/react'; | ||||||
|  | 
 | ||||||
|  | import { DisappearingTimerSelect } from './DisappearingTimerSelect'; | ||||||
|  | import { setup as setupI18n } from '../../js/modules/i18n'; | ||||||
|  | import enMessages from '../../_locales/en/messages.json'; | ||||||
|  | 
 | ||||||
|  | const story = storiesOf('Components/DisappearingTimerSelect', module); | ||||||
|  | 
 | ||||||
|  | const i18n = setupI18n('en', enMessages); | ||||||
|  | 
 | ||||||
|  | type Props = { | ||||||
|  |   initialValue: number; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const TimerSelectWrap: React.FC<Props> = ({ initialValue }) => { | ||||||
|  |   const [value, setValue] = useState(initialValue); | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <DisappearingTimerSelect | ||||||
|  |       i18n={i18n} | ||||||
|  |       value={value} | ||||||
|  |       onChange={newValue => setValue(newValue)} | ||||||
|  |     /> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | story.add('Initial value: 1 day', () => ( | ||||||
|  |   <TimerSelectWrap initialValue={24 * 3600} /> | ||||||
|  | )); | ||||||
|  | 
 | ||||||
|  | story.add('Initial value 3 days (Custom time)', () => ( | ||||||
|  |   <TimerSelectWrap initialValue={3 * 24 * 3600} /> | ||||||
|  | )); | ||||||
							
								
								
									
										106
									
								
								ts/components/DisappearingTimerSelect.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								ts/components/DisappearingTimerSelect.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,106 @@ | ||||||
|  | // Copyright 2021 Signal Messenger, LLC
 | ||||||
|  | // SPDX-License-Identifier: AGPL-3.0-only
 | ||||||
|  | 
 | ||||||
|  | import React, { useState, ReactNode } from 'react'; | ||||||
|  | import classNames from 'classnames'; | ||||||
|  | 
 | ||||||
|  | import { LocalizerType } from '../types/Util'; | ||||||
|  | import * as expirationTimer from '../util/expirationTimer'; | ||||||
|  | import { DisappearingTimeDialog } from './DisappearingTimeDialog'; | ||||||
|  | 
 | ||||||
|  | import { Select } from './Select'; | ||||||
|  | 
 | ||||||
|  | const CSS_MODULE = 'module-disappearing-timer-select'; | ||||||
|  | 
 | ||||||
|  | export type Props = { | ||||||
|  |   i18n: LocalizerType; | ||||||
|  | 
 | ||||||
|  |   value?: number; | ||||||
|  |   onChange(value: number): void; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const DisappearingTimerSelect: React.FC<Props> = (props: Props) => { | ||||||
|  |   const { i18n, value = 0, onChange } = props; | ||||||
|  | 
 | ||||||
|  |   const [isModalOpen, setIsModalOpen] = useState(false); | ||||||
|  | 
 | ||||||
|  |   let expirationTimerOptions: ReadonlyArray<{ | ||||||
|  |     readonly value: number; | ||||||
|  |     readonly text: string; | ||||||
|  |   }> = expirationTimer.DEFAULT_DURATIONS_IN_SECONDS.map(seconds => { | ||||||
|  |     const text = expirationTimer.format(i18n, seconds, { | ||||||
|  |       capitalizeOff: true, | ||||||
|  |     }); | ||||||
|  |     return { | ||||||
|  |       value: seconds, | ||||||
|  |       text, | ||||||
|  |     }; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   const isCustomTimeSelected = !expirationTimer.DEFAULT_DURATIONS_SET.has( | ||||||
|  |     value | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   const onSelectChange = (newValue: string) => { | ||||||
|  |     const intValue = parseInt(newValue, 10); | ||||||
|  |     if (intValue === -1) { | ||||||
|  |       setIsModalOpen(true); | ||||||
|  |     } else { | ||||||
|  |       onChange(intValue); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // Custom time...
 | ||||||
|  |   expirationTimerOptions = [ | ||||||
|  |     ...expirationTimerOptions, | ||||||
|  |     { | ||||||
|  |       value: -1, | ||||||
|  |       text: i18n( | ||||||
|  |         isCustomTimeSelected | ||||||
|  |           ? 'selectedCustomDisappearingTimeOption' | ||||||
|  |           : 'customDisappearingTimeOption' | ||||||
|  |       ), | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | 
 | ||||||
|  |   let modalNode: ReactNode = null; | ||||||
|  |   if (isModalOpen) { | ||||||
|  |     modalNode = ( | ||||||
|  |       <DisappearingTimeDialog | ||||||
|  |         i18n={i18n} | ||||||
|  |         initialValue={value} | ||||||
|  |         onSubmit={newValue => { | ||||||
|  |           setIsModalOpen(false); | ||||||
|  |           onChange(newValue); | ||||||
|  |         }} | ||||||
|  |         onClose={() => setIsModalOpen(false)} | ||||||
|  |       /> | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   let info: ReactNode; | ||||||
|  |   if (isCustomTimeSelected) { | ||||||
|  |     info = ( | ||||||
|  |       <div className={`${CSS_MODULE}__info`}> | ||||||
|  |         {expirationTimer.format(i18n, value)} | ||||||
|  |       </div> | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div | ||||||
|  |       className={classNames( | ||||||
|  |         CSS_MODULE, | ||||||
|  |         isCustomTimeSelected ? `${CSS_MODULE}--custom-time` : false | ||||||
|  |       )} | ||||||
|  |     > | ||||||
|  |       <Select | ||||||
|  |         onChange={onSelectChange} | ||||||
|  |         value={isCustomTimeSelected ? -1 : value} | ||||||
|  |         options={expirationTimerOptions} | ||||||
|  |       /> | ||||||
|  |       {info} | ||||||
|  |       {modalNode} | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | @ -126,6 +126,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({ | ||||||
|   setComposeSearchTerm: action('setComposeSearchTerm'), |   setComposeSearchTerm: action('setComposeSearchTerm'), | ||||||
|   setComposeGroupAvatar: action('setComposeGroupAvatar'), |   setComposeGroupAvatar: action('setComposeGroupAvatar'), | ||||||
|   setComposeGroupName: action('setComposeGroupName'), |   setComposeGroupName: action('setComposeGroupName'), | ||||||
|  |   setComposeGroupExpireTimer: action('setComposeGroupExpireTimer'), | ||||||
|   showArchivedConversations: action('showArchivedConversations'), |   showArchivedConversations: action('showArchivedConversations'), | ||||||
|   showInbox: action('showInbox'), |   showInbox: action('showInbox'), | ||||||
|   startComposing: action('startComposing'), |   startComposing: action('startComposing'), | ||||||
|  | @ -514,3 +515,53 @@ story.add('Captcha dialog: pending', () => ( | ||||||
|     })} |     })} | ||||||
|   /> |   /> | ||||||
| )); | )); | ||||||
|  | 
 | ||||||
|  | // Set group metadata
 | ||||||
|  | 
 | ||||||
|  | story.add('Group Metadata: No Timer', () => ( | ||||||
|  |   <LeftPane | ||||||
|  |     {...createProps({ | ||||||
|  |       modeSpecificProps: { | ||||||
|  |         mode: LeftPaneMode.SetGroupMetadata, | ||||||
|  |         groupAvatar: undefined, | ||||||
|  |         groupName: 'Group 1', | ||||||
|  |         groupExpireTimer: 0, | ||||||
|  |         hasError: false, | ||||||
|  |         isCreating: false, | ||||||
|  |         selectedContacts: defaultConversations, | ||||||
|  |       }, | ||||||
|  |     })} | ||||||
|  |   /> | ||||||
|  | )); | ||||||
|  | 
 | ||||||
|  | story.add('Group Metadata: Regular Timer', () => ( | ||||||
|  |   <LeftPane | ||||||
|  |     {...createProps({ | ||||||
|  |       modeSpecificProps: { | ||||||
|  |         mode: LeftPaneMode.SetGroupMetadata, | ||||||
|  |         groupAvatar: undefined, | ||||||
|  |         groupName: 'Group 1', | ||||||
|  |         groupExpireTimer: 24 * 3600, | ||||||
|  |         hasError: false, | ||||||
|  |         isCreating: false, | ||||||
|  |         selectedContacts: defaultConversations, | ||||||
|  |       }, | ||||||
|  |     })} | ||||||
|  |   /> | ||||||
|  | )); | ||||||
|  | 
 | ||||||
|  | story.add('Group Metadata: Custom Timer', () => ( | ||||||
|  |   <LeftPane | ||||||
|  |     {...createProps({ | ||||||
|  |       modeSpecificProps: { | ||||||
|  |         mode: LeftPaneMode.SetGroupMetadata, | ||||||
|  |         groupAvatar: undefined, | ||||||
|  |         groupName: 'Group 1', | ||||||
|  |         groupExpireTimer: 7 * 3600, | ||||||
|  |         hasError: false, | ||||||
|  |         isCreating: false, | ||||||
|  |         selectedContacts: defaultConversations, | ||||||
|  |       }, | ||||||
|  |     })} | ||||||
|  |   /> | ||||||
|  | )); | ||||||
|  |  | ||||||
|  | @ -98,6 +98,7 @@ export type PropsType = { | ||||||
|   setComposeSearchTerm: (composeSearchTerm: string) => void; |   setComposeSearchTerm: (composeSearchTerm: string) => void; | ||||||
|   setComposeGroupAvatar: (_: undefined | ArrayBuffer) => void; |   setComposeGroupAvatar: (_: undefined | ArrayBuffer) => void; | ||||||
|   setComposeGroupName: (_: string) => void; |   setComposeGroupName: (_: string) => void; | ||||||
|  |   setComposeGroupExpireTimer: (_: number) => void; | ||||||
|   showArchivedConversations: () => void; |   showArchivedConversations: () => void; | ||||||
|   showInbox: () => void; |   showInbox: () => void; | ||||||
|   startComposing: () => void; |   startComposing: () => void; | ||||||
|  | @ -139,6 +140,7 @@ export const LeftPane: React.FC<PropsType> = ({ | ||||||
|   setComposeSearchTerm, |   setComposeSearchTerm, | ||||||
|   setComposeGroupAvatar, |   setComposeGroupAvatar, | ||||||
|   setComposeGroupName, |   setComposeGroupName, | ||||||
|  |   setComposeGroupExpireTimer, | ||||||
|   showArchivedConversations, |   showArchivedConversations, | ||||||
|   showInbox, |   showInbox, | ||||||
|   startComposing, |   startComposing, | ||||||
|  | @ -342,6 +344,7 @@ export const LeftPane: React.FC<PropsType> = ({ | ||||||
|     i18n, |     i18n, | ||||||
|     setComposeGroupAvatar, |     setComposeGroupAvatar, | ||||||
|     setComposeGroupName, |     setComposeGroupName, | ||||||
|  |     setComposeGroupExpireTimer, | ||||||
|     onChangeComposeSearchTerm: event => { |     onChangeComposeSearchTerm: event => { | ||||||
|       setComposeSearchTerm(event.target.value); |       setComposeSearchTerm(event.target.value); | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ import { | ||||||
| } from 'react-contextmenu'; | } from 'react-contextmenu'; | ||||||
| 
 | 
 | ||||||
| import { Emojify } from './Emojify'; | import { Emojify } from './Emojify'; | ||||||
| import { DisappearingTimeDialog } from './DisappearingTimeDialog'; | import { DisappearingTimeDialog } from '../DisappearingTimeDialog'; | ||||||
| import { Avatar, AvatarSize } from '../Avatar'; | import { Avatar, AvatarSize } from '../Avatar'; | ||||||
| import { InContactsIcon } from '../InContactsIcon'; | import { InContactsIcon } from '../InContactsIcon'; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -11,6 +11,8 @@ export type Props = { | ||||||
|   expireTimer: number; |   expireTimer: number; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | const CSS_MODULE = 'module-universal-timer-notification'; | ||||||
|  | 
 | ||||||
| export const UniversalTimerNotification: React.FC<Props> = props => { | export const UniversalTimerNotification: React.FC<Props> = props => { | ||||||
|   const { i18n, expireTimer } = props; |   const { i18n, expireTimer } = props; | ||||||
| 
 | 
 | ||||||
|  | @ -18,11 +20,19 @@ export const UniversalTimerNotification: React.FC<Props> = props => { | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   const timeValue = expirationTimer.format(i18n, expireTimer); | ||||||
|  | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div className="module-universal-timer-notification"> |     <div className={CSS_MODULE}> | ||||||
|       {i18n('UniversalTimerNotification__text', { |       <div className={`${CSS_MODULE}__icon-container`}> | ||||||
|         timeValue: expirationTimer.format(i18n, expireTimer), |         <div className={`${CSS_MODULE}__icon`} /> | ||||||
|       })} |         <div className={`${CSS_MODULE}__icon-label`}>{timeValue}</div> | ||||||
|  |       </div> | ||||||
|  |       <div className={`${CSS_MODULE}__message`}> | ||||||
|  |         {i18n('UniversalTimerNotification__text', { | ||||||
|  |           timeValue, | ||||||
|  |         })} | ||||||
|  |       </div> | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -5,15 +5,12 @@ import React, { useState, ReactNode } from 'react'; | ||||||
| 
 | 
 | ||||||
| import { ConversationType } from '../../../state/ducks/conversations'; | import { ConversationType } from '../../../state/ducks/conversations'; | ||||||
| import { assert } from '../../../util/assert'; | import { assert } from '../../../util/assert'; | ||||||
| import * as expirationTimer from '../../../util/expirationTimer'; |  | ||||||
| 
 | 
 | ||||||
| import { LocalizerType } from '../../../types/Util'; | import { LocalizerType } from '../../../types/Util'; | ||||||
| import { MediaItemType } from '../../LightboxGallery'; | import { MediaItemType } from '../../LightboxGallery'; | ||||||
| import { missingCaseError } from '../../../util/missingCaseError'; | import { missingCaseError } from '../../../util/missingCaseError'; | ||||||
| 
 | 
 | ||||||
| import { Select } from '../../Select'; | import { DisappearingTimerSelect } from '../../DisappearingTimerSelect'; | ||||||
| 
 |  | ||||||
| import { DisappearingTimeDialog } from '../DisappearingTimeDialog'; |  | ||||||
| 
 | 
 | ||||||
| import { PanelRow } from './PanelRow'; | import { PanelRow } from './PanelRow'; | ||||||
| import { PanelSection } from './PanelSection'; | import { PanelSection } from './PanelSection'; | ||||||
|  | @ -39,7 +36,6 @@ enum ModalState { | ||||||
|   EditingGroupDescription, |   EditingGroupDescription, | ||||||
|   EditingGroupTitle, |   EditingGroupTitle, | ||||||
|   AddingGroupMembers, |   AddingGroupMembers, | ||||||
|   CustomDisappearingTimeout, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export type StateProps = { | export type StateProps = { | ||||||
|  | @ -114,15 +110,6 @@ export const ConversationDetails: React.ComponentType<Props> = ({ | ||||||
|     setAddGroupMembersRequestState, |     setAddGroupMembersRequestState, | ||||||
|   ] = useState<RequestState>(RequestState.Inactive); |   ] = useState<RequestState>(RequestState.Inactive); | ||||||
| 
 | 
 | ||||||
|   const updateExpireTimer = (value: string) => { |  | ||||||
|     const intValue = parseInt(value, 10); |  | ||||||
|     if (intValue === -1) { |  | ||||||
|       setModalState(ModalState.CustomDisappearingTimeout); |  | ||||||
|     } else { |  | ||||||
|       setDisappearingMessages(intValue); |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   if (conversation === undefined) { |   if (conversation === undefined) { | ||||||
|     throw new Error('ConversationDetails rendered without a conversation'); |     throw new Error('ConversationDetails rendered without a conversation'); | ||||||
|   } |   } | ||||||
|  | @ -218,55 +205,10 @@ export const ConversationDetails: React.ComponentType<Props> = ({ | ||||||
|         /> |         /> | ||||||
|       ); |       ); | ||||||
|       break; |       break; | ||||||
|     case ModalState.CustomDisappearingTimeout: |  | ||||||
|       modalNode = ( |  | ||||||
|         <DisappearingTimeDialog |  | ||||||
|           i18n={i18n} |  | ||||||
|           initialValue={conversation.expireTimer} |  | ||||||
|           onSubmit={value => { |  | ||||||
|             setModalState(ModalState.NothingOpen); |  | ||||||
|             setDisappearingMessages(value); |  | ||||||
|           }} |  | ||||||
|           onClose={() => setModalState(ModalState.NothingOpen)} |  | ||||||
|         /> |  | ||||||
|       ); |  | ||||||
|       break; |  | ||||||
|     default: |     default: | ||||||
|       throw missingCaseError(modalState); |       throw missingCaseError(modalState); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const expireTimer: number = conversation.expireTimer || 0; |  | ||||||
| 
 |  | ||||||
|   let expirationTimerOptions: ReadonlyArray<{ |  | ||||||
|     readonly value: number; |  | ||||||
|     readonly text: string; |  | ||||||
|   }> = expirationTimer.DEFAULT_DURATIONS_IN_SECONDS.map(seconds => { |  | ||||||
|     const text = expirationTimer.format(i18n, seconds, { |  | ||||||
|       capitalizeOff: true, |  | ||||||
|     }); |  | ||||||
|     return { |  | ||||||
|       value: seconds, |  | ||||||
|       text, |  | ||||||
|     }; |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   const isCustomTimeSelected = !expirationTimer.DEFAULT_DURATIONS_SET.has( |  | ||||||
|     expireTimer |  | ||||||
|   ); |  | ||||||
| 
 |  | ||||||
|   // Custom time...
 |  | ||||||
|   expirationTimerOptions = [ |  | ||||||
|     ...expirationTimerOptions, |  | ||||||
|     { |  | ||||||
|       value: -1, |  | ||||||
|       text: i18n( |  | ||||||
|         isCustomTimeSelected |  | ||||||
|           ? 'selectedCustomDisappearingTimeOption' |  | ||||||
|           : 'customDisappearingTimeOption' |  | ||||||
|       ), |  | ||||||
|     }, |  | ||||||
|   ]; |  | ||||||
| 
 |  | ||||||
|   return ( |   return ( | ||||||
|     <div className="conversation-details-panel"> |     <div className="conversation-details-panel"> | ||||||
|       <ConversationDetailsHeader |       <ConversationDetailsHeader | ||||||
|  | @ -297,17 +239,12 @@ export const ConversationDetails: React.ComponentType<Props> = ({ | ||||||
|             info={i18n('ConversationDetails--disappearing-messages-info')} |             info={i18n('ConversationDetails--disappearing-messages-info')} | ||||||
|             label={i18n('ConversationDetails--disappearing-messages-label')} |             label={i18n('ConversationDetails--disappearing-messages-label')} | ||||||
|             right={ |             right={ | ||||||
|               <Select |               <DisappearingTimerSelect | ||||||
|                 onChange={updateExpireTimer} |                 i18n={i18n} | ||||||
|                 value={isCustomTimeSelected ? -1 : expireTimer} |                 value={conversation.expireTimer || 0} | ||||||
|                 options={expirationTimerOptions} |                 onChange={setDisappearingMessages} | ||||||
|               /> |               /> | ||||||
|             } |             } | ||||||
|             rightInfo={ |  | ||||||
|               isCustomTimeSelected |  | ||||||
|                 ? expirationTimer.format(i18n, expireTimer) |  | ||||||
|                 : undefined |  | ||||||
|             } |  | ||||||
|           /> |           /> | ||||||
|         ) : null} |         ) : null} | ||||||
|         <PanelRow |         <PanelRow | ||||||
|  |  | ||||||
|  | @ -13,7 +13,6 @@ export type Props = { | ||||||
|   label: string | React.ReactNode; |   label: string | React.ReactNode; | ||||||
|   info?: string; |   info?: string; | ||||||
|   right?: string | React.ReactNode; |   right?: string | React.ReactNode; | ||||||
|   rightInfo?: string; |  | ||||||
|   actions?: React.ReactNode; |   actions?: React.ReactNode; | ||||||
|   onClick?: () => void; |   onClick?: () => void; | ||||||
| }; | }; | ||||||
|  | @ -28,7 +27,6 @@ export const PanelRow: React.ComponentType<Props> = ({ | ||||||
|   label, |   label, | ||||||
|   info, |   info, | ||||||
|   right, |   right, | ||||||
|   rightInfo, |  | ||||||
|   actions, |   actions, | ||||||
|   onClick, |   onClick, | ||||||
| }) => { | }) => { | ||||||
|  | @ -39,14 +37,7 @@ export const PanelRow: React.ComponentType<Props> = ({ | ||||||
|         <div>{label}</div> |         <div>{label}</div> | ||||||
|         {info !== undefined ? <div className={bem('info')}>{info}</div> : null} |         {info !== undefined ? <div className={bem('info')}>{info}</div> : null} | ||||||
|       </div> |       </div> | ||||||
|       {right !== undefined ? ( |       {right !== undefined ? <div className={bem('right')}>{right}</div> : null} | ||||||
|         <div className={bem('right')}> |  | ||||||
|           {right} |  | ||||||
|           {rightInfo !== undefined ? ( |  | ||||||
|             <div className={bem('right-info')}>{rightInfo}</div> |  | ||||||
|           ) : null} |  | ||||||
|         </div> |  | ||||||
|       ) : null} |  | ||||||
|       {actions !== undefined ? ( |       {actions !== undefined ? ( | ||||||
|         <div className={alwaysShowActions ? '' : bem('actions')}>{actions}</div> |         <div className={alwaysShowActions ? '' : bem('actions')}>{actions}</div> | ||||||
|       ) : null} |       ) : null} | ||||||
|  |  | ||||||
|  | @ -50,6 +50,7 @@ export abstract class LeftPaneHelper<T> { | ||||||
|       i18n: LocalizerType; |       i18n: LocalizerType; | ||||||
|       setComposeGroupAvatar: (_: undefined | ArrayBuffer) => unknown; |       setComposeGroupAvatar: (_: undefined | ArrayBuffer) => unknown; | ||||||
|       setComposeGroupName: (_: string) => unknown; |       setComposeGroupName: (_: string) => unknown; | ||||||
|  |       setComposeGroupExpireTimer: (_: number) => void; | ||||||
|       onChangeComposeSearchTerm: ( |       onChangeComposeSearchTerm: ( | ||||||
|         event: ChangeEvent<HTMLInputElement> |         event: ChangeEvent<HTMLInputElement> | ||||||
|       ) => unknown; |       ) => unknown; | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ import React, { ReactChild } from 'react'; | ||||||
| import { LeftPaneHelper } from './LeftPaneHelper'; | import { LeftPaneHelper } from './LeftPaneHelper'; | ||||||
| import { Row, RowType } from '../ConversationList'; | import { Row, RowType } from '../ConversationList'; | ||||||
| import { PropsDataType as ContactListItemPropsType } from '../conversationList/ContactListItem'; | import { PropsDataType as ContactListItemPropsType } from '../conversationList/ContactListItem'; | ||||||
|  | import { DisappearingTimerSelect } from '../DisappearingTimerSelect'; | ||||||
| import { LocalizerType } from '../../types/Util'; | import { LocalizerType } from '../../types/Util'; | ||||||
| import { AvatarInput } from '../AvatarInput'; | import { AvatarInput } from '../AvatarInput'; | ||||||
| import { Alert } from '../Alert'; | import { Alert } from '../Alert'; | ||||||
|  | @ -16,6 +17,7 @@ import { GroupTitleInput } from '../GroupTitleInput'; | ||||||
| export type LeftPaneSetGroupMetadataPropsType = { | export type LeftPaneSetGroupMetadataPropsType = { | ||||||
|   groupAvatar: undefined | ArrayBuffer; |   groupAvatar: undefined | ArrayBuffer; | ||||||
|   groupName: string; |   groupName: string; | ||||||
|  |   groupExpireTimer: number; | ||||||
|   hasError: boolean; |   hasError: boolean; | ||||||
|   isCreating: boolean; |   isCreating: boolean; | ||||||
|   selectedContacts: ReadonlyArray<ContactListItemPropsType>; |   selectedContacts: ReadonlyArray<ContactListItemPropsType>; | ||||||
|  | @ -28,6 +30,8 @@ export class LeftPaneSetGroupMetadataHelper extends LeftPaneHelper<LeftPaneSetGr | ||||||
| 
 | 
 | ||||||
|   private readonly groupName: string; |   private readonly groupName: string; | ||||||
| 
 | 
 | ||||||
|  |   private readonly groupExpireTimer: number; | ||||||
|  | 
 | ||||||
|   private readonly hasError: boolean; |   private readonly hasError: boolean; | ||||||
| 
 | 
 | ||||||
|   private readonly isCreating: boolean; |   private readonly isCreating: boolean; | ||||||
|  | @ -37,6 +41,7 @@ export class LeftPaneSetGroupMetadataHelper extends LeftPaneHelper<LeftPaneSetGr | ||||||
|   constructor({ |   constructor({ | ||||||
|     groupAvatar, |     groupAvatar, | ||||||
|     groupName, |     groupName, | ||||||
|  |     groupExpireTimer, | ||||||
|     isCreating, |     isCreating, | ||||||
|     hasError, |     hasError, | ||||||
|     selectedContacts, |     selectedContacts, | ||||||
|  | @ -45,6 +50,7 @@ export class LeftPaneSetGroupMetadataHelper extends LeftPaneHelper<LeftPaneSetGr | ||||||
| 
 | 
 | ||||||
|     this.groupAvatar = groupAvatar; |     this.groupAvatar = groupAvatar; | ||||||
|     this.groupName = groupName; |     this.groupName = groupName; | ||||||
|  |     this.groupExpireTimer = groupExpireTimer; | ||||||
|     this.hasError = hasError; |     this.hasError = hasError; | ||||||
|     this.isCreating = isCreating; |     this.isCreating = isCreating; | ||||||
|     this.selectedContacts = selectedContacts; |     this.selectedContacts = selectedContacts; | ||||||
|  | @ -89,12 +95,14 @@ export class LeftPaneSetGroupMetadataHelper extends LeftPaneHelper<LeftPaneSetGr | ||||||
|     createGroup, |     createGroup, | ||||||
|     i18n, |     i18n, | ||||||
|     setComposeGroupAvatar, |     setComposeGroupAvatar, | ||||||
|  |     setComposeGroupExpireTimer, | ||||||
|     setComposeGroupName, |     setComposeGroupName, | ||||||
|   }: Readonly<{ |   }: Readonly<{ | ||||||
|     clearGroupCreationError: () => unknown; |     clearGroupCreationError: () => unknown; | ||||||
|     createGroup: () => unknown; |     createGroup: () => unknown; | ||||||
|     i18n: LocalizerType; |     i18n: LocalizerType; | ||||||
|     setComposeGroupAvatar: (_: undefined | ArrayBuffer) => unknown; |     setComposeGroupAvatar: (_: undefined | ArrayBuffer) => unknown; | ||||||
|  |     setComposeGroupExpireTimer: (_: number) => void; | ||||||
|     setComposeGroupName: (_: string) => unknown; |     setComposeGroupName: (_: string) => unknown; | ||||||
|   }>): ReactChild { |   }>): ReactChild { | ||||||
|     const disabled = this.isCreating; |     const disabled = this.isCreating; | ||||||
|  | @ -128,6 +136,17 @@ export class LeftPaneSetGroupMetadataHelper extends LeftPaneHelper<LeftPaneSetGr | ||||||
|           value={this.groupName} |           value={this.groupName} | ||||||
|         /> |         /> | ||||||
| 
 | 
 | ||||||
|  |         <section className="module-left-pane__header__form__expire-timer"> | ||||||
|  |           <div className="module-left-pane__header__form__expire-timer__label"> | ||||||
|  |             {i18n('disappearingMessages')} | ||||||
|  |           </div> | ||||||
|  |           <DisappearingTimerSelect | ||||||
|  |             i18n={i18n} | ||||||
|  |             value={this.groupExpireTimer} | ||||||
|  |             onChange={setComposeGroupExpireTimer} | ||||||
|  |           /> | ||||||
|  |         </section> | ||||||
|  | 
 | ||||||
|         {this.hasError && ( |         {this.hasError && ( | ||||||
|           <Alert |           <Alert | ||||||
|             body={i18n('setGroupMetadata__error-message')} |             body={i18n('setGroupMetadata__error-message')} | ||||||
|  |  | ||||||
|  | @ -48,7 +48,6 @@ import { | ||||||
|   getClientZkGroupCipher, |   getClientZkGroupCipher, | ||||||
|   getClientZkProfileOperations, |   getClientZkProfileOperations, | ||||||
| } from './util/zkgroup'; | } from './util/zkgroup'; | ||||||
| import * as universalExpireTimer from './util/universalExpireTimer'; |  | ||||||
| import { | import { | ||||||
|   computeHash, |   computeHash, | ||||||
|   deriveMasterKeyFromGroupV1, |   deriveMasterKeyFromGroupV1, | ||||||
|  | @ -1477,10 +1476,12 @@ export async function fetchMembershipProof({ | ||||||
| export async function createGroupV2({ | export async function createGroupV2({ | ||||||
|   name, |   name, | ||||||
|   avatar, |   avatar, | ||||||
|  |   expireTimer, | ||||||
|   conversationIds, |   conversationIds, | ||||||
| }: Readonly<{ | }: Readonly<{ | ||||||
|   name: string; |   name: string; | ||||||
|   avatar: undefined | ArrayBuffer; |   avatar: undefined | ArrayBuffer; | ||||||
|  |   expireTimer: undefined | number; | ||||||
|   conversationIds: Array<string>; |   conversationIds: Array<string>; | ||||||
| }>): Promise<ConversationModel> { | }>): Promise<ConversationModel> { | ||||||
|   // Ensure we have the credentials we need before attempting GroupsV2 operations
 |   // Ensure we have the credentials we need before attempting GroupsV2 operations
 | ||||||
|  | @ -1709,7 +1710,6 @@ export async function createGroupV2({ | ||||||
|   window.MessageController.register(model.id, model); |   window.MessageController.register(model.id, model); | ||||||
|   conversation.trigger('newmessage', model); |   conversation.trigger('newmessage', model); | ||||||
| 
 | 
 | ||||||
|   const expireTimer = universalExpireTimer.get(); |  | ||||||
|   if (expireTimer) { |   if (expireTimer) { | ||||||
|     await conversation.updateExpirationTimer(expireTimer); |     await conversation.updateExpirationTimer(expireTimer); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ import * as groups from '../../groups'; | ||||||
| import { calling } from '../../services/calling'; | import { calling } from '../../services/calling'; | ||||||
| import { getOwn } from '../../util/getOwn'; | import { getOwn } from '../../util/getOwn'; | ||||||
| import { assert } from '../../util/assert'; | import { assert } from '../../util/assert'; | ||||||
|  | import * as universalExpireTimer from '../../util/universalExpireTimer'; | ||||||
| import { trigger } from '../../shims/events'; | import { trigger } from '../../shims/events'; | ||||||
| 
 | 
 | ||||||
| import { | import { | ||||||
|  | @ -208,9 +209,9 @@ export type PreJoinConversationType = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export enum ComposerStep { | export enum ComposerStep { | ||||||
|   StartDirectConversation, |   StartDirectConversation = 'StartDirectConversation', | ||||||
|   ChooseGroupMembers, |   ChooseGroupMembers = 'ChooseGroupMembers', | ||||||
|   SetGroupMetadata, |   SetGroupMetadata = 'SetGroupMetadata', | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export enum OneTimeModalState { | export enum OneTimeModalState { | ||||||
|  | @ -222,6 +223,7 @@ export enum OneTimeModalState { | ||||||
| type ComposerGroupCreationState = { | type ComposerGroupCreationState = { | ||||||
|   groupAvatar: undefined | ArrayBuffer; |   groupAvatar: undefined | ArrayBuffer; | ||||||
|   groupName: string; |   groupName: string; | ||||||
|  |   groupExpireTimer: number; | ||||||
|   maximumGroupSizeModalState: OneTimeModalState; |   maximumGroupSizeModalState: OneTimeModalState; | ||||||
|   recommendedGroupSizeModalState: OneTimeModalState; |   recommendedGroupSizeModalState: OneTimeModalState; | ||||||
|   selectedConversationIds: Array<string>; |   selectedConversationIds: Array<string>; | ||||||
|  | @ -557,6 +559,10 @@ type SetComposeGroupNameActionType = { | ||||||
|   type: 'SET_COMPOSE_GROUP_NAME'; |   type: 'SET_COMPOSE_GROUP_NAME'; | ||||||
|   payload: { groupName: string }; |   payload: { groupName: string }; | ||||||
| }; | }; | ||||||
|  | type SetComposeGroupExpireTimerActionType = { | ||||||
|  |   type: 'SET_COMPOSE_GROUP_EXPIRE_TIMER'; | ||||||
|  |   payload: { groupExpireTimer: number }; | ||||||
|  | }; | ||||||
| type SetComposeSearchTermActionType = { | type SetComposeSearchTermActionType = { | ||||||
|   type: 'SET_COMPOSE_SEARCH_TERM'; |   type: 'SET_COMPOSE_SEARCH_TERM'; | ||||||
|   payload: { searchTerm: string }; |   payload: { searchTerm: string }; | ||||||
|  | @ -625,6 +631,7 @@ export type ConversationActionType = | ||||||
|   | SelectedConversationChangedActionType |   | SelectedConversationChangedActionType | ||||||
|   | SetComposeGroupAvatarActionType |   | SetComposeGroupAvatarActionType | ||||||
|   | SetComposeGroupNameActionType |   | SetComposeGroupNameActionType | ||||||
|  |   | SetComposeGroupExpireTimerActionType | ||||||
|   | SetComposeSearchTermActionType |   | SetComposeSearchTermActionType | ||||||
|   | SetConversationHeaderTitleActionType |   | SetConversationHeaderTitleActionType | ||||||
|   | SetIsNearBottomActionType |   | SetIsNearBottomActionType | ||||||
|  | @ -679,6 +686,7 @@ export const actions = { | ||||||
|   selectMessage, |   selectMessage, | ||||||
|   setComposeGroupAvatar, |   setComposeGroupAvatar, | ||||||
|   setComposeGroupName, |   setComposeGroupName, | ||||||
|  |   setComposeGroupExpireTimer, | ||||||
|   setComposeSearchTerm, |   setComposeSearchTerm, | ||||||
|   setIsNearBottom, |   setIsNearBottom, | ||||||
|   setLoadCountdownStart, |   setLoadCountdownStart, | ||||||
|  | @ -903,6 +911,7 @@ function createGroup(): ThunkAction< | ||||||
|       const conversation = await groups.createGroupV2({ |       const conversation = await groups.createGroupV2({ | ||||||
|         name: composer.groupName.trim(), |         name: composer.groupName.trim(), | ||||||
|         avatar: composer.groupAvatar, |         avatar: composer.groupAvatar, | ||||||
|  |         expireTimer: composer.groupExpireTimer, | ||||||
|         conversationIds: composer.selectedConversationIds, |         conversationIds: composer.selectedConversationIds, | ||||||
|       }); |       }); | ||||||
|       dispatch({ |       dispatch({ | ||||||
|  | @ -1192,6 +1201,15 @@ function setComposeGroupName(groupName: string): SetComposeGroupNameActionType { | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | function setComposeGroupExpireTimer( | ||||||
|  |   groupExpireTimer: number | ||||||
|  | ): SetComposeGroupExpireTimerActionType { | ||||||
|  |   return { | ||||||
|  |     type: 'SET_COMPOSE_GROUP_EXPIRE_TIMER', | ||||||
|  |     payload: { groupExpireTimer }, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| function setComposeSearchTerm( | function setComposeSearchTerm( | ||||||
|   searchTerm: string |   searchTerm: string | ||||||
| ): SetComposeSearchTermActionType { | ): SetComposeSearchTermActionType { | ||||||
|  | @ -2346,6 +2364,7 @@ export function reducer( | ||||||
|     let maximumGroupSizeModalState: OneTimeModalState; |     let maximumGroupSizeModalState: OneTimeModalState; | ||||||
|     let groupName: string; |     let groupName: string; | ||||||
|     let groupAvatar: undefined | ArrayBuffer; |     let groupAvatar: undefined | ArrayBuffer; | ||||||
|  |     let groupExpireTimer: number; | ||||||
| 
 | 
 | ||||||
|     switch (state.composer?.step) { |     switch (state.composer?.step) { | ||||||
|       case ComposerStep.ChooseGroupMembers: |       case ComposerStep.ChooseGroupMembers: | ||||||
|  | @ -2357,6 +2376,7 @@ export function reducer( | ||||||
|           maximumGroupSizeModalState, |           maximumGroupSizeModalState, | ||||||
|           groupName, |           groupName, | ||||||
|           groupAvatar, |           groupAvatar, | ||||||
|  |           groupExpireTimer, | ||||||
|         } = state.composer); |         } = state.composer); | ||||||
|         break; |         break; | ||||||
|       default: |       default: | ||||||
|  | @ -2364,6 +2384,7 @@ export function reducer( | ||||||
|         recommendedGroupSizeModalState = OneTimeModalState.NeverShown; |         recommendedGroupSizeModalState = OneTimeModalState.NeverShown; | ||||||
|         maximumGroupSizeModalState = OneTimeModalState.NeverShown; |         maximumGroupSizeModalState = OneTimeModalState.NeverShown; | ||||||
|         groupName = ''; |         groupName = ''; | ||||||
|  |         groupExpireTimer = universalExpireTimer.get(); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -2379,6 +2400,7 @@ export function reducer( | ||||||
|         maximumGroupSizeModalState, |         maximumGroupSizeModalState, | ||||||
|         groupName, |         groupName, | ||||||
|         groupAvatar, |         groupAvatar, | ||||||
|  |         groupExpireTimer, | ||||||
|       }, |       }, | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
|  | @ -2398,6 +2420,7 @@ export function reducer( | ||||||
|             ...pick(composer, [ |             ...pick(composer, [ | ||||||
|               'groupAvatar', |               'groupAvatar', | ||||||
|               'groupName', |               'groupName', | ||||||
|  |               'groupExpireTimer', | ||||||
|               'maximumGroupSizeModalState', |               'maximumGroupSizeModalState', | ||||||
|               'recommendedGroupSizeModalState', |               'recommendedGroupSizeModalState', | ||||||
|               'selectedConversationIds', |               'selectedConversationIds', | ||||||
|  | @ -2453,6 +2476,25 @@ export function reducer( | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   if (action.type === 'SET_COMPOSE_GROUP_EXPIRE_TIMER') { | ||||||
|  |     const { composer } = state; | ||||||
|  | 
 | ||||||
|  |     switch (composer?.step) { | ||||||
|  |       case ComposerStep.ChooseGroupMembers: | ||||||
|  |       case ComposerStep.SetGroupMetadata: | ||||||
|  |         return { | ||||||
|  |           ...state, | ||||||
|  |           composer: { | ||||||
|  |             ...composer, | ||||||
|  |             groupExpireTimer: action.payload.groupExpireTimer, | ||||||
|  |           }, | ||||||
|  |         }; | ||||||
|  |       default: | ||||||
|  |         assert(false, 'Setting compose group name at this step is a no-op'); | ||||||
|  |         return state; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   if (action.type === 'SET_COMPOSE_SEARCH_TERM') { |   if (action.type === 'SET_COMPOSE_SEARCH_TERM') { | ||||||
|     const { composer } = state; |     const { composer } = state; | ||||||
|     if (!composer) { |     if (!composer) { | ||||||
|  |  | ||||||
|  | @ -518,6 +518,7 @@ const getGroupCreationComposerState = createSelector( | ||||||
|   ): { |   ): { | ||||||
|     groupName: string; |     groupName: string; | ||||||
|     groupAvatar: undefined | ArrayBuffer; |     groupAvatar: undefined | ArrayBuffer; | ||||||
|  |     groupExpireTimer: number; | ||||||
|     selectedConversationIds: Array<string>; |     selectedConversationIds: Array<string>; | ||||||
|   } => { |   } => { | ||||||
|     switch (composerState?.step) { |     switch (composerState?.step) { | ||||||
|  | @ -532,6 +533,7 @@ const getGroupCreationComposerState = createSelector( | ||||||
|         return { |         return { | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|           selectedConversationIds: [], |           selectedConversationIds: [], | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
|  | @ -548,6 +550,11 @@ export const getComposeGroupName = createSelector( | ||||||
|   (composerState): string => composerState.groupName |   (composerState): string => composerState.groupName | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
|  | export const getComposeGroupExpireTimer = createSelector( | ||||||
|  |   getGroupCreationComposerState, | ||||||
|  |   (composerState): number => composerState.groupExpireTimer | ||||||
|  | ); | ||||||
|  | 
 | ||||||
| export const getComposeSelectedContacts = createSelector( | export const getComposeSelectedContacts = createSelector( | ||||||
|   getConversationLookup, |   getConversationLookup, | ||||||
|   getGroupCreationComposerState, |   getGroupCreationComposerState, | ||||||
|  |  | ||||||
|  | @ -23,6 +23,7 @@ import { | ||||||
|   getFilteredComposeGroups, |   getFilteredComposeGroups, | ||||||
|   getComposeGroupAvatar, |   getComposeGroupAvatar, | ||||||
|   getComposeGroupName, |   getComposeGroupName, | ||||||
|  |   getComposeGroupExpireTimer, | ||||||
|   getComposeSelectedContacts, |   getComposeSelectedContacts, | ||||||
|   getComposerConversationSearchTerm, |   getComposerConversationSearchTerm, | ||||||
|   getComposerStep, |   getComposerStep, | ||||||
|  | @ -129,6 +130,7 @@ const getModeSpecificProps = ( | ||||||
|         mode: LeftPaneMode.SetGroupMetadata, |         mode: LeftPaneMode.SetGroupMetadata, | ||||||
|         groupAvatar: getComposeGroupAvatar(state), |         groupAvatar: getComposeGroupAvatar(state), | ||||||
|         groupName: getComposeGroupName(state), |         groupName: getComposeGroupName(state), | ||||||
|  |         groupExpireTimer: getComposeGroupExpireTimer(state), | ||||||
|         hasError: hasGroupCreationError(state), |         hasError: hasGroupCreationError(state), | ||||||
|         isCreating: isCreatingGroup(state), |         isCreating: isCreatingGroup(state), | ||||||
|         selectedContacts: getComposeSelectedContacts(state), |         selectedContacts: getComposeSelectedContacts(state), | ||||||
|  |  | ||||||
|  | @ -325,6 +325,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|           }, |           }, | ||||||
|         }, |         }, | ||||||
|       }; |       }; | ||||||
|  | @ -346,6 +347,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             isCreating: false, |             isCreating: false, | ||||||
|             hasError: false as const, |             hasError: false as const, | ||||||
|           }, |           }, | ||||||
|  | @ -388,6 +390,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|               maximumGroupSizeModalState: OneTimeModalState.NeverShown, |               maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|               groupName: '', |               groupName: '', | ||||||
|               groupAvatar: undefined, |               groupAvatar: undefined, | ||||||
|  |               groupExpireTimer: 0, | ||||||
|               isCreating: false as const, |               isCreating: false as const, | ||||||
|               hasError: false as const, |               hasError: false as const, | ||||||
|             }, |             }, | ||||||
|  | @ -409,6 +412,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|               maximumGroupSizeModalState: OneTimeModalState.NeverShown, |               maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|               groupName: '', |               groupName: '', | ||||||
|               groupAvatar: undefined, |               groupAvatar: undefined, | ||||||
|  |               groupExpireTimer: 0, | ||||||
|               isCreating: false as const, |               isCreating: false as const, | ||||||
|               hasError: true as const, |               hasError: true as const, | ||||||
|             }, |             }, | ||||||
|  | @ -449,6 +453,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|               maximumGroupSizeModalState: OneTimeModalState.NeverShown, |               maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|               groupName: '', |               groupName: '', | ||||||
|               groupAvatar: undefined, |               groupAvatar: undefined, | ||||||
|  |               groupExpireTimer: 0, | ||||||
|               isCreating: false as const, |               isCreating: false as const, | ||||||
|               hasError: true as const, |               hasError: true as const, | ||||||
|             }, |             }, | ||||||
|  | @ -470,6 +475,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|               maximumGroupSizeModalState: OneTimeModalState.NeverShown, |               maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|               groupName: '', |               groupName: '', | ||||||
|               groupAvatar: undefined, |               groupAvatar: undefined, | ||||||
|  |               groupExpireTimer: 0, | ||||||
|               isCreating: true as const, |               isCreating: true as const, | ||||||
|               hasError: false as const, |               hasError: false as const, | ||||||
|             }, |             }, | ||||||
|  | @ -1080,6 +1086,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|           }, |           }, | ||||||
|         }, |         }, | ||||||
|         user: { |         user: { | ||||||
|  | @ -1135,6 +1142,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|               cantAddContactIdForModal: undefined, |               cantAddContactIdForModal: undefined, | ||||||
|               searchTerm: '', |               searchTerm: '', | ||||||
|               groupAvatar: undefined, |               groupAvatar: undefined, | ||||||
|  |               groupExpireTimer: 0, | ||||||
|               groupName: '', |               groupName: '', | ||||||
|               maximumGroupSizeModalState: OneTimeModalState.NeverShown, |               maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|               recommendedGroupSizeModalState: OneTimeModalState.NeverShown, |               recommendedGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|  | @ -1159,6 +1167,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|               cantAddContactIdForModal: 'abc123', |               cantAddContactIdForModal: 'abc123', | ||||||
|               searchTerm: '', |               searchTerm: '', | ||||||
|               groupAvatar: undefined, |               groupAvatar: undefined, | ||||||
|  |               groupExpireTimer: 0, | ||||||
|               groupName: '', |               groupName: '', | ||||||
|               maximumGroupSizeModalState: OneTimeModalState.NeverShown, |               maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|               recommendedGroupSizeModalState: OneTimeModalState.NeverShown, |               recommendedGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|  | @ -1596,6 +1605,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|             cantAddContactIdForModal: undefined, |             cantAddContactIdForModal: undefined, | ||||||
|             searchTerm: 'to be cleared', |             searchTerm: 'to be cleared', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.Showing, |             maximumGroupSizeModalState: OneTimeModalState.Showing, | ||||||
|             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, |             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|  | @ -1621,6 +1631,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|             cantAddContactIdForModal: undefined, |             cantAddContactIdForModal: undefined, | ||||||
|             searchTerm: 'to be cleared', |             searchTerm: 'to be cleared', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             recommendedGroupSizeModalState: OneTimeModalState.Showing, |             recommendedGroupSizeModalState: OneTimeModalState.Showing, | ||||||
|  | @ -1650,6 +1661,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             isCreating: false, |             isCreating: false, | ||||||
|             hasError: false as const, |             hasError: false as const, | ||||||
|           }, |           }, | ||||||
|  | @ -1671,6 +1683,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: new Uint8Array([1, 2, 3]).buffer, |             groupAvatar: new Uint8Array([1, 2, 3]).buffer, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             isCreating: false, |             isCreating: false, | ||||||
|             hasError: false as const, |             hasError: false as const, | ||||||
|           }, |           }, | ||||||
|  | @ -1697,6 +1710,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: 'foo bar', |             groupName: 'foo bar', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             isCreating: false, |             isCreating: false, | ||||||
|             hasError: false as const, |             hasError: false as const, | ||||||
|           }, |           }, | ||||||
|  | @ -1730,6 +1744,7 @@ describe('both/state/selectors/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: 'foo bar', |             groupName: 'foo bar', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             isCreating: false, |             isCreating: false, | ||||||
|             hasError: false as const, |             hasError: false as const, | ||||||
|           }, |           }, | ||||||
|  |  | ||||||
|  | @ -454,6 +454,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             cantAddContactIdForModal: undefined, |             cantAddContactIdForModal: undefined, | ||||||
|             searchTerm: '', |             searchTerm: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, |             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|  | @ -482,6 +483,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             isCreating: false as const, |             isCreating: false as const, | ||||||
|             hasError: true as const, |             hasError: true as const, | ||||||
|           }, |           }, | ||||||
|  | @ -517,6 +519,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             cantAddContactIdForModal: 'abc123', |             cantAddContactIdForModal: 'abc123', | ||||||
|             searchTerm: '', |             searchTerm: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, |             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|  | @ -567,6 +570,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             cantAddContactIdForModal: 'abc123', |             cantAddContactIdForModal: 'abc123', | ||||||
|             searchTerm: '', |             searchTerm: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.Showing, |             maximumGroupSizeModalState: OneTimeModalState.Showing, | ||||||
|             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, |             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|  | @ -592,6 +596,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             cantAddContactIdForModal: 'abc123', |             cantAddContactIdForModal: 'abc123', | ||||||
|             searchTerm: '', |             searchTerm: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, |             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|  | @ -612,6 +617,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             cantAddContactIdForModal: 'abc123', |             cantAddContactIdForModal: 'abc123', | ||||||
|             searchTerm: '', |             searchTerm: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.Shown, |             maximumGroupSizeModalState: OneTimeModalState.Shown, | ||||||
|             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, |             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|  | @ -634,6 +640,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             cantAddContactIdForModal: 'abc123', |             cantAddContactIdForModal: 'abc123', | ||||||
|             searchTerm: '', |             searchTerm: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             recommendedGroupSizeModalState: OneTimeModalState.Showing, |             recommendedGroupSizeModalState: OneTimeModalState.Showing, | ||||||
|  | @ -659,6 +666,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             cantAddContactIdForModal: 'abc123', |             cantAddContactIdForModal: 'abc123', | ||||||
|             searchTerm: '', |             searchTerm: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, |             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|  | @ -679,6 +687,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             cantAddContactIdForModal: 'abc123', |             cantAddContactIdForModal: 'abc123', | ||||||
|             searchTerm: '', |             searchTerm: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             recommendedGroupSizeModalState: OneTimeModalState.Shown, |             recommendedGroupSizeModalState: OneTimeModalState.Shown, | ||||||
|  | @ -703,6 +712,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|           maximumGroupSizeModalState: OneTimeModalState.NeverShown, |           maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|           groupName: 'Foo Bar Group', |           groupName: 'Foo Bar Group', | ||||||
|           groupAvatar: new Uint8Array([1, 2, 3]).buffer, |           groupAvatar: new Uint8Array([1, 2, 3]).buffer, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|           isCreating: false as const, |           isCreating: false as const, | ||||||
|           hasError: true as const, |           hasError: true as const, | ||||||
|         }, |         }, | ||||||
|  | @ -748,6 +758,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|         sinon.assert.calledWith(createGroupStub, { |         sinon.assert.calledWith(createGroupStub, { | ||||||
|           name: 'Foo Bar Group', |           name: 'Foo Bar Group', | ||||||
|           avatar: new Uint8Array([1, 2, 3]).buffer, |           avatar: new Uint8Array([1, 2, 3]).buffer, | ||||||
|  |           expireTimer: 0, | ||||||
|           conversationIds: ['abc123'], |           conversationIds: ['abc123'], | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
|  | @ -1205,6 +1216,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: 'foo', |             groupName: 'foo', | ||||||
|             groupAvatar: new ArrayBuffer(2), |             groupAvatar: new ArrayBuffer(2), | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             isCreating: false as const, |             isCreating: false as const, | ||||||
|             hasError: false as const, |             hasError: false as const, | ||||||
|           }, |           }, | ||||||
|  | @ -1230,6 +1242,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: 'foo', |             groupName: 'foo', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             isCreating: false as const, |             isCreating: false as const, | ||||||
|             hasError: false as const, |             hasError: false as const, | ||||||
|           }, |           }, | ||||||
|  | @ -1255,6 +1268,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             isCreating: false as const, |             isCreating: false as const, | ||||||
|             hasError: false as const, |             hasError: false as const, | ||||||
|           }, |           }, | ||||||
|  | @ -1424,6 +1438,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             cantAddContactIdForModal: undefined, |             cantAddContactIdForModal: undefined, | ||||||
|             searchTerm: 'to be cleared', |             searchTerm: 'to be cleared', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, |             recommendedGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|  | @ -1451,6 +1466,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             isCreating: false, |             isCreating: false, | ||||||
|             hasError: false as const, |             hasError: false as const, | ||||||
|           }, |           }, | ||||||
|  | @ -1515,6 +1531,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|           maximumGroupSizeModalState: OneTimeModalState.NeverShown, |           maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  | @ -1530,6 +1547,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|           }, |           }, | ||||||
|         }; |         }; | ||||||
|         const action = showChooseGroupMembers(); |         const action = showChooseGroupMembers(); | ||||||
|  | @ -1549,6 +1567,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: 'Foo Bar Group', |             groupName: 'Foo Bar Group', | ||||||
|             groupAvatar: new Uint8Array([4, 2]).buffer, |             groupAvatar: new Uint8Array([4, 2]).buffer, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             isCreating: false, |             isCreating: false, | ||||||
|             hasError: false as const, |             hasError: false as const, | ||||||
|           }, |           }, | ||||||
|  | @ -1566,6 +1585,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|           maximumGroupSizeModalState: OneTimeModalState.NeverShown, |           maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|           groupName: 'Foo Bar Group', |           groupName: 'Foo Bar Group', | ||||||
|           groupAvatar: new Uint8Array([4, 2]).buffer, |           groupAvatar: new Uint8Array([4, 2]).buffer, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  | @ -1584,6 +1604,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|           maximumGroupSizeModalState: OneTimeModalState.NeverShown, |           maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  | @ -1605,6 +1626,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|           maximumGroupSizeModalState: OneTimeModalState.NeverShown, |           maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|  | @ -1622,6 +1644,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|           }, |           }, | ||||||
|         }; |         }; | ||||||
|         const action = startSettingGroupMetadata(); |         const action = startSettingGroupMetadata(); | ||||||
|  | @ -1634,6 +1657,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|           maximumGroupSizeModalState: OneTimeModalState.NeverShown, |           maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|           isCreating: false, |           isCreating: false, | ||||||
|           hasError: false, |           hasError: false, | ||||||
|         }); |         }); | ||||||
|  | @ -1651,6 +1675,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: 'Foo Bar Group', |             groupName: 'Foo Bar Group', | ||||||
|             groupAvatar: new Uint8Array([6, 9]).buffer, |             groupAvatar: new Uint8Array([6, 9]).buffer, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|           }, |           }, | ||||||
|         }; |         }; | ||||||
|         const action = startSettingGroupMetadata(); |         const action = startSettingGroupMetadata(); | ||||||
|  | @ -1663,6 +1688,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|           maximumGroupSizeModalState: OneTimeModalState.NeverShown, |           maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|           groupName: 'Foo Bar Group', |           groupName: 'Foo Bar Group', | ||||||
|           groupAvatar: new Uint8Array([6, 9]).buffer, |           groupAvatar: new Uint8Array([6, 9]).buffer, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|           isCreating: false, |           isCreating: false, | ||||||
|           hasError: false as const, |           hasError: false as const, | ||||||
|         }); |         }); | ||||||
|  | @ -1678,6 +1704,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: 'Foo Bar Group', |             groupName: 'Foo Bar Group', | ||||||
|             groupAvatar: new Uint8Array([4, 2]).buffer, |             groupAvatar: new Uint8Array([4, 2]).buffer, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|             isCreating: false, |             isCreating: false, | ||||||
|             hasError: false as const, |             hasError: false as const, | ||||||
|           }, |           }, | ||||||
|  | @ -1731,6 +1758,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|           }, |           }, | ||||||
|         }; |         }; | ||||||
|         const one = reducer(zero, getAction('abc', zero)); |         const one = reducer(zero, getAction('abc', zero)); | ||||||
|  | @ -1745,6 +1773,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|           maximumGroupSizeModalState: OneTimeModalState.NeverShown, |           maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  | @ -1760,6 +1789,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|           }, |           }, | ||||||
|         }; |         }; | ||||||
|         const action = getAction('abc', state); |         const action = getAction('abc', state); | ||||||
|  | @ -1774,6 +1804,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|           maximumGroupSizeModalState: OneTimeModalState.NeverShown, |           maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  | @ -1792,6 +1823,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|           }, |           }, | ||||||
|         }; |         }; | ||||||
|         const action = getAction(newUuid, state); |         const action = getAction(newUuid, state); | ||||||
|  | @ -1806,6 +1838,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|           maximumGroupSizeModalState: OneTimeModalState.NeverShown, |           maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  | @ -1824,6 +1857,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|           }, |           }, | ||||||
|         }; |         }; | ||||||
|         const action = getAction(newUuid, state); |         const action = getAction(newUuid, state); | ||||||
|  | @ -1838,6 +1872,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|           maximumGroupSizeModalState: OneTimeModalState.NeverShown, |           maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  | @ -1858,6 +1893,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|               maximumGroupSizeModalState: OneTimeModalState.NeverShown, |               maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|               groupName: '', |               groupName: '', | ||||||
|               groupAvatar: undefined, |               groupAvatar: undefined, | ||||||
|  |               groupExpireTimer: 0, | ||||||
|             }, |             }, | ||||||
|           }; |           }; | ||||||
|           const action = getAction(uuid(), state); |           const action = getAction(uuid(), state); | ||||||
|  | @ -1881,6 +1917,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|           }, |           }, | ||||||
|         }; |         }; | ||||||
|         const action = getAction(newUuid, state); |         const action = getAction(newUuid, state); | ||||||
|  | @ -1895,6 +1932,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|           maximumGroupSizeModalState: OneTimeModalState.Showing, |           maximumGroupSizeModalState: OneTimeModalState.Showing, | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  | @ -1913,6 +1951,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.Shown, |             maximumGroupSizeModalState: OneTimeModalState.Shown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|           }, |           }, | ||||||
|         }; |         }; | ||||||
|         const action = getAction(newUuid, state); |         const action = getAction(newUuid, state); | ||||||
|  | @ -1927,6 +1966,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|           maximumGroupSizeModalState: OneTimeModalState.Shown, |           maximumGroupSizeModalState: OneTimeModalState.Shown, | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  | @ -1942,6 +1982,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|           }, |           }, | ||||||
|         }; |         }; | ||||||
|         const action = getAction(uuid(), state); |         const action = getAction(uuid(), state); | ||||||
|  | @ -1969,6 +2010,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|               maximumGroupSizeModalState: OneTimeModalState.NeverShown, |               maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|               groupName: '', |               groupName: '', | ||||||
|               groupAvatar: undefined, |               groupAvatar: undefined, | ||||||
|  |               groupExpireTimer: 0, | ||||||
|             }, |             }, | ||||||
|           }; |           }; | ||||||
|           const action = getAction(uuid(), state); |           const action = getAction(uuid(), state); | ||||||
|  | @ -1995,6 +2037,7 @@ describe('both/state/ducks/conversations', () => { | ||||||
|             maximumGroupSizeModalState: OneTimeModalState.NeverShown, |             maximumGroupSizeModalState: OneTimeModalState.NeverShown, | ||||||
|             groupName: '', |             groupName: '', | ||||||
|             groupAvatar: undefined, |             groupAvatar: undefined, | ||||||
|  |             groupExpireTimer: 0, | ||||||
|           }, |           }, | ||||||
|         }; |         }; | ||||||
|         const action = getAction(uuid(), state); |         const action = getAction(uuid(), state); | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ describe('LeftPaneSetGroupMetadataHelper', () => { | ||||||
|       const showChooseGroupMembers = sinon.fake(); |       const showChooseGroupMembers = sinon.fake(); | ||||||
|       const helper = new LeftPaneSetGroupMetadataHelper({ |       const helper = new LeftPaneSetGroupMetadataHelper({ | ||||||
|         groupAvatar: undefined, |         groupAvatar: undefined, | ||||||
|  |         groupExpireTimer: 0, | ||||||
|         groupName: '', |         groupName: '', | ||||||
|         hasError: false, |         hasError: false, | ||||||
|         isCreating: false, |         isCreating: false, | ||||||
|  | @ -29,6 +30,7 @@ describe('LeftPaneSetGroupMetadataHelper', () => { | ||||||
|     it("returns undefined (i.e., you can't go back) if a request is active", () => { |     it("returns undefined (i.e., you can't go back) if a request is active", () => { | ||||||
|       const helper = new LeftPaneSetGroupMetadataHelper({ |       const helper = new LeftPaneSetGroupMetadataHelper({ | ||||||
|         groupAvatar: undefined, |         groupAvatar: undefined, | ||||||
|  |         groupExpireTimer: 0, | ||||||
|         groupName: 'Foo Bar', |         groupName: 'Foo Bar', | ||||||
|         hasError: false, |         hasError: false, | ||||||
|         isCreating: true, |         isCreating: true, | ||||||
|  | @ -46,6 +48,7 @@ describe('LeftPaneSetGroupMetadataHelper', () => { | ||||||
|       assert.strictEqual( |       assert.strictEqual( | ||||||
|         new LeftPaneSetGroupMetadataHelper({ |         new LeftPaneSetGroupMetadataHelper({ | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           hasError: false, |           hasError: false, | ||||||
|           isCreating: false, |           isCreating: false, | ||||||
|  | @ -59,6 +62,7 @@ describe('LeftPaneSetGroupMetadataHelper', () => { | ||||||
|       assert.strictEqual( |       assert.strictEqual( | ||||||
|         new LeftPaneSetGroupMetadataHelper({ |         new LeftPaneSetGroupMetadataHelper({ | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           hasError: false, |           hasError: false, | ||||||
|           isCreating: false, |           isCreating: false, | ||||||
|  | @ -77,6 +81,7 @@ describe('LeftPaneSetGroupMetadataHelper', () => { | ||||||
|       assert.isUndefined( |       assert.isUndefined( | ||||||
|         new LeftPaneSetGroupMetadataHelper({ |         new LeftPaneSetGroupMetadataHelper({ | ||||||
|           groupAvatar: undefined, |           groupAvatar: undefined, | ||||||
|  |           groupExpireTimer: 0, | ||||||
|           groupName: '', |           groupName: '', | ||||||
|           hasError: false, |           hasError: false, | ||||||
|           isCreating: false, |           isCreating: false, | ||||||
|  | @ -92,6 +97,7 @@ describe('LeftPaneSetGroupMetadataHelper', () => { | ||||||
|       ]; |       ]; | ||||||
|       const helper = new LeftPaneSetGroupMetadataHelper({ |       const helper = new LeftPaneSetGroupMetadataHelper({ | ||||||
|         groupAvatar: undefined, |         groupAvatar: undefined, | ||||||
|  |         groupExpireTimer: 0, | ||||||
|         groupName: '', |         groupName: '', | ||||||
|         hasError: false, |         hasError: false, | ||||||
|         isCreating: false, |         isCreating: false, | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								ts/window.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								ts/window.d.ts
									
										
									
									
										vendored
									
									
								
							|  | @ -107,7 +107,7 @@ import { MessageDetail } from './components/conversation/MessageDetail'; | ||||||
| import { ProgressModal } from './components/ProgressModal'; | import { ProgressModal } from './components/ProgressModal'; | ||||||
| import { Quote } from './components/conversation/Quote'; | import { Quote } from './components/conversation/Quote'; | ||||||
| import { StagedLinkPreview } from './components/conversation/StagedLinkPreview'; | import { StagedLinkPreview } from './components/conversation/StagedLinkPreview'; | ||||||
| import { DisappearingTimeDialog } from './components/conversation/DisappearingTimeDialog'; | import { DisappearingTimeDialog } from './components/DisappearingTimeDialog'; | ||||||
| import { MIMEType } from './types/MIME'; | import { MIMEType } from './types/MIME'; | ||||||
| import { AttachmentType } from './types/Attachment'; | import { AttachmentType } from './types/Attachment'; | ||||||
| import { ElectronLocaleType } from './util/mapToSupportLocale'; | import { ElectronLocaleType } from './util/mapToSupportLocale'; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Fedor Indutny
				Fedor Indutny