Make keyboard dark on dark themes (iOS only) (#3102)

* Support keyboardAppearance theme parameter

* Change approach of keyboardAppearance to auto-detect

* Fix style

* Update snapshots

* Add unit test for getKeyboardAppearanceFromTheme

* Update snapshot

* Fix failing test
This commit is contained in:
Matheus Cardoso 2019-08-23 14:18:05 -03:00 committed by Elias Nahum
parent 91a7cb499c
commit 73518da91e
29 changed files with 180 additions and 18 deletions

View file

@ -21,7 +21,12 @@ import Loading from 'app/components/loading';
import StatusBar from 'app/components/status_bar';
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {getShortenedURL} from 'app/utils/url';
import {t} from 'app/utils/i18n';
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
@ -247,6 +252,7 @@ export default class EditChannelInfo extends PureComponent {
placeholderTextColor={changeOpacity('#000', 0.5)}
underlineColorAndroid='transparent'
disableFullscreenUI={true}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
/>
</View>
</View>
@ -276,6 +282,7 @@ export default class EditChannelInfo extends PureComponent {
placeholderTextColor={changeOpacity('#000', 0.5)}
underlineColorAndroid='transparent'
disableFullscreenUI={true}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
/>
</View>
</View>
@ -309,6 +316,7 @@ export default class EditChannelInfo extends PureComponent {
textAlignVertical='top'
underlineColorAndroid='transparent'
disableFullscreenUI={true}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
/>
</View>
<View>
@ -355,6 +363,7 @@ export default class EditChannelInfo extends PureComponent {
textAlignVertical='top'
underlineColorAndroid='transparent'
disableFullscreenUI={true}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
/>
</View>
<View ref={this.lastText}>

View file

@ -25,7 +25,11 @@ import SafeAreaView from 'app/components/safe_area_view';
import SearchBar from 'app/components/search_bar';
import {DeviceTypes} from 'app/constants';
import {emptyFunction} from 'app/utils/general';
import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
import {
makeStyleSheetFromTheme,
changeOpacity,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import EmojiPickerRow from './emoji_picker_row';
@ -484,6 +488,7 @@ export default class EmojiPicker extends PureComponent {
onCancelButtonPress={this.cancelSearch}
autoCapitalize='none'
value={searchTerm}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
/>
</View>
<View style={styles.container}>

View file

@ -74,6 +74,7 @@ exports[`PostTextBox should match, full snapshot 1`] = `
blurOnSubmit={false}
disableFullscreenUI={true}
editable={true}
keyboardAppearance="light"
keyboardType="default"
multiline={true}
onChangeText={[Function]}

View file

@ -31,7 +31,11 @@ import {INSERT_TO_COMMENT, INSERT_TO_DRAFT, IS_REACTION_REGEX, MAX_CONTENT_HEIGH
import {NOTIFY_ALL_MEMBERS} from 'app/constants/view';
import {t} from 'app/utils/i18n';
import {confirmOutOfOfficeDisabled} from 'app/utils/status';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
const {RNTextInputReset} = NativeModules;
@ -722,6 +726,7 @@ export default class PostTextBoxBase extends PureComponent {
onEndEditing={this.handleEndEditing}
disableFullscreenUI={true}
editable={!channelIsReadOnly}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
/>
<Fade visible={this.isSendButtonVisible()}>
<SendButton

View file

@ -45,6 +45,7 @@ export default class SearchBarAndroid extends PureComponent {
value: PropTypes.string,
containerStyle: CustomPropTypes.Style,
leftComponent: PropTypes.element,
keyboardAppearance: PropTypes.string,
};
static defaultProps = {
@ -150,6 +151,7 @@ export default class SearchBarAndroid extends PureComponent {
containerStyle,
value,
showArrow,
keyboardAppearance,
} = this.props;
const {isFocused} = this.state;
@ -223,6 +225,7 @@ export default class SearchBarAndroid extends PureComponent {
selectionColor={selectionColor}
underlineColorAndroid='transparent'
disableFullscreenUI={true}
keyboardAppearance={keyboardAppearance}
style={[
styles.searchBarInput,
inputNoBackground,

View file

@ -39,6 +39,7 @@ export default class SearchBarIos extends PureComponent {
leftComponent: PropTypes.element,
searchIconCollapsedMargin: PropTypes.number,
searchIconExpandedMargin: PropTypes.number,
keyboardAppearance: PropTypes.string,
};
static defaultProps = {

View file

@ -77,6 +77,7 @@ export default class Search extends Component {
shadowVisible: PropTypes.bool,
leftComponent: PropTypes.element,
inputCollapsedMargin: PropTypes.number,
keyboardAppearance: PropTypes.string,
};
static defaultProps = {
@ -435,6 +436,7 @@ export default class Search extends Component {
onFocus={this.onFocus}
underlineColorAndroid='transparent'
enablesReturnKeyAutomatically={true}
keyboardAppearance={this.props.keyboardAppearance}
/>
</Animated.View>
<TouchableWithoutFeedback onPress={this.onFocus}>

View file

@ -13,7 +13,11 @@ import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import SearchBar from 'app/components/search_bar';
import {ViewTypes} from 'app/constants';
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import List from './list';
import SwitchTeamsButton from './switch_teams_button';
@ -149,6 +153,7 @@ export default class ChannelsList extends PureComponent {
onFocus={this.onSearchFocused}
searchIconCollapsedMargin={5}
searchIconExpandedMargin={5}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
value={term}
leftComponent={(
<SwitchTeamsButton

View file

@ -11,7 +11,11 @@ import {
} from 'react-native';
import FormattedText from 'app/components/formatted_text';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
export default class TextSetting extends PureComponent {
@ -172,6 +176,7 @@ export default class TextSetting extends PureComponent {
multiline={multiline}
keyboardType={keyboardType}
secureTextEntry={secureTextEntry}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
/>
</View>
</View>

View file

@ -24,7 +24,12 @@ import SearchBar from 'app/components/search_bar';
import StatusBar from 'app/components/status_bar';
import {alertErrorIfInvalidPermissions} from 'app/utils/general';
import {createProfilesSections, loadingText} from 'app/utils/member_list';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
setNavigatorStyles,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
export default class ChannelAddMembers extends PureComponent {
static propTypes = {
@ -352,6 +357,7 @@ export default class ChannelAddMembers extends PureComponent {
onSearchButtonPress={this.onSearch}
onCancelButtonPress={this.clearSearch}
autoCapitalize='none'
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
value={term}
/>
</View>

View file

@ -26,6 +26,7 @@ exports[`ChannelMembers should match snapshot 1`] = `
"fontSize": 15,
}
}
keyboardAppearance="light"
leftComponent={null}
onBlur={[Function]}
onCancelButtonPress={[Function]}

View file

@ -24,7 +24,12 @@ import SearchBar from 'app/components/search_bar';
import StatusBar from 'app/components/status_bar';
import {alertErrorIfInvalidPermissions} from 'app/utils/general';
import {createProfilesSections, loadingText} from 'app/utils/member_list';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
setNavigatorStyles,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
export default class ChannelMembers extends PureComponent {
static propTypes = {
@ -380,6 +385,7 @@ export default class ChannelMembers extends PureComponent {
onSearchButtonPress={this.onSearch}
onCancelButtonPress={this.clearSearch}
autoCapitalize='none'
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
value={term}
/>
</View>

View file

@ -14,7 +14,12 @@ import {
} from 'react-native';
import {getCodeFont} from 'app/utils/markdown';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
setNavigatorStyles,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
export default class Code extends React.PureComponent {
static propTypes = {
@ -75,6 +80,7 @@ export default class Code extends React.PureComponent {
multiline={true}
value={this.props.content}
style={[style.codeText]}
keyboardAppearance={getKeyboardAppearanceFromTheme(this.props.theme)}
/>
);
} else {

View file

@ -13,7 +13,12 @@ import ErrorText from 'app/components/error_text';
import Loading from 'app/components/loading';
import StatusBar from 'app/components/status_bar';
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
setNavigatorStyles,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
@ -195,6 +200,7 @@ export default class EditPost extends PureComponent {
placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.4)}
underlineColorAndroid='transparent'
disableFullscreenUI={true}
keyboardAppearance={getKeyboardAppearanceFromTheme(this.props.theme)}
/>
</View>
</View>

View file

@ -26,6 +26,7 @@ exports[`MoreChannels should match snapshot 1`] = `
"fontSize": 15,
}
}
keyboardAppearance="light"
leftComponent={null}
onBlur={[Function]}
onCancelButtonPress={[Function]}

View file

@ -19,7 +19,12 @@ import Loading from 'app/components/loading';
import SearchBar from 'app/components/search_bar';
import StatusBar from 'app/components/status_bar';
import {alertErrorWithFallback} from 'app/utils/general';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
setNavigatorStyles,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
export default class MoreChannels extends PureComponent {
static propTypes = {
@ -351,6 +356,7 @@ export default class MoreChannels extends PureComponent {
onSearchButtonPress={this.searchChannels}
onCancelButtonPress={this.cancelSearch}
autoCapitalize='none'
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
value={term}
/>
</View>

View file

@ -23,7 +23,12 @@ import SearchBar from 'app/components/search_bar';
import StatusBar from 'app/components/status_bar';
import {alertErrorWithFallback} from 'app/utils/general';
import {createProfilesSections, loadingText} from 'app/utils/member_list';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
setNavigatorStyles,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
import SelectedUsers from './selected_users';
@ -470,6 +475,7 @@ export default class MoreDirectMessages extends PureComponent {
onSearchButtonPress={this.onSearch}
onCancelButtonPress={this.clearSearch}
autoCapitalize='none'
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
value={term}
/>
<SelectedUsers

View file

@ -31,7 +31,11 @@ import StatusBar from 'app/components/status_bar';
import {DeviceTypes, ListTypes} from 'app/constants';
import mattermostManaged from 'app/mattermost_managed';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import ChannelDisplayName from './channel_display_name';
@ -709,6 +713,7 @@ export default class Search extends PureComponent {
value={value}
containerStyle={style.searchBarContainer}
backArrowSize={28}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
/>
</View>
<SectionList

View file

@ -30,6 +30,7 @@ exports[`SelectorScreen should match snapshot for channels 1`] = `
"fontSize": 15,
}
}
keyboardAppearance="light"
leftComponent={null}
onBlur={[Function]}
onCancelButtonPress={[Function]}
@ -121,6 +122,7 @@ exports[`SelectorScreen should match snapshot for channels 2`] = `
"fontSize": 15,
}
}
keyboardAppearance="light"
leftComponent={null}
onBlur={[Function]}
onCancelButtonPress={[Function]}
@ -212,6 +214,7 @@ exports[`SelectorScreen should match snapshot for explicit options 1`] = `
"fontSize": 15,
}
}
keyboardAppearance="light"
leftComponent={null}
onBlur={[Function]}
onCancelButtonPress={[Function]}
@ -310,6 +313,7 @@ exports[`SelectorScreen should match snapshot for searching 1`] = `
"fontSize": 15,
}
}
keyboardAppearance="light"
leftComponent={null}
onBlur={[Function]}
onCancelButtonPress={[Function]}
@ -401,6 +405,7 @@ exports[`SelectorScreen should match snapshot for users 1`] = `
"fontSize": 15,
}
}
keyboardAppearance="light"
leftComponent={null}
onBlur={[Function]}
onCancelButtonPress={[Function]}
@ -492,6 +497,7 @@ exports[`SelectorScreen should match snapshot for users 2`] = `
"fontSize": 15,
}
}
keyboardAppearance="light"
leftComponent={null}
onBlur={[Function]}
onCancelButtonPress={[Function]}

View file

@ -24,7 +24,12 @@ import SearchBar from 'app/components/search_bar';
import StatusBar from 'app/components/status_bar';
import {ViewTypes} from 'app/constants';
import {createProfilesSections, loadingText} from 'app/utils/member_list';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
setNavigatorStyles,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
export default class SelectorScreen extends PureComponent {
@ -333,6 +338,7 @@ export default class SelectorScreen extends PureComponent {
onSearchButtonPress={this.onSearch}
onCancelButtonPress={this.clearSearch}
autoCapitalize='none'
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
value={term}
/>
</View>

View file

@ -15,7 +15,11 @@ import FormattedText from 'app/components/formatted_text';
import StatusBar from 'app/components/status_bar';
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
import {getNotificationProps} from 'app/utils/notify_props';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
import Section from 'app/screens/settings/section';
@ -136,6 +140,7 @@ export default class NotificationSettingsAutoResponder extends PureComponent {
textAlignVertical='top'
underlineColorAndroid='transparent'
returnKeyType='done'
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
/>
</View>
</Section>

View file

@ -16,7 +16,11 @@ import RadioButtonGroup from 'app/components/radio_button';
import StatusBar from 'app/components/status_bar';
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
import SectionItem from 'app/screens/settings/section_item';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
import NotificationSettingsMentionsBase from './notification_settings_mention_base';
@ -79,6 +83,7 @@ class NotificationSettingsMentionsAndroid extends NotificationSettingsMentionsBa
returnKeyType='done'
returnKeyLabel={this.props.intl.formatMessage({id: 'mobile.notification_settings.modal_save'})}
underlineColorAndroid={theme.linkColor}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
/>
<FormattedText
id='mobile.notification_settings_mentions.keywordsHelp'

View file

@ -200,6 +200,7 @@ NotificationSettingsMentionsKeywords {
autoCorrect={false}
autoFocus={true}
blurOnSubmit={true}
keyboardAppearance="light"
multiline={true}
numberOfLines={1}
onChangeText={[Function]}

View file

@ -8,7 +8,12 @@ import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone
import FormattedText from 'app/components/formatted_text';
import StatusBar from 'app/components/status_bar';
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
setNavigatorStyles,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
export default class NotificationSettingsMentionsKeywords extends PureComponent {
static propTypes = {
@ -84,6 +89,7 @@ export default class NotificationSettingsMentionsKeywords extends PureComponent
placeholder={{id: 'mobile.notification_settings_mentions.keywordsDescription', defaultMessage: 'Other words that trigger a mention'}}
placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.4)}
returnKeyType='done'
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
/>
</View>
<View style={[style.helpContainer, padding(isLandscape)]}>

View file

@ -19,6 +19,7 @@ describe('NotificationSettingsMentionsKeywords', () => {
isLandscape: false,
onBack: jest.fn(),
theme: Preferences.THEMES.default,
isLandscape: false,
};
test('should match snapshot', () => {

View file

@ -16,7 +16,11 @@ import StatusBar from 'app/components/status_bar';
import SelectTimezoneRow from './select_timezone_row';
import {ListTypes} from 'app/constants';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
const ITEM_HEIGHT = 45;
@ -123,6 +127,7 @@ export default class Timezone extends PureComponent {
value={value}
containerStyle={style.searchBarContainer}
showArrow={false}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
/>
</View>
<FlatList

View file

@ -13,7 +13,12 @@ import {
} from 'react-native';
import {getCodeFont} from 'app/utils/markdown';
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import {
changeOpacity,
makeStyleSheetFromTheme,
setNavigatorStyles,
getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';
export default class TextPreview extends React.PureComponent {
static propTypes = {
@ -58,6 +63,7 @@ export default class TextPreview extends React.PureComponent {
multiline={true}
value={this.props.content}
style={[style.codeText]}
keyboardAppearance={getKeyboardAppearanceFromTheme(this.props.theme)}
/>
);
} else {

View file

@ -4,6 +4,8 @@
import {StyleSheet} from 'react-native';
import {Navigation} from 'react-native-navigation';
import tinyColor from 'tinycolor2';
import * as ThemeUtils from 'mattermost-redux/utils/theme_utils';
export function makeStyleSheetFromTheme(getStyleFromTheme) {
@ -45,3 +47,7 @@ export function isThemeSwitchingEnabled(state) {
const {config} = state.entities.general;
return config.EnableThemeSelection === 'true';
}
export function getKeyboardAppearanceFromTheme(theme) {
return tinyColor(theme.centerChannelBg).isLight() ? 'light' : 'dark';
}

36
app/utils/theme.test.js Normal file
View file

@ -0,0 +1,36 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {getKeyboardAppearanceFromTheme} from 'app/utils/theme';
describe('getKeyboardAppearanceFromTheme', () => {
const themes = [{
centerChannelBg: '#ffffff', // Mattermost
}, {
centerChannelBg: '#f2f4f8', // Organization
}, {
centerChannelBg: '#2f3e4e', // Mattermost Dark
}, {
centerChannelBg: '#1f1f1f', // Windows Dark
}];
it('should return "light" keyboard appearance for centerChannelBg="#ffffff"', () => {
const keyboardAppearance = getKeyboardAppearanceFromTheme(themes[0]);
expect(keyboardAppearance).toBe('light');
});
it('should return "light" keyboard appearance for centerChannelBg="#f2f4f8"', () => {
const keyboardAppearance = getKeyboardAppearanceFromTheme(themes[1]);
expect(keyboardAppearance).toBe('light');
});
it('should return "dark" keyboard appearance for centerChannelBg="#2f3e4e"', () => {
const keyboardAppearance = getKeyboardAppearanceFromTheme(themes[2]);
expect(keyboardAppearance).toBe('dark');
});
it('should return "dark" keyboard appearance for centerChannelBg="#1f1f1f"', () => {
const keyboardAppearance = getKeyboardAppearanceFromTheme(themes[3]);
expect(keyboardAppearance).toBe('dark');
});
});