MM-39711 Gekidou Settings Main Screen (#6281)

* added chevron to menu item component

* starting with the skeleton

* starting with the skeleton

* starting with the skeleton

* starting with the skeleton

* remove extra line

* tested on tablets

* some corrections

* corrections as per review

* updated snapshot

* correction after review

* removed unnecessary screen

* refactored

* updated the testIDs

* Update Package.resolved

* refactor

* removed Mattermost as default server name
This commit is contained in:
Avinash Lingaloo
2022-05-18 11:37:11 +04:00
committed by GitHub
parent e2d45165ab
commit b94790960f
11 changed files with 445 additions and 94 deletions

View File

@@ -30,8 +30,7 @@ exports[`DrawerItem should match snapshot 1`] = `
<View
style={
Object {
"flexDirection": "row",
"minHeight": 50,
"flexDirection": "column",
}
}
>
@@ -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,
]
}
>
<Icon
name="icon-name"
<View
style={
Array [
Object {
"color": "rgba(63,67,80,0.64)",
"fontSize": 24,
},
Object {
"color": "#d24b4e",
"alignItems": "center",
"height": 50,
"justifyContent": "center",
"marginLeft": 5,
"width": 45,
},
undefined,
]
}
/>
</View>
<View
style={
Object {
"flex": 1,
}
}
>
>
<Icon
name="icon-name"
style={
Array [
Object {
"color": "rgba(63,67,80,0.64)",
"fontSize": 24,
},
Object {
"color": "#d24b4e",
},
]
}
/>
</View>
<View
style={
Object {
"flex": 1,
"justifyContent": "center",
"paddingBottom": 14,
"paddingTop": 14,
"paddingVertical": 14,
}
}
>
@@ -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
</Text>
</View>
<View
style={
Object {
"backgroundColor": "rgba(63,67,80,0.2)",
"height": 1,
}
}
/>
</View>
<View
style={
Object {
"backgroundColor": "rgba(63,67,80,0.2)",
"height": 1,
}
}
/>
</View>
</View>
</View>
@@ -147,8 +151,7 @@ exports[`DrawerItem should match snapshot without separator and centered false 1
<View
style={
Object {
"flexDirection": "row",
"minHeight": 50,
"flexDirection": "column",
}
}
>
@@ -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,
]
}
>
<Icon
name="icon-name"
<View
style={
Array [
Object {
"color": "rgba(63,67,80,0.64)",
"fontSize": 24,
},
Object {
"color": "#d24b4e",
"alignItems": "center",
"height": 50,
"justifyContent": "center",
"marginLeft": 5,
"width": 45,
},
undefined,
]
}
/>
</View>
<View
style={
Object {
"flex": 1,
}
}
>
>
<Icon
name="icon-name"
style={
Array [
Object {
"color": "rgba(63,67,80,0.64)",
"fontSize": 24,
},
Object {
"color": "#d24b4e",
},
]
}
/>
</View>
<View
style={
Object {
"flex": 1,
"justifyContent": "center",
"paddingBottom": 14,
"paddingTop": 14,
"paddingVertical": 14,
}
}
>
@@ -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,
]
}
>

View File

@@ -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<ViewStyle>;
iconName?: string;
containerStyle?: StyleProp<ViewStyle>;
isDestructor?: boolean;
labelComponent?: ReactNode;
leftComponent?: ReactNode;
messageValues?: Record<string, any>;
onPress: () => void;
separator?: boolean;
showArrow?: boolean;
testID: string;
theme: Theme;
labelStyle?: StyleProp<TextStyle>;
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 = (<View style={style.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)}
>
<View style={style.container}>
{icon && (
<View style={[style.iconContainer, iconContainerStyle]}>
{icon}
</View>
)}
<View style={style.wrapper}>
<View style={style.mainContainer}>
<View style={[style.container, containerStyle]}>
{icon && (
<View style={[style.iconContainer, iconContainerStyle]}>
{icon}
</View>
)}
<View style={style.labelContainer}>
{label}
</View>
{divider}
{Boolean(showArrow) && (
<CompassIcon
name='chevron-right'
style={style.chevron}
/>
)}
</View>
{Boolean(separator) && (<View style={style.divider}/>)}
</View>
</TouchableWithFeedback>
);

View File

@@ -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,

View File

@@ -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 (

View File

@@ -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;
}

View File

@@ -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,
},
};

View File

@@ -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));

View File

@@ -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<MenuItemProps, 'testID'| 'theme'>;
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 (
<MenuItem
labelStyle={styles.menuLabel}
onPress={onPress}
separator={true}
showArrow={Platform.select({ios: true, default: false})}
theme={theme}
{...options}
/>
);
};
export default SettingOption;

View File

@@ -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 (
<SafeAreaView
edges={edges}
style={styles.container}
testID='account.screen'
>
<ScrollView
alwaysBounceVertical={false}
contentContainerStyle={styles.wrapper}
>
<View style={styles.divider}/>
<View
style={styles.group}
>
<SettingOption
type='notification'
onPress={onPressHandler}
/>
<SettingOption
type='display'
onPress={onPressHandler}
/>
<SettingOption
type='advanced_settings'
onPress={onPressHandler}
/>
<SettingOption
type='about'
onPress={goToAbout}
messageValues={{appTitle: serverName}}
separator={Platform.OS === 'ios'}
/>
</View>
<View style={middleDividerStyle}/>
<View
style={styles.group}
>
{showHelp &&
<SettingOption
type='help'
onPress={onPressHandler}
isLink={true}
containerStyle={styles.innerContainerStyle}
/>
}
</View>
</ScrollView>
</SafeAreaView>
);
};
export default Settings;

View File

@@ -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.",

1
package-lock.json generated
View File

@@ -5,6 +5,7 @@
"requires": true,
"packages": {
"": {
"name": "mattermost-mobile",
"version": "2.0.0",
"hasInstallScript": true,
"license": "Apache 2.0",