diff --git a/app/components/menu_item/__snapshots__/index.test.tsx.snap b/app/components/menu_item/__snapshots__/index.test.tsx.snap
index 7f502a137..39d2569e3 100644
--- a/app/components/menu_item/__snapshots__/index.test.tsx.snap
+++ b/app/components/menu_item/__snapshots__/index.test.tsx.snap
@@ -30,8 +30,7 @@ exports[`DrawerItem should match snapshot 1`] = `
@@ -39,45 +38,48 @@ exports[`DrawerItem should match snapshot 1`] = `
style={
Array [
Object {
- "alignItems": "center",
- "height": 50,
- "justifyContent": "center",
- "marginLeft": 5,
- "width": 45,
+ "flexDirection": "row",
+ "minHeight": 50,
},
undefined,
]
}
>
-
-
-
+ >
+
+
@@ -90,6 +92,7 @@ exports[`DrawerItem should match snapshot 1`] = `
"includeFontPadding": false,
"textAlignVertical": "center",
},
+ undefined,
Object {
"color": "#d24b4e",
},
@@ -97,21 +100,22 @@ exports[`DrawerItem should match snapshot 1`] = `
"textAlign": "center",
"textAlignVertical": "center",
},
+ false,
]
}
>
default message
-
+
@@ -147,8 +151,7 @@ exports[`DrawerItem should match snapshot without separator and centered false 1
@@ -156,45 +159,48 @@ exports[`DrawerItem should match snapshot without separator and centered false 1
style={
Array [
Object {
- "alignItems": "center",
- "height": 50,
- "justifyContent": "center",
- "marginLeft": 5,
- "width": 45,
+ "flexDirection": "row",
+ "minHeight": 50,
},
undefined,
]
}
>
-
-
-
+ >
+
+
@@ -207,10 +213,12 @@ exports[`DrawerItem should match snapshot without separator and centered false 1
"includeFontPadding": false,
"textAlignVertical": "center",
},
+ undefined,
Object {
"color": "#d24b4e",
},
Object {},
+ false,
]
}
>
diff --git a/app/components/menu_item/index.tsx b/app/components/menu_item/index.tsx
index 49aaab63a..239027bd0 100644
--- a/app/components/menu_item/index.tsx
+++ b/app/components/menu_item/index.tsx
@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React, {ReactNode} from 'react';
-import {Platform, StyleProp, View, ViewStyle} from 'react-native';
+import {Platform, StyleProp, TextStyle, View, ViewStyle} from 'react-native';
import CompassIcon from '@components/compass_icon';
import FormattedText from '@components/formatted_text';
@@ -11,19 +11,24 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
export const ITEM_HEIGHT = 50;
-type MenuItemProps = {
+export type MenuItemProps = {
centered?: boolean;
defaultMessage?: string;
i18nId?: string;
iconContainerStyle?: StyleProp;
iconName?: string;
+ containerStyle?: StyleProp;
isDestructor?: boolean;
labelComponent?: ReactNode;
leftComponent?: ReactNode;
+ messageValues?: Record;
onPress: () => void;
separator?: boolean;
+ showArrow?: boolean;
testID: string;
theme: Theme;
+ labelStyle?: StyleProp;
+ isLink?: boolean;
};
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
@@ -43,14 +48,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
color: changeOpacity(theme.centerChannelColor, 0.64),
fontSize: 24,
},
- wrapper: {
- flex: 1,
- },
labelContainer: {
flex: 1,
justifyContent: 'center',
- paddingTop: 14,
- paddingBottom: 14,
+ paddingVertical: 14,
},
centerLabel: {
textAlign: 'center',
@@ -66,6 +67,19 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
height: 1,
},
+ chevron: {
+ alignSelf: 'center',
+ color: changeOpacity(theme.centerChannelColor, 0.64),
+ fontSize: 24,
+ marginRight: 8,
+ },
+ linkContainer: {
+ marginHorizontal: 15,
+ color: theme.linkColor,
+ },
+ mainContainer: {
+ flexDirection: 'column',
+ },
};
});
@@ -76,11 +90,16 @@ const MenuItem = (props: MenuItemProps) => {
i18nId,
iconContainerStyle,
iconName,
+ containerStyle,
isDestructor = false,
+ isLink = false,
labelComponent,
+ labelStyle,
leftComponent,
+ messageValues,
onPress,
separator = true,
+ showArrow = false,
testID,
theme,
} = props;
@@ -92,11 +111,6 @@ const MenuItem = (props: MenuItemProps) => {
destructor.color = theme.errorTextColor;
}
- let divider;
- if (separator) {
- divider = ();
- }
-
let icon;
if (leftComponent) {
icon = leftComponent;
@@ -119,9 +133,12 @@ const MenuItem = (props: MenuItemProps) => {
defaultMessage={defaultMessage}
style={[
style.label,
+ labelStyle,
destructor,
centered ? style.centerLabel : {},
+ isLink && style.linkContainer,
]}
+ values={messageValues}
/>
);
}
@@ -132,18 +149,24 @@ const MenuItem = (props: MenuItemProps) => {
onPress={onPress}
underlayColor={changeOpacity(theme.centerChannelColor, Platform.select({android: 0.1, ios: 0.3}) || 0.3)}
>
-
- {icon && (
-
- {icon}
-
- )}
-
+
+
+ {icon && (
+
+ {icon}
+
+ )}
{label}
- {divider}
+ {Boolean(showArrow) && (
+
+ )}
+ {Boolean(separator) && ()}
);
diff --git a/app/constants/screens.ts b/app/constants/screens.ts
index ef4478b45..c1e0d53b8 100644
--- a/app/constants/screens.ts
+++ b/app/constants/screens.ts
@@ -3,7 +3,6 @@
export const ABOUT = 'About';
export const ACCOUNT = 'Account';
-export const EMOJI_PICKER = 'EmojiPicker';
export const APPS_FORM = 'AppForm';
export const BOTTOM_SHEET = 'BottomSheet';
export const BROWSE_CHANNELS = 'BrowseChannels';
@@ -20,6 +19,7 @@ export const CUSTOM_STATUS_CLEAR_AFTER = 'CustomStatusClearAfter';
export const EDIT_POST = 'EditPost';
export const EDIT_PROFILE = 'EditProfile';
export const EDIT_SERVER = 'EditServer';
+export const EMOJI_PICKER = 'EmojiPicker';
export const FIND_CHANNELS = 'FindChannels';
export const FORGOT_PASSWORD = 'ForgotPassword';
export const GALLERY = 'Gallery';
@@ -32,40 +32,41 @@ export const LATEX = 'Latex';
export const LOGIN = 'Login';
export const MENTIONS = 'Mentions';
export const MFA = 'MFA';
-export const SELECT_TEAM = 'SelectTeam';
export const PERMALINK = 'Permalink';
export const POST_OPTIONS = 'PostOptions';
export const REACTIONS = 'Reactions';
export const SAVED_POSTS = 'SavedPosts';
export const SEARCH = 'Search';
+export const SELECT_TEAM = 'SelectTeam';
export const SERVER = 'Server';
+export const SETTINGS = 'Settings';
+export const SNACK_BAR = 'SnackBar';
export const SSO = 'SSO';
export const THREAD = 'Thread';
export const THREAD_FOLLOW_BUTTON = 'ThreadFollowButton';
export const THREAD_OPTIONS = 'ThreadOptions';
export const USER_PROFILE = 'UserProfile';
-export const SNACK_BAR = 'SnackBar';
export default {
ABOUT,
ACCOUNT,
- EMOJI_PICKER,
APPS_FORM,
BOTTOM_SHEET,
BROWSE_CHANNELS,
CHANNEL,
- CREATE_OR_EDIT_CHANNEL,
CHANNEL_ADD_PEOPLE,
- CHANNEL_EDIT,
CHANNEL_DETAILS,
+ CHANNEL_EDIT,
CODE,
CREATE_DIRECT_MESSAGE,
+ CREATE_OR_EDIT_CHANNEL,
CREATE_TEAM,
- CUSTOM_STATUS_CLEAR_AFTER,
CUSTOM_STATUS,
+ CUSTOM_STATUS_CLEAR_AFTER,
EDIT_POST,
EDIT_PROFILE,
EDIT_SERVER,
+ EMOJI_PICKER,
FIND_CHANNELS,
FORGOT_PASSWORD,
GALLERY,
@@ -78,19 +79,20 @@ export default {
LOGIN,
MENTIONS,
MFA,
- SELECT_TEAM,
PERMALINK,
POST_OPTIONS,
REACTIONS,
SAVED_POSTS,
SEARCH,
+ SELECT_TEAM,
SERVER,
+ SETTINGS,
+ SNACK_BAR,
SSO,
THREAD,
THREAD_FOLLOW_BUTTON,
THREAD_OPTIONS,
USER_PROFILE,
- SNACK_BAR,
};
export const MODAL_SCREENS_WITHOUT_BACK = [
@@ -99,12 +101,12 @@ export const MODAL_SCREENS_WITHOUT_BACK = [
CREATE_DIRECT_MESSAGE,
CREATE_TEAM,
CUSTOM_STATUS,
- EMOJI_PICKER,
EDIT_POST,
EDIT_PROFILE,
EDIT_SERVER,
- GALLERY,
+ EMOJI_PICKER,
FIND_CHANNELS,
+ GALLERY,
PERMALINK,
REACTIONS,
SAVED_POSTS,
diff --git a/app/screens/home/account/components/options/settings/index.tsx b/app/screens/home/account/components/options/settings/index.tsx
index 1df0aa0ff..9ebb67a30 100644
--- a/app/screens/home/account/components/options/settings/index.tsx
+++ b/app/screens/home/account/components/options/settings/index.tsx
@@ -2,10 +2,13 @@
// See LICENSE.txt for license information.
import React, {useCallback} from 'react';
+import {useIntl} from 'react-intl';
import {TextStyle} from 'react-native';
import FormattedText from '@components/formatted_text';
import MenuItem from '@components/menu_item';
+import Screens from '@constants/screens';
+import {showModal} from '@screens/navigation';
import {preventDoubleTap} from '@utils/tap';
type Props = {
@@ -15,8 +18,18 @@ type Props = {
}
const Settings = ({isTablet, style, theme}: Props) => {
+ const intl = useIntl();
+
const openSettings = useCallback(preventDoubleTap(() => {
- // TODO: Open Saved messages screen in either a screen or in line for tablets
+ if (isTablet) {
+ //todo: https://mattermost.atlassian.net/browse/MM-39711
+ // eslint-disable-next-line no-console
+ console.log('Settings on tablets need to be figured out and implemented - @Avinash');
+ }
+ showModal(
+ Screens.SETTINGS,
+ intl.formatMessage({id: 'mobile.screen.settings', defaultMessage: 'Settings'}),
+ );
}), [isTablet]);
return (
diff --git a/app/screens/index.tsx b/app/screens/index.tsx
index a47b21d28..5a28418e1 100644
--- a/app/screens/index.tsx
+++ b/app/screens/index.tsx
@@ -171,7 +171,6 @@ Navigation.setLazyComponentRegistrator((screenName) => {
break;
case Screens.SNACK_BAR: {
const snackBarScreen = withServerDatabase(require('@screens/snack_bar').default);
-
Navigation.registerComponent(Screens.SNACK_BAR, () =>
Platform.select({
default: snackBarScreen,
@@ -181,9 +180,10 @@ Navigation.setLazyComponentRegistrator((screenName) => {
break;
}
case Screens.THREAD_OPTIONS:
- screen = withServerDatabase(
- require('@screens/thread_options').default,
- );
+ screen = withServerDatabase(require('@screens/thread_options').default);
+ break;
+ case Screens.SETTINGS:
+ screen = withServerDatabase(require('@screens/settings').default);
break;
}
diff --git a/app/screens/settings/constant.ts b/app/screens/settings/constant.ts
new file mode 100644
index 000000000..cc5ca275c
--- /dev/null
+++ b/app/screens/settings/constant.ts
@@ -0,0 +1,37 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {t} from '@i18n';
+
+export const SettingOptionConfig = {
+ notification: {
+ defaultMessage: 'Notifications',
+ i18nId: t('general_settings.notifications'),
+ iconName: 'bell-outline',
+ testID: 'general_settings.notifications',
+ },
+ display: {
+ defaultMessage: 'Display',
+ i18nId: t('general_settings.display'),
+ iconName: 'layers-outline',
+ testID: 'general_settings.display',
+ },
+ advanced_settings: {
+ defaultMessage: 'Advanced Settings',
+ i18nId: t('general_settings.advanced_settings'),
+ iconName: 'tune',
+ testID: 'general_settings.advanced',
+ },
+ about: {
+ defaultMessage: 'About {appTitle}',
+ i18nId: t('general_settings.about'),
+ iconName: 'information-outline',
+ testID: 'general_settings.about',
+ },
+ help: {
+ defaultMessage: 'Help',
+ i18nId: t('general_settings.help'),
+ testID: 'general_settings.help',
+ showArrow: false,
+ },
+};
diff --git a/app/screens/settings/index.tsx b/app/screens/settings/index.tsx
new file mode 100644
index 000000000..dfca8eaff
--- /dev/null
+++ b/app/screens/settings/index.tsx
@@ -0,0 +1,27 @@
+// 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 {of as of$} from 'rxjs';
+import {switchMap} from 'rxjs/operators';
+
+import {observeConfig} from '@queries/servers/system';
+import {isValidUrl} from '@utils/url';
+
+import Settings from './settings';
+
+import type {WithDatabaseArgs} from '@typings/database/database';
+
+const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
+ const config = observeConfig(database);
+ const siteName = config.pipe(switchMap((c) => of$(c?.SiteName)));
+ const showHelp = observeConfig(database).pipe(switchMap((c) => of$(c?.HelpLink ? isValidUrl(c.HelpLink) : false)));
+
+ return {
+ showHelp,
+ siteName,
+ };
+});
+
+export default withDatabase(enhanced(Settings));
diff --git a/app/screens/settings/setting_option.tsx b/app/screens/settings/setting_option.tsx
new file mode 100644
index 000000000..57dbb788e
--- /dev/null
+++ b/app/screens/settings/setting_option.tsx
@@ -0,0 +1,46 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+import {Platform} from 'react-native';
+
+import MenuItem, {MenuItemProps} from '@components/menu_item';
+import {useTheme} from '@context/theme';
+import {makeStyleSheetFromTheme} from '@utils/theme';
+import {typography} from '@utils/typography';
+
+import {SettingOptionConfig} from './constant';
+
+type Props = {
+ type: 'notification' | 'display' | 'advanced_settings' | 'about' | 'help';
+ onPress: () => void;
+} & Omit;
+
+const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
+ return {
+ menuLabel: {
+ color: theme.centerChannelColor,
+ ...typography('Body', 200),
+ },
+ };
+});
+
+const SettingOption = ({type, onPress, ...rest}: Props) => {
+ const theme = useTheme();
+ const styles = getStyleSheet(theme);
+ const optionConfig = SettingOptionConfig[type];
+ const options = {...optionConfig, ...rest};
+
+ return (
+
+ );
+};
+
+export default SettingOption;
diff --git a/app/screens/settings/settings.tsx b/app/screens/settings/settings.tsx
new file mode 100644
index 000000000..10c97b7ab
--- /dev/null
+++ b/app/screens/settings/settings.tsx
@@ -0,0 +1,187 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {useCallback, useEffect, useMemo} from 'react';
+import {useIntl} from 'react-intl';
+import {Alert, BackHandler, Platform, ScrollView, View} from 'react-native';
+import {Navigation} from 'react-native-navigation';
+import {Edge, SafeAreaView} from 'react-native-safe-area-context';
+
+import CompassIcon from '@components/compass_icon';
+import {Screens} from '@constants';
+import {useServerDisplayName} from '@context/server';
+import {useTheme} from '@context/theme';
+import {dismissModal, goToScreen, setButtons} from '@screens/navigation';
+import {preventDoubleTap} from '@utils/tap';
+import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
+import {typography} from '@utils/typography';
+
+import SettingOption from './setting_option';
+
+const edges: Edge[] = ['left', 'right'];
+const CLOSE_BUTTON_ID = 'close-settings';
+
+const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
+ return {
+ container: {
+ flex: 1,
+ backgroundColor: theme.centerChannelBg,
+ },
+ wrapper: {
+ backgroundColor: changeOpacity(theme.centerChannelColor, 0.06),
+ ...Platform.select({
+ ios: {
+ flex: 1,
+ paddingTop: 35,
+ },
+ }),
+ },
+ divider: {
+ backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
+ height: 1,
+ },
+ middleDivider: {
+ borderTopWidth: 1,
+ borderBottomWidth: 1,
+ borderColor: changeOpacity(theme.centerChannelColor, 0.1),
+ height: 35,
+ },
+ group: {
+ backgroundColor: theme.centerChannelBg,
+ },
+ innerContainerStyle: {
+ paddingLeft: 8,
+ },
+ menuLabel: {
+ color: theme.centerChannelColor,
+ ...typography('Body', 200),
+ },
+ };
+});
+
+type SettingsProps = {
+ componentId: string;
+ siteName: string;
+ showHelp: boolean;
+}
+
+//todo: handle display on tablet and Profile the whole feature - https://mattermost.atlassian.net/browse/MM-39711
+
+const Settings = ({componentId, showHelp, siteName}: SettingsProps) => {
+ const theme = useTheme();
+ const intl = useIntl();
+ const styles = getStyleSheet(theme);
+ const serverDisplayName = useServerDisplayName();
+ const serverName = siteName || serverDisplayName;
+
+ const closeButton = useMemo(() => {
+ return {
+ id: CLOSE_BUTTON_ID,
+ icon: CompassIcon.getImageSourceSync('close', 24, theme.centerChannelColor),
+ testID: CLOSE_BUTTON_ID,
+ };
+ }, [theme.centerChannelColor]);
+
+ const close = useCallback(() => {
+ dismissModal({componentId});
+
+ return true;
+ }, []);
+
+ useEffect(() => {
+ setButtons(componentId, {
+ leftButtons: [closeButton],
+ });
+ }, []);
+
+ useEffect(() => {
+ const backHandler = BackHandler.addEventListener('hardwareBackPress', close);
+ return () => {
+ backHandler.remove();
+ };
+ }, []);
+
+ useEffect(() => {
+ const unsubscribe = Navigation.events().registerComponentListener({
+ navigationButtonPressed: ({buttonId}: { buttonId: string }) => {
+ if (buttonId === CLOSE_BUTTON_ID) {
+ close();
+ }
+ },
+ }, componentId);
+
+ return () => {
+ unsubscribe.remove();
+ };
+ }, []);
+
+ const onPressHandler = () => {
+ return Alert.alert(
+ 'The functionality you are trying to use has not yet been implemented.',
+ );
+ };
+
+ const goToAbout = preventDoubleTap(() => {
+ const screen = Screens.ABOUT;
+ const title = intl.formatMessage({id: 'about.title', defaultMessage: 'About {appTitle}'}, {appTitle: serverName});
+
+ goToScreen(screen, title);
+ });
+
+ let middleDividerStyle = styles.divider;
+ if (Platform.OS === 'ios') {
+ middleDividerStyle = styles.middleDivider;
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {showHelp &&
+
+ }
+
+
+
+ );
+};
+
+export default Settings;
diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json
index 5607c1faf..cdb174136 100644
--- a/assets/base/i18n/en.json
+++ b/assets/base/i18n/en.json
@@ -9,6 +9,7 @@
"about.teamEditionSt": "All your team communication in one place, instantly searchable and accessible anywhere.",
"about.teamEditiont0": "Team Edition",
"about.teamEditiont1": "Enterprise Edition",
+ "about.title": "About {appTitle}",
"account.logout": "Log out",
"account.logout_from": "Log out of {serverName}",
"account.saved_messages": "Saved Messages",
@@ -216,6 +217,11 @@
"gallery.save_failed": "Unable to save the file",
"gallery.unsupported": "Preview isn't supported for this file type. Try downloading or sharing to open it in another app.",
"gallery.video_saved": "Video saved",
+ "general_settings.about": "About {appTitle}",
+ "general_settings.advanced_settings": "Advanced Settings",
+ "general_settings.display": "Display",
+ "general_settings.help": "Help",
+ "general_settings.notifications": "Notifications",
"get_post_link_modal.title": "Copy Link",
"global_threads.allThreads": "All Your Threads",
"global_threads.emptyThreads.message": "Any threads you are mentioned in or have participated in will show here along with any threads you have followed.",
@@ -437,6 +443,7 @@
"mobile.routes.table": "Table",
"mobile.routes.user_profile": "Profile",
"mobile.screen.saved_posts": "Saved Messages",
+ "mobile.screen.settings": "Settings",
"mobile.screen.your_profile": "Your Profile",
"mobile.search.jump": "Jump to recent messages",
"mobile.server_identifier.exists": "You are already connected to this server.",
diff --git a/package-lock.json b/package-lock.json
index 6afc1ca95..77184dc7f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,6 +5,7 @@
"requires": true,
"packages": {
"": {
+ "name": "mattermost-mobile",
"version": "2.0.0",
"hasInstallScript": true,
"license": "Apache 2.0",