@@ -15,12 +15,14 @@ See the License for the specific language governing permissions and
1515limitations under the License.
1616*/
1717
18- import React , { useCallback , useEffect , useState } from "react" ;
18+ import React , { ReactElement , useCallback , useEffect , useState } from "react" ;
1919
20+ import { NonEmptyArray } from "../../../../../@types/common" ;
2021import { _t , getCurrentLanguage } from "../../../../../languageHandler" ;
2122import { UseCase } from "../../../../../settings/enums/UseCase" ;
2223import SettingsStore from "../../../../../settings/SettingsStore" ;
2324import Field from "../../../elements/Field" ;
25+ import Dropdown from "../../../elements/Dropdown" ;
2426import { SettingLevel } from "../../../../../settings/SettingLevel" ;
2527import SettingsFlag from "../../../elements/SettingsFlag" ;
2628import AccessibleButton from "../../../elements/AccessibleButton" ;
@@ -38,12 +40,16 @@ import PlatformPeg from "../../../../../PlatformPeg";
3840import { IS_MAC } from "../../../../../Keyboard" ;
3941import SpellCheckSettings from "../../SpellCheckSettings" ;
4042import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch" ;
43+ import * as TimezoneHandler from "../../../../../TimezoneHandler" ;
4144
4245interface IProps {
4346 closeSettingsFn ( success : boolean ) : void ;
4447}
4548
4649interface IState {
50+ timezone : string | undefined ;
51+ timezones : string [ ] ;
52+ timezoneSearch : string | undefined ;
4753 autocompleteDelay : string ;
4854 readMarkerInViewThresholdMs : string ;
4955 readMarkerOutOfViewThresholdMs : string ;
@@ -68,7 +74,7 @@ const LanguageSection: React.FC = () => {
6874 ) ;
6975
7076 return (
71- < div className = "mx_SettingsSubsection_contentStretch " >
77+ < div className = "mx_SettingsSubsection_dropdown " >
7278 { _t ( "settings|general|application_language" ) }
7379 < LanguageDropdown onOptionChange = { onLanguageChange } value = { language } />
7480 < div className = "mx_PreferencesUserSettingsTab_section_hint" >
@@ -173,6 +179,9 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
173179 super ( props ) ;
174180
175181 this . state = {
182+ timezone : TimezoneHandler . getUserTimezone ( ) ,
183+ timezones : TimezoneHandler . getAllTimezones ( ) ,
184+ timezoneSearch : undefined ,
176185 autocompleteDelay : SettingsStore . getValueAt ( SettingLevel . DEVICE , "autocompleteDelay" ) . toString ( 10 ) ,
177186 readMarkerInViewThresholdMs : SettingsStore . getValueAt (
178187 SettingLevel . DEVICE ,
@@ -185,6 +194,25 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
185194 } ;
186195 }
187196
197+ private onTimezoneChange = ( tz : string ) : void => {
198+ this . setState ( { timezone : tz } ) ;
199+ TimezoneHandler . setUserTimezone ( tz ) ;
200+ } ;
201+
202+ /**
203+ * If present filter the time zones matching the search term
204+ */
205+ private onTimezoneSearchChange = ( search : string ) : void => {
206+ const timezoneSearch = search . toLowerCase ( ) ;
207+ const timezones = timezoneSearch
208+ ? TimezoneHandler . getAllTimezones ( ) . filter ( ( tz ) => {
209+ return tz . toLowerCase ( ) . includes ( timezoneSearch ) ;
210+ } )
211+ : TimezoneHandler . getAllTimezones ( ) ;
212+
213+ this . setState ( { timezones, timezoneSearch } ) ;
214+ } ;
215+
188216 private onAutocompleteDelayChange = ( e : React . ChangeEvent < HTMLInputElement > ) : void => {
189217 this . setState ( { autocompleteDelay : e . target . value } ) ;
190218 SettingsStore . setValue ( "autocompleteDelay" , null , SettingLevel . DEVICE , e . target . value ) ;
@@ -217,6 +245,16 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
217245 // Only show the user onboarding setting if the user should see the user onboarding page
218246 . filter ( ( it ) => it !== "FTUE.userOnboardingButton" || showUserOnboardingPage ( useCase ) ) ;
219247
248+ const browserTimezoneLabel : string = _t ( "settings|preferences|default_timezone" , {
249+ timezone : TimezoneHandler . shortBrowserTimezone ( ) ,
250+ } ) ;
251+
252+ // Always Preprend the default option
253+ const timezones = this . state . timezones . map ( ( tz ) => {
254+ return < div key = { tz } > { tz } </ div > ;
255+ } ) ;
256+ timezones . unshift ( < div key = "" > { browserTimezoneLabel } </ div > ) ;
257+
220258 return (
221259 < SettingsTab data-testid = "mx_PreferencesUserSettingsTab" >
222260 < SettingsSection >
@@ -254,6 +292,23 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
254292 </ SettingsSubsection >
255293
256294 < SettingsSubsection heading = { _t ( "settings|preferences|time_heading" ) } >
295+ < div className = "mx_SettingsSubsection_dropdown" >
296+ { _t ( "settings|preferences|user_timezone" ) }
297+ < Dropdown
298+ id = "mx_dropdownUserTimezone"
299+ className = "mx_dropdownUserTimezone"
300+ data-testid = "mx_dropdownUserTimezone"
301+ searchEnabled = { true }
302+ value = { this . state . timezone }
303+ label = { _t ( "settings|preferences|user_timezone" ) }
304+ placeholder = { browserTimezoneLabel }
305+ onOptionChange = { this . onTimezoneChange }
306+ onSearchChange = { this . onTimezoneSearchChange }
307+ >
308+ { timezones as NonEmptyArray < ReactElement & { key : string } > }
309+ </ Dropdown >
310+ </ div >
311+
257312 { this . renderGroup ( PreferencesUserSettingsTab . TIME_SETTINGS ) }
258313 </ SettingsSubsection >
259314
0 commit comments