MM-39711 - Gekidou Settings Timezone (#6384)
This commit is contained in:
parent
adfb90eb59
commit
fe3f0cc892
29 changed files with 470 additions and 30 deletions
|
|
@ -12,7 +12,6 @@ import {prepareMyPreferences, queryPreferencesByCategoryAndName} from '@queries/
|
|||
import {prepareCommonSystemValues, getCommonSystemValues} from '@queries/servers/system';
|
||||
import {prepareMyTeams} from '@queries/servers/team';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
import TeamModel from '@typings/database/models/servers/team';
|
||||
import {isDMorGM, selectDefaultChannelForTeam} from '@utils/channel';
|
||||
|
||||
import {fetchMissingDirectChannelsInfo, fetchMyChannelsForTeam, MyChannelsRequest} from './channel';
|
||||
|
|
@ -23,6 +22,7 @@ import {ConfigAndLicenseRequest, fetchConfigAndLicense} from './systems';
|
|||
import {fetchMyTeams, MyTeamsRequest} from './team';
|
||||
|
||||
import type {Model} from '@nozbe/watermelondb';
|
||||
import type TeamModel from '@typings/database/models/servers/team';
|
||||
|
||||
export async function retryInitialTeamAndChannel(serverUrl: string) {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
|
|
|
|||
|
|
@ -867,3 +867,15 @@ export const fetchTeamAndChannelMembership = async (serverUrl: string, userId: s
|
|||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const getAllSupportedTimezones = async (serverUrl: string) => {
|
||||
try {
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const allTzs = await client.getTimezones();
|
||||
return allTzs;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('FAILED TO GET ALL TIMEZONES', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ export type SearchProps = TextInputProps & {
|
|||
containerStyle?: StyleProp<ViewStyle>;
|
||||
inputContainerStyle?: StyleProp<ViewStyle>;
|
||||
inputStyle?: StyleProp<TextStyle>;
|
||||
loadingProps?: ActivityIndicatorProps;
|
||||
leftIconContainerStyle?: StyleProp<ViewStyle>;
|
||||
loadingProps?: ActivityIndicatorProps;
|
||||
onCancel?(): void;
|
||||
onClear?(): void;
|
||||
rightIconContainerStyle?: StyleProp<ViewStyle>;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ export const BOTTOM_SHEET = 'BottomSheet';
|
|||
export const BROWSE_CHANNELS = 'BrowseChannels';
|
||||
export const CHANNEL = 'Channel';
|
||||
export const CHANNEL_ADD_PEOPLE = 'ChannelAddPeople';
|
||||
export const CHANNEL_EDIT = 'ChannelEdit';
|
||||
export const CHANNEL_INFO = 'ChannelInfo';
|
||||
export const CHANNEL_MENTION = 'ChannelMention';
|
||||
export const CODE = 'Code';
|
||||
|
|
@ -45,6 +44,8 @@ export const SETTINGS = 'Settings';
|
|||
export const SETTINGS_DISPLAY = 'SettingsDisplay';
|
||||
export const SETTINGS_DISPLAY_CLOCK = 'SettingsDisplayClock';
|
||||
export const SETTINGS_DISPLAY_THEME = 'SettingsDisplayTheme';
|
||||
export const SETTINGS_DISPLAY_TIMEZONE = 'SettingsDisplayTimezone';
|
||||
export const SETTINGS_DISPLAY_TIMEZONE_SELECT = 'SettingsDisplayTimezoneSelect';
|
||||
export const SETTINGS_NOTIFICATION = 'SettingsNotification';
|
||||
export const SETTINGS_NOTIFICATION_AUTO_RESPONDER = 'SettingsNotificationAutoResponder';
|
||||
export const SETTINGS_NOTIFICATION_MENTION = 'SettingsNotificationMention';
|
||||
|
|
@ -101,6 +102,8 @@ export default {
|
|||
SETTINGS_DISPLAY,
|
||||
SETTINGS_DISPLAY_CLOCK,
|
||||
SETTINGS_DISPLAY_THEME,
|
||||
SETTINGS_DISPLAY_TIMEZONE,
|
||||
SETTINGS_DISPLAY_TIMEZONE_SELECT,
|
||||
SETTINGS_NOTIFICATION,
|
||||
SETTINGS_NOTIFICATION_AUTO_RESPONDER,
|
||||
SETTINGS_NOTIFICATION_MENTION,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {
|
||||
transformGroupRecord,
|
||||
} from '@database/operator/server_data_operator/transformers/group';
|
||||
import {transformGroupRecord} from '@database/operator/server_data_operator/transformers/group';
|
||||
import {createTestConnection} from '@database/operator/utils/create_test_connection';
|
||||
import {OperationType} from '@typings/database/enums';
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ import {switchMap} from 'rxjs/operators';
|
|||
|
||||
import {Preferences} from '@constants';
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import PostModel from '@typings/database/models/servers/post';
|
||||
|
||||
import {queryPreferencesByCategoryAndName} from './preference';
|
||||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type PostInChannelModel from '@typings/database/models/servers/posts_in_channel';
|
||||
import type PostsInThreadModel from '@typings/database/models/servers/posts_in_thread';
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import {Database, Model, Q} from '@nozbe/watermelondb';
|
|||
import {Preferences} from '@constants';
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import {getPreferenceValue} from '@helpers/api/preference';
|
||||
import {ServerDatabase} from '@typings/database/database';
|
||||
|
||||
import {getCurrentTeamId} from './system';
|
||||
|
||||
import type ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import type {ServerDatabase} from '@typings/database/database';
|
||||
import type PreferenceModel from '@typings/database/models/servers/preference';
|
||||
|
||||
const {SERVER: {PREFERENCE}} = MM_TABLES;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ import {switchMap} from 'rxjs/operators';
|
|||
|
||||
import {observeConfig} from '@queries/servers/system';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
import EditProfile from './edit_profile';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const config = observeConfig(database);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ import {observeChannelsByCategoryChannelSortOrder, observeChannelsByLastPostAtIn
|
|||
import {observeNotifyPropsByChannels, queryChannelsByNames} from '@queries/servers/channel';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeCurrentChannelId, observeCurrentUserId, observeLastUnreadChannelId} from '@queries/servers/system';
|
||||
import {WithDatabaseArgs} from '@typings/database/database';
|
||||
import {getDirectChannelName} from '@utils/channel';
|
||||
|
||||
import CategoryBody from './category_body';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type CategoryModel from '@typings/database/models/servers/category';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type PreferenceModel from '@typings/database/models/servers/preference';
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import CompassIcon from '@components/compass_icon';
|
|||
import NetworkManager from '@managers/network_manager';
|
||||
import {observeConfig} from '@queries/servers/system';
|
||||
import {observeUser} from '@queries/servers/user';
|
||||
import {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
import type {Client} from '@client/rest';
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
interface NotificationIconProps {
|
||||
|
|
|
|||
|
|
@ -160,6 +160,12 @@ Navigation.setLazyComponentRegistrator((screenName) => {
|
|||
case Screens.SETTINGS_DISPLAY_THEME:
|
||||
screen = withServerDatabase(require('@screens/settings/display_theme').default);
|
||||
break;
|
||||
case Screens.SETTINGS_DISPLAY_TIMEZONE:
|
||||
screen = withServerDatabase(require('@screens/settings/display_timezone').default);
|
||||
break;
|
||||
case Screens.SETTINGS_DISPLAY_TIMEZONE_SELECT:
|
||||
screen = withServerDatabase(require('@screens/settings/display_timezone_select').default);
|
||||
break;
|
||||
case Screens.SETTINGS_NOTIFICATION:
|
||||
screen = withServerDatabase(require('@screens/settings/notifications').default);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ import {observePost} from '@queries/servers/post';
|
|||
import {observeCurrentTeamId, observeCurrentUserId} from '@queries/servers/system';
|
||||
import {queryMyTeamsByIds, queryTeamByName} from '@queries/servers/team';
|
||||
import {observeIsCRTEnabled} from '@queries/servers/thread';
|
||||
import {WithDatabaseArgs} from '@typings/database/database';
|
||||
import PostModel from '@typings/database/models/servers/post';
|
||||
|
||||
import Permalink from './permalink';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
type OwnProps = {
|
||||
postId: PostModel['id'];
|
||||
teamName?: string;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import {switchMap} from 'rxjs/operators';
|
|||
|
||||
import {observePost} from '@queries/servers/post';
|
||||
import {observeUser} from '@queries/servers/user';
|
||||
import {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
import Reactor from './reactor';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type ReactionModel from '@typings/database/models/servers/reaction';
|
||||
|
||||
const enhance = withObservables(['reaction'], ({database, reaction}: {reaction: ReactionModel} & WithDatabaseArgs) => ({
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Alert, Platform, ScrollView, View} from 'react-native';
|
||||
import {Platform, ScrollView, View} from 'react-native';
|
||||
import {SafeAreaView} from 'react-native-safe-area-context';
|
||||
|
||||
import {Screens} from '@constants';
|
||||
|
|
@ -44,11 +44,6 @@ const Display = ({isTimezoneEnabled, isThemeSwitchingEnabled}: DisplayProps) =>
|
|||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
const intl = useIntl();
|
||||
const onPressHandler = () => {
|
||||
return Alert.alert(
|
||||
'The functionality you are trying to use has not yet been implemented.',
|
||||
);
|
||||
};
|
||||
|
||||
const goToThemeSettings = preventDoubleTap(() => {
|
||||
const screen = Screens.SETTINGS_DISPLAY_THEME;
|
||||
|
|
@ -63,6 +58,13 @@ const Display = ({isTimezoneEnabled, isThemeSwitchingEnabled}: DisplayProps) =>
|
|||
goToScreen(screen, title);
|
||||
});
|
||||
|
||||
const goToTimezoneSettings = preventDoubleTap(() => {
|
||||
const screen = Screens.SETTINGS_DISPLAY_TIMEZONE;
|
||||
const title = intl.formatMessage({id: 'display_settings.timezone', defaultMessage: 'Timezone'});
|
||||
|
||||
goToScreen(screen, title);
|
||||
});
|
||||
|
||||
return (
|
||||
<SafeAreaView
|
||||
edges={['left', 'right']}
|
||||
|
|
@ -87,7 +89,7 @@ const Display = ({isTimezoneEnabled, isThemeSwitchingEnabled}: DisplayProps) =>
|
|||
{isTimezoneEnabled && (
|
||||
<SettingOption
|
||||
optionName='timezone'
|
||||
onPress={onPressHandler}
|
||||
onPress={goToTimezoneSettings}
|
||||
/>
|
||||
)}
|
||||
<View style={styles.divider}/>
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ import {combineLatest, of as of$} from 'rxjs';
|
|||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {observeAllowedThemesKeys, observeConfigBooleanValue} from '@queries/servers/system';
|
||||
import {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
import DisplaySettings from './display';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const isTimezoneEnabled = observeConfigBooleanValue(database, 'ExperimentalTimezone');
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import React, {useCallback, useEffect, useMemo, useState} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {StatusBar, View} from 'react-native';
|
||||
import {View} from 'react-native';
|
||||
import {Edge, SafeAreaView} from 'react-native-safe-area-context';
|
||||
|
||||
import {savePreference} from '@actions/remote/preference';
|
||||
|
|
@ -119,7 +119,6 @@ const DisplayClock = ({componentId, currentUserId, hasMilitaryTimeFormat}: Displ
|
|||
style={styles.container}
|
||||
testID='settings_display.screen'
|
||||
>
|
||||
<StatusBar/>
|
||||
<View style={styles.wrapper}>
|
||||
<Block
|
||||
disableHeader={true}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,11 @@ import {Preferences} from '@constants';
|
|||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeCurrentUserId} from '@queries/servers/system';
|
||||
import {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
import DisplayClock from './display_clock';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
return {
|
||||
currentUserId: observeCurrentUserId(database),
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@ import {
|
|||
observeCurrentTeamId,
|
||||
observeCurrentUserId,
|
||||
} from '@queries/servers/system';
|
||||
import {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
import DisplayTheme from './display_theme';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const currentTeamId = observeCurrentTeamId(database);
|
||||
const currentUserId = observeCurrentUserId(database);
|
||||
|
|
|
|||
163
app/screens/settings/display_timezone/display_timezone.tsx
Normal file
163
app/screens/settings/display_timezone/display_timezone.tsx
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback, useEffect, useMemo, useState} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {View} from 'react-native';
|
||||
import {Edge, SafeAreaView} from 'react-native-safe-area-context';
|
||||
|
||||
import {updateMe} from '@actions/remote/user';
|
||||
import OptionItem from '@components/option_item';
|
||||
import {Screens} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
|
||||
import useNavButtonPressed from '@hooks/navigation_button_pressed';
|
||||
import {goToScreen, popTopScreen, setButtons} from '@screens/navigation';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {getDeviceTimezone} from '@utils/timezone';
|
||||
import {getTimezoneRegion, getUserTimezoneProps} from '@utils/user';
|
||||
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
wrapper: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06),
|
||||
flex: 1,
|
||||
paddingTop: 35,
|
||||
},
|
||||
divider: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
height: 1,
|
||||
},
|
||||
separator: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
|
||||
height: 1,
|
||||
marginLeft: 15,
|
||||
},
|
||||
content: {
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const edges: Edge[] = ['left', 'right'];
|
||||
const SAVE_TIMEZONE_BUTTON_ID = 'save_timezone';
|
||||
|
||||
type DisplayTimezoneProps = {
|
||||
currentUser: UserModel;
|
||||
componentId: string;
|
||||
}
|
||||
const DisplayTimezone = ({currentUser, componentId}: DisplayTimezoneProps) => {
|
||||
const intl = useIntl();
|
||||
const serverUrl = useServerUrl();
|
||||
const timezone = useMemo(() => getUserTimezoneProps(currentUser), [currentUser.timezone]);
|
||||
const [userTimezone, setUserTimezone] = useState(timezone);
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
const updateAutomaticTimezone = (useAutomaticTimezone: boolean) => {
|
||||
const automaticTimezone = getDeviceTimezone();
|
||||
setUserTimezone((prev) => ({
|
||||
...prev,
|
||||
useAutomaticTimezone,
|
||||
automaticTimezone,
|
||||
}));
|
||||
};
|
||||
|
||||
const updateManualTimezone = (mtz: string) => {
|
||||
setUserTimezone({
|
||||
useAutomaticTimezone: false,
|
||||
automaticTimezone: '',
|
||||
manualTimezone: mtz,
|
||||
});
|
||||
};
|
||||
|
||||
const goToSelectTimezone = preventDoubleTap(() => {
|
||||
const screen = Screens.SETTINGS_DISPLAY_TIMEZONE_SELECT;
|
||||
const title = intl.formatMessage({id: 'settings_display.timezone.select', defaultMessage: 'Select Timezone'});
|
||||
const passProps = {
|
||||
selectedTimezone: timezone.manualTimezone,
|
||||
onBack: updateManualTimezone,
|
||||
};
|
||||
|
||||
goToScreen(screen, title, passProps);
|
||||
});
|
||||
|
||||
const close = () => popTopScreen(componentId);
|
||||
|
||||
const saveTimezone = useCallback(() => {
|
||||
const timeZone = {
|
||||
useAutomaticTimezone: userTimezone.useAutomaticTimezone.toString(),
|
||||
automaticTimezone: userTimezone.automaticTimezone,
|
||||
manualTimezone: userTimezone.manualTimezone,
|
||||
};
|
||||
|
||||
updateMe(serverUrl, {timezone: timeZone});
|
||||
close();
|
||||
}, [userTimezone, currentUser.timezone, serverUrl]);
|
||||
|
||||
const saveButton = useMemo(() => {
|
||||
return {
|
||||
id: SAVE_TIMEZONE_BUTTON_ID,
|
||||
enabled: false,
|
||||
showAsAction: 'always' as const,
|
||||
testID: 'notification_settings.auto_res.save.button',
|
||||
color: theme.sidebarHeaderTextColor,
|
||||
text: intl.formatMessage({id: 'settings.save', defaultMessage: 'Save'}),
|
||||
};
|
||||
}, [theme.sidebarHeaderTextColor]);
|
||||
|
||||
useEffect(() => {
|
||||
const buttons = {
|
||||
rightButtons: [{
|
||||
...saveButton,
|
||||
enabled: timezone.useAutomaticTimezone !== userTimezone.useAutomaticTimezone,
|
||||
}],
|
||||
};
|
||||
setButtons(componentId, buttons);
|
||||
}, [componentId, currentUser.timezone]);
|
||||
|
||||
useNavButtonPressed(SAVE_TIMEZONE_BUTTON_ID, componentId, saveTimezone, [saveTimezone]);
|
||||
|
||||
useAndroidHardwareBackHandler(componentId, close);
|
||||
|
||||
return (
|
||||
<SafeAreaView
|
||||
edges={edges}
|
||||
style={styles.container}
|
||||
>
|
||||
<View style={styles.wrapper}>
|
||||
<View style={styles.divider}/>
|
||||
<OptionItem
|
||||
action={updateAutomaticTimezone}
|
||||
containerStyle={styles.content}
|
||||
description={getTimezoneRegion(userTimezone.automaticTimezone)}
|
||||
label={intl.formatMessage({id: 'settings_display.timezone.automatically', defaultMessage: 'Set automatically'})}
|
||||
selected={userTimezone.useAutomaticTimezone}
|
||||
type='toggle'
|
||||
/>
|
||||
{!userTimezone.useAutomaticTimezone && (
|
||||
<View>
|
||||
<View style={styles.separator}/>
|
||||
<OptionItem
|
||||
action={goToSelectTimezone}
|
||||
containerStyle={styles.content}
|
||||
description={getTimezoneRegion(userTimezone.manualTimezone)}
|
||||
label={intl.formatMessage({id: 'settings_display.timezone.manual', defaultMessage: 'Change timezone'})}
|
||||
type='arrow'
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.divider}/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
export default DisplayTimezone;
|
||||
20
app/screens/settings/display_timezone/index.tsx
Normal file
20
app/screens/settings/display_timezone/index.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
|
||||
import DisplayTimezone from './display_timezone';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
return {
|
||||
currentUser: observeCurrentUser(database),
|
||||
};
|
||||
});
|
||||
|
||||
export default withDatabase(enhanced(DisplayTimezone));
|
||||
|
||||
143
app/screens/settings/display_timezone_select/index.tsx
Normal file
143
app/screens/settings/display_timezone_select/index.tsx
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback, useEffect, useState} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {FlatList, View} from 'react-native';
|
||||
import {Edge, SafeAreaView} from 'react-native-safe-area-context';
|
||||
|
||||
import {getAllSupportedTimezones} from '@actions/remote/user';
|
||||
import Search from '@components/search';
|
||||
import {List} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {popTopScreen} from '@screens/navigation';
|
||||
import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
import {getTimezoneRegion} from '@utils/user';
|
||||
|
||||
import TimezoneRow from './timezone_row';
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
searchBarInput: {
|
||||
color: theme.centerChannelColor,
|
||||
...typography('Body', 100, 'Regular'),
|
||||
},
|
||||
searchBar: {
|
||||
height: 38,
|
||||
marginVertical: 5,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const EDGES: Edge[] = ['left', 'right'];
|
||||
const EMPTY_TIMEZONES: string[] = [];
|
||||
const ITEM_HEIGHT = 45;
|
||||
const VIEWABILITY_CONFIG = List.VISIBILITY_CONFIG_DEFAULTS;
|
||||
|
||||
const keyExtractor = (item: string) => item;
|
||||
const getItemLayout = (_data: string[], index: number) => ({
|
||||
length: ITEM_HEIGHT,
|
||||
offset: ITEM_HEIGHT * index,
|
||||
index,
|
||||
});
|
||||
|
||||
type SelectTimezonesProps = {
|
||||
selectedTimezone: string;
|
||||
onBack: (tz: string) => void;
|
||||
}
|
||||
const SelectTimezones = ({selectedTimezone, onBack}: SelectTimezonesProps) => {
|
||||
const intl = useIntl();
|
||||
const serverUrl = useServerUrl();
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
const [timezones, setTimezones] = useState<string[]>(EMPTY_TIMEZONES);
|
||||
const [initialScrollIndex, setInitialScrollIndex] = useState<number>(0);
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
const filteredTimezones = (timezonePrefix: string) => {
|
||||
if (timezonePrefix.length === 0) {
|
||||
return timezones;
|
||||
}
|
||||
|
||||
const lowerCasePrefix = timezonePrefix.toLowerCase();
|
||||
|
||||
return timezones.filter((t) => (
|
||||
getTimezoneRegion(t).toLowerCase().indexOf(lowerCasePrefix) >= 0 ||
|
||||
t.toLowerCase().indexOf(lowerCasePrefix) >= 0
|
||||
));
|
||||
};
|
||||
|
||||
const onPressTimezone = useCallback((tzne: string) => {
|
||||
onBack(tzne);
|
||||
popTopScreen();
|
||||
}, [onBack]);
|
||||
|
||||
const renderItem = ({item: timezone}: {item: string}) => {
|
||||
return (
|
||||
<TimezoneRow
|
||||
onPressTimezone={onPressTimezone}
|
||||
selectedTimezone={selectedTimezone}
|
||||
timezone={timezone}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// let's get all supported timezones
|
||||
const getSupportedTimezones = async () => {
|
||||
const allTzs = await getAllSupportedTimezones(serverUrl);
|
||||
if (allTzs.length > 0) {
|
||||
setTimezones(allTzs);
|
||||
const timezoneIndex = allTzs.findIndex((timezone) => timezone === selectedTimezone);
|
||||
if (timezoneIndex > 0) {
|
||||
setInitialScrollIndex(timezoneIndex);
|
||||
}
|
||||
}
|
||||
};
|
||||
getSupportedTimezones();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<SafeAreaView
|
||||
edges={EDGES}
|
||||
style={styles.container}
|
||||
testID='settings.select_timezone.screen'
|
||||
>
|
||||
<View style={styles.searchBar}>
|
||||
<Search
|
||||
autoCapitalize='none'
|
||||
containerStyle={styles.searchBarContainer}
|
||||
inputStyle={styles.searchBarInput}
|
||||
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
|
||||
onChangeText={setValue}
|
||||
placeholder={intl.formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
|
||||
placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.5)}
|
||||
selectionColor={changeOpacity(theme.centerChannelColor, 0.5)}
|
||||
testID='settings.select_timezone.search_bar'
|
||||
value={value}
|
||||
/>
|
||||
</View>
|
||||
<FlatList
|
||||
data={filteredTimezones(value)}
|
||||
getItemLayout={getItemLayout}
|
||||
initialScrollIndex={initialScrollIndex}
|
||||
keyExtractor={keyExtractor}
|
||||
keyboardDismissMode='on-drag'
|
||||
keyboardShouldPersistTaps='always'
|
||||
maxToRenderPerBatch={15}
|
||||
removeClippedSubviews={true}
|
||||
renderItem={renderItem}
|
||||
viewabilityConfig={VIEWABILITY_CONFIG}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectTimezones;
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {TouchableOpacity, View, Text} from 'react-native';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
const ITEM_HEIGHT = 45;
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
itemContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
paddingHorizontal: 15,
|
||||
height: ITEM_HEIGHT,
|
||||
},
|
||||
item: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
itemText: {
|
||||
color: theme.centerChannelColor,
|
||||
...typography('Body', 100, 'Regular'),
|
||||
},
|
||||
};
|
||||
});
|
||||
type TimezoneRowProps = {
|
||||
onPressTimezone: (timezone: string) => void;
|
||||
selectedTimezone: string;
|
||||
timezone: string;
|
||||
}
|
||||
const TimezoneRow = ({onPressTimezone, selectedTimezone, timezone}: TimezoneRowProps) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
const onTimezoneSelect = () => {
|
||||
onPressTimezone(timezone);
|
||||
};
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={styles.itemContainer}
|
||||
key={timezone}
|
||||
onPress={onTimezoneSelect}
|
||||
>
|
||||
<View style={styles.item}>
|
||||
<Text style={styles.itemText}>
|
||||
{timezone}
|
||||
</Text>
|
||||
</View>
|
||||
{timezone === selectedTimezone && (
|
||||
<CompassIcon
|
||||
name='check'
|
||||
size={24}
|
||||
color={theme.linkColor}
|
||||
/>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
export default TimezoneRow;
|
||||
|
|
@ -5,10 +5,11 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
|||
import withObservables from '@nozbe/with-observables';
|
||||
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
import NotificationAutoResponder from './notification_auto_responder';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
return {
|
||||
currentUser: observeCurrentUser(database),
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ import withObservables from '@nozbe/with-observables';
|
|||
|
||||
import {observeIsCRTEnabled} from '@queries/servers/thread';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
import Mentions from './notification_mention';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
return {
|
||||
isCRTEnabled: observeIsCRTEnabled(database),
|
||||
|
|
|
|||
|
|
@ -15,11 +15,12 @@ import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
|
|||
import useNavButtonPressed from '@hooks/navigation_button_pressed';
|
||||
import {t} from '@i18n';
|
||||
import {popTopScreen, setButtons} from '@screens/navigation';
|
||||
import UserModel from '@typings/database/models/servers/user';
|
||||
import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
import {getNotificationProps} from '@utils/user';
|
||||
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
const mentionHeaderText = {
|
||||
id: t('notification_settings.mentions.wordsTrigger'),
|
||||
defaultMessage: 'Words that trigger mentions',
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ import withObservables from '@nozbe/with-observables';
|
|||
import {observeConfigBooleanValue} from '@queries/servers/system';
|
||||
import {observeIsCRTEnabled} from '@queries/servers/thread';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
import NotificationPush from './notification_push';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
return {
|
||||
currentUser: observeCurrentUser(database),
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ import withObservables from '@nozbe/with-observables';
|
|||
|
||||
import {observeConfigBooleanValue} from '@queries/servers/system';
|
||||
import {observeIsCRTEnabled} from '@queries/servers/thread';
|
||||
import {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
import NotificationSettings from './notifications';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
return {
|
||||
isCRTEnabled: observeIsCRTEnabled(database),
|
||||
|
|
|
|||
|
|
@ -138,6 +138,17 @@ export const getTimezone = (timezone: UserTimezone | null) => {
|
|||
return timezone.manualTimezone;
|
||||
};
|
||||
|
||||
export const getTimezoneRegion = (timezone: string): string => {
|
||||
if (timezone) {
|
||||
const split = timezone.split('/');
|
||||
if (split.length > 1) {
|
||||
return split.pop()!.replace(/_/g, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
return timezone;
|
||||
};
|
||||
|
||||
export const getUserCustomStatus = (user?: UserModel | UserProfile): UserCustomStatus | undefined => {
|
||||
try {
|
||||
if (typeof user?.props?.customStatus === 'string') {
|
||||
|
|
|
|||
|
|
@ -229,6 +229,7 @@
|
|||
"date_separator.yesterday": "Yesterday",
|
||||
"display_settings.clockDisplay": "Clock Display",
|
||||
"display_settings.theme": "Theme",
|
||||
"display_settings.timezone": "Timezone",
|
||||
"download.error": "Unable to download the file. Try again later",
|
||||
"edit_post.editPost": "Edit the post...",
|
||||
"edit_post.save": "Save",
|
||||
|
|
@ -691,6 +692,9 @@
|
|||
"settings_display.clock.normal": "12-hour clock (example: 4:00 PM)",
|
||||
"settings_display.clock.preferTime": "Select how you prefer time displayed.",
|
||||
"settings_display.custom_theme": "Custom Theme",
|
||||
"settings_display.timezone.automatically": "Set automatically",
|
||||
"settings_display.timezone.manual": "Change timezone",
|
||||
"settings_display.timezone.select": "Select Timezone",
|
||||
"settings.about": "About {appTitle}",
|
||||
"settings.display": "Display",
|
||||
"settings.display.militaryClock.save": "Save",
|
||||
|
|
|
|||
Loading…
Reference in a new issue