diff --git a/src/boot/TranslationProvider.js b/src/boot/TranslationProvider.js index fccec2a94ff..7454a74f8ab 100644 --- a/src/boot/TranslationProvider.js +++ b/src/boot/TranslationProvider.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent, type Context } from 'react'; +import React, { type Context, useContext } from 'react'; import type { ComponentType, ElementConfig, Node } from 'react'; import { Text } from 'react-native'; import { IntlProvider, IntlContext } from 'react-intl'; @@ -23,14 +23,13 @@ export const TranslationContext: Context = React.createContext(undefine export function withGetText>( WrappedComponent: C, ): ComponentType<$ReadOnly<$Exact<$Diff, {| _: GetText |}>>>> { - return class extends React.Component<$Exact<$Diff, {| _: GetText |}>>> { - render() { - return ( - - {_ => } - - ); - } + // eslint-disable-next-line func-names + return function (props: $Exact<$Diff, {| _: GetText |}>>): Node { + return ( + + {_ => } + + ); }; } @@ -61,19 +60,14 @@ const makeGetText = (intl: IntlShape): GetText => { * * See the `GetTypes` type for why we like the new shape. */ -class TranslationContextTranslator extends PureComponent<{| - +children: Node, -|}> { - static contextType = IntlContext; - context: IntlShape; +function TranslationContextTranslator(props: {| +children: Node |}): Node { + const intlContextValue = useContext(IntlContext); - render() { - return ( - - {this.props.children} - - ); - } + return ( + + {props.children} + + ); } type Props = $ReadOnly<{| diff --git a/src/chat/FetchError.js b/src/chat/FetchError.js index c7bf87197bc..d51a205e594 100644 --- a/src/chat/FetchError.js +++ b/src/chat/FetchError.js @@ -1,6 +1,6 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { StyleSheet, View } from 'react-native'; @@ -26,18 +26,16 @@ type Props = $ReadOnly<{| error: mixed, |}>; -export default class FetchError extends PureComponent { - render(): Node { - return ( - - {(() => { - if (this.props.error instanceof TimeoutError) { - return ; - } else { - return ; - } - })()} - - ); - } +export default function FetchError(props: Props): Node { + return ( + + {(() => { + if (props.error instanceof TimeoutError) { + return ; + } else { + return ; + } + })()} + + ); } diff --git a/src/chat/InvalidNarrow.js b/src/chat/InvalidNarrow.js index 71b55ca5fa0..a0a236de2f3 100644 --- a/src/chat/InvalidNarrow.js +++ b/src/chat/InvalidNarrow.js @@ -1,6 +1,6 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { StyleSheet, View } from 'react-native'; @@ -24,12 +24,10 @@ type Props = $ReadOnly<{| narrow: Narrow, |}>; -export default class InvalidNarrow extends PureComponent { - render(): Node { - return ( - - - - ); - } +export default function InvalidNarrow(props: Props): Node { + return ( + + + + ); } diff --git a/src/common/CountOverlay.js b/src/common/CountOverlay.js index 0a615b049b0..ef4530608ad 100644 --- a/src/common/CountOverlay.js +++ b/src/common/CountOverlay.js @@ -1,6 +1,6 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; @@ -23,26 +23,24 @@ type Props = $ReadOnly<{| unreadCount: number, |}>; -export default class CountOverlay extends PureComponent { - render(): Node { - const { children, unreadCount } = this.props; +export default function CountOverlay(props: Props): Node { + const { children, unreadCount } = props; - return ( - - 0} - customOverlayStyle={styles.overlayStyle} - overlay={} - > - {children} - - - ); - } + return ( + + 0} + customOverlayStyle={styles.overlayStyle} + overlay={} + > + {children} + + + ); } diff --git a/src/common/ErrorMsg.js b/src/common/ErrorMsg.js index 6b73bafd760..13bf230cd39 100644 --- a/src/common/ErrorMsg.js +++ b/src/common/ErrorMsg.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; @@ -29,18 +29,16 @@ type Props = $ReadOnly<{| * * @prop error - The error message string. */ -export default class ErrorMsg extends PureComponent { - render(): Node { - const { error } = this.props; +export default function ErrorMsg(props: Props): Node { + const { error } = props; - if (!error) { - return null; - } - - return ( - - - - ); + if (!error) { + return null; } + + return ( + + + + ); } diff --git a/src/common/GroupAvatar.js b/src/common/GroupAvatar.js index 368716b63f9..9dd78e53ee3 100644 --- a/src/common/GroupAvatar.js +++ b/src/common/GroupAvatar.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; // $FlowFixMe[untyped-import] @@ -34,28 +34,26 @@ type Props = $ReadOnly<{| * @prop children - If provided, will render inside the component body. * @prop onPress - Event fired on pressing the component. */ -export default class GroupAvatar extends PureComponent { - render(): Node { - const { children, names, size, onPress } = this.props; +export default function GroupAvatar(props: Props): Node { + const { children, names, size, onPress } = props; - const frameSize = { - height: size, - width: size, - borderRadius: size / 8, - backgroundColor: Color(colorHashFromString(names.join(', '))) - .lighten(0.6) - .hex(), - }; + const frameSize = { + height: size, + width: size, + borderRadius: size / 8, + backgroundColor: Color(colorHashFromString(names.join(', '))) + .lighten(0.6) + .hex(), + }; - const iconColor = Color(colorHashFromString(names.join(', '))).string(); + const iconColor = Color(colorHashFromString(names.join(', '))).string(); - return ( - - - - {children} - - - ); - } + return ( + + + + {children} + + + ); } diff --git a/src/common/KeyboardAvoider.js b/src/common/KeyboardAvoider.js index f5195eaeaf1..3e9aa833209 100644 --- a/src/common/KeyboardAvoider.js +++ b/src/common/KeyboardAvoider.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { Platform, View } from 'react-native'; import type { ViewStyleProp } from 'react-native/Libraries/StyleSheet/StyleSheet'; @@ -39,24 +39,22 @@ type Props = $ReadOnly<{| * `KeyboardAvoidingView`'s special props get passed straight through * to that component. */ -export default class KeyboardAvoider extends PureComponent { - render(): Node { - const { behavior, children, style, contentContainerStyle, keyboardVerticalOffset } = this.props; +export default function KeyboardAvoider(props: Props): Node { + const { behavior, children, style, contentContainerStyle, keyboardVerticalOffset } = props; - if (Platform.OS === 'android') { - return {children}; - } - - return ( - - {children} - - ); + if (Platform.OS === 'android') { + return {children}; } + + return ( + + {children} + + ); } diff --git a/src/common/SearchEmptyState.js b/src/common/SearchEmptyState.js index 67c9176e68f..4eb6803a53e 100644 --- a/src/common/SearchEmptyState.js +++ b/src/common/SearchEmptyState.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; @@ -23,14 +23,12 @@ type Props = $ReadOnly<{| text: string, |}>; -export default class SearchEmptyState extends PureComponent { - render(): Node { - const { text } = this.props; +export default function SearchEmptyState(props: Props): Node { + const { text } = props; - return ( - - - - ); - } + return ( + + + + ); } diff --git a/src/common/SectionHeader.js b/src/common/SectionHeader.js index a76f8aa9954..87da675ca74 100644 --- a/src/common/SectionHeader.js +++ b/src/common/SectionHeader.js @@ -1,9 +1,8 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; -import type { Node, Context } from 'react'; +import React, { useContext } from 'react'; +import type { Node } from 'react'; import { View } from 'react-native'; -import type { ThemeData } from '../styles'; import { ThemeContext, createStyleSheet } from '../styles'; import ZulipTextIntl from './ZulipTextIntl'; @@ -18,16 +17,13 @@ type Props = $ReadOnly<{| text: string, |}>; -export default class SectionHeader extends PureComponent { - static contextType: Context = ThemeContext; - context: ThemeData; +export default function SectionHeader(props: Props): Node { + const { text } = props; + const themeData = useContext(ThemeContext); - render(): Node { - const { text } = this.props; - return ( - - - - ); - } + return ( + + + + ); } diff --git a/src/common/SectionSeparator.js b/src/common/SectionSeparator.js index 69158edf2f5..2768d10d922 100644 --- a/src/common/SectionSeparator.js +++ b/src/common/SectionSeparator.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; @@ -13,8 +13,6 @@ const styles = createStyleSheet({ }, }); -export default class SectionSeparator extends PureComponent<{||}> { - render(): Node { - return ; - } +export default function SectionSeparator(props: {||}): Node { + return ; } diff --git a/src/common/SectionSeparatorBetween.js b/src/common/SectionSeparatorBetween.js index 0705715d47c..fec15dce0c4 100644 --- a/src/common/SectionSeparatorBetween.js +++ b/src/common/SectionSeparatorBetween.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import SectionSeparator from './SectionSeparator'; @@ -13,14 +13,12 @@ type Props = $ReadOnly<{| |}>; /** Can be passed to RN's `SectionList` as `SectionSeparatorComponent`. */ -export default class SectionSeparatorBetween extends PureComponent { - render(): Node { - const { leadingItem, leadingSection } = this.props; +export default function SectionSeparatorBetween(props: Props): Node { + const { leadingItem, leadingSection } = props; - if (leadingItem || !leadingSection || leadingSection.data.length === 0) { - return null; - } - - return ; + if (leadingItem || !leadingSection || leadingSection.data.length === 0) { + return null; } + + return ; } diff --git a/src/common/SpinningProgress.js b/src/common/SpinningProgress.js index eea4589fee3..3c97e6061b3 100644 --- a/src/common/SpinningProgress.js +++ b/src/common/SpinningProgress.js @@ -22,25 +22,23 @@ type Props = $ReadOnly<{| * @prop color - The color of the circle. * @prop size - Diameter of the circle in pixels. */ -export default class SpinningProgress extends React.PureComponent { - render(): Node { - const { color, size } = this.props; - const style = { width: size, height: size }; - const source = (() => { - switch (color) { - case 'white': - return spinningProgressWhiteImg; - case 'black': - return spinningProgressBlackImg; - default: - return spinningProgressImg; - } - })(); +export default function SpinningProgress(props: Props): Node { + const { color, size } = props; + const style = { width: size, height: size }; + const source = (() => { + switch (color) { + case 'white': + return spinningProgressWhiteImg; + case 'black': + return spinningProgressBlackImg; + default: + return spinningProgressImg; + } + })(); - return ( - - - - ); - } + return ( + + + + ); } diff --git a/src/common/Touchable.js b/src/common/Touchable.js index bede4e2efd9..372e6f4915d 100644 --- a/src/common/Touchable.js +++ b/src/common/Touchable.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node, ElementConfig } from 'react'; import { TouchableHighlight, TouchableNativeFeedback, Platform, View } from 'react-native'; import type { ViewStyleProp } from 'react-native/Libraries/StyleSheet/StyleSheet'; @@ -51,55 +51,53 @@ type Props = $ReadOnly<{| * @prop [hitSlop] - Passed through; see upstream docs. */ // TODO(?): Use Pressable API: https://reactnative.dev/docs/pressable -export default class Touchable extends PureComponent { - render(): Node { - const { accessibilityLabel, style, onPress, onLongPress, hitSlop } = this.props; - const child: Node = React.Children.only(this.props.children); +export default function Touchable(props: Props): Node { + const { accessibilityLabel, style, onPress, onLongPress, hitSlop } = props; + const child: Node = React.Children.only(props.children); - if (!onPress && !onLongPress) { - return ( - - {child} - - ); - } - - if (Platform.OS === 'ios') { - // TouchableHighlight makes its own wrapper View to be the touch - // target, passing the `style` prop through. - return ( - - {child} - - ); - } + if (!onPress && !onLongPress) { + return ( + + {child} + + ); + } - // TouchableNativeFeedback doesn't create any wrapper component -- it - // returns a clone of the child it's given, with added props to make it - // a touch target. We make our own wrapper View, in order to provide - // the same interface as we do with TouchableHighlight. + if (Platform.OS === 'ios') { + // TouchableHighlight makes its own wrapper View to be the touch + // target, passing the `style` prop through. return ( - - {child} - + {child} + ); } + + // TouchableNativeFeedback doesn't create any wrapper component -- it + // returns a clone of the child it's given, with added props to make it + // a touch target. We make our own wrapper View, in order to provide + // the same interface as we do with TouchableHighlight. + return ( + + {child} + + ); } diff --git a/src/common/UserAvatarWithPresence.js b/src/common/UserAvatarWithPresence.js index 3a82683d10d..40c12ae4272 100644 --- a/src/common/UserAvatarWithPresence.js +++ b/src/common/UserAvatarWithPresence.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import type { UserId } from '../types'; @@ -38,21 +38,19 @@ type Props = $ReadOnly<{| * @prop [size] - Sets width and height in logical pixels. * @prop [onPress] - Event fired on pressing the component. */ -export default class UserAvatarWithPresence extends PureComponent { - render(): Node { - const { avatarUrl, email, isMuted, size, onPress } = this.props; +export default function UserAvatarWithPresence(props: Props): Node { + const { avatarUrl, email, isMuted, size, onPress } = props; - return ( - - - - ); - } + return ( + + + + ); } /** diff --git a/src/common/ViewPlaceholder.js b/src/common/ViewPlaceholder.js index 95f072493da..957c56d83f1 100644 --- a/src/common/ViewPlaceholder.js +++ b/src/common/ViewPlaceholder.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; @@ -16,10 +16,8 @@ type Props = $ReadOnly<{| * @prop [width] - Width of the component in pixels. * @prop [height] - Height of the component in pixels. */ -export default class ViewPlaceholder extends PureComponent { - render(): Node { - const { width, height } = this.props; - const style = { width, height }; - return ; - } +export default function ViewPlaceholder(props: Props): Node { + const { width, height } = props; + const style = { width, height }; + return ; } diff --git a/src/common/ZulipText.js b/src/common/ZulipText.js index cf2ef368ea2..a47779bbd68 100644 --- a/src/common/ZulipText.js +++ b/src/common/ZulipText.js @@ -1,10 +1,9 @@ /* @flow strict-local */ import invariant from 'invariant'; -import React, { PureComponent } from 'react'; -import type { Node, Context } from 'react'; +import React, { useContext } from 'react'; +import type { Node } from 'react'; import { Text } from 'react-native'; -import type { ThemeData } from '../styles'; import { ThemeContext } from '../styles'; type Props = $ReadOnly<{| @@ -24,35 +23,28 @@ type Props = $ReadOnly<{| * @prop ...all other Text props - Passed through verbatim to Text. * See upstream: https://reactnative.dev/docs/text */ -export default class ZulipText extends PureComponent { - static contextType: Context = ThemeContext; - context: ThemeData; +export default function ZulipText(props: Props): Node { + const { text, children, style, ...restProps } = props; + const themeData = useContext(ThemeContext); - render(): Node { - const { text, children, style, ...restProps } = this.props; + invariant( + text != null || children != null, + 'ZulipText: `text` or `children` should be non-nullish', + ); + invariant(text == null || children == null, 'ZulipText: `text` or `children` should be nullish'); - invariant( - text != null || children != null, - 'ZulipText: `text` or `children` should be non-nullish', - ); - invariant( - text == null || children == null, - 'ZulipText: `text` or `children` should be nullish', - ); + // These attributes will be applied unless specifically overridden + // with the `style` prop -- even if this `` is nested + // and would otherwise inherit the attributes from its ancestors. + const aggressiveDefaultStyle = { + fontSize: 15, + color: themeData.color, + }; - // These attributes will be applied unless specifically overridden - // with the `style` prop -- even if this `` is nested - // and would otherwise inherit the attributes from its ancestors. - const aggressiveDefaultStyle = { - fontSize: 15, - color: this.context.color, - }; - - return ( - - {text} - {children} - - ); - } + return ( + + {text} + {children} + + ); } diff --git a/src/common/ZulipTextIntl.js b/src/common/ZulipTextIntl.js index 207426f9b23..a1a0d103ff4 100644 --- a/src/common/ZulipTextIntl.js +++ b/src/common/ZulipTextIntl.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { FormattedMessage } from 'react-intl'; @@ -20,28 +20,24 @@ type Props = $ReadOnly<{| * Unlike `ZulipText`, only accepts a `LocalizableReactText`, as the `text` * prop, and doesn't support `children`. */ -export default class ZulipTextIntl extends PureComponent { - render(): Node { - const { text, ...restProps } = this.props; +export default function ZulipTextIntl(props: Props): Node { + const { text, ...restProps } = props; - const message = typeof text === 'object' ? text.text : text; - const values = typeof text === 'object' ? text.values : undefined; + const message = typeof text === 'object' ? text.text : text; + const values = typeof text === 'object' ? text.values : undefined; - return ( - - - - ); - } + return ( + + + + ); } diff --git a/src/diagnostics/DiagnosticsScreen.js b/src/diagnostics/DiagnosticsScreen.js index 7692d228aef..469200a8ea2 100644 --- a/src/diagnostics/DiagnosticsScreen.js +++ b/src/diagnostics/DiagnosticsScreen.js @@ -1,6 +1,6 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { nativeApplicationVersion } from 'expo-application'; @@ -24,37 +24,36 @@ type Props = $ReadOnly<{| route: RouteProp<'diagnostics', void>, |}>; -export default class DiagnosticsScreen extends PureComponent { - render(): Node { - return ( - - - - { - this.props.navigation.push('variables'); - }} - /> - { - this.props.navigation.push('timing'); - }} - /> - { - this.props.navigation.push('storage'); - }} - /> - { - this.props.navigation.push('debug'); - }} - /> - - ); - } +export default function DiagnosticsScreen(props: Props): Node { + const { navigation } = props; + return ( + + + + { + navigation.push('variables'); + }} + /> + { + navigation.push('timing'); + }} + /> + { + navigation.push('storage'); + }} + /> + { + navigation.push('debug'); + }} + /> + + ); } diff --git a/src/diagnostics/InfoItem.js b/src/diagnostics/InfoItem.js index aec9128d4ac..af3e1d365f2 100644 --- a/src/diagnostics/InfoItem.js +++ b/src/diagnostics/InfoItem.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; @@ -26,15 +26,13 @@ type Props = $ReadOnly<{| value: JSONable, |}>; -export default class InfoItem extends PureComponent { - render(): Node { - const { label, value } = this.props; +export default function InfoItem(props: Props): Node { + const { label, value } = props; - return ( - - - - - ); - } + return ( + + + + + ); } diff --git a/src/diagnostics/SizeItem.js b/src/diagnostics/SizeItem.js index 74d7e72a2ed..97e5bd72e82 100644 --- a/src/diagnostics/SizeItem.js +++ b/src/diagnostics/SizeItem.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; @@ -26,15 +26,13 @@ type Props = $ReadOnly<{| size: number, |}>; -export default class SizeItem extends PureComponent { - render(): Node { - const { text, size } = this.props; +export default function SizeItem(props: Props): Node { + const { text, size } = props; - return ( - - - - - ); - } + return ( + + + + + ); } diff --git a/src/diagnostics/TimeItem.js b/src/diagnostics/TimeItem.js index dc3fceda9e2..7c2e533a90c 100644 --- a/src/diagnostics/TimeItem.js +++ b/src/diagnostics/TimeItem.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; import format from 'date-fns/format'; @@ -23,20 +23,16 @@ const styles = createStyleSheet({ }, }); -export default class TimeItem extends PureComponent { - props: TimingItemType; +export default function TimeItem(props: TimingItemType): Node { + const { text, startMs, endMs } = props; + const startStr = format(startMs, 'HH:mm:ss.S'); + const durationStrMs = numberWithSeparators(endMs - startMs); + const timingStr = `Start: ${startStr} Duration: ${durationStrMs} ms`; - render(): Node { - const { text, startMs, endMs } = this.props; - const startStr = format(startMs, 'HH:mm:ss.S'); - const durationStrMs = numberWithSeparators(endMs - startMs); - const timingStr = `Start: ${startStr} Duration: ${durationStrMs} ms`; - - return ( - - - - - ); - } + return ( + + + + + ); } diff --git a/src/diagnostics/VariablesScreen.js b/src/diagnostics/VariablesScreen.js index a95a826230d..9cd04353852 100644 --- a/src/diagnostics/VariablesScreen.js +++ b/src/diagnostics/VariablesScreen.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { FlatList } from 'react-native'; @@ -14,23 +14,21 @@ type Props = $ReadOnly<{| route: RouteProp<'variables', void>, |}>; -export default class VariablesScreen extends PureComponent { - render(): Node { - const variables = { - enableReduxLogging: config.enableReduxLogging, - enableReduxSlowReducerWarnings: config.enableReduxSlowReducerWarnings, - 'process.env.NODE_ENV': process.env.NODE_ENV ?? '(not defined)', - 'global.btoa': !!global.btoa, - }; +export default function VariablesScreen(props: Props): Node { + const variables = { + enableReduxLogging: config.enableReduxLogging, + enableReduxSlowReducerWarnings: config.enableReduxSlowReducerWarnings, + 'process.env.NODE_ENV': process.env.NODE_ENV ?? '(not defined)', + 'global.btoa': !!global.btoa, + }; - return ( - - item} - renderItem={({ item }) => } - /> - - ); - } + return ( + + item} + renderItem={({ item }) => } + /> + + ); } diff --git a/src/lightbox/LightboxFooter.js b/src/lightbox/LightboxFooter.js index 5da3f82c29b..0b6ba371e65 100644 --- a/src/lightbox/LightboxFooter.js +++ b/src/lightbox/LightboxFooter.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { Text, View, Pressable } from 'react-native'; import type { ViewStyleProp } from 'react-native/Libraries/StyleSheet/StyleSheet'; @@ -33,20 +33,18 @@ type Props = $ReadOnly<{| onOptionsPress: () => void, |}>; -export default class LightboxFooter extends PureComponent { - render(): Node { - const { displayMessage, onOptionsPress, style } = this.props; - return ( - - - {displayMessage} - - {({ pressed }) => ( - - )} - - - - ); - } +export default function LightboxFooter(props: Props): Node { + const { displayMessage, onOptionsPress, style } = props; + return ( + + + {displayMessage} + + {({ pressed }) => ( + + )} + + + + ); } diff --git a/src/lightbox/LightboxHeader.js b/src/lightbox/LightboxHeader.js index 388cbd17cf8..2caccfbbef6 100644 --- a/src/lightbox/LightboxHeader.js +++ b/src/lightbox/LightboxHeader.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View, Text, Pressable } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -53,30 +53,28 @@ type Props = $ReadOnly<{| * @prop [timestamp] * @prop [onPressBack] */ -export default class LightboxHeader extends PureComponent { - render(): Node { - const { onPressBack, senderName, senderEmail, timestamp, avatarUrl } = this.props; - const displayDate = humanDate(new Date(timestamp * 1000)); - const time = shortTime(new Date(timestamp * 1000)); - const subheader = `${displayDate} at ${time}`; +export default function LightboxHeader(props: Props): Node { + const { onPressBack, senderName, senderEmail, timestamp, avatarUrl } = props; + const displayDate = humanDate(new Date(timestamp * 1000)); + const time = shortTime(new Date(timestamp * 1000)); + const subheader = `${displayDate} at ${time}`; - return ( - - - - - - {senderName} - - - {subheader} - - - - {({ pressed }) => } - + return ( + + + + + + {senderName} + + + {subheader} + - - ); - } + + {({ pressed }) => } + + + + ); } diff --git a/src/message/AnnouncementOnly.js b/src/message/AnnouncementOnly.js index df768bd5ec7..e673df54499 100644 --- a/src/message/AnnouncementOnly.js +++ b/src/message/AnnouncementOnly.js @@ -1,22 +1,18 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; import ZulipTextIntl from '../common/ZulipTextIntl'; import styles from '../styles'; -class AnnouncementOnly extends PureComponent<{||}> { - render(): Node { - return ( - - - - ); - } +export default function AnnouncementOnly(props: {||}): Node { + return ( + + + + ); } - -export default AnnouncementOnly; diff --git a/src/message/NoMessages.js b/src/message/NoMessages.js index 0065f611945..de2e0db8950 100644 --- a/src/message/NoMessages.js +++ b/src/message/NoMessages.js @@ -1,6 +1,6 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; @@ -51,19 +51,17 @@ type Props = $ReadOnly<{| narrow: Narrow, |}>; -export default class NoMessages extends PureComponent { - render(): Node { - const { narrow } = this.props; +export default function NoMessages(props: Props): Node { + const { narrow } = props; - const message = messages.find(x => x.isFunc(narrow)) || {}; + const message = messages.find(x => x.isFunc(narrow)) || {}; - return ( - - - {showComposeBoxOnNarrow(narrow) ? ( - - ) : null} - - ); - } + return ( + + + {showComposeBoxOnNarrow(narrow) ? ( + + ) : null} + + ); } diff --git a/src/search/SearchMessagesCard.js b/src/search/SearchMessagesCard.js index bf34f5877cb..da94b7d4386 100644 --- a/src/search/SearchMessagesCard.js +++ b/src/search/SearchMessagesCard.js @@ -1,6 +1,6 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; @@ -22,42 +22,40 @@ type Props = $ReadOnly<{| isFetching: boolean, |}>; -export default class SearchMessagesCard extends PureComponent { - render(): Node { - const { isFetching, messages } = this.props; +export default function SearchMessagesCard(props: Props): Node { + const { narrow, isFetching, messages } = props; - if (isFetching) { - // Display loading indicator only if there are no messages to - // display from a previous search. - if (!messages || messages.length === 0) { - return ; - } - } - - if (!messages) { - return null; + if (isFetching) { + // Display loading indicator only if there are no messages to + // display from a previous search. + if (!messages || messages.length === 0) { + return ; } + } - if (messages.length === 0) { - return ; - } + if (!messages) { + return null; + } - return ( - - undefined} - /> - - ); + if (messages.length === 0) { + return ; } + + return ( + + undefined} + /> + + ); } diff --git a/src/start/IosCompliantAppleAuthButton/Custom.js b/src/start/IosCompliantAppleAuthButton/Custom.js index 1654c571961..6043745fa26 100644 --- a/src/start/IosCompliantAppleAuthButton/Custom.js +++ b/src/start/IosCompliantAppleAuthButton/Custom.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View, Image, TouchableWithoutFeedback } from 'react-native'; import type { ViewStyleProp } from 'react-native/Libraries/StyleSheet/StyleSheet'; @@ -55,26 +55,24 @@ type Props = $ReadOnly<{| * IosCompliantAppleAuthButton, which controls whether the custom * button should be used. */ -export default class Custom extends PureComponent { - render(): Node { - const { style, onPress, theme } = this.props; - const logoSource = theme === 'default' ? appleLogoBlackImg : appleLogoWhiteImg; - const frameStyle = [ - styles.frame, - theme === 'default' ? styles.dayFrame : styles.nightFrame, - style, - ]; - const textStyle = [styles.text, theme === 'default' ? styles.dayText : styles.nightText]; +export default function Custom(props: Props): Node { + const { style, onPress, theme } = props; + const logoSource = theme === 'default' ? appleLogoBlackImg : appleLogoWhiteImg; + const frameStyle = [ + styles.frame, + theme === 'default' ? styles.dayFrame : styles.nightFrame, + style, + ]; + const textStyle = [styles.text, theme === 'default' ? styles.dayText : styles.nightText]; - return ( - - - - - - - - - ); - } + return ( + + + + + + + + + ); } diff --git a/src/start/RealmInfo.js b/src/start/RealmInfo.js index 02a02740633..0b650695b43 100644 --- a/src/start/RealmInfo.js +++ b/src/start/RealmInfo.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View, Image } from 'react-native'; @@ -28,15 +28,13 @@ type Props = $ReadOnly<{| iconUrl: string, |}>; -export default class RealmInfo extends PureComponent { - render(): Node { - const { name, iconUrl } = this.props; +export default function RealmInfo(props: Props): Node { + const { name, iconUrl } = props; - return ( - - {iconUrl && } - - - ); - } + return ( + + {iconUrl && } + + + ); } diff --git a/src/streams/StreamCard.js b/src/streams/StreamCard.js index eb8002950cd..f71bfb99465 100644 --- a/src/streams/StreamCard.js +++ b/src/streams/StreamCard.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; @@ -31,32 +31,30 @@ type Props = $ReadOnly<{| subscription?: Subscription, |}>; -export default class StreamCard extends PureComponent { - render(): Node { - const { stream, subscription } = this.props; +export default function StreamCard(props: Props): Node { + const { stream, subscription } = props; - return ( - - - - - - {stream.description.length > 0 && ( - - )} + return ( + + + + - ); - } + {stream.description.length > 0 && ( + + )} + + ); } diff --git a/src/title/Title.js b/src/title/Title.js index 0ad7610e66c..58965f0a639 100644 --- a/src/title/Title.js +++ b/src/title/Title.js @@ -1,6 +1,6 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { caseNarrow } from '../utils/narrow'; @@ -18,26 +18,24 @@ type Props = $ReadOnly<{| editMessage: EditMessage | null, |}>; -export default class Title extends PureComponent { - render(): Node { - const { narrow, color, editMessage } = this.props; - if (editMessage != null) { - return ; - } - return caseNarrow(narrow, { - home: () => , - starred: () => , - mentioned: () => , - allPrivate: () => , - stream: () => , - topic: () => , - pm: ids => - ids.length === 1 ? ( - - ) : ( - - ), - search: () => null, - }); +export default function Title(props: Props): Node { + const { narrow, color, editMessage } = props; + if (editMessage != null) { + return ; } + return caseNarrow(narrow, { + home: () => , + starred: () => , + mentioned: () => , + allPrivate: () => , + stream: () => , + topic: () => , + pm: ids => + ids.length === 1 ? ( + + ) : ( + + ), + search: () => null, + }); } diff --git a/src/title/TitlePlain.js b/src/title/TitlePlain.js index 266402c4ac9..ac9783370de 100644 --- a/src/title/TitlePlain.js +++ b/src/title/TitlePlain.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { Text } from 'react-native'; @@ -10,9 +10,7 @@ type Props = $ReadOnly<{| color: string, |}>; -export default class TitlePlain extends PureComponent { - render(): Node { - const { text, color } = this.props; - return {text}; - } +export default function TitlePlain(props: Props): Node { + const { text, color } = props; + return {text}; } diff --git a/src/title/TitleSpecial.js b/src/title/TitleSpecial.js index d957bf8bfeb..6dbcb8bf455 100644 --- a/src/title/TitleSpecial.js +++ b/src/title/TitleSpecial.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { View } from 'react-native'; @@ -19,16 +19,14 @@ type Props = $ReadOnly<{| color: string, |}>; -export default class TitleSpecial extends PureComponent { - render(): Node { - const { code, color } = this.props; - const { name, icon } = specials[code]; +export default function TitleSpecial(props: Props): Node { + const { code, color } = props; + const { name, icon } = specials[code]; - return ( - - - - - ); - } + return ( + + + + + ); } diff --git a/src/topics/TopicList.js b/src/topics/TopicList.js index 43136d337f7..0fb4ec5e969 100644 --- a/src/topics/TopicList.js +++ b/src/topics/TopicList.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { FlatList } from 'react-native'; @@ -22,34 +22,32 @@ type Props = $ReadOnly<{| onPress: (streamId: number, topic: string) => void, |}>; -export default class TopicList extends PureComponent { - render(): Node { - const { stream, topics, onPress } = this.props; +export default function TopicList(props: Props): Node { + const { stream, topics, onPress } = props; - if (!topics) { - return ; - } - - if (topics.length === 0) { - return ; - } + if (!topics) { + return ; + } - return ( - item.name} - renderItem={({ item }) => ( - - )} - /> - ); + if (topics.length === 0) { + return ; } + + return ( + item.name} + renderItem={({ item }) => ( + + )} + /> + ); } diff --git a/src/user-picker/AvatarList.js b/src/user-picker/AvatarList.js index 8fece61d991..c2cb96139f9 100644 --- a/src/user-picker/AvatarList.js +++ b/src/user-picker/AvatarList.js @@ -1,5 +1,5 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node } from 'react'; import { FlatList } from 'react-native'; @@ -12,20 +12,18 @@ type Props = $ReadOnly<{| onPress: UserId => void, |}>; -export default class AvatarList extends PureComponent { - render(): Node { - const { listRef, users, onPress } = this.props; +export default function AvatarList(props: Props): Node { + const { listRef, users, onPress } = props; - return ( - String(user.user_id)} - renderItem={({ item: user }) => } - /> - ); - } + return ( + String(user.user_id)} + renderItem={({ item: user }) => } + /> + ); }