diff --git a/app/screens/theme/__snapshots__/theme.test.js.snap b/app/screens/theme/__snapshots__/theme.test.js.snap index 1969d6b11..1836adba6 100644 --- a/app/screens/theme/__snapshots__/theme.test.js.snap +++ b/app/screens/theme/__snapshots__/theme.test.js.snap @@ -19,51 +19,206 @@ exports[`Theme should match snapshot 1`] = ` } } > -
- + } + selected={true} + theme={ + Object { + "awayIndicator": "#ffbc42", + "buttonBg": "#166de0", + "buttonColor": "#ffffff", + "centerChannelBg": "#ffffff", + "centerChannelColor": "#3d3c40", + "codeTheme": "github", + "dndIndicator": "#f74343", + "errorTextColor": "#fd5960", + "linkColor": "#2389d7", + "mentionBj": "#ffffff", + "mentionColor": "#145dbf", + "mentionHighlightBg": "#ffe577", + "mentionHighlightLink": "#166de0", + "newMessageSeparator": "#ff8800", + "onlineIndicator": "#06d6a0", + "sidebarBg": "#145dbf", + "sidebarHeaderBg": "#1153ab", + "sidebarHeaderTextColor": "#ffffff", + "sidebarText": "#ffffff", + "sidebarTextActiveBorder": "#579eff", + "sidebarTextActiveColor": "#ffffff", + "sidebarTextHoverBg": "#4578bf", + "sidebarUnreadText": "#ffffff", + "type": "Mattermost", + } + } /> -
+ + } + selected={false} + theme={ + Object { + "awayIndicator": "#ffbc42", + "buttonBg": "#166de0", + "buttonColor": "#ffffff", + "centerChannelBg": "#ffffff", + "centerChannelColor": "#3d3c40", + "codeTheme": "github", + "dndIndicator": "#f74343", + "errorTextColor": "#fd5960", + "linkColor": "#2389d7", + "mentionBj": "#ffffff", + "mentionColor": "#145dbf", + "mentionHighlightBg": "#ffe577", + "mentionHighlightLink": "#166de0", + "newMessageSeparator": "#ff8800", + "onlineIndicator": "#06d6a0", + "sidebarBg": "#145dbf", + "sidebarHeaderBg": "#1153ab", + "sidebarHeaderTextColor": "#ffffff", + "sidebarText": "#ffffff", + "sidebarTextActiveBorder": "#579eff", + "sidebarTextActiveColor": "#ffffff", + "sidebarTextHoverBg": "#4578bf", + "sidebarUnreadText": "#ffffff", + "type": "Mattermost", + } + } + /> + + } + selected={false} + theme={ + Object { + "awayIndicator": "#ffbc42", + "buttonBg": "#166de0", + "buttonColor": "#ffffff", + "centerChannelBg": "#ffffff", + "centerChannelColor": "#3d3c40", + "codeTheme": "github", + "dndIndicator": "#f74343", + "errorTextColor": "#fd5960", + "linkColor": "#2389d7", + "mentionBj": "#ffffff", + "mentionColor": "#145dbf", + "mentionHighlightBg": "#ffe577", + "mentionHighlightLink": "#166de0", + "newMessageSeparator": "#ff8800", + "onlineIndicator": "#06d6a0", + "sidebarBg": "#145dbf", + "sidebarHeaderBg": "#1153ab", + "sidebarHeaderTextColor": "#ffffff", + "sidebarText": "#ffffff", + "sidebarTextActiveBorder": "#579eff", + "sidebarTextActiveColor": "#ffffff", + "sidebarTextHoverBg": "#4578bf", + "sidebarUnreadText": "#ffffff", + "type": "Mattermost", + } + } + /> + + } + selected={false} + theme={ + Object { + "awayIndicator": "#ffbc42", + "buttonBg": "#166de0", + "buttonColor": "#ffffff", + "centerChannelBg": "#ffffff", + "centerChannelColor": "#3d3c40", + "codeTheme": "github", + "dndIndicator": "#f74343", + "errorTextColor": "#fd5960", + "linkColor": "#2389d7", + "mentionBj": "#ffffff", + "mentionColor": "#145dbf", + "mentionHighlightBg": "#ffe577", + "mentionHighlightLink": "#166de0", + "newMessageSeparator": "#ff8800", + "onlineIndicator": "#06d6a0", + "sidebarBg": "#145dbf", + "sidebarHeaderBg": "#1153ab", + "sidebarHeaderTextColor": "#ffffff", + "sidebarText": "#ffffff", + "sidebarTextActiveBorder": "#579eff", + "sidebarTextActiveColor": "#ffffff", + "sidebarTextHoverBg": "#4578bf", + "sidebarUnreadText": "#ffffff", + "type": "Mattermost", + } + } + /> + `; diff --git a/app/screens/theme/index.js b/app/screens/theme/index.js index b8fa82868..3ed397309 100644 --- a/app/screens/theme/index.js +++ b/app/screens/theme/index.js @@ -8,15 +8,18 @@ import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams'; import {savePreferences} from 'mattermost-redux/actions/preferences'; import {getAllowedThemes, getCustomTheme} from 'app/selectors/theme'; +import {isLandscape, isTablet} from 'app/selectors/device'; import Theme from './theme'; const mapStateToProps = (state) => ({ - userId: getCurrentUserId(state), - teamId: getCurrentTeamId(state), - theme: getTheme(state), allowedThemes: getAllowedThemes(state), customTheme: getCustomTheme(state), + isLandscape: isLandscape(state), + isTablet: isTablet(state), + teamId: getCurrentTeamId(state), + theme: getTheme(state), + userId: getCurrentUserId(state), }); const mapDispatchToProps = (dispatch) => ({ diff --git a/app/screens/theme/theme.js b/app/screens/theme/theme.js index 4a0518ca4..326ce1e98 100644 --- a/app/screens/theme/theme.js +++ b/app/screens/theme/theme.js @@ -2,18 +2,26 @@ // See LICENSE.txt for license information. import React from 'react'; -import {View, FlatList} from 'react-native'; +import {View} from 'react-native'; import PropTypes from 'prop-types'; import {intlShape} from 'react-intl'; import StatusBar from 'app/components/status_bar'; import Section from 'app/screens/settings/section'; import SectionItem from 'app/screens/settings/section_item'; +import ThemeTile from './theme_tile'; import FormattedText from 'app/components/formatted_text'; import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme'; import Preferences from 'mattermost-redux/constants/preferences'; +const thumbnailImages = { + default: require('assets/images/themes/mattermost.png'), + organization: require('assets/images/themes/organization.png'), + mattermostDark: require('assets/images/themes/mattermost_dark.png'), + windows10: require('assets/images/themes/windows_dark.png'), +}; + export default class Theme extends React.PureComponent { static propTypes = { actions: PropTypes.shape({ @@ -21,6 +29,8 @@ export default class Theme extends React.PureComponent { }).isRequired, allowedThemes: PropTypes.arrayOf(PropTypes.object), customTheme: PropTypes.object, + isLandscape: PropTypes.bool.isRequired, + isTablet: PropTypes.bool.isRequired, navigator: PropTypes.object.isRequired, teamId: PropTypes.string.isRequired, theme: PropTypes.object.isRequired, @@ -63,57 +73,67 @@ export default class Theme extends React.PureComponent { }]); } - renderThemeRow = ({item, title}) => { + renderAllowedThemeTiles = () => { + const {theme, allowedThemes, isLandscape, isTablet} = this.props; + + return allowedThemes.map((allowedTheme) => ( + + )} + action={this.setTheme} + actionValue={allowedTheme.key} + selected={allowedTheme.type.toLowerCase() === theme.type.toLowerCase()} + theme={theme} + imageSrc={thumbnailImages[allowedTheme.key]} + isLandscape={isLandscape} + isTablet={isTablet} + /> + )); + }; + + renderCustomThemeRow = ({item}) => { const {theme} = this.props; - const style = getStyleSheet(theme); return ( - - - )} - action={this.setTheme} - actionType='select' - actionValue={item.key} - selected={item.type.toLowerCase() === theme.type.toLowerCase()} - theme={theme} - /> - - + + )} + action={this.setTheme} + actionType='select' + actionValue={item.key} + selected={item.type.toLowerCase() === theme.type.toLowerCase()} + theme={theme} + /> ); }; - keyExtractor = (item) => item.key; - render() { - const {theme, allowedThemes} = this.props; + const {theme} = this.props; const {customTheme} = this.state; const style = getStyleSheet(theme); return ( -
- -
+ + {this.renderAllowedThemeTiles()} + {customTheme &&
- {this.renderThemeRow({item: customTheme, title: 'Custom'})} + {this.renderCustomThemeRow({item: customTheme})}
}
@@ -133,9 +153,16 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { flex: 1, paddingTop: 35, }, - divider: { - backgroundColor: changeOpacity(theme.centerChannelColor, 0.1), - height: 1, + tilesContainer: { + marginBottom: 30, + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'center', + backgroundColor: theme.centerChannelBg, + borderTopWidth: 1, + borderBottomWidth: 1, + borderTopColor: changeOpacity(theme.centerChannelColor, 0.1), + borderBottomColor: changeOpacity(theme.centerChannelColor, 0.1), }, }; }); diff --git a/app/screens/theme/theme.test.js b/app/screens/theme/theme.test.js index d279d4822..3a2651ba5 100644 --- a/app/screens/theme/theme.test.js +++ b/app/screens/theme/theme.test.js @@ -2,9 +2,9 @@ // See LICENSE.txt for license information. import React from 'react'; -import {FlatList} from 'react-native'; import {shallow} from 'enzyme'; -import Section from 'app/screens/settings/section'; + +import ThemeTile from './theme_tile'; import Preferences from 'mattermost-redux/constants/preferences'; @@ -12,12 +12,125 @@ import Theme from './theme'; jest.mock('react-intl'); +const allowedThemes = [ + { + type: 'Mattermost', + sidebarBg: '#145dbf', + sidebarText: '#ffffff', + sidebarUnreadText: '#ffffff', + sidebarTextHoverBg: '#4578bf', + sidebarTextActiveBorder: '#579eff', + sidebarTextActiveColor: '#ffffff', + sidebarHeaderBg: '#1153ab', + sidebarHeaderTextColor: '#ffffff', + onlineIndicator: '#06d6a0', + awayIndicator: '#ffbc42', + dndIndicator: '#f74343', + mentionBg: '#ffffff', + mentionColor: '#145dbf', + centerChannelBg: '#ffffff', + centerChannelColor: '#3d3c40', + newMessageSeparator: '#ff8800', + linkColor: '#2389d7', + buttonBg: '#166de0', + buttonColor: '#ffffff', + errorTextColor: '#fd5960', + mentionHighlightBg: '#ffe577', + mentionHighlightLink: '#166de0', + codeTheme: 'github', + key: 'default', + }, + { + type: 'Organization', + sidebarBg: '#2071a7', + sidebarText: '#ffffff', + sidebarUnreadText: '#ffffff', + sidebarTextHoverBg: '#136197', + sidebarTextActiveBorder: '#7ab0d6', + sidebarTextActiveColor: '#ffffff', + sidebarHeaderBg: '#2f81b7', + sidebarHeaderTextColor: '#ffffff', + onlineIndicator: '#7dbe00', + awayIndicator: '#dcbd4e', + dndIndicator: '#ff6a6a', + mentionBg: '#fbfbfb', + mentionColor: '#2071f7', + centerChannelBg: '#f2f4f8', + centerChannelColor: '#333333', + newMessageSeparator: '#ff8800', + linkColor: '#2f81b7', + buttonBg: '#1dacfc', + buttonColor: '#ffffff', + errorTextColor: '#a94442', + mentionHighlightBg: '#f3e197', + mentionHighlightLink: '#2f81b7', + codeTheme: 'github', + key: 'organization', + }, + { + type: 'Mattermost Dark', + sidebarBg: '#1b2c3e', + sidebarText: '#ffffff', + sidebarUnreadText: '#ffffff', + sidebarTextHoverBg: '#4a5664', + sidebarTextActiveBorder: '#66b9a7', + sidebarTextActiveColor: '#ffffff', + sidebarHeaderBg: '#1b2c3e', + sidebarHeaderTextColor: '#ffffff', + onlineIndicator: '#65dcc8', + awayIndicator: '#c1b966', + dndIndicator: '#e81023', + mentionBg: '#b74a4a', + mentionColor: '#ffffff', + centerChannelBg: '#2f3e4e', + centerChannelColor: '#dddddd', + newMessageSeparator: '#5de5da', + linkColor: '#a4ffeb', + buttonBg: '#4cbba4', + buttonColor: '#ffffff', + errorTextColor: '#ff6461', + mentionHighlightBg: '#984063', + mentionHighlightLink: '#a4ffeb', + codeTheme: 'solarized-dark', + key: 'mattermostDark', + }, + { + type: 'Windows Dark', + sidebarBg: '#171717', + sidebarText: '#ffffff', + sidebarUnreadText: '#ffffff', + sidebarTextHoverBg: '#302e30', + sidebarTextActiveBorder: '#196caf', + sidebarTextActiveColor: '#ffffff', + sidebarHeaderBg: '#1f1f1f', + sidebarHeaderTextColor: '#ffffff', + onlineIndicator: '#399fff', + awayIndicator: '#c1b966', + dndIndicator: '#e81023', + mentionBg: '#0177e7', + mentionColor: '#ffffff', + centerChannelBg: '#1f1f1f', + centerChannelColor: '#dddddd', + newMessageSeparator: '#cc992d', + linkColor: '#0d93ff', + buttonBg: '#0177e7', + buttonColor: '#ffffff', + errorTextColor: '#ff6461', + mentionHighlightBg: '#784098', + mentionHighlightLink: '#a4ffeb', + codeTheme: 'monokai', + key: 'windows10', + }, +]; + describe('Theme', () => { const baseProps = { actions: { savePreferences: jest.fn(), }, allowedThemes, + isLandscape: false, + isTablet: false, navigator: { setOnNavigatorEvent: jest.fn(), }, @@ -32,122 +145,6 @@ describe('Theme', () => { ); expect(wrapper.getElement()).toMatchSnapshot(); - expect(wrapper.find(Section).first().exists()).toEqual(true); - expect(wrapper.find(FlatList).first().exists()).toEqual(true); + expect(wrapper.find(ThemeTile)).toHaveLength(4); }); }); - -const allowedThemes = [ - { - default: { - type: 'Mattermost', - sidebarBg: '#145dbf', - sidebarText: '#ffffff', - sidebarUnreadText: '#ffffff', - sidebarTextHoverBg: '#4578bf', - sidebarTextActiveBorder: '#579eff', - sidebarTextActiveColor: '#ffffff', - sidebarHeaderBg: '#1153ab', - sidebarHeaderTextColor: '#ffffff', - onlineIndicator: '#06d6a0', - awayIndicator: '#ffbc42', - dndIndicator: '#f74343', - mentionBg: '#ffffff', - mentionColor: '#145dbf', - centerChannelBg: '#ffffff', - centerChannelColor: '#3d3c40', - newMessageSeparator: '#ff8800', - linkColor: '#2389d7', - buttonBg: '#166de0', - buttonColor: '#ffffff', - errorTextColor: '#fd5960', - mentionHighlightBg: '#ffe577', - mentionHighlightLink: '#166de0', - codeTheme: 'github', - }, - }, - { - organization: { - type: 'Organization', - sidebarBg: '#2071a7', - sidebarText: '#ffffff', - sidebarUnreadText: '#ffffff', - sidebarTextHoverBg: '#136197', - sidebarTextActiveBorder: '#7ab0d6', - sidebarTextActiveColor: '#ffffff', - sidebarHeaderBg: '#2f81b7', - sidebarHeaderTextColor: '#ffffff', - onlineIndicator: '#7dbe00', - awayIndicator: '#dcbd4e', - dndIndicator: '#ff6a6a', - mentionBg: '#fbfbfb', - mentionColor: '#2071f7', - centerChannelBg: '#f2f4f8', - centerChannelColor: '#333333', - newMessageSeparator: '#ff8800', - linkColor: '#2f81b7', - buttonBg: '#1dacfc', - buttonColor: '#ffffff', - errorTextColor: '#a94442', - mentionHighlightBg: '#f3e197', - mentionHighlightLink: '#2f81b7', - codeTheme: 'github', - }, - }, - { - mattermostDark: { - type: 'Mattermost Dark', - sidebarBg: '#1b2c3e', - sidebarText: '#ffffff', - sidebarUnreadText: '#ffffff', - sidebarTextHoverBg: '#4a5664', - sidebarTextActiveBorder: '#66b9a7', - sidebarTextActiveColor: '#ffffff', - sidebarHeaderBg: '#1b2c3e', - sidebarHeaderTextColor: '#ffffff', - onlineIndicator: '#65dcc8', - awayIndicator: '#c1b966', - dndIndicator: '#e81023', - mentionBg: '#b74a4a', - mentionColor: '#ffffff', - centerChannelBg: '#2f3e4e', - centerChannelColor: '#dddddd', - newMessageSeparator: '#5de5da', - linkColor: '#a4ffeb', - buttonBg: '#4cbba4', - buttonColor: '#ffffff', - errorTextColor: '#ff6461', - mentionHighlightBg: '#984063', - mentionHighlightLink: '#a4ffeb', - codeTheme: 'solarized-dark', - }, - }, - { - windows10: { - type: 'Windows Dark', - sidebarBg: '#171717', - sidebarText: '#ffffff', - sidebarUnreadText: '#ffffff', - sidebarTextHoverBg: '#302e30', - sidebarTextActiveBorder: '#196caf', - sidebarTextActiveColor: '#ffffff', - sidebarHeaderBg: '#1f1f1f', - sidebarHeaderTextColor: '#ffffff', - onlineIndicator: '#399fff', - awayIndicator: '#c1b966', - dndIndicator: '#e81023', - mentionBg: '#0177e7', - mentionColor: '#ffffff', - centerChannelBg: '#1f1f1f', - centerChannelColor: '#dddddd', - newMessageSeparator: '#cc992d', - linkColor: '#0d93ff', - buttonBg: '#0177e7', - buttonColor: '#ffffff', - errorTextColor: '#ff6461', - mentionHighlightBg: '#784098', - mentionHighlightLink: '#a4ffeb', - codeTheme: 'monokai', - }, - }, -]; diff --git a/app/screens/theme/theme_tile.js b/app/screens/theme/theme_tile.js new file mode 100644 index 000000000..2ade75e0d --- /dev/null +++ b/app/screens/theme/theme_tile.js @@ -0,0 +1,127 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import PropTypes from 'prop-types'; +import { + Dimensions, + Image, + TouchableOpacity, + View, +} from 'react-native'; + +import {makeStyleSheetFromTheme} from 'app/utils/theme'; + +const {width: deviceWidth, height: deviceHeight} = Dimensions.get('window'); + +const checkmark = require('assets/images/themes/check.png'); + +const tilePadding = 8; + +const ThemeTile = (props) => { + const { + action, + actionValue, + imageSrc, + isLandscape, + isTablet, + label, + selected, + theme, + } = props; + + const style = getStyleSheet(theme); + + const labelComponent = React.cloneElement( + label, + {style: style.label} + ); + + const tilesPerLine = isLandscape || isTablet ? 4 : 2; + const fullWidth = isLandscape ? deviceHeight - 40 : deviceWidth; + const layoutStyle = { + container: { + width: (fullWidth / tilesPerLine) - tilePadding, + }, + thumbnail: { + width: (fullWidth / tilesPerLine) - (tilePadding + 20), + }, + }; + + return ( + action(actionValue)} + > + + {imageSrc && ( + + )} + {selected && ( + + )} + + {labelComponent} + + ); +}; + +ThemeTile.propTypes = { + action: PropTypes.func, + actionValue: PropTypes.string, + imageSrc: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.object, + ]).isRequired, + isLandscape: PropTypes.bool.isRequired, + isTablet: PropTypes.bool.isRequired, + label: PropTypes.node.isRequired, + selected: PropTypes.bool, + theme: PropTypes.object.isRequired, +}; + +ThemeTile.defaultProps = { + action: () => true, +}; + +const getStyleSheet = makeStyleSheetFromTheme((theme) => { + return { + container: { + flexDirection: 'column', + padding: tilePadding, + marginTop: 8, + }, + imageWrapper: { + position: 'relative', + alignItems: 'flex-start', + marginBottom: 12, + }, + thumbnail: { + resizeMode: 'stretch', + }, + selectedThumbnail: { + opacity: 0.5, + }, + check: { + position: 'absolute', + right: 10, + bottom: 10, + }, + label: { + color: theme.centerChannelColor, + fontSize: 15, + }, + }; +}); + +export default ThemeTile; diff --git a/assets/base/images/themes/check.png b/assets/base/images/themes/check.png new file mode 100644 index 000000000..7d389d2f6 Binary files /dev/null and b/assets/base/images/themes/check.png differ diff --git a/assets/base/images/themes/check@2x.png b/assets/base/images/themes/check@2x.png new file mode 100644 index 000000000..0ca7fb713 Binary files /dev/null and b/assets/base/images/themes/check@2x.png differ diff --git a/assets/base/images/themes/check@3x.png b/assets/base/images/themes/check@3x.png new file mode 100644 index 000000000..8b9fe1cc1 Binary files /dev/null and b/assets/base/images/themes/check@3x.png differ diff --git a/assets/base/images/themes/mattermost.png b/assets/base/images/themes/mattermost.png new file mode 100644 index 000000000..f2c8a5929 Binary files /dev/null and b/assets/base/images/themes/mattermost.png differ diff --git a/assets/base/images/themes/mattermost@2x.png b/assets/base/images/themes/mattermost@2x.png new file mode 100644 index 000000000..07c3d88d0 Binary files /dev/null and b/assets/base/images/themes/mattermost@2x.png differ diff --git a/assets/base/images/themes/mattermost@3x.png b/assets/base/images/themes/mattermost@3x.png new file mode 100644 index 000000000..d6629aa6a Binary files /dev/null and b/assets/base/images/themes/mattermost@3x.png differ diff --git a/assets/base/images/themes/mattermost_dark.png b/assets/base/images/themes/mattermost_dark.png new file mode 100644 index 000000000..24779997a Binary files /dev/null and b/assets/base/images/themes/mattermost_dark.png differ diff --git a/assets/base/images/themes/mattermost_dark@2x.png b/assets/base/images/themes/mattermost_dark@2x.png new file mode 100644 index 000000000..13f1cd668 Binary files /dev/null and b/assets/base/images/themes/mattermost_dark@2x.png differ diff --git a/assets/base/images/themes/mattermost_dark@3x.png b/assets/base/images/themes/mattermost_dark@3x.png new file mode 100644 index 000000000..efc9f5217 Binary files /dev/null and b/assets/base/images/themes/mattermost_dark@3x.png differ diff --git a/assets/base/images/themes/organization.png b/assets/base/images/themes/organization.png new file mode 100644 index 000000000..9f0e230cb Binary files /dev/null and b/assets/base/images/themes/organization.png differ diff --git a/assets/base/images/themes/organization@2x.png b/assets/base/images/themes/organization@2x.png new file mode 100644 index 000000000..cb182816b Binary files /dev/null and b/assets/base/images/themes/organization@2x.png differ diff --git a/assets/base/images/themes/organization@3x.png b/assets/base/images/themes/organization@3x.png new file mode 100644 index 000000000..c661344b1 Binary files /dev/null and b/assets/base/images/themes/organization@3x.png differ diff --git a/assets/base/images/themes/windows_dark.png b/assets/base/images/themes/windows_dark.png new file mode 100644 index 000000000..a41ba4a0e Binary files /dev/null and b/assets/base/images/themes/windows_dark.png differ diff --git a/assets/base/images/themes/windows_dark@2x.png b/assets/base/images/themes/windows_dark@2x.png new file mode 100644 index 000000000..e08c8209b Binary files /dev/null and b/assets/base/images/themes/windows_dark@2x.png differ diff --git a/assets/base/images/themes/windows_dark@3x.png b/assets/base/images/themes/windows_dark@3x.png new file mode 100644 index 000000000..043844de9 Binary files /dev/null and b/assets/base/images/themes/windows_dark@3x.png differ