Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4710418
flow: Annotate some exported React components, where very easy.
chrisbobbe Jul 26, 2021
eee2bad
userHelpers types [nfc]: Annotate return type of `getUsersAndWildcards`.
chrisbobbe Jul 26, 2021
38e748b
AccountDetails [nfc]: Convert to function component.
chrisbobbe Jul 26, 2021
5f460df
AccountDetails [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 26, 2021
3b8def2
AccountDetailsScreen [nfc]: Convert to function component.
chrisbobbe Jul 26, 2021
01c0317
AccountDetailsScreen [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 26, 2021
1df591e
ZulipStatusBar [nfc]: Convert to function component.
chrisbobbe Jul 26, 2021
7cbcb5e
ZulipStatusBar [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 26, 2021
5a9c4da
Emoji [nfc]: Pull out `componentStyles`.
chrisbobbe Jul 26, 2021
99a882f
Emoji [nfc]: Convert to function component.
chrisbobbe Jul 26, 2021
c50515e
Emoji [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 26, 2021
6b72238
EmojiPickerScreen [nfc]: Convert to function component.
chrisbobbe Jul 26, 2021
7b99de8
EmojiPickerScreen [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 26, 2021
195f86d
MentionedUserNotSubscribed [nfc]: Convert to function component.
chrisbobbe Jul 26, 2021
af157e4
MentionedUserNotSubscribed [nfc]: Use `useSelector` instead of `conne…
chrisbobbe Jul 26, 2021
169a462
NotSubscribed [nfc]: Convert to function component.
chrisbobbe Jul 26, 2021
dbd4f6c
NotSubscribed [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 26, 2021
74557b4
ZulipNavigationContainer [nfc]: Convert to function component.
chrisbobbe Jul 26, 2021
5600fb0
ZulipNavigationContainer [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 26, 2021
fa2371c
InviteUsersScreen [nfc]: Convert to function component.
chrisbobbe Jul 26, 2021
e4478e8
InviteUsersScreen [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 26, 2021
00eb514
InviteUsersScreen [nfc]: Inline `handleFilterChange`.
chrisbobbe Jul 26, 2021
9614a31
StreamSettingsScreen [nfc]: Convert to function component.
chrisbobbe Jul 26, 2021
d2d5dda
StreamSettingsScreen [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 26, 2021
2e6fb5c
SubscriptionsCard [nfc]: Convert to function component.
chrisbobbe Jul 26, 2021
2f1bfdc
SubscriptionsCard [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 26, 2021
cb291be
StreamListCard [nfc]: Convert to function component.
chrisbobbe Jul 26, 2021
824d3d9
StreamListCard [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 26, 2021
beb8845
ActivityText [nfc]: Convert to function component.
chrisbobbe Jul 26, 2021
395f43c
ActivityText [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 26, 2021
e11b3c8
TopicListScreen [nfc]: Add a TODO that we'll address soon.
chrisbobbe Jul 26, 2021
7897c50
TopicListScreen: Convert to function component.
chrisbobbe Jul 26, 2021
dca6a5d
TopicListScreen [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 27, 2021
0ef79d4
TopicListScreen [nfc]: Inline `handleFilterChange`.
chrisbobbe Jul 26, 2021
2dd97e1
UserStatusScreen [nfc]: Convert to function component.
chrisbobbe Jul 27, 2021
9dcca95
UserStatusScreen [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 27, 2021
830f0b2
UserStatusScreen [nfc]: Inline `setStatusTextState`.
chrisbobbe Jul 27, 2021
a6e5fce
UserStatusScreen [nfc]: Improve some variable names.
chrisbobbe Jul 27, 2021
a094d3d
EditStreamScreen [nfc]: Convert to function component.
chrisbobbe Jul 27, 2021
f3f8104
EditStreamScreen [nfc]: Use `useSelector` instead of `connect`.
chrisbobbe Jul 27, 2021
197521c
flow: Annotate some more exported React components.
chrisbobbe Jul 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 45 additions & 56 deletions src/account-info/AccountDetails.js
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<Props> {
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 (
<ComponentList outerSpacing itemStyle={componentStyles.componentListItem}>
return (
<ComponentList outerSpacing itemStyle={componentStyles.componentListItem}>
<View>
<UserAvatar avatarUrl={user.avatar_url} size={200} />
</View>
<View style={componentStyles.statusWrapper}>
<PresenceStatusIndicator
style={componentStyles.presenceStatusIndicator}
email={user.email}
hideIfOffline={false}
useOpaqueBackground={false}
/>
<RawLabel style={[styles.largerText, styles.halfMarginRight]} text={user.full_name} />
</View>
{userStatusText !== undefined && (
<RawLabel style={[styles.largerText, componentStyles.statusText]} text={userStatusText} />
)}
{!isSelf && (
<View>
<UserAvatar avatarUrl={user.avatar_url} size={200} />
<ActivityText style={styles.largerText} user={user} />
</View>
<View style={componentStyles.statusWrapper}>
<PresenceStatusIndicator
style={componentStyles.presenceStatusIndicator}
email={user.email}
hideIfOffline={false}
useOpaqueBackground={false}
/>
<RawLabel style={[styles.largerText, styles.halfMarginRight]} text={user.full_name} />
)}
{!isSelf && localTime !== null && (
<View>
<RawLabel style={styles.largerText} text={localTime} />
</View>
{userStatusText !== undefined && (
<RawLabel style={[styles.largerText, componentStyles.statusText]} text={userStatusText} />
)}
{!isSelf && (
<View>
<ActivityText style={styles.largerText} user={user} />
</View>
)}
{!isSelf && localTime !== null && (
<View>
<RawLabel style={styles.largerText} text={localTime} />
</View>
)}
</ComponentList>
);
}
)}
</ComponentList>
);
}

export default connect<SelectorProps, _, _>((state, props) => ({
ownUser: getOwnUser(state),
userStatusText: getUserStatusTextForUser(state, props.user.user_id),
}))(AccountDetails);
76 changes: 32 additions & 44 deletions src/account-info/AccountDetailsScreen.js
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<Props> {
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 (
<Screen title={title}>
<AccountDetails user={user} />
{!isActive && (
<Label style={styles.deactivatedText} text="(This user has been deactivated)" />
)}
<ZulipButton
style={styles.pmButton}
text={isActive ? 'Send private message' : 'View private messages'}
onPress={this.handleChatPress}
Icon={IconPrivateChat}
/>
</Screen>
);
}
return (
<Screen title={title}>
<AccountDetails user={user} />
{!isActive && (
<Label style={styles.deactivatedText} text="(This user has been deactivated)" />
)}
<ZulipButton
style={styles.pmButton}
text={isActive ? 'Send private message' : 'View private messages'}
onPress={handleChatPress}
Icon={IconPrivateChat}
/>
</Screen>
);
}

export default connect<SelectorProps, _, _>((state, props) => ({
user: getUserForId(state, props.route.params.userId),
isActive: getUserIsActive(state, props.route.params.userId),
}))(AccountDetailsScreen);
3 changes: 2 additions & 1 deletion src/autocomplete/TopicAutocomplete.js
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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));
Expand Down
3 changes: 2 additions & 1 deletion src/chat/GroupDetailsScreen.js
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -15,7 +16,7 @@ type Props = $ReadOnly<{|
route: RouteProp<'group-details', {| recipients: $ReadOnlyArray<UserId> |}>,
|}>;

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));
Expand Down
3 changes: 2 additions & 1 deletion src/chat/UnreadNotice.js
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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));

Expand Down
3 changes: 2 additions & 1 deletion src/common/LoadingBanner.js
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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);

Expand Down
66 changes: 25 additions & 41 deletions src/common/ZulipStatusBar.js
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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,
|}>;

/**
Expand All @@ -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<Props> {
static defaultProps = {
hidden: false,
};

render() {
const { theme, hidden, orientation } = this.props;
const backgroundColor = this.props.backgroundColor;
const statusBarColor = getStatusBarColor(backgroundColor, theme);
return (
orientation === 'PORTRAIT' && (
<StatusBar
animated
showHideTransition="slide"
hidden={hidden && Platform.OS !== 'android'}
backgroundColor={Color(statusBarColor)
.darken(0.1)
.hsl()
.string()}
barStyle={getStatusBarStyle(statusBarColor)}
/>
)
);
}
export default function ZulipStatusBar(props: Props): Node {
const { hidden = false } = props;
const theme = useSelector(state => getSettings(state).theme);
const orientation = useSelector(state => getSession(state).orientation);
const backgroundColor = props.backgroundColor;
const statusBarColor = getStatusBarColor(backgroundColor, theme);
return (
orientation === 'PORTRAIT' && (
<StatusBar
animated
showHideTransition="slide"
hidden={hidden && Platform.OS !== 'android'}
backgroundColor={Color(statusBarColor)
.darken(0.1)
.hsl()
.string()}
barStyle={getStatusBarStyle(statusBarColor)}
/>
)
);
}

export default connect<SelectorProps, _, _>((state, props) => ({
theme: getSettings(state).theme,
orientation: getSession(state).orientation,
}))(ZulipStatusBar);
Loading