diff --git a/src/account-info/AccountDetails.js b/src/account-info/AccountDetails.js index fcdfff8022e..cdb61a7ad4a 100644 --- a/src/account-info/AccountDetails.js +++ b/src/account-info/AccountDetails.js @@ -1,10 +1,11 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; +import type { Node } from 'react'; import { View } from 'react-native'; -import type { User, UserOrBot, Dispatch } from '../types'; +import type { UserOrBot } from '../types'; import styles, { createStyleSheet } from '../styles'; -import { connect } from '../react-redux'; +import { useSelector } from '../react-redux'; import { UserAvatar, ComponentList, RawLabel } from '../common'; import { getOwnUser, getUserStatusTextForUser } from '../selectors'; import PresenceStatusIndicator from '../common/PresenceStatusIndicator'; @@ -29,68 +30,56 @@ const componentStyles = createStyleSheet({ }, }); -type SelectorProps = {| - ownUser: User, - userStatusText: string | void, -|}; - type Props = $ReadOnly<{| user: UserOrBot, - - dispatch: Dispatch, - ...SelectorProps, |}>; -class AccountDetails extends PureComponent { - render() { - const { ownUser, user, userStatusText } = this.props; +export default function AccountDetails(props: Props): Node { + const { user } = props; - const isSelf = user.user_id === ownUser.user_id; + const ownUser = useSelector(getOwnUser); + const userStatusText = useSelector(state => getUserStatusTextForUser(state, props.user.user_id)); - let localTime: string | null = null; - // See comments at CrossRealmBot and User at src/api/modelTypes.js. - if (user.timezone !== '' && user.timezone !== undefined) { - try { - localTime = `${nowInTimeZone(user.timezone)} Local time`; - } catch (err) { - // The set of timezone names in the tz database is subject to change over - // time. Handle unrecognized timezones by quietly discarding them. - } + const isSelf = user.user_id === ownUser.user_id; + + let localTime: string | null = null; + // See comments at CrossRealmBot and User at src/api/modelTypes.js. + if (user.timezone !== '' && user.timezone !== undefined) { + try { + localTime = `${nowInTimeZone(user.timezone)} Local time`; + } catch (err) { + // The set of timezone names in the tz database is subject to change over + // time. Handle unrecognized timezones by quietly discarding them. } + } - return ( - + return ( + + + + + + + + + {userStatusText !== undefined && ( + + )} + {!isSelf && ( - + - - - + )} + {!isSelf && localTime !== null && ( + + - {userStatusText !== undefined && ( - - )} - {!isSelf && ( - - - - )} - {!isSelf && localTime !== null && ( - - - - )} - - ); - } + )} + + ); } - -export default connect((state, props) => ({ - ownUser: getOwnUser(state), - userStatusText: getUserStatusTextForUser(state, props.user.user_id), -}))(AccountDetails); diff --git a/src/account-info/AccountDetailsScreen.js b/src/account-info/AccountDetailsScreen.js index 9ffdfa66743..9a02cf37f36 100644 --- a/src/account-info/AccountDetailsScreen.js +++ b/src/account-info/AccountDetailsScreen.js @@ -1,11 +1,12 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React, { useCallback } from 'react'; +import type { Node } from 'react'; import type { RouteProp } from '../react-navigation'; import type { AppNavigationProp } from '../nav/AppNavigator'; -import type { Dispatch, UserOrBot, UserId } from '../types'; +import type { UserId } from '../types'; import { createStyleSheet } from '../styles'; -import { connect } from '../react-redux'; +import { useSelector, useDispatch } from '../react-redux'; import { Screen, ZulipButton, Label } from '../common'; import { IconPrivateChat } from '../common/Icons'; import { pm1to1NarrowFromUser } from '../utils/narrow'; @@ -25,53 +26,40 @@ const styles = createStyleSheet({ }, }); -type SelectorProps = $ReadOnly<{| - isActive: boolean, - user: UserOrBot, -|}>; - type Props = $ReadOnly<{| navigation: AppNavigationProp<'account-details'>, route: RouteProp<'account-details', {| userId: UserId |}>, - - dispatch: Dispatch, - ...SelectorProps, |}>; -class AccountDetailsScreen extends PureComponent { - handleChatPress = () => { - const { user, dispatch } = this.props; +export default function AccountDetailsScreen(props: Props): Node { + const dispatch = useDispatch(); + const user = useSelector(state => getUserForId(state, props.route.params.userId)); + const isActive = useSelector(state => getUserIsActive(state, props.route.params.userId)); + + const handleChatPress = useCallback(() => { dispatch(doNarrow(pm1to1NarrowFromUser(user))); - }; + }, [user, dispatch]); - render() { - const { user, isActive } = this.props; - const title = { - text: '{_}', - values: { - // This causes the name not to get translated. - _: user.full_name || ' ', - }, - }; + const title = { + text: '{_}', + values: { + // This causes the name not to get translated. + _: user.full_name || ' ', + }, + }; - return ( - - - {!isActive && ( - - ); - } + return ( + + + {!isActive && ( + + ); } - -export default connect((state, props) => ({ - user: getUserForId(state, props.route.params.userId), - isActive: getUserIsActive(state, props.route.params.userId), -}))(AccountDetailsScreen); diff --git a/src/autocomplete/TopicAutocomplete.js b/src/autocomplete/TopicAutocomplete.js index 4dd6bb55015..3371cb972c7 100644 --- a/src/autocomplete/TopicAutocomplete.js +++ b/src/autocomplete/TopicAutocomplete.js @@ -1,6 +1,7 @@ /* @flow strict-local */ import React, { useEffect } from 'react'; +import type { Node } from 'react'; import { FlatList } from 'react-native'; import type { Narrow } from '../types'; @@ -24,7 +25,7 @@ type Props = $ReadOnly<{| onAutocomplete: (name: string) => void, |}>; -export default function TopicAutocomplete(props: Props) { +export default function TopicAutocomplete(props: Props): Node { const { narrow, isFocused, text, onAutocomplete } = props; const dispatch = useDispatch(); const topics = useSelector(state => getTopicsForNarrow(state, narrow)); diff --git a/src/chat/GroupDetailsScreen.js b/src/chat/GroupDetailsScreen.js index 7f8e62baeec..b9c15e0c87b 100644 --- a/src/chat/GroupDetailsScreen.js +++ b/src/chat/GroupDetailsScreen.js @@ -1,5 +1,6 @@ /* @flow strict-local */ import React, { useCallback } from 'react'; +import type { Node } from 'react'; import { FlatList } from 'react-native'; import type { RouteProp } from '../react-navigation'; @@ -15,7 +16,7 @@ type Props = $ReadOnly<{| route: RouteProp<'group-details', {| recipients: $ReadOnlyArray |}>, |}>; -export default function GroupDetailsScreen(props: Props) { +export default function GroupDetailsScreen(props: Props): Node { const { recipients } = props.route.params; const handlePress = useCallback((user: UserOrBot) => { NavigationService.dispatch(navigateToAccountDetails(user.user_id)); diff --git a/src/chat/UnreadNotice.js b/src/chat/UnreadNotice.js index 01d898692c4..d6fe5340151 100644 --- a/src/chat/UnreadNotice.js +++ b/src/chat/UnreadNotice.js @@ -1,6 +1,7 @@ /* @flow strict-local */ import React from 'react'; +import type { Node } from 'react'; import { View } from 'react-native'; import type { Narrow } from '../types'; @@ -39,7 +40,7 @@ type Props = $ReadOnly<{| narrow: Narrow, |}>; -export default function UnreadNotice(props: Props) { +export default function UnreadNotice(props: Props): Node { const { narrow } = props; const unreadCount = useSelector(state => getUnreadCountForNarrow(state, narrow)); diff --git a/src/common/LoadingBanner.js b/src/common/LoadingBanner.js index 59f1cdf5541..d40e5ee396e 100644 --- a/src/common/LoadingBanner.js +++ b/src/common/LoadingBanner.js @@ -1,6 +1,7 @@ /* @flow strict-local */ import React, { useContext } from 'react'; +import type { Node } from 'react'; import { View } from 'react-native'; import { useSelector } from '../react-redux'; @@ -30,7 +31,7 @@ type Props = $ReadOnly<{| /** * Display a notice that the app is connecting to the server, when appropriate. */ -export default function LoadingBanner(props: Props) { +export default function LoadingBanner(props: Props): Node { const loading = useSelector(getLoading); const themeContext = useContext(ThemeContext); diff --git a/src/common/ZulipStatusBar.js b/src/common/ZulipStatusBar.js index ffd3322f95d..b740ea819e1 100644 --- a/src/common/ZulipStatusBar.js +++ b/src/common/ZulipStatusBar.js @@ -1,12 +1,13 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; +import type { Node } from 'react'; import { Platform, StatusBar } from 'react-native'; // $FlowFixMe[untyped-import] import Color from 'color'; -import type { Orientation, ThemeName, Dispatch } from '../types'; -import { connect } from '../react-redux'; +import type { ThemeName } from '../types'; +import { useSelector } from '../react-redux'; import { foregroundColorFromBackground } from '../utils/color'; import { getSession, getSettings } from '../selectors'; @@ -20,17 +21,9 @@ export const getStatusBarStyle = (statusBarColor: string): BarStyle => ? 'light-content' : 'dark-content'; -type SelectorProps = $ReadOnly<{| - theme: ThemeName, - orientation: Orientation, -|}>; - type Props = $ReadOnly<{| backgroundColor?: string | void, - hidden: boolean, - - dispatch: Dispatch, - ...SelectorProps, + hidden?: boolean, |}>; /** @@ -50,33 +43,24 @@ type Props = $ReadOnly<{| * top inset grows to accommodate a visible status bar, and shrinks to * give more room to the app's content when the status bar is hidden. */ -class ZulipStatusBar extends PureComponent { - static defaultProps = { - hidden: false, - }; - - render() { - const { theme, hidden, orientation } = this.props; - const backgroundColor = this.props.backgroundColor; - const statusBarColor = getStatusBarColor(backgroundColor, theme); - return ( - orientation === 'PORTRAIT' && ( -