Chore: Migrate SettingsView to hooks (#4542)

* migrate settings view to hooks

* temp - add non-null assertion

* fix options

* Update app/views/SettingsView/index.tsx

Co-authored-by: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com>

* Update app/views/SettingsView/index.tsx

Co-authored-by: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com>

* fix options

Co-authored-by: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com>
This commit is contained in:
Gleidson Daniel Silva 2022-09-30 11:50:33 -03:00 committed by GitHub
parent 2f03ca52c5
commit fe04faac64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 183 additions and 206 deletions

View File

@ -165,7 +165,7 @@ const SettingsStackNavigator = () => {
<SettingsStack.Navigator <SettingsStack.Navigator
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions} screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}
> >
<SettingsStack.Screen name='SettingsView' component={SettingsView} options={SettingsView.navigationOptions} /> <SettingsStack.Screen name='SettingsView' component={SettingsView} />
<SettingsStack.Screen name='SecurityPrivacyView' component={SecurityPrivacyView} /> <SettingsStack.Screen name='SecurityPrivacyView' component={SecurityPrivacyView} />
<SettingsStack.Screen <SettingsStack.Screen
name='E2EEncryptionSecurityView' name='E2EEncryptionSecurityView'

View File

@ -170,11 +170,7 @@ const ModalStackNavigator = React.memo(({ navigation }: INavigation) => {
component={ReadReceiptsView} component={ReadReceiptsView}
options={props => ReadReceiptsView.navigationOptions!({ ...props, isMasterDetail: true })} options={props => ReadReceiptsView.navigationOptions!({ ...props, isMasterDetail: true })}
/> />
<ModalStack.Screen <ModalStack.Screen name='SettingsView' component={SettingsView} />
name='SettingsView'
component={SettingsView}
options={props => SettingsView.navigationOptions!({ ...props, isMasterDetail: true })}
/>
<ModalStack.Screen name='LanguageView' component={LanguageView} /> <ModalStack.Screen name='LanguageView' component={LanguageView} />
<ModalStack.Screen name='ThemeView' component={ThemeView} /> <ModalStack.Screen name='ThemeView' component={ThemeView} />
<ModalStack.Screen name='DefaultBrowserView' component={DefaultBrowserView} /> <ModalStack.Screen name='DefaultBrowserView' component={DefaultBrowserView} />

View File

@ -1,62 +1,66 @@
import CookieManager from '@react-native-cookies/cookies';
import { StackNavigationOptions } from '@react-navigation/stack';
import FastImage from 'react-native-fast-image';
import React from 'react';
import { Linking, Share } from 'react-native';
import Clipboard from '@react-native-clipboard/clipboard'; import Clipboard from '@react-native-clipboard/clipboard';
import { connect } from 'react-redux'; import CookieManager from '@react-native-cookies/cookies';
import { useNavigation } from '@react-navigation/native';
import React, { useLayoutEffect } from 'react';
import { Linking, Share } from 'react-native';
import FastImage from 'react-native-fast-image';
import { useDispatch } from 'react-redux';
import { StackNavigationProp } from '@react-navigation/stack';
import { appStart } from '../../actions/app'; import { appStart } from '../../actions/app';
import { logout } from '../../actions/login'; import { logout } from '../../actions/login';
import { selectServerRequest } from '../../actions/server'; import { selectServerRequest } from '../../actions/server';
import { APP_STORE_LINK, FDROID_MARKET_LINK, LICENSE_LINK, PLAY_MARKET_LINK, isFDroidBuild, themes } from '../../lib/constants';
import * as HeaderButton from '../../containers/HeaderButton'; import * as HeaderButton from '../../containers/HeaderButton';
import * as List from '../../containers/List'; import * as List from '../../containers/List';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import StatusBar from '../../containers/StatusBar'; import StatusBar from '../../containers/StatusBar';
import { LISTENER } from '../../containers/Toast'; import { LISTENER } from '../../containers/Toast';
import { IApplicationState, IBaseScreen, IUser, RootEnum } from '../../definitions'; import { RootEnum } from '../../definitions';
import I18n from '../../i18n'; import I18n from '../../i18n';
import { APP_STORE_LINK, FDROID_MARKET_LINK, isFDroidBuild, LICENSE_LINK, PLAY_MARKET_LINK } from '../../lib/constants';
import database from '../../lib/database'; import database from '../../lib/database';
import { IServer } from '../../reducers/server'; import { useAppSelector } from '../../lib/hooks';
import { getUserSelector } from '../../selectors/login'; import { clearCache } from '../../lib/methods';
import { SettingsStackParamList } from '../../stacks/types'; import { deleteAllAudioFiles } from '../../lib/methods/audioFile';
import { withTheme } from '../../theme';
import { getDeviceModel, getReadableVersion, isAndroid } from '../../lib/methods/helpers'; import { getDeviceModel, getReadableVersion, isAndroid } from '../../lib/methods/helpers';
import EventEmitter from '../../lib/methods/helpers/events'; import EventEmitter from '../../lib/methods/helpers/events';
import { showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers/info'; import { showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers/info';
import { events, logEvent } from '../../lib/methods/helpers/log'; import { events, logEvent } from '../../lib/methods/helpers/log';
import openLink from '../../lib/methods/helpers/openLink'; import openLink from '../../lib/methods/helpers/openLink';
import { onReviewPress } from '../../lib/methods/helpers/review'; import { onReviewPress } from '../../lib/methods/helpers/review';
import SidebarView from '../SidebarView';
import { clearCache } from '../../lib/methods';
import { Services } from '../../lib/services'; import { Services } from '../../lib/services';
import { deleteAllAudioFiles } from '../../lib/methods/audioFile'; import { getUserSelector } from '../../selectors/login';
import { SettingsStackParamList } from '../../stacks/types';
import { useTheme } from '../../theme';
import SidebarView from '../SidebarView';
type TLogScreenName = 'SE_GO_LANGUAGE' | 'SE_GO_DEFAULTBROWSER' | 'SE_GO_THEME' | 'SE_GO_PROFILE' | 'SE_GO_SECURITYPRIVACY'; type TLogScreenName = 'SE_GO_LANGUAGE' | 'SE_GO_DEFAULTBROWSER' | 'SE_GO_THEME' | 'SE_GO_PROFILE' | 'SE_GO_SECURITYPRIVACY';
interface ISettingsViewProps extends IBaseScreen<SettingsStackParamList, 'SettingsView'> { const SettingsView = (): React.ReactElement => {
server: IServer; const { colors, theme } = useTheme();
user: IUser; const navigation = useNavigation<StackNavigationProp<SettingsStackParamList, 'SettingsView'>>();
} const dispatch = useDispatch();
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
const userId = useAppSelector(state => getUserSelector(state).id);
const { server, version } = useAppSelector(state => state.server);
class SettingsView extends React.Component<ISettingsViewProps> { useLayoutEffect(() => {
static navigationOptions = ({ navigation, isMasterDetail }: ISettingsViewProps): StackNavigationOptions => ({ navigation.setOptions({
headerLeft: () => headerLeft: () =>
isMasterDetail ? ( isMasterDetail ? (
<HeaderButton.CloseModal navigation={navigation} testID='settings-view-close' /> <HeaderButton.CloseModal navigation={navigation} testID='settings-view-close' />
) : ( ) : (
<HeaderButton.Drawer navigation={navigation} testID='settings-view-drawer' /> <HeaderButton.Drawer navigation={navigation} testID='settings-view-drawer' />
), ),
title: I18n.t('Settings') title: I18n.t('Settings')
}); });
}, [navigation, isMasterDetail]);
checkCookiesAndLogout = async () => { const checkCookiesAndLogout = async () => {
const { dispatch, user } = this.props;
const db = database.servers; const db = database.servers;
const usersCollection = db.get('users'); const usersCollection = db.get('users');
try { try {
const userRecord = await usersCollection.find(user.id); const userRecord = await usersCollection.find(userId);
if (userRecord.isFromWebView) { if (userRecord.isFromWebView) {
showConfirmationAlert({ showConfirmationAlert({
title: I18n.t('Clear_cookies_alert'), title: I18n.t('Clear_cookies_alert'),
@ -79,25 +83,21 @@ class SettingsView extends React.Component<ISettingsViewProps> {
} }
}; };
handleLogout = () => { const handleLogout = () => {
logEvent(events.SE_LOG_OUT); logEvent(events.SE_LOG_OUT);
showConfirmationAlert({ showConfirmationAlert({
message: I18n.t('You_will_be_logged_out_of_this_application'), message: I18n.t('You_will_be_logged_out_of_this_application'),
confirmationText: I18n.t('Logout'), confirmationText: I18n.t('Logout'),
onPress: this.checkCookiesAndLogout onPress: checkCookiesAndLogout
}); });
}; };
handleClearCache = () => { const handleClearCache = () => {
logEvent(events.SE_CLEAR_LOCAL_SERVER_CACHE); logEvent(events.SE_CLEAR_LOCAL_SERVER_CACHE);
showConfirmationAlert({ showConfirmationAlert({
message: I18n.t('This_will_clear_all_your_offline_data'), message: I18n.t('This_will_clear_all_your_offline_data'),
confirmationText: I18n.t('Clear'), confirmationText: I18n.t('Clear'),
onPress: async () => { onPress: async () => {
const {
server: { server },
dispatch
} = this.props;
dispatch(appStart({ root: RootEnum.ROOT_LOADING, text: I18n.t('Clear_cache_loading') })); dispatch(appStart({ root: RootEnum.ROOT_LOADING, text: I18n.t('Clear_cache_loading') }));
await deleteAllAudioFiles(server); await deleteAllAudioFiles(server);
await clearCache({ server }); await clearCache({ server });
@ -109,14 +109,13 @@ class SettingsView extends React.Component<ISettingsViewProps> {
}); });
}; };
navigateToScreen = (screen: keyof SettingsStackParamList) => { const navigateToScreen = (screen: keyof SettingsStackParamList) => {
const screenName = screen.replace('View', '').toUpperCase(); const screenName = screen.replace('View', '').toUpperCase();
logEvent(events[`SE_GO_${screenName}` as TLogScreenName]); logEvent(events[`SE_GO_${screenName}` as TLogScreenName]);
const { navigation } = this.props;
navigation.navigate(screen); navigation.navigate(screen);
}; };
sendEmail = async () => { const sendEmail = async () => {
logEvent(events.SE_CONTACT_US); logEvent(events.SE_CONTACT_US);
const subject = encodeURI('Rocket.Chat Mobile App Support'); const subject = encodeURI('Rocket.Chat Mobile App Support');
const email = encodeURI('support@rocket.chat'); const email = encodeURI('support@rocket.chat');
@ -132,7 +131,7 @@ class SettingsView extends React.Component<ISettingsViewProps> {
} }
}; };
shareApp = () => { const shareApp = () => {
let message; let message;
if (isAndroid) { if (isAndroid) {
message = PLAY_MARKET_LINK; message = PLAY_MARKET_LINK;
@ -145,157 +144,139 @@ class SettingsView extends React.Component<ISettingsViewProps> {
Share.share({ message }); Share.share({ message });
}; };
copyServerVersion = () => { const saveToClipboard = async (content: string) => {
const {
server: { version }
} = this.props;
const vers = version as string;
logEvent(events.SE_COPY_SERVER_VERSION, { serverVersion: vers });
this.saveToClipboard(vers);
};
copyAppVersion = () => {
logEvent(events.SE_COPY_APP_VERSION, { appVersion: getReadableVersion });
this.saveToClipboard(getReadableVersion);
};
saveToClipboard = async (content: string) => {
await Clipboard.setString(content); await Clipboard.setString(content);
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') }); EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
}; };
onPressLicense = () => { const copyServerVersion = () => {
const vers = version as string;
logEvent(events.SE_COPY_SERVER_VERSION, { serverVersion: vers });
saveToClipboard(vers);
};
const copyAppVersion = () => {
logEvent(events.SE_COPY_APP_VERSION, { appVersion: getReadableVersion });
saveToClipboard(getReadableVersion);
};
const onPressLicense = () => {
logEvent(events.SE_READ_LICENSE); logEvent(events.SE_READ_LICENSE);
const { theme } = this.props;
openLink(LICENSE_LINK, theme); openLink(LICENSE_LINK, theme);
}; };
render() { return (
const { server, isMasterDetail, theme } = this.props; <SafeAreaView testID='settings-view'>
return ( <StatusBar />
<SafeAreaView testID='settings-view'> <List.Container>
<StatusBar /> {isMasterDetail ? (
<List.Container> <>
{isMasterDetail ? ( <List.Section>
<List.Separator />
<SidebarView />
<List.Separator />
</List.Section>
<List.Section>
<List.Separator />
<List.Item title='Display' onPress={() => navigateToScreen('DisplayPrefsView')} showActionIndicator />
<List.Separator />
<List.Item
title='Profile'
onPress={() => navigateToScreen('ProfileView')}
showActionIndicator
testID='settings-profile'
/>
<List.Separator />
</List.Section>
</>
) : null}
<List.Section>
<List.Separator />
<List.Item title='Contact_us' onPress={sendEmail} showActionIndicator testID='settings-view-contact' />
<List.Separator />
<List.Item
title='Language'
onPress={() => navigateToScreen('LanguageView')}
showActionIndicator
testID='settings-view-language'
/>
<List.Separator />
{!isFDroidBuild ? (
<> <>
<List.Section> <List.Item title='Review_this_app' showActionIndicator onPress={onReviewPress} testID='settings-view-review-app' />
<List.Separator />
<SidebarView theme={theme} />
<List.Separator />
</List.Section>
<List.Section>
<List.Separator />
<List.Item title='Display' onPress={() => this.navigateToScreen('DisplayPrefsView')} showActionIndicator />
<List.Separator />
<List.Item
title='Profile'
onPress={() => this.navigateToScreen('ProfileView')}
showActionIndicator
testID='settings-profile'
/>
<List.Separator />
</List.Section>
</> </>
) : null} ) : null}
<List.Separator />
<List.Item title='Share_this_app' showActionIndicator onPress={shareApp} testID='settings-view-share-app' />
<List.Separator />
<List.Item
title='Default_browser'
showActionIndicator
onPress={() => navigateToScreen('DefaultBrowserView')}
testID='settings-view-default-browser'
/>
<List.Separator />
<List.Item
title='Theme'
showActionIndicator
onPress={() => navigateToScreen('ThemeView')}
testID='settings-view-theme'
/>
<List.Separator />
<List.Item
title='Security_and_privacy'
showActionIndicator
onPress={() => navigateToScreen('SecurityPrivacyView')}
testID='settings-view-security-privacy'
/>
<List.Separator />
</List.Section>
<List.Section> <List.Section>
<List.Separator /> <List.Separator />
<List.Item title='Contact_us' onPress={this.sendEmail} showActionIndicator testID='settings-view-contact' /> <List.Item title='License' onPress={onPressLicense} showActionIndicator testID='settings-view-license' />
<List.Separator /> <List.Separator />
<List.Item <List.Item
title='Language' title={I18n.t('Version_no', { version: getReadableVersion })}
onPress={() => this.navigateToScreen('LanguageView')} onPress={copyAppVersion}
showActionIndicator testID='settings-view-version'
testID='settings-view-language' translateTitle={false}
/> />
<List.Separator /> <List.Separator />
{!isFDroidBuild ? ( <List.Item
<> title={I18n.t('Server_version', { version })}
<List.Item onPress={copyServerVersion}
title='Review_this_app' subtitle={`${server.split('//')[1]}`}
showActionIndicator testID='settings-view-server-version'
onPress={onReviewPress} translateTitle={false}
testID='settings-view-review-app' translateSubtitle={false}
/> />
</> <List.Separator />
) : null} </List.Section>
<List.Separator />
<List.Item title='Share_this_app' showActionIndicator onPress={this.shareApp} testID='settings-view-share-app' />
<List.Separator />
<List.Item
title='Default_browser'
showActionIndicator
onPress={() => this.navigateToScreen('DefaultBrowserView')}
testID='settings-view-default-browser'
/>
<List.Separator />
<List.Item
title='Theme'
showActionIndicator
onPress={() => this.navigateToScreen('ThemeView')}
testID='settings-view-theme'
/>
<List.Separator />
<List.Item
title='Security_and_privacy'
showActionIndicator
onPress={() => this.navigateToScreen('SecurityPrivacyView')}
testID='settings-view-security-privacy'
/>
<List.Separator />
</List.Section>
<List.Section> <List.Section>
<List.Separator /> <List.Separator />
<List.Item title='License' onPress={this.onPressLicense} showActionIndicator testID='settings-view-license' /> <List.Item
<List.Separator /> title='Clear_cache'
<List.Item testID='settings-view-clear-cache'
title={I18n.t('Version_no', { version: getReadableVersion })} onPress={handleClearCache}
onPress={this.copyAppVersion} showActionIndicator
testID='settings-view-version' color={colors.dangerColor}
translateTitle={false} />
/> <List.Separator />
<List.Separator /> <List.Item
<List.Item title='Logout'
title={I18n.t('Server_version', { version: server.version })} testID='settings-logout'
onPress={this.copyServerVersion} onPress={handleLogout}
subtitle={`${server.server.split('//')[1]}`} showActionIndicator
testID='settings-view-server-version' color={colors.dangerColor}
translateTitle={false} />
translateSubtitle={false} <List.Separator />
/> </List.Section>
<List.Separator /> </List.Container>
</List.Section> </SafeAreaView>
);
};
<List.Section> export default SettingsView;
<List.Separator />
<List.Item
title='Clear_cache'
testID='settings-view-clear-cache'
onPress={this.handleClearCache}
showActionIndicator
color={themes[theme].dangerColor}
/>
<List.Separator />
<List.Item
title='Logout'
testID='settings-logout'
onPress={this.handleLogout}
showActionIndicator
color={themes[theme].dangerColor}
/>
<List.Separator />
</List.Section>
</List.Container>
</SafeAreaView>
);
}
}
const mapStateToProps = (state: IApplicationState) => ({
server: state.server,
user: getUserSelector(state),
isMasterDetail: state.app.isMasterDetail
});
export default connect(mapStateToProps)(withTheme(SettingsView));

View File

@ -32,7 +32,7 @@ interface ISidebarProps {
state?: DrawerNavigationState<DrawerParamList>; state?: DrawerNavigationState<DrawerParamList>;
Site_Name: string; Site_Name: string;
user: IUser; user: IUser;
theme: TSupportedThemes; theme?: TSupportedThemes;
loadingServer: boolean; loadingServer: boolean;
useRealName: boolean; useRealName: boolean;
allowStatusMessage: boolean; allowStatusMessage: boolean;
@ -167,10 +167,10 @@ class Sidebar extends Component<ISidebarProps, ISidebarState> {
<List.Separator /> <List.Separator />
<SidebarItem <SidebarItem
text={I18n.t('Admin_Panel')} text={I18n.t('Admin_Panel')}
left={<CustomIcon name='settings' size={20} color={themes[theme].titleText} />} left={<CustomIcon name='settings' size={20} color={themes[theme!].titleText} />}
onPress={() => this.sidebarNavigate(routeName)} onPress={() => this.sidebarNavigate(routeName)}
testID='sidebar-admin' testID='sidebar-admin'
theme={theme} theme={theme!}
current={this.currentItemKey === routeName} current={this.currentItemKey === routeName}
/> />
</> </>
@ -183,34 +183,34 @@ class Sidebar extends Component<ISidebarProps, ISidebarState> {
<> <>
<SidebarItem <SidebarItem
text={I18n.t('Chats')} text={I18n.t('Chats')}
left={<CustomIcon name='message' size={20} color={themes[theme].titleText} />} left={<CustomIcon name='message' size={20} color={themes[theme!].titleText} />}
onPress={() => this.sidebarNavigate('ChatsStackNavigator')} onPress={() => this.sidebarNavigate('ChatsStackNavigator')}
testID='sidebar-chats' testID='sidebar-chats'
theme={theme} theme={theme!}
current={this.currentItemKey === 'ChatsStackNavigator'} current={this.currentItemKey === 'ChatsStackNavigator'}
/> />
<SidebarItem <SidebarItem
text={I18n.t('Profile')} text={I18n.t('Profile')}
left={<CustomIcon name='user' size={20} color={themes[theme].titleText} />} left={<CustomIcon name='user' size={20} color={themes[theme!].titleText} />}
onPress={() => this.sidebarNavigate('ProfileStackNavigator')} onPress={() => this.sidebarNavigate('ProfileStackNavigator')}
testID='sidebar-profile' testID='sidebar-profile'
theme={theme} theme={theme!}
current={this.currentItemKey === 'ProfileStackNavigator'} current={this.currentItemKey === 'ProfileStackNavigator'}
/> />
<SidebarItem <SidebarItem
text={I18n.t('Display')} text={I18n.t('Display')}
left={<CustomIcon name='sort' size={20} color={themes[theme].titleText} />} left={<CustomIcon name='sort' size={20} color={themes[theme!].titleText} />}
onPress={() => this.sidebarNavigate('DisplayPrefStackNavigator')} onPress={() => this.sidebarNavigate('DisplayPrefStackNavigator')}
testID='sidebar-display' testID='sidebar-display'
theme={theme} theme={theme!}
current={this.currentItemKey === 'DisplayPrefStackNavigator'} current={this.currentItemKey === 'DisplayPrefStackNavigator'}
/> />
<SidebarItem <SidebarItem
text={I18n.t('Settings')} text={I18n.t('Settings')}
left={<CustomIcon name='administration' size={20} color={themes[theme].titleText} />} left={<CustomIcon name='administration' size={20} color={themes[theme!].titleText} />}
onPress={() => this.sidebarNavigate('SettingsStackNavigator')} onPress={() => this.sidebarNavigate('SettingsStackNavigator')}
testID='sidebar-settings' testID='sidebar-settings'
theme={theme} theme={theme!}
current={this.currentItemKey === 'SettingsStackNavigator'} current={this.currentItemKey === 'SettingsStackNavigator'}
/> />
{this.renderAdmin()} {this.renderAdmin()}
@ -224,8 +224,8 @@ class Sidebar extends Component<ISidebarProps, ISidebarState> {
<SidebarItem <SidebarItem
text={user.statusText || I18n.t('Edit_Status')} text={user.statusText || I18n.t('Edit_Status')}
left={<Status size={24} status={user?.status} />} left={<Status size={24} status={user?.status} />}
theme={theme} theme={theme!}
right={<CustomIcon name='edit' size={20} color={themes[theme].titleText} />} right={<CustomIcon name='edit' size={20} color={themes[theme!].titleText} />}
onPress={() => this.sidebarNavigate('StatusView')} onPress={() => this.sidebarNavigate('StatusView')}
testID={`sidebar-custom-status-${user.status}`} testID={`sidebar-custom-status-${user.status}`}
/> />
@ -239,12 +239,12 @@ class Sidebar extends Component<ISidebarProps, ISidebarState> {
return null; return null;
} }
return ( return (
<SafeAreaView testID='sidebar-view' style={{ backgroundColor: themes[theme].focusedBackground }} vertical={isMasterDetail}> <SafeAreaView testID='sidebar-view' style={{ backgroundColor: themes[theme!].focusedBackground }} vertical={isMasterDetail}>
<ScrollView <ScrollView
style={[ style={[
styles.container, styles.container,
{ {
backgroundColor: isMasterDetail ? themes[theme].backgroundColor : themes[theme].focusedBackground backgroundColor: isMasterDetail ? themes[theme!].backgroundColor : themes[theme!].focusedBackground
} }
]} ]}
{...scrollPersistTaps} {...scrollPersistTaps}
@ -254,12 +254,12 @@ class Sidebar extends Component<ISidebarProps, ISidebarState> {
<Avatar text={user.username} style={styles.avatar} size={30} /> <Avatar text={user.username} style={styles.avatar} size={30} />
<View style={styles.headerTextContainer}> <View style={styles.headerTextContainer}>
<View style={styles.headerUsername}> <View style={styles.headerUsername}>
<Text numberOfLines={1} style={[styles.username, { color: themes[theme].titleText }]}> <Text numberOfLines={1} style={[styles.username, { color: themes[theme!].titleText }]}>
{useRealName ? user.name : user.username} {useRealName ? user.name : user.username}
</Text> </Text>
</View> </View>
<Text <Text
style={[styles.currentServerText, { color: themes[theme].titleText }]} style={[styles.currentServerText, { color: themes[theme!].titleText }]}
numberOfLines={1} numberOfLines={1}
accessibilityLabel={`Connected to ${baseUrl}`} accessibilityLabel={`Connected to ${baseUrl}`}
> >