MM-45344 Gekidou Remove <MenuItem/> (#6522)

* added MENU_ITEM_HEIGHT to constant/view

* fix user presence and your profile

* added MENU_ITEM_HEIGHT to constant/view

* fix user presence and your profile

* UI Polish - Custom Status

* UI Polish - Settings

* UI Polish - logout

* refactored styles

* removed 'throws DataOperatorException' from './database/`

* fix for copy link option

* fix autoresponder

1.  user should be allowed to enter paragraph

2. the OOO was not immediately being updated on the notification main screen.  The fix is to cal fetchStatusInBatch after the updateMe operation

* About Screen - code clean up

* removed MenuItem component from common_post_options

* removed MenuItem from Settings

* refactored show_more and recent_item

* removed menu_item component

* Update setting_container.tsx

* PR review correction

* Update setting_container.tsx

* Update recent_item.tsx
This commit is contained in:
Avinash Lingaloo
2022-08-04 12:26:27 +04:00
committed by GitHub
parent 6397548f68
commit e443a69265
30 changed files with 284 additions and 902 deletions

View File

@@ -1,64 +1,38 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useMemo} from 'react';
import React from 'react';
import {useIntl} from 'react-intl';
import FormattedText from '@components/formatted_text';
import MenuItem from '@components/menu_item';
import {useTheme} from '@context/theme';
import {makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
destructive: {
color: theme.dndIndicator,
},
label: {
color: theme.centerChannelColor,
...typography('Body', 200),
},
iconContainerStyle: {
marginLeft: 0,
},
}));
import OptionItem from '@components/option_item';
type BaseOptionType = {
i18nId: string;
defaultMessage: string;
i18nId: string;
iconName: string;
isDestructive?: boolean;
onPress: () => void;
testID: string;
isDestructive?: boolean;
}
const BaseOption = ({
i18nId,
defaultMessage,
i18nId,
iconName,
isDestructive = false,
onPress,
testID,
isDestructive = false,
}: BaseOptionType) => {
const theme = useTheme();
const styles = getStyleSheet(theme);
const label = useMemo(() => (
<FormattedText
id={i18nId}
defaultMessage={defaultMessage}
style={[styles.label, isDestructive && styles.destructive]}
/>
), [i18nId, defaultMessage, theme]);
const intl = useIntl();
return (
<MenuItem
iconContainerStyle={styles.iconContainerStyle}
iconName={iconName}
isDestructor={isDestructive}
labelComponent={label}
onPress={onPress}
separator={false}
<OptionItem
action={onPress}
destructive={isDestructive}
icon={iconName}
label={intl.formatMessage({id: i18nId, defaultMessage})}
testID={testID}
type='default'
/>
);
};

View File

@@ -1,235 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DrawerItem should match snapshot 1`] = `
<View
onMoveShouldSetResponder={[Function]}
onMoveShouldSetResponderCapture={[Function]}
onResponderEnd={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderReject={[Function]}
onResponderRelease={[Function]}
onResponderStart={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
onStartShouldSetResponderCapture={[Function]}
testID="test-id"
>
<View
accessible={true}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
>
<View
style={
Object {
"flexDirection": "column",
}
}
>
<View
style={
Array [
Object {
"flexDirection": "row",
"minHeight": 50,
},
undefined,
]
}
>
<View
style={
Array [
Object {
"alignItems": "center",
"height": 50,
"justifyContent": "center",
"marginLeft": 5,
"width": 45,
},
undefined,
]
}
>
<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",
"paddingVertical": 14,
}
}
>
<Text
style={
Array [
Object {
"color": "rgba(63,67,80,0.5)",
"fontSize": 17,
"includeFontPadding": false,
"textAlignVertical": "center",
},
undefined,
Object {
"color": "#d24b4e",
},
Object {
"textAlign": "center",
"textAlignVertical": "center",
},
false,
]
}
>
default message
</Text>
</View>
</View>
<View
style={
Array [
Object {
"backgroundColor": "rgba(63,67,80,0.12)",
"height": 1,
},
undefined,
]
}
/>
</View>
</View>
</View>
`;
exports[`DrawerItem should match snapshot without separator and centered false 1`] = `
<View
onMoveShouldSetResponder={[Function]}
onMoveShouldSetResponderCapture={[Function]}
onResponderEnd={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderReject={[Function]}
onResponderRelease={[Function]}
onResponderStart={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
onStartShouldSetResponderCapture={[Function]}
testID="test-id"
>
<View
accessible={true}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
>
<View
style={
Object {
"flexDirection": "column",
}
}
>
<View
style={
Array [
Object {
"flexDirection": "row",
"minHeight": 50,
},
undefined,
]
}
>
<View
style={
Array [
Object {
"alignItems": "center",
"height": 50,
"justifyContent": "center",
"marginLeft": 5,
"width": 45,
},
undefined,
]
}
>
<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",
"paddingVertical": 14,
}
}
>
<Text
style={
Array [
Object {
"color": "rgba(63,67,80,0.5)",
"fontSize": 17,
"includeFontPadding": false,
"textAlignVertical": "center",
},
undefined,
Object {
"color": "#d24b4e",
},
Object {},
false,
]
}
>
default message
</Text>
</View>
</View>
</View>
</View>
</View>
`;

View File

@@ -1,42 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {Preferences} from '@constants';
import {renderWithIntl} from '@test/intl-test-helper';
import MenuItem from '.';
describe('DrawerItem', () => {
const baseProps = {
onPress: () => null,
testID: 'test-id',
centered: true,
defaultMessage: 'default message',
i18nId: 'i18-id',
iconName: 'icon-name',
isDestructor: true,
separator: true,
theme: Preferences.THEMES.denim,
};
test('should match snapshot', () => {
const wrapper = renderWithIntl(<MenuItem {...baseProps}/>);
expect(wrapper.toJSON()).toMatchSnapshot();
});
test('should match snapshot without separator and centered false', () => {
const props = {
...baseProps,
centered: false,
separator: false,
};
const wrapper = renderWithIntl(
<MenuItem {...props}/>,
);
expect(wrapper.toJSON()).toMatchSnapshot();
});
});

View File

@@ -1,173 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {ReactNode} from 'react';
import {Platform, StyleProp, TextStyle, View, ViewStyle} from 'react-native';
import CompassIcon from '@components/compass_icon';
import FormattedText from '@components/formatted_text';
import TouchableWithFeedback from '@components/touchable_with_feedback';
import {useTheme} from '@context/theme';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
export const ITEM_HEIGHT = 50;
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
container: {
flexDirection: 'row',
minHeight: ITEM_HEIGHT,
},
iconContainer: {
width: 45,
height: ITEM_HEIGHT,
alignItems: 'center',
justifyContent: 'center',
marginLeft: 5,
},
icon: {
color: changeOpacity(theme.centerChannelColor, 0.64),
fontSize: 24,
},
labelContainer: {
flex: 1,
justifyContent: 'center',
paddingVertical: 14,
},
centerLabel: {
textAlign: 'center',
textAlignVertical: 'center',
},
label: {
color: changeOpacity(theme.centerChannelColor, 0.5),
fontSize: 17,
textAlignVertical: 'center',
includeFontPadding: false,
},
divider: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.12),
height: 1,
},
chevron: {
alignSelf: 'center',
color: changeOpacity(theme.centerChannelColor, 0.64),
fontSize: 24,
marginRight: 8,
},
linkContainer: {
marginHorizontal: 15,
color: theme.linkColor,
},
mainContainer: {
flexDirection: 'column',
},
};
});
export type MenuItemProps = {
centered?: boolean;
chevronStyle?: StyleProp<ViewStyle>;
containerStyle?: StyleProp<ViewStyle>;
defaultMessage?: string;
i18nId?: string;
iconContainerStyle?: StyleProp<ViewStyle>;
iconName?: string;
isDestructor?: boolean;
isLink?: boolean;
labelComponent?: ReactNode;
labelStyle?: StyleProp<TextStyle>;
leftComponent?: ReactNode;
messageValues?: Record<string, any>;
onPress: () => void;
rightComponent?: ReactNode;
separator?: boolean;
separatorStyle?: StyleProp<ViewStyle>;
showArrow?: boolean;
testID: string;
};
const MenuItem = ({
centered,
chevronStyle,
containerStyle,
defaultMessage = '',
i18nId,
iconContainerStyle,
iconName,
isDestructor = false,
isLink = false,
labelComponent,
labelStyle,
leftComponent,
messageValues,
onPress,
rightComponent,
separator = true,
separatorStyle,
showArrow = false,
testID,
}: MenuItemProps) => {
const theme = useTheme();
const style = getStyleSheet(theme);
let icon;
if (leftComponent) {
icon = leftComponent;
} else if (iconName) {
icon = (
<CompassIcon
name={iconName}
style={[style.icon, isDestructor && {color: theme.errorTextColor}]}
/>
);
}
let label;
if (labelComponent) {
label = labelComponent;
} else if (i18nId) {
label = (
<FormattedText
id={i18nId}
defaultMessage={defaultMessage}
style={[
style.label,
labelStyle,
isDestructor && {color: theme.errorTextColor},
centered ? style.centerLabel : {},
isLink && style.linkContainer,
]}
values={messageValues}
/>
);
}
return (
<TouchableWithFeedback
testID={testID}
onPress={onPress}
underlayColor={changeOpacity(theme.centerChannelColor, Platform.select({android: 0.1, ios: 0.3}) || 0.3)}
>
<View style={style.mainContainer}>
<View style={[style.container, containerStyle]}>
{icon && (
<View style={[style.iconContainer, iconContainerStyle]}>
{icon}
</View>
)}
<View style={style.labelContainer}>
{label}
</View>
{rightComponent}
{Boolean(showArrow) && (
<CompassIcon
name='chevron-right'
style={[style.chevron, chevronStyle]}
/>
)}
</View>
{Boolean(separator) && (<View style={[style.divider, separatorStyle]}/>)}
</View>
</TouchableWithFeedback>
);
};
export default MenuItem;

View File

@@ -98,6 +98,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
export type OptionItemProps = {
action?: (React.Dispatch<React.SetStateAction<string | boolean>>)|((value: string | boolean) => void);
arrowStyle?: StyleProp<ViewStyle>;
containerStyle?: StyleProp<ViewStyle>;
description?: string;
destructive?: boolean;
@@ -117,6 +118,7 @@ export type OptionItemProps = {
const OptionItem = ({
action,
arrowStyle,
containerStyle,
description,
destructive,
@@ -197,6 +199,7 @@ const OptionItem = ({
color={changeOpacity(theme.centerChannelColor, 0.32)}
name='chevron-right'
size={24}
style={arrowStyle}
/>
);
} else if (type === OptionType.REMOVE) {
@@ -239,14 +242,14 @@ const OptionItem = ({
{type === OptionType.RADIO && radioComponent}
<View style={labelStyle}>
<Text
style={[optionLabelTextStyle, labelTextStyle]}
style={[labelTextStyle, optionLabelTextStyle]}
testID={`${testID}.label`}
>
{label}
</Text>
{Boolean(description) &&
<Text
style={[optionDescriptionTextStyle, descriptionTextStyle]}
style={[descriptionTextStyle, optionDescriptionTextStyle]}
testID={`${testID}.description`}
>
{description}

View File

@@ -2,24 +2,25 @@
// See LICENSE.txt for license information.
import React from 'react';
import {TextStyle} from 'react-native';
import {StyleProp, TextStyle} from 'react-native';
import FormattedText from '@components/formatted_text';
import {General} from '@constants';
import {useTheme} from '@context/theme';
import {t} from '@i18n';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
type StatusLabelProps = {
status?: string;
labelStyle?: TextStyle;
labelStyle?: StyleProp<TextStyle>;
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
label: {
color: changeOpacity(theme.centerChannelColor, 0.5),
fontSize: 17,
...typography('Body', 200),
textAlignVertical: 'center',
includeFontPadding: false,
},

View File

@@ -93,8 +93,6 @@ export default class BaseDataOperator {
// We found a record in the database that matches this element; hence, we'll proceed for an UPDATE operation
if (existingRecord) {
// const existingRecord = createOrUpdateRaws[findIndex];
// Some raw value has an update_at field. We'll proceed to update only if the update_at value is different from the record's value in database
const updateRecords = getValidRecordsForUpdate({
tableName,
@@ -125,8 +123,7 @@ export default class BaseDataOperator {
* @param {RawValue[]} prepareRecord.createRaws
* @param {RawValue[]} prepareRecord.updateRaws
* @param {Model[]} prepareRecord.deleteRaws
* @param {(TransformerArgs) => Promise<Model>;} prepareRecord.composer
* @throws {DataOperatorException}
* @param {(TransformerArgs) => Promise<Model>;} transformer
* @returns {Promise<Model[]>}
*/
prepareRecords = async ({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs): Promise<Model[]> => {
@@ -184,7 +181,6 @@ export default class BaseDataOperator {
* batchRecords: Accepts an instance of Database (either Default or Server) and an array of
* prepareCreate/prepareUpdate 'models' and executes the actions on the database.
* @param {Array} models
* @throws {DataOperatorException}
* @returns {Promise<void>}
*/
async batchRecords(models: Model[]): Promise<void> {

View File

@@ -28,7 +28,6 @@ const GroupHandler = (superclass: any) => class extends superclass implements Gr
* handleGroups: Handler responsible for the Create/Update operations occurring on the Group table from the 'Server' schema
*
* @param {HandleGroupArgs}
* @throws DataOperatorException
* @returns {Promise<GroupModel[]>}
*/
handleGroups = async ({groups, prepareRecordsOnly = true}: HandleGroupArgs): Promise<GroupModel[]> => {
@@ -111,7 +110,6 @@ const GroupHandler = (superclass: any) => class extends superclass implements Gr
* handleGroupMembershipsForMember: Handler responsible for the Create/Update operations occurring on the GroupMembership table from the 'Server' schema
*
* @param {HandleGroupMembershipForMemberArgs}
* @throws DataOperatorException
* @returns {Promise<GroupMembershipModel[]>}
*/
handleGroupMembershipsForMember = async ({userId, groups, prepareRecordsOnly = true}: HandleGroupMembershipForMemberArgs): Promise<GroupMembershipModel[]> => {
@@ -169,7 +167,6 @@ const GroupHandler = (superclass: any) => class extends superclass implements Gr
* handleGroupTeamsForTeam: Handler responsible for the Create/Update operations occurring on the GroupTeam table from the 'Server' schema
*
* @param {HandleGroupTeamsForTeamArgs}
* @throws DataOperatorException
* @returns {Promise<GroupTeamModel[]>}
*/
handleGroupTeamsForTeam = async ({teamId, groups, prepareRecordsOnly = true}: HandleGroupTeamsForTeamArgs): Promise<GroupTeamModel[]> => {

View File

@@ -1,57 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import FormattedText from '@components/formatted_text';
import {useTheme} from '@context/theme';
import {t} from '@i18n';
import {makeStyleSheetFromTheme} from '@utils/theme';
type ServerVersionProps = {
config: ClientConfig;
}
const ServerVersion = ({config}: ServerVersionProps) => {
const buildNumber = config.BuildNumber;
const version = config.Version;
const theme = useTheme();
const style = getStyleSheet(theme);
let id = t('mobile.about.serverVersion');
let defaultMessage = 'Server Version: {version} (Build {number})';
let values: {version: string; number?: string} = {
version,
number: buildNumber,
};
if (buildNumber === version) {
id = t('mobile.about.serverVersionNoBuild');
defaultMessage = 'Server Version: {version}';
values = {
version,
number: undefined,
};
}
return (
<FormattedText
id={id}
defaultMessage={defaultMessage}
style={style.info}
values={values}
testID='about.server_version'
/>
);
};
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
info: {
color: theme.centerChannelColor,
fontSize: 16,
lineHeight: 19,
},
};
});
export default ServerVersion;

View File

@@ -8,6 +8,7 @@ import {View} from 'react-native';
import ClearButton from '@components/custom_status/clear_button';
import CustomStatusExpiry from '@components/custom_status/custom_status_expiry';
import FormattedText from '@components/formatted_text';
import {useTheme} from '@context/theme';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import CustomStatusText from './custom_status_text';
@@ -16,9 +17,8 @@ type CustomLabelProps = {
customStatus: UserCustomStatus;
isCustomStatusExpirySupported: boolean;
isStatusSet: boolean;
showRetryMessage: boolean;
theme: Theme;
onClearCustomStatus: () => void;
showRetryMessage: boolean;
};
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
@@ -30,6 +30,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
},
customStatusTextContainer: {
width: '70%',
marginLeft: 16,
},
customStatusExpiryText: {
paddingTop: 3,
@@ -43,14 +44,14 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
};
});
const CustomLabel = ({customStatus, isCustomStatusExpirySupported, isStatusSet, onClearCustomStatus, showRetryMessage, theme}: CustomLabelProps) => {
const style = getStyleSheet(theme);
const CustomLabel = ({customStatus, isCustomStatusExpirySupported, isStatusSet, onClearCustomStatus, showRetryMessage}: CustomLabelProps) => {
const theme = useTheme();
const styles = getStyleSheet(theme);
return (
<>
<View style={style.customStatusTextContainer}>
<View style={styles.customStatusTextContainer}>
<CustomStatusText
theme={theme}
isStatusSet={Boolean(isStatusSet)}
customStatus={customStatus}
/>
@@ -58,7 +59,7 @@ const CustomLabel = ({customStatus, isCustomStatusExpirySupported, isStatusSet,
<CustomStatusExpiry
time={moment(customStatus?.expires_at)}
theme={theme}
textStyles={style.customStatusExpiryText}
textStyles={styles.customStatusExpiryText}
withinBrackets={true}
showPrefix={true}
testID={'custom_status.expiry'}
@@ -69,11 +70,11 @@ const CustomLabel = ({customStatus, isCustomStatusExpirySupported, isStatusSet,
<FormattedText
id={'custom_status.failure_message'}
defaultMessage='Failed to update status. Try again'
style={style.retryMessage}
style={styles.retryMessage}
/>
)}
{isStatusSet && (
<View style={style.clearButton}>
<View style={styles.clearButton}>
<ClearButton
handlePress={onClearCustomStatus}
theme={theme}

View File

@@ -6,12 +6,12 @@ import {View} from 'react-native';
import CompassIcon from '@components/compass_icon';
import Emoji from '@components/emoji';
import {useTheme} from '@context/theme';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
type CustomStatusEmojiProps = {
emoji?: string;
isStatusSet: boolean;
theme: Theme;
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
@@ -22,7 +22,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
};
});
const CustomStatusEmoji = ({emoji, isStatusSet, theme}: CustomStatusEmojiProps) => {
const CustomStatusEmoji = ({emoji, isStatusSet}: CustomStatusEmojiProps) => {
const theme = useTheme();
const styles = getStyleSheet(theme);
return (

View File

@@ -5,27 +5,33 @@ import React from 'react';
import CustomText from '@components/custom_status/custom_status_text';
import FormattedText from '@components/formatted_text';
import {useTheme} from '@context/theme';
import {makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
type CustomStatusTextProps = {
customStatus?: UserCustomStatus;
isStatusSet: boolean;
theme: Theme;
};
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
text: {
color: theme.centerChannelColor,
...typography('Body', 200),
},
}));
const CustomStatusText = ({isStatusSet, customStatus, theme}: CustomStatusTextProps) => {
const CustomStatusText = ({isStatusSet, customStatus}: CustomStatusTextProps) => {
const theme = useTheme();
const styles = getStyleSheet(theme);
let text: React.ReactNode | string;
text = (
<FormattedText
id='mobile.routes.custom_status'
defaultMessage='Set a Status'
style={styles.text}
/>
);
@@ -33,7 +39,6 @@ const CustomStatusText = ({isStatusSet, customStatus, theme}: CustomStatusTextPr
text = customStatus.text;
}
const styles = getStyleSheet(theme);
return (
<CustomText
text={text}

View File

@@ -3,17 +3,18 @@
import React, {useCallback, useEffect, useState} from 'react';
import {useIntl} from 'react-intl';
import {DeviceEventEmitter} from 'react-native';
import {DeviceEventEmitter, TouchableOpacity, View} from 'react-native';
import {updateLocalCustomStatus} from '@actions/local/user';
import {unsetCustomStatus} from '@actions/remote/user';
import MenuItem from '@components/menu_item';
import {Events, Screens} from '@constants';
import {SET_CUSTOM_STATUS_FAILURE} from '@constants/custom_status';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {showModal} from '@screens/navigation';
import {preventDoubleTap} from '@utils/tap';
import {makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
import {getUserCustomStatus, isCustomStatusExpired as checkCustomStatusIsExpired} from '@utils/user';
import CustomLabel from './custom_label';
@@ -21,6 +22,21 @@ import CustomStatusEmoji from './custom_status_emoji';
import type UserModel from '@typings/database/models/servers/user';
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
label: {
color: theme.centerChannelColor,
...typography('Body', 200),
textAlignVertical: 'center',
includeFontPadding: false,
},
body: {
flexDirection: 'row',
marginVertical: 18,
},
};
});
type CustomStatusProps = {
isCustomStatusExpirySupported: boolean;
isTablet: boolean;
@@ -35,6 +51,7 @@ const CustomStatus = ({isCustomStatusExpirySupported, isTablet, currentUser}: Cu
const customStatus = getUserCustomStatus(currentUser);
const isCustomStatusExpired = checkCustomStatusIsExpired(currentUser);
const isStatusSet = !isCustomStatusExpired && (customStatus?.text || customStatus?.emoji);
const styles = getStyleSheet(theme);
useEffect(() => {
const onSetCustomStatusError = () => {
@@ -68,27 +85,23 @@ const CustomStatus = ({isCustomStatusExpirySupported, isTablet, currentUser}: Cu
}), [isTablet]);
return (
<MenuItem
testID='settings.sidebar.custom_status.action'
labelComponent={
<TouchableOpacity
onPress={goToCustomStatusScreen}
>
<View style={styles.body}>
<CustomStatusEmoji
emoji={customStatus?.emoji}
isStatusSet={Boolean(isStatusSet)}
/>
<CustomLabel
theme={theme}
customStatus={customStatus!}
isCustomStatusExpirySupported={isCustomStatusExpirySupported}
isStatusSet={Boolean(isStatusSet)}
onClearCustomStatus={clearCustomStatus}
showRetryMessage={showRetryMessage}
/>
}
leftComponent={
<CustomStatusEmoji
emoji={customStatus?.emoji}
isStatusSet={Boolean(isStatusSet)}
theme={theme}
/>}
separator={false}
onPress={goToCustomStatusScreen}
/>
</View>
</TouchableOpacity>
);
};

View File

@@ -36,13 +36,12 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
divider: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
height: 1,
marginHorizontal: 15,
width: '90%',
alignSelf: 'center',
marginVertical: 8,
},
menuLabel: {
color: theme.centerChannelColor,
fontSize: 16,
lineHeight: 24,
fontFamily: 'OpenSans',
group: {
paddingLeft: 16,
},
};
});
@@ -64,37 +63,29 @@ const AccountOptions = ({user, enableCustomUserStatuses, isCustomStatusExpirySup
}}
>
<View style={styles.container}>
<UserPresence
currentUser={user}
style={styles.menuLabel}
theme={theme}
/>
{enableCustomUserStatuses &&
<CustomStatus
isCustomStatusExpirySupported={isCustomStatusExpirySupported}
isTablet={isTablet}
currentUser={user}
/>}
<View style={styles.group}>
<UserPresence
currentUser={user}
/>
{enableCustomUserStatuses &&
<CustomStatus
isCustomStatusExpirySupported={isCustomStatusExpirySupported}
isTablet={isTablet}
currentUser={user}
/>}
</View>
<View style={styles.divider}/>
<YourProfile
isTablet={isTablet}
style={styles.menuLabel}
theme={theme}
/>
{/* <SavedMessages
isTablet={isTablet}
style={styles.menuLabel}
theme={theme}
/> */}
<Settings
isTablet={isTablet}
style={styles.menuLabel}
/>
<View style={styles.group}>
<YourProfile
isTablet={isTablet}
theme={theme}
/>
<Settings/>
</View>
<View style={styles.divider}/>
<Logout
style={styles.menuLabel}
theme={theme}
/>
<View style={styles.group}>
<Logout/>
</View>
</View>
</Shadow>
);

View File

@@ -3,75 +3,48 @@
import React, {useCallback} from 'react';
import {useIntl} from 'react-intl';
import {TextStyle, View} from 'react-native';
import {logout} from '@actions/remote/session';
import FormattedText from '@components/formatted_text';
import MenuItem from '@components/menu_item';
import OptionItem from '@components/option_item';
import {useServerDisplayName, useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {alertServerLogout} from '@utils/server';
import {preventDoubleTap} from '@utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
type Props = {
style: TextStyle;
theme: Theme;
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
desc: {
color: changeOpacity(theme.centerChannelColor, 0.64),
...typography('Body', 75),
},
};
});
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
label: {
color: theme.dndIndicator,
marginTop: 5,
},
logOutFrom: {
color: changeOpacity(theme.centerChannelColor, 0.64),
fontSize: 12,
height: 30,
lineHeight: 16,
fontFamily: 'OpenSans',
},
}));
const Settings = ({style, theme}: Props) => {
const LogOut = () => {
const theme = useTheme();
const styles = getStyleSheet(theme);
const intl = useIntl();
const serverUrl = useServerUrl();
const serverDisplayName = useServerDisplayName();
const onLogout = useCallback(preventDoubleTap(() => {
alertServerLogout(
serverDisplayName,
() => {
logout(serverUrl);
},
intl,
);
alertServerLogout(serverDisplayName, () => logout(serverUrl), intl);
}), [serverDisplayName, serverUrl, intl]);
return (
<MenuItem
iconName='exit-to-app'
isDestructor={true}
labelComponent={(
<View>
<FormattedText
id='account.logout'
defaultMessage='Log out'
style={[style, styles.label]}
/>
<FormattedText
id={'account.logout_from'}
defaultMessage={'Log out of {serverName}'}
values={{serverName: serverDisplayName}}
style={styles.logOutFrom}
/>
</View>
)}
onPress={onLogout}
separator={false}
<OptionItem
action={onLogout}
description={intl.formatMessage({id: 'account.logout_from', defaultMessage: 'Log out from'}, {serverName: serverDisplayName})}
destructive={true}
icon='exit-to-app'
label={intl.formatMessage({id: 'account.logout', defaultMessage: 'Log out'})}
optionDescriptionTextStyle={styles.desc}
testID='account.logout.action'
type='default'
/>
);
};
export default Settings;
export default LogOut;

View File

@@ -3,47 +3,29 @@
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 OptionItem from '@components/option_item';
import Screens from '@constants/screens';
import {showModal} from '@screens/navigation';
import {logInfo} from '@utils/log';
import {preventDoubleTap} from '@utils/tap';
type Props = {
isTablet: boolean;
style: TextStyle;
}
const Settings = ({isTablet, style}: Props) => {
const Settings = () => {
const intl = useIntl();
const openSettings = useCallback(preventDoubleTap(() => {
if (isTablet) {
//todo: https://mattermost.atlassian.net/browse/MM-39711
logInfo('Settings on tablets need to be figured out and implemented - @Avinash');
}
showModal(
Screens.SETTINGS,
intl.formatMessage({id: 'mobile.screen.settings', defaultMessage: 'Settings'}),
);
}), [isTablet]);
}), []);
return (
<MenuItem
iconName='settings-outline'
labelComponent={
<FormattedText
id='account.settings'
defaultMessage='Settings'
style={style}
/>
}
onPress={openSettings}
separator={false}
<OptionItem
action={openSettings}
icon='settings-outline'
label={intl.formatMessage({id: 'account.settings', defaultMessage: 'Settings'})}
testID='account.settings.action'
type='default'
/>
);
};

View File

@@ -3,36 +3,54 @@
import React, {useCallback} from 'react';
import {useIntl} from 'react-intl';
import {TextStyle} from 'react-native';
import {TouchableOpacity, View} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {setStatus} from '@actions/remote/user';
import MenuItem from '@components/menu_item';
import SlideUpPanelItem, {ITEM_HEIGHT} from '@components/slide_up_panel_item';
import StatusLabel from '@components/status_label';
import UserStatusIndicator from '@components/user_status';
import General from '@constants/general';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {bottomSheet, dismissBottomSheet, dismissModal} from '@screens/navigation';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {preventDoubleTap} from '@utils/tap';
import {changeOpacity} from '@utils/theme';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
import {confirmOutOfOfficeDisabled} from '@utils/user';
import type UserModel from '@typings/database/models/servers/user';
type Props = {
currentUser: UserModel;
style: TextStyle;
theme: Theme;
};
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
label: {
color: theme.centerChannelColor,
...typography('Body', 200),
textAlignVertical: 'center',
includeFontPadding: false,
},
body: {
flexDirection: 'row',
marginTop: 18,
},
spacer: {
marginLeft: 16,
},
};
});
const {OUT_OF_OFFICE, OFFLINE, AWAY, ONLINE, DND} = General;
const UserStatus = ({currentUser, style, theme}: Props) => {
type Props = {
currentUser: UserModel;
};
const UserStatus = ({currentUser}: Props) => {
const intl = useIntl();
const insets = useSafeAreaInsets();
const serverUrl = useServerUrl();
const theme = useTheme();
const styles = getStyleSheet(theme);
const handleSetStatus = useCallback(preventDoubleTap(() => {
const renderContent = () => {
@@ -47,7 +65,7 @@ const UserStatus = ({currentUser, style, theme}: Props) => {
id: 'mobile.set_status.online',
defaultMessage: 'Online',
})}
textStyles={style}
textStyles={styles.label}
/>
<SlideUpPanelItem
icon='clock'
@@ -58,7 +76,7 @@ const UserStatus = ({currentUser, style, theme}: Props) => {
id: 'mobile.set_status.away',
defaultMessage: 'Away',
})}
textStyles={style}
textStyles={styles.label}
/>
<SlideUpPanelItem
icon='minus-circle'
@@ -69,7 +87,7 @@ const UserStatus = ({currentUser, style, theme}: Props) => {
id: 'mobile.set_status.dnd',
defaultMessage: 'Do Not Disturb',
})}
textStyles={style}
textStyles={styles.label}
/>
<SlideUpPanelItem
icon='circle-outline'
@@ -80,7 +98,7 @@ const UserStatus = ({currentUser, style, theme}: Props) => {
id: 'mobile.set_status.offline',
defaultMessage: 'Offline',
})}
textStyles={style}
textStyles={styles.label}
/>
</>
);
@@ -119,23 +137,20 @@ const UserStatus = ({currentUser, style, theme}: Props) => {
}, []);
return (
<MenuItem
labelComponent={
<StatusLabel
labelStyle={style}
status={currentUser.status}
/>
}
leftComponent={
<TouchableOpacity
onPress={handleSetStatus}
>
<View style={styles.body}>
<UserStatusIndicator
size={24}
status={currentUser.status}
/>
}
onPress={handleSetStatus}
separator={false}
testID='account.status.action'
/>
<StatusLabel
labelStyle={[styles.label, styles.spacer]}
status={currentUser.status}
/>
</View>
</TouchableOpacity>
);
};

View File

@@ -3,10 +3,9 @@
import React, {useCallback} from 'react';
import {useIntl} from 'react-intl';
import {DeviceEventEmitter, TextStyle} from 'react-native';
import {DeviceEventEmitter} from 'react-native';
import FormattedText from '@components/formatted_text';
import MenuItem from '@components/menu_item';
import OptionItem from '@components/option_item';
import {Events, Screens} from '@constants';
import {ACCOUNT_OUTLINE_IMAGE} from '@constants/profile';
import {showModal} from '@screens/navigation';
@@ -14,11 +13,10 @@ import {preventDoubleTap} from '@utils/tap';
type Props = {
isTablet: boolean;
style: TextStyle;
theme: Theme;
}
const YourProfile = ({isTablet, style, theme}: Props) => {
const YourProfile = ({isTablet, theme}: Props) => {
const intl = useIntl();
const openProfile = useCallback(preventDoubleTap(() => {
if (isTablet) {
@@ -32,18 +30,12 @@ const YourProfile = ({isTablet, style, theme}: Props) => {
}), [isTablet, theme]);
return (
<MenuItem
iconName={ACCOUNT_OUTLINE_IMAGE}
labelComponent={
<FormattedText
id='account.your_profile'
defaultMessage='Your Profile'
style={style}
/>
}
onPress={openProfile}
separator={false}
<OptionItem
icon={ACCOUNT_OUTLINE_IMAGE}
label={intl.formatMessage({id: 'account.your_profile', defaultMessage: 'Your Profile'})}
testID='account.your_profile.action'
type='default'
action={openProfile}
/>
);
};

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {useIntl} from 'react-intl';
import FormattedText from '@components/formatted_text';
import MenuItem from '@components/menu_item';
import OptionItem from '@components/option_item';
import {useTheme} from '@context/theme';
import {t} from '@i18n';
import {makeStyleSheetFromTheme} from '@utils/theme';
@@ -26,7 +26,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
const ShowMoreButton = ({onPress, showMore}: ShowMoreButtonProps) => {
const theme = useTheme();
const style = getStyleSheet(theme);
const intl = useIntl();
const styles = getStyleSheet(theme);
let id = t('mobile.search.show_more');
let defaultMessage = 'Show more';
@@ -36,17 +37,12 @@ const ShowMoreButton = ({onPress, showMore}: ShowMoreButtonProps) => {
}
return (
<MenuItem
labelComponent={
<FormattedText
id={id}
defaultMessage={defaultMessage}
style={style.showMore}
/>
}
onPress={onPress}
separator={false}
<OptionItem
action={onPress}
testID={'mobile.search.show_more'}
type='default'
label={intl.formatMessage({id, defaultMessage})}
optionLabelTextStyle={styles.showMore}
/>
);
};

View File

@@ -5,7 +5,7 @@ import {useManagedConfig} from '@mattermost/react-native-emm';
import React from 'react';
import {CopyPermalinkOption, FollowThreadOption, ReplyOption, SaveOption} from '@components/common_post_options';
import {ITEM_HEIGHT} from '@components/menu_item';
import {ITEM_HEIGHT} from '@components/option_item';
import {Screens} from '@constants';
import useNavButtonPressed from '@hooks/navigation_button_pressed';
import BottomSheet from '@screens/bottom_sheet';

View File

@@ -8,18 +8,37 @@ import FormattedText from '@components/formatted_text';
import {useTheme} from '@context/theme';
import {t} from '@i18n';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
noticeLink: {
color: theme.linkColor,
...typography('Body', 50),
},
footerText: {
color: changeOpacity(theme.centerChannelColor, 0.5),
...typography('Body', 50),
marginBottom: 10,
},
hyphenText: {
marginBottom: 0,
},
};
});
type TosPrivacyContainerProps = {
config: ClientConfig;
onPressTOS: () => void;
onPressPrivacyPolicy: () => void;
}
const TosPrivacyContainer = ({config, onPressTOS, onPressPrivacyPolicy}: TosPrivacyContainerProps) => {
const hasTermsOfServiceLink = config.TermsOfServiceLink;
const hasPrivacyPolicyLink = config.PrivacyPolicyLink;
const theme = useTheme();
const style = getStyleSheet(theme);
const hasTermsOfServiceLink = Boolean(config.TermsOfServiceLink);
const hasPrivacyPolicyLink = Boolean(config.PrivacyPolicyLink);
return (
<>
{hasTermsOfServiceLink && (
@@ -49,23 +68,4 @@ const TosPrivacyContainer = ({config, onPressTOS, onPressPrivacyPolicy}: TosPriv
);
};
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
noticeLink: {
color: theme.linkColor,
fontSize: 11,
lineHeight: 13,
},
footerText: {
color: changeOpacity(theme.centerChannelColor, 0.5),
fontSize: 11,
lineHeight: 13,
marginBottom: 10,
},
hyphenText: {
marginBottom: 0,
},
};
});
export default TosPrivacyContainer;

View File

@@ -16,81 +16,87 @@ export const getSaveButton = (buttonId: string, intl: IntlShape, color: string)
...typography('Body', 100, 'SemiBold'),
});
export const SettingOptionConfig = {
type SettingConfigDetails = {
defaultMessage?: string;
i18nId?: string;
icon?: string;
testID?: string;
}
export const SettingOptionConfig: Record<string, SettingConfigDetails> = {
notification: {
defaultMessage: 'Notifications',
i18nId: t('general_settings.notifications'),
iconName: 'bell-outline',
icon: 'bell-outline',
testID: 'general_settings.notifications',
},
display: {
defaultMessage: 'Display',
i18nId: t('general_settings.display'),
iconName: 'layers-outline',
icon: 'layers-outline',
testID: 'general_settings.display',
},
advanced_settings: {
defaultMessage: 'Advanced Settings',
i18nId: t('general_settings.advanced_settings'),
iconName: 'tune',
icon: 'tune',
testID: 'general_settings.advanced',
},
about: {
defaultMessage: 'About {appTitle}',
i18nId: t('general_settings.about'),
iconName: 'information-outline',
icon: 'information-outline',
testID: 'general_settings.about',
},
help: {
defaultMessage: 'Help',
i18nId: t('general_settings.help'),
testID: 'general_settings.help',
showArrow: false,
},
};
export const NotificationsOptionConfig = {
export const NotificationsOptionConfig: Record<string, SettingConfigDetails> = {
mentions: {
iconName: 'at',
icon: 'at',
testID: 'notification_settings.mentions_replies',
},
push_notification: {
defaultMessage: 'Push Notifications',
i18nId: t('notification_settings.mobile'),
iconName: 'cellphone',
icon: 'cellphone',
testID: 'notification_settings.push_notification',
},
email: {
defaultMessage: 'Email',
i18nId: t('notification_settings.email'),
iconName: 'email-outline',
icon: 'email-outline',
testID: 'notification_settings.email',
},
automatic_dm_replies: {
defaultMessage: 'Automatic replies',
i18nId: t('notification_settings.ooo_auto_responder'),
iconName: 'reply-outline',
icon: 'reply-outline',
testID: 'notification_settings.automatic_dm_replies',
},
};
export const DisplayOptionConfig = {
export const DisplayOptionConfig: Record<string, SettingConfigDetails> = {
clock: {
defaultMessage: 'Clock Display',
i18nId: t('mobile.display_settings.clockDisplay'),
iconName: 'clock-outline',
icon: 'clock-outline',
testID: 'display_settings.clock',
},
theme: {
defaultMessage: 'Theme',
i18nId: t('mobile.display_settings.theme'),
iconName: 'palette-outline',
icon: 'palette-outline',
testID: 'display_settings.theme',
},
timezone: {
defaultMessage: 'Timezone',
i18nId: t('mobile.display_settings.timezone'),
iconName: 'globe',
icon: 'globe',
testID: 'display_settings.timezone',
},
};

View File

@@ -3,7 +3,6 @@
import React, {useMemo} from 'react';
import {useIntl} from 'react-intl';
import {StyleSheet} from 'react-native';
import {Screens} from '@constants';
import {useTheme} from '@context/theme';
@@ -14,7 +13,6 @@ import {getUserTimezoneProps} from '@utils/user';
import SettingContainer from '../setting_container';
import SettingItem from '../setting_item';
import SettingRowLabel from '../setting_row_label';
import type UserModel from '@typings/database/models/servers/user';
@@ -40,12 +38,6 @@ const TIMEZONE_FORMAT = [
},
];
const styles = StyleSheet.create({
title: {
textTransform: 'capitalize',
},
});
type DisplayProps = {
currentUser: UserModel;
hasMilitaryTimeFormat: boolean;
@@ -83,32 +75,19 @@ const Display = ({currentUser, hasMilitaryTimeFormat, isThemeSwitchingEnabled, i
<SettingItem
optionName='theme'
onPress={goToThemeSettings}
rightComponent={Boolean(theme.type) &&
<SettingRowLabel
text={theme.type!}
textStyle={styles.title}
/>
}
info={theme.type!}
/>
)}
<SettingItem
optionName='clock'
onPress={goToClockDisplaySettings}
rightComponent={
<SettingRowLabel
text={intl.formatMessage(hasMilitaryTimeFormat ? TIME_FORMAT[1] : TIME_FORMAT[0])}
/>
}
info={intl.formatMessage(hasMilitaryTimeFormat ? TIME_FORMAT[1] : TIME_FORMAT[0])}
/>
{isTimezoneEnabled && (
<SettingItem
optionName='timezone'
onPress={goToTimezoneSettings}
rightComponent={
<SettingRowLabel
text={intl.formatMessage(timezone.useAutomaticTimezone ? TIMEZONE_FORMAT[0] : TIMEZONE_FORMAT[1])}
/>
}
info={intl.formatMessage(timezone.useAutomaticTimezone ? TIMEZONE_FORMAT[0] : TIMEZONE_FORMAT[1])}
/>
)}
</SettingContainer>

View File

@@ -4,7 +4,7 @@
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {useIntl} from 'react-intl';
import {updateMe} from '@actions/remote/user';
import {fetchStatusInBatch, updateMe} from '@actions/remote/user';
import FloatingTextInput from '@components/floating_text_input_label';
import FormattedText from '@components/formatted_text';
import {General} from '@constants';
@@ -88,8 +88,9 @@ const NotificationAutoResponder = ({currentUser, componentId}: NotificationAutoR
auto_responder_message: autoResponderMessage,
},
});
fetchStatusInBatch(serverUrl, currentUser.id);
close();
}, [serverUrl, autoResponderActive, autoResponderMessage, notifyProps]);
}, [serverUrl, autoResponderActive, autoResponderMessage, notifyProps, currentUser.id]);
useEffect(() => {
const enabled = initialAutoResponderActive !== autoResponderActive || initialOOOMsg !== autoResponderMessage;
@@ -120,7 +121,6 @@ const NotificationAutoResponder = ({currentUser, componentId}: NotificationAutoR
allowFontScaling={true}
autoCapitalize='none'
autoCorrect={false}
blurOnSubmit={true}
containerStyle={styles.textInputContainer}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
label={intl.formatMessage(label)}
@@ -128,7 +128,7 @@ const NotificationAutoResponder = ({currentUser, componentId}: NotificationAutoR
onChangeText={setAutoResponderMessage}
placeholder={intl.formatMessage(label)}
placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.4)}
returnKeyType='done'
returnKeyType='default'
textAlignVertical='top'
textInputStyle={styles.input}
theme={theme}

View File

@@ -7,7 +7,6 @@ import {useIntl} from 'react-intl';
import {General, Screens} from '@constants';
import {t} from '@i18n';
import {goToScreen} from '@screens/navigation';
import SettingRowLabel from '@screens/settings/setting_row_label';
import {getEmailInterval, getEmailIntervalTexts, getNotificationProps} from '@utils/user';
import SettingContainer from '../setting_container';
@@ -91,10 +90,12 @@ const Notifications = ({
return (
<SettingContainer>
<SettingItem
defaultMessage={isCRTEnabled ? mentionTexts.crtOn.defaultMessage : mentionTexts.crtOff.defaultMessage}
i18nId={isCRTEnabled ? mentionTexts.crtOn.id : mentionTexts.crtOff.id}
onPress={goToNotificationSettingsMentions}
optionName='mentions'
label={intl.formatMessage({
id: isCRTEnabled ? mentionTexts.crtOn.id : mentionTexts.crtOff.id,
defaultMessage: isCRTEnabled ? mentionTexts.crtOn.defaultMessage : mentionTexts.crtOff.defaultMessage,
})}
/>
<SettingItem
optionName='push_notification'
@@ -103,21 +104,13 @@ const Notifications = ({
<SettingItem
optionName='email'
onPress={goToEmailSettings}
rightComponent={
<SettingRowLabel
text={intl.formatMessage(getEmailIntervalTexts(emailIntervalPref))}
/>
}
info={intl.formatMessage(getEmailIntervalTexts(emailIntervalPref))}
/>
{enableAutoResponder && (
<SettingItem
onPress={goToNotificationAutoResponder}
optionName='automatic_dm_replies'
rightComponent={
<SettingRowLabel
text={currentUser.status === General.OUT_OF_OFFICE && notifyProps.auto_responder_active === 'true' ? 'On' : 'Off'}
/>
}
info={currentUser.status === General.OUT_OF_OFFICE && notifyProps.auto_responder_active === 'true' ? 'On' : 'Off'}
/>
)}
</SettingContainer>

View File

@@ -2,10 +2,12 @@
// See LICENSE.txt for license information.
import React from 'react';
import {useIntl} from 'react-intl';
import {Platform} from 'react-native';
import MenuItem, {MenuItemProps} from '@components/menu_item';
import OptionItem, {OptionItemProps} from '@components/option_item';
import {useTheme} from '@context/theme';
import SettingSeparator from '@screens/settings/settings_separator';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
@@ -15,7 +17,8 @@ type SettingsConfig = keyof typeof SettingOptionConfig | keyof typeof Notificati
type SettingOptionProps = {
optionName: SettingsConfig;
onPress: () => void;
} & Omit<MenuItemProps, 'testID'| 'theme'>;
separator?: boolean;
} & Partial<OptionItemProps>;
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
@@ -23,10 +26,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
color: theme.centerChannelColor,
...typography('Body', 200, 'Regular'),
},
separatorStyle: {
width: '91%',
alignSelf: 'center',
},
chevronStyle: {
marginRight: 14,
color: changeOpacity(theme.centerChannelColor, 0.32),
@@ -34,21 +33,35 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
};
});
const SettingItem = ({onPress, optionName, ...rest}: SettingOptionProps) => {
const SettingItem = ({
info,
onPress,
optionName,
separator = true,
...props
}: SettingOptionProps) => {
const theme = useTheme();
const intl = useIntl();
const styles = getStyleSheet(theme);
const props = {...rest, ...Options[optionName]} as unknown as Omit<MenuItemProps, 'onPress'| 'theme'>;
const config = Options[optionName];
const label = props.label || intl.formatMessage({id: config.i18nId, defaultMessage: config.defaultMessage});
return (
<MenuItem
chevronStyle={styles.chevronStyle}
labelStyle={styles.menuLabel}
onPress={onPress}
separator={Platform.OS === 'ios'}
separatorStyle={styles.separatorStyle}
showArrow={Platform.select({ios: true, default: false})}
{...props}
/>
<>
<OptionItem
action={onPress}
arrowStyle={styles.chevronStyle}
containerStyle={{marginLeft: 16}}
icon={config.icon}
info={info}
label={label}
optionLabelTextStyle={[styles.menuLabel, props.optionLabelTextStyle]}
type={Platform.select({ios: 'arrow', default: 'default'})}
{...props}
/>
{separator && <SettingSeparator/>}
</>
);
};

View File

@@ -1,44 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {Platform, StyleProp, Text, TextStyle} from 'react-native';
import {useTheme} from '@context/theme';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
rightLabel: {
color: changeOpacity(theme.centerChannelColor, 0.56),
...typography('Body', 100, 'Regular'),
alignSelf: 'center',
...Platform.select({
android: {
marginRight: 20,
},
}),
},
};
});
type SettingRowLabelProps = {
text: string;
textStyle?: StyleProp<TextStyle>;
}
const SettingRowLabel = ({text, textStyle}: SettingRowLabelProps) => {
const theme = useTheme();
const styles = getStyleSheet(theme);
return (
<Text
style={[styles.rightLabel, textStyle]}
>
{text}
</Text>
);
};
export default SettingRowLabel;

View File

@@ -135,18 +135,20 @@ const Settings = ({componentId, helpLink, showHelp, siteName}: SettingsProps) =>
optionName='advanced_settings'
/>
<SettingItem
messageValues={{appTitle: serverName}}
icon='information-outline'
label={intl.formatMessage({id: 'settings.about', defaultMessage: 'About {appTitle}'}, {appTitle: serverName})}
onPress={goToAbout}
optionName='about'
testID='general_settings.about'
/>
{Platform.OS === 'android' && <View style={styles.helpGroup}/>}
{showHelp &&
<SettingItem
containerStyle={styles.containerStyle}
isLink={true}
optionLabelTextStyle={{color: theme.linkColor}}
onPress={openHelp}
optionName='help'
separator={false}
type='default'
/>
}
</SettingContainer>

View File

@@ -234,10 +234,10 @@ const SnackBar = ({barType, componentId, onAction, sourceScreen}: SnackBarProps)
>
<Toast
animatedStyle={snackBarStyle}
message={intl.formatMessage({id: config.id, defaultMessage: config.defaultMessage})}
iconName={config.iconName}
message={intl.formatMessage({id: config.id, defaultMessage: config.defaultMessage})}
style={[styles.toast, barType === SNACK_BAR_TYPE.LINK_COPIED && {backgroundColor: theme.onlineIndicator}]}
textStyle={styles.text}
style={styles.toast}
>
{config.canUndo && onAction && (
<TouchableOpacity onPress={onUndoPressHandler}>

View File

@@ -7,7 +7,7 @@ import {View} from 'react-native';
import {CopyPermalinkOption, FollowThreadOption, ReplyOption, SaveOption} from '@components/common_post_options';
import FormattedText from '@components/formatted_text';
import {ITEM_HEIGHT} from '@components/menu_item';
import {ITEM_HEIGHT} from '@components/option_item';
import {Screens} from '@constants';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';