diff --git a/native/account/logged-out-modal.react.js b/native/account/logged-out-modal.react.js --- a/native/account/logged-out-modal.react.js +++ b/native/account/logged-out-modal.react.js @@ -270,7 +270,7 @@ activeAlert = false; contentHeight: Value; - keyboardHeightValue = new Value(0); + keyboardHeightValue: Value = new Value(0); modeValue: Value; buttonOpacity: Value; @@ -448,7 +448,7 @@ this.props.dispatch({ type: resetUserStateActionType }); } - hardwareBack = () => { + hardwareBack: () => boolean = () => { if (this.nextMode !== 'prompt') { this.goBackToPrompt(); return true; @@ -456,7 +456,7 @@ return false; }; - panelPaddingTop() { + panelPaddingTop(): Node { const headerHeight = Platform.OS === 'ios' ? 62.33 : 58.54; const promptButtonsSize = Platform.OS === 'ios' ? 40 : 61; const logInContainerSize = 140; @@ -525,7 +525,7 @@ ]); } - panelOpacity() { + panelOpacity(): Node { const targetPanelOpacity = isPastPrompt(this.modeValue); const panelOpacity = new Value(-1); @@ -593,7 +593,7 @@ Keyboard.dismiss(); }; - render() { + render(): React.Node { const { styles } = this.props; const siweButton = ( diff --git a/native/account/register-panel.react.js b/native/account/register-panel.react.js --- a/native/account/register-panel.react.js +++ b/native/account/register-panel.react.js @@ -84,7 +84,7 @@ confirmPasswordInput: ?TextInput; passwordBeingAutoFilled = false; - render() { + render(): React.Node { let confirmPasswordTextInputExtraProps; if ( Platform.OS !== 'ios' || @@ -346,7 +346,7 @@ ); }; - async registerAction(extraInfo: LogInExtraInfo) { + async registerAction(extraInfo: LogInExtraInfo): Promise<RegisterResult> { try { const result = await this.props.register({ ...extraInfo, diff --git a/native/account/registration/emoji-avatar-selection.react.js b/native/account/registration/emoji-avatar-selection.react.js --- a/native/account/registration/emoji-avatar-selection.react.js +++ b/native/account/registration/emoji-avatar-selection.react.js @@ -6,6 +6,7 @@ import { EditUserAvatarContext } from 'lib/components/edit-user-avatar-provider.react.js'; import { getDefaultAvatar } from 'lib/shared/avatar-utils.js'; +import type { UpdateUserAvatarRequest } from 'lib/types/avatar-types'; import RegistrationContainer from './registration-container.react.js'; import RegistrationContentContainer from './registration-content-container.react.js'; @@ -38,7 +39,7 @@ const { goBack } = props.navigation; const onSuccess = React.useCallback( - avatarRequest => { + (avatarRequest: UpdateUserAvatarRequest) => { goBack(); return nativeSetUserAvatar(avatarRequest); }, diff --git a/native/account/siwe-hooks.js b/native/account/siwe-hooks.js --- a/native/account/siwe-hooks.js +++ b/native/account/siwe-hooks.js @@ -4,7 +4,10 @@ import { siweAuth, siweAuthActionTypes } from 'lib/actions/siwe-actions.js'; import { useInitialNotificationsEncryptedMessage } from 'lib/shared/crypto-utils.js'; -import type { LogInStartingPayload } from 'lib/types/account-types.js'; +import type { + LogInStartingPayload, + LogInExtraInfo, +} from 'lib/types/account-types.js'; import { useServerCall, useDispatchActionPromise, @@ -32,7 +35,12 @@ const siweAuthCall = useServerCall(siweAuth); const callSIWE = React.useCallback( - async (message, signature, extraInfo, callServerEndpointOptions) => { + async ( + message: string, + signature: string, + extraInfo: LogInExtraInfo, + callServerEndpointOptions: ?CallServerEndpointOptions, + ) => { try { return await siweAuthCall( { diff --git a/native/account/siwe-panel.react.js b/native/account/siwe-panel.react.js --- a/native/account/siwe-panel.react.js +++ b/native/account/siwe-panel.react.js @@ -32,6 +32,14 @@ const siweAuthLoadingStatusSelector = createLoadingStatusSelector(siweAuthActionTypes); +type WebViewMessageEvent = { + +nativeEvent: { + +data: string, + ... + }, + ... +}; + type Props = { +onClosed: () => mixed, +onClosing: () => mixed, @@ -119,7 +127,7 @@ const closeBottomSheet = bottomSheetRef.current?.close; const { closing, onSuccessfulWalletSignature } = props; const handleMessage = React.useCallback( - async event => { + async (event: WebViewMessageEvent) => { const data: SIWEWebViewMessage = JSON.parse(event.nativeEvent.data); if (data.type === 'siwe_success') { const { address, message, signature } = data; diff --git a/native/avatars/emoji-avatar-creation.react.js b/native/avatars/emoji-avatar-creation.react.js --- a/native/avatars/emoji-avatar-creation.react.js +++ b/native/avatars/emoji-avatar-creation.react.js @@ -11,12 +11,13 @@ import type { UpdateUserAvatarRequest, ClientEmojiAvatar, -} from 'lib/types/avatar-types'; +} from 'lib/types/avatar-types.js'; import Avatar from './avatar.react.js'; import Button from '../components/button.react.js'; import ColorRows from '../components/color-rows.react.js'; import EmojiKeyboard from '../components/emoji-keyboard.react.js'; +import type { EmojiSelection } from '../components/emoji-keyboard.react.js'; import { useStyles } from '../themes/colors.js'; type Props = { @@ -60,7 +61,7 @@ setPendingColor(resetEmojiAvatar.color); }, [savedEmojiAvatarFunc]); - const onEmojiSelected = React.useCallback(emoji => { + const onEmojiSelected = React.useCallback((emoji: EmojiSelection) => { setPendingEmoji(emoji.emoji); }, []); diff --git a/native/calendar/calendar.react.js b/native/calendar/calendar.react.js --- a/native/calendar/calendar.react.js +++ b/native/calendar/calendar.react.js @@ -215,7 +215,7 @@ topLoaderWaitingToLeaveView = true; bottomLoaderWaitingToLeaveView = true; // We keep refs to the entries so CalendarInputBar can save them - entryRefs = new Map(); + entryRefs: Map<string, ?InternalEntry> = new Map(); constructor(props: Props) { super(props); @@ -389,7 +389,12 @@ static datesFromListData( lastLDWH: $ReadOnlyArray<CalendarItemWithHeight>, newLDWH: $ReadOnlyArray<CalendarItemWithHeight>, - ) { + ): { + +lastStartDate: Date, + +newStartDate: Date, + +lastEndDate: Date, + +newEndDate: Date, + } { const lastSecondItem = lastLDWH[1]; const newSecondItem = newLDWH[1]; invariant( @@ -472,7 +477,7 @@ // ESLint doesn't recognize that invariant always throws // eslint-disable-next-line consistent-return - renderItem = (row: { item: CalendarItemWithHeight, ... }) => { + renderItem = (row: { +item: CalendarItemWithHeight, ... }): React.Node => { const item = row.item; if (item.itemType === 'loader') { return <ListLoadingIndicator />; @@ -500,7 +505,7 @@ invariant(false, 'renderItem conditions should be exhaustive'); }; - renderSectionHeader = (item: SectionHeaderItem) => { + renderSectionHeader = (item: SectionHeaderItem): React.Node => { let date = prettyDate(item.dateString); if (dateString(new Date()) === item.dateString) { date += ' (today)'; @@ -519,7 +524,7 @@ ); }; - renderSectionFooter = (item: SectionFooterItem) => { + renderSectionFooter = (item: SectionFooterItem): React.Node => { return ( <SectionFooter dateString={item.dateString} @@ -536,9 +541,11 @@ }); }; - // ESLint doesn't recognize that invariant always throws - // eslint-disable-next-line consistent-return - static keyExtractor = (item: CalendarItemWithHeight | CalendarItem) => { + static keyExtractor = ( + item: CalendarItemWithHeight | CalendarItem, + // ESLint doesn't recognize that invariant always throws + // eslint-disable-next-line consistent-return + ): string => { if (item.itemType === 'loader') { return item.key; } else if (item.itemType === 'header') { @@ -554,7 +561,7 @@ static getItemLayout = ( data: ?$ReadOnlyArray<CalendarItemWithHeight>, index: number, - ) => { + ): { length: number, offset: number, index: number } => { if (!data) { return { length: 0, offset: 0, index }; } @@ -566,7 +573,7 @@ // ESLint doesn't recognize that invariant always throws // eslint-disable-next-line consistent-return - static itemHeight = (item: CalendarItemWithHeight) => { + static itemHeight = (item: CalendarItemWithHeight): number => { if (item.itemType === 'loader') { return 56; } else if (item.itemType === 'header') { @@ -580,11 +587,13 @@ invariant(false, 'itemHeight conditions should be exhaustive'); }; - static heightOfItems = (data: $ReadOnlyArray<CalendarItemWithHeight>) => { + static heightOfItems = ( + data: $ReadOnlyArray<CalendarItemWithHeight>, + ): number => { return _sum(data.map(Calendar.itemHeight)); }; - render() { + render(): React.Node { const { listDataWithHeights } = this.state; let flatList = null; if (listDataWithHeights) { @@ -648,12 +657,12 @@ ); } - flatListHeight() { + flatListHeight(): number { const { safeAreaHeight, tabBarHeight } = this.props.dimensions; return safeAreaHeight - tabBarHeight; } - initialScrollIndex(data: $ReadOnlyArray<CalendarItemWithHeight>) { + initialScrollIndex(data: $ReadOnlyArray<CalendarItemWithHeight>): number { const todayIndex = _findIndex(['dateString', dateString(new Date())])(data); const heightOfTodayHeader = Calendar.itemHeight(data[todayIndex]); @@ -811,14 +820,14 @@ this.flatList.scrollToOffset({ offset, animated: true }); } - heightMeasurerKey = (item: CalendarItem) => { + heightMeasurerKey = (item: CalendarItem): ?string => { if (item.itemType !== 'entryInfo') { return null; } return item.entryInfo.text; }; - heightMeasurerDummy = (item: CalendarItem) => { + heightMeasurerDummy = (item: CalendarItem): React.MixedElement => { invariant( item.itemType === 'entryInfo', 'NodeHeightMeasurer asked for dummy for non-entryInfo item', @@ -826,7 +835,10 @@ return dummyNodeForEntryHeightMeasurement(item.entryInfo.text); }; - heightMeasurerMergeItem = (item: CalendarItem, height: ?number) => { + heightMeasurerMergeItem = ( + item: CalendarItem, + height: ?number, + ): CalendarItemWithHeight => { if (item.itemType !== 'entryInfo') { return item; } @@ -994,7 +1006,7 @@ ); } - loadMoreAbove = _throttle(() => { + loadMoreAbove: () => void = _throttle(() => { if ( this.topLoadingFromScroll && this.topLoaderWaitingToLeaveView && @@ -1004,7 +1016,7 @@ } }, 1000); - loadMoreBelow = _throttle(() => { + loadMoreBelow: () => void = _throttle(() => { if ( this.bottomLoadingFromScroll && this.bottomLoaderWaitingToLeaveView && diff --git a/native/calendar/section-footer.react.js b/native/calendar/section-footer.react.js --- a/native/calendar/section-footer.react.js +++ b/native/calendar/section-footer.react.js @@ -48,7 +48,7 @@ +styles: typeof unboundStyles, }; class SectionFooter extends React.PureComponent<Props> { - render() { + render(): React.Node { return ( <TouchableWithoutFeedback onPress={this.props.onPressWhitespace}> <View style={this.props.styles.sectionFooter}> diff --git a/native/chat/chat-input-bar.react.js b/native/chat/chat-input-bar.react.js --- a/native/chat/chat-input-bar.react.js +++ b/native/chat/chat-input-bar.react.js @@ -1,6 +1,7 @@ // @flow import Icon from '@expo/vector-icons/Ionicons.js'; +import type { GenericNavigationAction } from '@react-navigation/core'; import invariant from 'invariant'; import _throttle from 'lodash/throttle.js'; import * as React from 'react'; @@ -455,17 +456,17 @@ this.sendButtonContainerStyle = { width: sendButtonContainerWidth }; } - static mediaGalleryOpen(props: Props) { + static mediaGalleryOpen(props: Props): boolean { const { keyboardState } = props; return !!(keyboardState && keyboardState.mediaGalleryOpen); } - static systemKeyboardShowing(props: Props) { + static systemKeyboardShowing(props: Props): boolean { const { keyboardState } = props; return !!(keyboardState && keyboardState.systemKeyboardShowing); } - get systemKeyboardShowing() { + get systemKeyboardShowing(): boolean { return ChatInputBar.systemKeyboardShowing(this.props); } @@ -629,7 +630,7 @@ return checkIfDefaultMembersAreVoiced(this.props.threadInfo); } - render() { + render(): React.Node { const isMember = viewerIsMember(this.props.threadInfo); const canJoin = threadHasPermission( this.props.threadInfo, @@ -805,7 +806,7 @@ ); } - renderInput() { + renderInput(): React.Node { const expandoButton = ( <TouchableOpacity onPress={this.expandButtons} @@ -929,7 +930,7 @@ this.setState({ selectionState: data }); }; - saveDraft = _throttle(text => { + saveDraft: (text: string) => void = _throttle(text => { this.props.dispatch({ type: updateDraftActionType, payload: { @@ -1021,14 +1022,14 @@ ); }; - isEditMode = () => { + isEditMode = (): boolean => { const editState = this.props.messageEditingContext?.editState; const isThisThread = editState?.editedMessage?.threadID === this.props.threadInfo.id; - return editState && editState.editedMessage !== null && isThisThread; + return editState?.editedMessage !== null && isThisThread; }; - isMessageEdited = newText => { + isMessageEdited = (newText?: string): boolean => { let text = newText ?? this.state.text; text = trimMessage(text); const originalText = this.props.editedMessageInfo?.text; @@ -1097,7 +1098,7 @@ } }; - getEditedMessage = () => { + getEditedMessage = (): ?MessageInfo => { const editState = this.props.messageEditingContext?.editState; return editState?.editedMessage; }; @@ -1144,7 +1145,11 @@ ); }; - onNavigationBeforeRemove = e => { + onNavigationBeforeRemove = (e: { + +data: { +action: GenericNavigationAction }, + +preventDefault: () => void, + ... + }) => { if (!this.isEditMode()) { return; } @@ -1173,7 +1178,7 @@ this.props.dispatchActionPromise(joinThreadActionTypes, this.joinAction()); }; - async joinAction() { + async joinAction(): Promise<ThreadJoinPayload> { const query = this.props.calendarQuery(); return await this.props.joinThread({ threadID: this.props.threadInfo.id, diff --git a/native/chat/chat-list.react.js b/native/chat/chat-list.react.js --- a/native/chat/chat-list.react.js +++ b/native/chat/chat-list.react.js @@ -70,7 +70,7 @@ flatList: ?FlatListElementRef; scrollPos = 0; - newMessagesPillProgress = new Animated.Value(0); + newMessagesPillProgress: Animated.Value = new Animated.Value(0); newMessagesPillStyle: ViewStyle; constructor(props: Props) { @@ -125,7 +125,7 @@ } }; - get scrolledToBottom() { + get scrolledToBottom(): boolean { return this.scrollPos <= 0; } @@ -218,7 +218,7 @@ } } - render() { + render(): React.Node { const { navigation, viewerID, ...rest } = this.props; const { newMessageCount } = this.state; return ( @@ -250,7 +250,7 @@ static getItemLayout = ( data: ?$ReadOnlyArray<ChatMessageItemWithHeight>, index: number, - ) => { + ): { length: number, offset: number, index: number } => { if (!data) { return { length: 0, offset: 0, index }; } diff --git a/native/chat/chat.react.js b/native/chat/chat.react.js --- a/native/chat/chat.react.js +++ b/native/chat/chat.react.js @@ -13,6 +13,8 @@ ParamListBase, StackRouterOptions, MaterialTopTabNavigationHelpers, + HeaderTitleInputProps, + StackHeaderLeftButtonProps, } from '@react-navigation/core'; import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'; import { @@ -72,6 +74,7 @@ type ChatTopTabsParamList, MessageSearchRouteName, ChangeRolesScreenRouteName, + type NavigationRoute, } from '../navigation/route-names.js'; import type { TabNavigationProp } from '../navigation/tab-navigator.react.js'; import ChangeRolesHeaderLeftButton from '../roles/change-roles-header-left-button.react.js'; @@ -107,14 +110,14 @@ const homeChatThreadListOptions = { title: 'Focused', // eslint-disable-next-line react/display-name - tabBarIcon: ({ color }) => ( + tabBarIcon: ({ color }: { +color: string, ... }) => ( <SWMansionIcon name="home-1" size={22} style={{ color }} /> ), }; const backgroundChatThreadListOptions = { title: 'Background', // eslint-disable-next-line react/display-name - tabBarIcon: ({ color }) => ( + tabBarIcon: ({ color }: { +color: string, ... }) => ( <SWMansionIcon name="bell-disabled" size={22} style={{ color }} /> ), }; @@ -234,7 +237,13 @@ const headerRightStyle = { flexDirection: 'row' }; -const messageListOptions = ({ navigation, route }) => { +const messageListOptions = ({ + navigation, + route, +}: { + +navigation: ChatNavigationProp<'MessageList'>, + +route: NavigationRoute<'MessageList'>, +}) => { const isSearchEmpty = !!route.params.searching && route.params.threadInfo.members.length === 1; @@ -244,7 +253,7 @@ return { // This is a render prop, not a component // eslint-disable-next-line react/display-name - headerTitle: props => ( + headerTitle: (props: HeaderTitleInputProps) => ( <MessageListHeaderTitle threadInfo={route.params.threadInfo} navigate={navigation.navigate} @@ -279,9 +288,14 @@ headerTitle: 'Compose chat', headerBackTitleVisible: false, }; -const threadSettingsOptions = ({ route }) => ({ +const threadSettingsOptions = ({ + route, +}: { + +route: NavigationRoute<'ThreadSettings'>, + ... +}) => ({ // eslint-disable-next-line react/display-name - headerTitle: props => ( + headerTitle: (props: HeaderTitleInputProps) => ( <ThreadSettingsHeaderTitle threadInfo={route.params.threadInfo} {...props} @@ -313,9 +327,14 @@ headerTitle: 'Pinned Messages', headerBackTitleVisible: false, }; -const changeRolesScreenOptions = ({ route }) => ({ +const changeRolesScreenOptions = ({ + route, +}: { + +route: NavigationRoute<'ChangeRolesScreen'>, + ... +}) => ({ // eslint-disable-next-line react/display-name - headerLeft: headerLeftProps => ( + headerLeft: (headerLeftProps: StackHeaderLeftButtonProps) => ( <ChangeRolesHeaderLeftButton {...headerLeftProps} route={route} /> ), headerTitle: 'Change Role', @@ -349,7 +368,7 @@ } const headerLeftButton = React.useCallback( - headerProps => { + (headerProps: StackHeaderLeftButtonProps) => { if (headerProps.canGoBack) { return <HeaderBackButton {...headerProps} />; } @@ -379,7 +398,12 @@ ); const chatThreadListOptions = React.useCallback( - ({ navigation }) => ({ + ({ + navigation, + }: { + +navigation: ChatNavigationProp<'ChatThreadList'>, + ... + }) => ({ headerTitle: 'Inbox', headerRight: Platform.OS === 'ios' diff --git a/native/chat/failed-send.react.js b/native/chat/failed-send.react.js --- a/native/chat/failed-send.react.js +++ b/native/chat/failed-send.react.js @@ -86,7 +86,7 @@ } } - render() { + render(): React.Node { if (!this.props.rawMessageInfo) { return null; } diff --git a/native/chat/message-list-container.react.js b/native/chat/message-list-container.react.js --- a/native/chat/message-list-container.react.js +++ b/native/chat/message-list-container.react.js @@ -107,7 +107,7 @@ }; pendingListDataWithHeights: ?$ReadOnlyArray<ChatMessageItemWithHeight>; - get frozen() { + get frozen(): boolean { const { overlayContext } = this.props; invariant( overlayContext, @@ -155,7 +155,7 @@ } } - render() { + render(): React.Node { const { threadInfo, styles } = this.props; const { listDataWithHeights } = this.state; const { searching } = this.props.route.params; diff --git a/native/chat/message-list-header-title.react.js b/native/chat/message-list-header-title.react.js --- a/native/chat/message-list-header-title.react.js +++ b/native/chat/message-list-header-title.react.js @@ -43,7 +43,7 @@ +title: string, }; class MessageListHeaderTitle extends React.PureComponent<Props> { - render() { + render(): React.Node { const { threadInfo, navigate, diff --git a/native/chat/message-list.react.js b/native/chat/message-list.react.js --- a/native/chat/message-list.react.js +++ b/native/chat/message-list.react.js @@ -113,40 +113,41 @@ }; flatListContainer: ?React.ElementRef<typeof View>; - flatListExtraDataSelector = createSelector( - (propsAndState: PropsAndState) => propsAndState.messageListVerticalBounds, - (propsAndState: PropsAndState) => propsAndState.focusedMessageKey, - (propsAndState: PropsAndState) => propsAndState.navigation, - (propsAndState: PropsAndState) => propsAndState.route, - ( - messageListVerticalBounds: ?VerticalBounds, - focusedMessageKey: ?string, - navigation: ChatNavigationProp<'MessageList'>, - route: NavigationRoute<'MessageList'>, - ) => ({ - messageListVerticalBounds, - focusedMessageKey, - navigation, - route, - }), - ); + flatListExtraDataSelector: PropsAndState => FlatListExtraData = + createSelector( + (propsAndState: PropsAndState) => propsAndState.messageListVerticalBounds, + (propsAndState: PropsAndState) => propsAndState.focusedMessageKey, + (propsAndState: PropsAndState) => propsAndState.navigation, + (propsAndState: PropsAndState) => propsAndState.route, + ( + messageListVerticalBounds: ?VerticalBounds, + focusedMessageKey: ?string, + navigation: ChatNavigationProp<'MessageList'>, + route: NavigationRoute<'MessageList'>, + ) => ({ + messageListVerticalBounds, + focusedMessageKey, + navigation, + route, + }), + ); get flatListExtraData(): FlatListExtraData { return this.flatListExtraDataSelector({ ...this.props, ...this.state }); } - static getOverlayContext(props: Props) { + static getOverlayContext(props: Props): OverlayContextType { const { overlayContext } = props; invariant(overlayContext, 'MessageList should have OverlayContext'); return overlayContext; } - static scrollDisabled(props: Props) { + static scrollDisabled(props: Props): boolean { const overlayContext = MessageList.getOverlayContext(props); return overlayContext.scrollBlockingModalStatus !== 'closed'; } - static modalOpen(props: Props) { + static modalOpen(props: Props): boolean { const overlayContext = MessageList.getOverlayContext(props); return overlayContext.scrollBlockingModalStatus === 'open'; } @@ -174,7 +175,7 @@ keyboardState && keyboardState.dismissKeyboard(); }; - renderItem = (row: { item: ChatMessageItemWithHeight, ... }) => { + renderItem = (row: { item: ChatMessageItemWithHeight, ... }): React.Node => { if (row.item.itemType === 'loader') { return ( <TouchableWithoutFeedback onPress={this.dismissKeyboard}> @@ -211,9 +212,11 @@ }; // Actually header, it's just that our FlatList is inverted - ListFooterComponent = () => <View style={this.props.styles.header} />; + ListFooterComponent = (): React.Node => ( + <View style={this.props.styles.header} /> + ); - render() { + render(): React.Node { const { messageListData, startReached } = this.props; const footer = startReached ? this.ListFooterComponent : undefined; let relationshipPrompt = null; diff --git a/native/chat/message-reactions-modal.react.js b/native/chat/message-reactions-modal.react.js --- a/native/chat/message-reactions-modal.react.js +++ b/native/chat/message-reactions-modal.react.js @@ -12,7 +12,10 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import type { ReactionInfo } from 'lib/selectors/chat-selectors.js'; -import { useMessageReactionsList } from 'lib/shared/reaction-utils.js'; +import { + useMessageReactionsList, + type MessageReactionListInfo, +} from 'lib/shared/reaction-utils.js'; import UserAvatar from '../avatars/user-avatar.react.js'; import Modal from '../components/modal.react.js'; @@ -70,7 +73,7 @@ ); const renderItem = React.useCallback( - ({ item }) => ( + ({ item }: { +item: MessageReactionListInfo, ... }) => ( <TouchableOpacity onPress={() => onPressUser(item.id)} key={item.id} diff --git a/native/chat/multimedia-message-multimedia.react.js b/native/chat/multimedia-message-multimedia.react.js --- a/native/chat/multimedia-message-multimedia.react.js +++ b/native/chat/multimedia-message-multimedia.react.js @@ -74,7 +74,7 @@ }; } - static getOverlayContext(props: Props) { + static getOverlayContext(props: Props): OverlayContextType { const { overlayContext } = props; invariant( overlayContext, @@ -83,7 +83,7 @@ return overlayContext; } - static getModalOverlayPosition(props: Props) { + static getModalOverlayPosition(props: Props): ?Animated.Value { const overlayContext = MultimediaMessageMultimedia.getOverlayContext(props); const { visibleOverlays } = overlayContext; for (const overlay of visibleOverlays) { @@ -98,7 +98,7 @@ return undefined; } - getOpacity() { + getOpacity(): number | Animated.Node { const overlayPosition = MultimediaMessageMultimedia.getModalOverlayPosition( this.props, ); @@ -136,7 +136,7 @@ } } - render() { + render(): React.Node { const { opacity } = this.state; const animatedWrapperStyle: AnimatedStyleObj = { opacity }; const wrapperStyles = [ @@ -202,7 +202,7 @@ }); }; - dismissKeyboardIfShowing = () => { + dismissKeyboardIfShowing = (): boolean => { const { keyboardState } = this.props; return !!(keyboardState && keyboardState.dismissKeyboardIfShowing()); }; diff --git a/native/chat/multimedia-message-tooltip-button.react.js b/native/chat/multimedia-message-tooltip-button.react.js --- a/native/chat/multimedia-message-tooltip-button.react.js +++ b/native/chat/multimedia-message-tooltip-button.react.js @@ -18,6 +18,7 @@ import SidebarInputBarHeightMeasurer from './sidebar-input-bar-height-measurer.react.js'; import { useAnimatedMessageTooltipButton } from './utils.js'; import EmojiKeyboard from '../components/emoji-keyboard.react.js'; +import type { EmojiSelection } from '../components/emoji-keyboard.react.js'; import type { AppNavigationProp } from '../navigation/app-navigator.react.js'; import { useSelector } from '../redux/redux-utils.js'; import { useTooltipActions } from '../tooltip/tooltip-hooks.js'; @@ -147,7 +148,7 @@ const { dismissTooltip } = useTooltipActions(navigation, tooltipRouteKey); const onEmojiSelected = React.useCallback( - emoji => { + (emoji: EmojiSelection) => { sendReaction(emoji.emoji); dismissTooltip(); }, diff --git a/native/chat/multimedia-message-tooltip-modal.react.js b/native/chat/multimedia-message-tooltip-modal.react.js --- a/native/chat/multimedia-message-tooltip-modal.react.js +++ b/native/chat/multimedia-message-tooltip-modal.react.js @@ -16,6 +16,7 @@ } from '../tooltip/tooltip.react.js'; import type { ChatMultimediaMessageInfoItem } from '../types/chat-types.js'; import type { VerticalBounds } from '../types/layout-types.js'; +import type { TextStyle } from '../types/styles.js'; import { useNavigateToPinModal } from '../utils/toggle-pin-utils.js'; export type MultimediaMessageTooltipModalParams = TooltipParams<{ @@ -33,17 +34,21 @@ const onPressTogglePin = useNavigateToPinModal(overlayContext, route); const renderPinIcon = React.useCallback( - style => <CommIcon name="pin-outline" style={style} size={16} />, + (style: TextStyle) => ( + <CommIcon name="pin-outline" style={style} size={16} /> + ), [], ); const renderUnpinIcon = React.useCallback( - style => <CommIcon name="unpin-outline" style={style} size={16} />, + (style: TextStyle) => ( + <CommIcon name="unpin-outline" style={style} size={16} /> + ), [], ); const onPressSidebar = useAnimatedNavigateToSidebar(route.params.item); const renderSidebarIcon = React.useCallback( - style => ( + (style: TextStyle) => ( <SWMansionIcon name="message-circle-lines" style={style} size={16} /> ), [], @@ -51,7 +56,9 @@ const onPressReport = useOnPressReport(route); const renderReportIcon = React.useCallback( - style => <SWMansionIcon name="warning-circle" style={style} size={16} />, + (style: TextStyle) => ( + <SWMansionIcon name="warning-circle" style={style} size={16} /> + ), [], ); diff --git a/native/chat/multimedia-message.react.js b/native/chat/multimedia-message.react.js --- a/native/chat/multimedia-message.react.js +++ b/native/chat/multimedia-message.react.js @@ -86,7 +86,7 @@ }); }; - visibleEntryIDs() { + visibleEntryIDs(): $ReadOnlyArray<string> { const result = []; if (this.props.canTogglePins) { @@ -182,14 +182,14 @@ }); }; - canNavigateToSidebar() { + canNavigateToSidebar(): boolean { return ( - this.props.item.threadCreatedFromMessage || + !!this.props.item.threadCreatedFromMessage || this.props.canCreateSidebarFromMessage ); } - render() { + render(): React.Node { const { item, focused, diff --git a/native/chat/robotext-message-tooltip-button.react.js b/native/chat/robotext-message-tooltip-button.react.js --- a/native/chat/robotext-message-tooltip-button.react.js +++ b/native/chat/robotext-message-tooltip-button.react.js @@ -17,6 +17,7 @@ import { Timestamp } from './timestamp.react.js'; import { useAnimatedMessageTooltipButton } from './utils.js'; import EmojiKeyboard from '../components/emoji-keyboard.react.js'; +import type { EmojiSelection } from '../components/emoji-keyboard.react.js'; import type { AppNavigationProp } from '../navigation/app-navigator.react.js'; import { useSelector } from '../redux/redux-utils.js'; import { useTooltipActions } from '../tooltip/tooltip-hooks.js'; @@ -130,7 +131,7 @@ const { dismissTooltip } = useTooltipActions(navigation, tooltipRouteKey); const onEmojiSelected = React.useCallback( - emoji => { + (emoji: EmojiSelection) => { sendReaction(emoji.emoji); dismissTooltip(); }, diff --git a/native/chat/robotext-message-tooltip-modal.react.js b/native/chat/robotext-message-tooltip-modal.react.js --- a/native/chat/robotext-message-tooltip-modal.react.js +++ b/native/chat/robotext-message-tooltip-modal.react.js @@ -12,6 +12,7 @@ type TooltipMenuProps, } from '../tooltip/tooltip.react.js'; import type { ChatRobotextMessageInfoItemWithHeight } from '../types/chat-types.js'; +import type { TextStyle } from '../types/styles.js'; export type RobotextMessageTooltipModalParams = TooltipParams<{ +item: ChatRobotextMessageInfoItemWithHeight, @@ -24,7 +25,7 @@ const onPress = useAnimatedNavigateToSidebar(route.params.item); const renderIcon = React.useCallback( - style => ( + (style: TextStyle) => ( <SWMansionIcon name="message-circle-lines" style={style} size={16} /> ), [], diff --git a/native/chat/robotext-message.react.js b/native/chat/robotext-message.react.js --- a/native/chat/robotext-message.react.js +++ b/native/chat/robotext-message.react.js @@ -106,7 +106,14 @@ }, [item.threadCreatedFromMessage, canCreateSidebarFromMessage]); const openRobotextTooltipModal = React.useCallback( - (x, y, width, height, pageX, pageY) => { + ( + x: number, + y: number, + width: number, + height: number, + pageX: number, + pageY: number, + ) => { invariant( verticalBounds, 'verticalBounds should be present in openRobotextTooltipModal', diff --git a/native/chat/settings/compose-subchannel-modal.react.js b/native/chat/settings/compose-subchannel-modal.react.js --- a/native/chat/settings/compose-subchannel-modal.react.js +++ b/native/chat/settings/compose-subchannel-modal.react.js @@ -69,7 +69,7 @@ +styles: typeof unboundStyles, }; class ComposeSubchannelModal extends React.PureComponent<Props> { - render() { + render(): React.Node { return ( <Modal modalStyle={this.props.styles.modal}> <Text style={this.props.styles.visibility}>Chat type</Text> diff --git a/native/chat/settings/delete-thread.react.js b/native/chat/settings/delete-thread.react.js --- a/native/chat/settings/delete-thread.react.js +++ b/native/chat/settings/delete-thread.react.js @@ -142,7 +142,7 @@ this.mounted = false; } - render() { + render(): React.Node { const buttonContent = this.props.loadingStatus === 'loading' ? ( <ActivityIndicator size="small" color="white" /> @@ -207,7 +207,7 @@ ); }; - async deleteThread() { + async deleteThread(): Promise<LeaveThreadPayload> { const { threadInfo, navDispatch } = this.props; navDispatch({ type: clearThreadsActionType, diff --git a/native/chat/settings/emoji-thread-avatar-creation.react.js b/native/chat/settings/emoji-thread-avatar-creation.react.js --- a/native/chat/settings/emoji-thread-avatar-creation.react.js +++ b/native/chat/settings/emoji-thread-avatar-creation.react.js @@ -5,6 +5,7 @@ import { EditThreadAvatarContext } from 'lib/components/base-edit-thread-avatar-provider.react.js'; import { savedEmojiAvatarSelectorForThread } from 'lib/selectors/thread-selectors.js'; +import type { UpdateUserAvatarRequest } from 'lib/types/avatar-types.js'; import type { MinimallyEncodedRawThreadInfo, MinimallyEncodedThreadInfo, @@ -47,7 +48,7 @@ const nativeSetThreadAvatar = useNativeSetThreadAvatar(); const setAvatar = React.useCallback( - async avatarRequest => { + async (avatarRequest: UpdateUserAvatarRequest) => { const result = await nativeSetThreadAvatar(threadID, avatarRequest); displayActionResultModal('Avatar updated!'); return result; diff --git a/native/chat/settings/thread-settings-color.react.js b/native/chat/settings/thread-settings-color.react.js --- a/native/chat/settings/thread-settings-color.react.js +++ b/native/chat/settings/thread-settings-color.react.js @@ -54,7 +54,7 @@ +styles: typeof unboundStyles, }; class ThreadSettingsColor extends React.PureComponent<Props> { - render() { + render(): React.Node { let colorButton; if (this.props.loadingStatus !== 'loading') { colorButton = ( diff --git a/native/chat/settings/thread-settings-description.react.js b/native/chat/settings/thread-settings-description.react.js --- a/native/chat/settings/thread-settings-description.react.js +++ b/native/chat/settings/thread-settings-description.react.js @@ -111,7 +111,7 @@ class ThreadSettingsDescription extends React.PureComponent<Props> { textInput: ?React.ElementRef<typeof BaseTextInput>; - render() { + render(): React.Node { if ( this.props.descriptionEditValue !== null && this.props.descriptionEditValue !== undefined @@ -197,7 +197,7 @@ return null; } - renderButton() { + renderButton(): React.Node { if (this.props.loadingStatus === 'loading') { return ( <ActivityIndicator @@ -264,7 +264,9 @@ }); }; - async editDescription(newDescription: string) { + async editDescription( + newDescription: string, + ): Promise<ChangeThreadSettingsPayload> { try { return await this.props.changeThreadSettings({ threadID: this.props.threadInfo.id, diff --git a/native/chat/settings/thread-settings-home-notifs.react.js b/native/chat/settings/thread-settings-home-notifs.react.js --- a/native/chat/settings/thread-settings-home-notifs.react.js +++ b/native/chat/settings/thread-settings-home-notifs.react.js @@ -66,7 +66,7 @@ }; } - render() { + render(): React.Node { const componentLabel = 'Background'; return ( <View style={this.props.styles.row}> diff --git a/native/chat/settings/thread-settings-leave-thread.react.js b/native/chat/settings/thread-settings-leave-thread.react.js --- a/native/chat/settings/thread-settings-leave-thread.react.js +++ b/native/chat/settings/thread-settings-leave-thread.react.js @@ -67,7 +67,7 @@ +navContext: ?NavContextType, }; class ThreadSettingsLeaveThread extends React.PureComponent<Props> { - render() { + render(): React.Node { const { panelIosHighlightUnderlay, panelForegroundSecondaryLabel } = this.props.colors; const loadingIndicator = @@ -122,7 +122,7 @@ ); }; - async leaveThread() { + async leaveThread(): Promise<LeaveThreadPayload> { const threadID = this.props.threadInfo.id; const { navContext } = this.props; invariant(navContext, 'navContext should exist in leaveThread'); diff --git a/native/chat/settings/thread-settings-media-gallery.react.js b/native/chat/settings/thread-settings-media-gallery.react.js --- a/native/chat/settings/thread-settings-media-gallery.react.js +++ b/native/chat/settings/thread-settings-media-gallery.react.js @@ -101,7 +101,7 @@ }, [activeTab, mediaInfos]); const renderItem = React.useCallback( - ({ item, index }) => ( + ({ item, index }: { +item: Media, +index: number, ... }) => ( <MediaGalleryItem item={item} index={index} diff --git a/native/chat/settings/thread-settings-member.react.js b/native/chat/settings/thread-settings-member.react.js --- a/native/chat/settings/thread-settings-member.react.js +++ b/native/chat/settings/thread-settings-member.react.js @@ -119,7 +119,7 @@ class ThreadSettingsMember extends React.PureComponent<Props> { editButton: ?React.ElementRef<typeof View>; - render() { + render(): React.Node { const userText = stringForUser(this.props.memberInfo); let usernameInfo = null; @@ -248,7 +248,7 @@ }); }; - dismissKeyboardIfShowing = () => { + dismissKeyboardIfShowing = (): boolean => { const { keyboardState } = this.props; return !!(keyboardState && keyboardState.dismissKeyboardIfShowing()); }; diff --git a/native/chat/settings/thread-settings-name.react.js b/native/chat/settings/thread-settings-name.react.js --- a/native/chat/settings/thread-settings-name.react.js +++ b/native/chat/settings/thread-settings-name.react.js @@ -83,7 +83,7 @@ class ThreadSettingsName extends React.PureComponent<Props> { textInput: ?React.ElementRef<typeof BaseTextInput>; - render() { + render(): React.Node { return ( <View style={this.props.styles.row}> <Text style={this.props.styles.label}>Name</Text> @@ -92,7 +92,7 @@ ); } - renderButton() { + renderButton(): React.Node { if (this.props.loadingStatus === 'loading') { return ( <ActivityIndicator @@ -114,7 +114,7 @@ return <SaveSettingButton onPress={this.onSubmit} />; } - renderContent() { + renderContent(): React.Node { if ( this.props.nameEditValue === null || this.props.nameEditValue === undefined @@ -152,7 +152,7 @@ this.textInput = textInput; }; - threadEditName() { + threadEditName(): string { return firstLine( this.props.threadInfo.name ? this.props.threadInfo.name : '', ); @@ -191,7 +191,7 @@ }); }; - async editName(newName: string) { + async editName(newName: string): Promise<ChangeThreadSettingsPayload> { try { return await this.props.changeThreadSettings({ threadID: this.props.threadInfo.id, diff --git a/native/chat/settings/thread-settings-promote-sidebar.react.js b/native/chat/settings/thread-settings-promote-sidebar.react.js --- a/native/chat/settings/thread-settings-promote-sidebar.react.js +++ b/native/chat/settings/thread-settings-promote-sidebar.react.js @@ -59,7 +59,7 @@ ); }; - render() { + render(): React.Node { const { panelIosHighlightUnderlay, panelForegroundSecondaryLabel } = this.props.colors; const loadingIndicator = diff --git a/native/chat/settings/thread-settings-push-notifs.react.js b/native/chat/settings/thread-settings-push-notifs.react.js --- a/native/chat/settings/thread-settings-push-notifs.react.js +++ b/native/chat/settings/thread-settings-push-notifs.react.js @@ -79,7 +79,7 @@ }; } - render() { + render(): React.Node { const componentLabel = 'Push notifs'; let notificationsSettingsLinkingIcon: React.Node = undefined; if (!this.props.hasPushPermissions) { diff --git a/native/chat/settings/thread-settings.react.js b/native/chat/settings/thread-settings.react.js --- a/native/chat/settings/thread-settings.react.js +++ b/native/chat/settings/thread-settings.react.js @@ -312,7 +312,7 @@ }; } - static scrollDisabled(props: Props) { + static scrollDisabled(props: Props): boolean { const { overlayContext } = props; invariant(overlayContext, 'ThreadSettings should have OverlayContext'); return overlayContext.scrollBlockingModalStatus !== 'closed'; @@ -340,548 +340,557 @@ } } - threadBasicsListDataSelector = createSelector( - (propsAndState: PropsAndState) => propsAndState.threadInfo, - (propsAndState: PropsAndState) => propsAndState.parentThreadInfo, - (propsAndState: PropsAndState) => propsAndState.nameEditValue, - (propsAndState: PropsAndState) => propsAndState.colorEditValue, - (propsAndState: PropsAndState) => propsAndState.descriptionEditValue, - (propsAndState: PropsAndState) => propsAndState.descriptionTextHeight, - (propsAndState: PropsAndState) => !propsAndState.somethingIsSaving, - (propsAndState: PropsAndState) => propsAndState.navigation.navigate, - (propsAndState: PropsAndState) => propsAndState.route.key, - ( - threadInfo: ResolvedThreadInfo | MinimallyEncodedResolvedThreadInfo, - parentThreadInfo: - | ?ResolvedThreadInfo - | ?MinimallyEncodedResolvedThreadInfo, - nameEditValue: ?string, - colorEditValue: string, - descriptionEditValue: ?string, - descriptionTextHeight: ?number, - canStartEditing: boolean, - navigate: ThreadSettingsNavigate, - routeKey: string, - ) => { - const canEditThreadAvatar = threadHasPermission( - threadInfo, - threadPermissions.EDIT_THREAD_AVATAR, - ); - const canEditThreadName = threadHasPermission( - threadInfo, - threadPermissions.EDIT_THREAD_NAME, - ); - const canEditThreadDescription = threadHasPermission( - threadInfo, - threadPermissions.EDIT_THREAD_DESCRIPTION, - ); - const canEditThreadColor = threadHasPermission( - threadInfo, - threadPermissions.EDIT_THREAD_COLOR, - ); + threadBasicsListDataSelector: PropsAndState => $ReadOnlyArray<ChatSettingsItem> = + createSelector( + (propsAndState: PropsAndState) => propsAndState.threadInfo, + (propsAndState: PropsAndState) => propsAndState.parentThreadInfo, + (propsAndState: PropsAndState) => propsAndState.nameEditValue, + (propsAndState: PropsAndState) => propsAndState.colorEditValue, + (propsAndState: PropsAndState) => propsAndState.descriptionEditValue, + (propsAndState: PropsAndState) => propsAndState.descriptionTextHeight, + (propsAndState: PropsAndState) => !propsAndState.somethingIsSaving, + (propsAndState: PropsAndState) => propsAndState.navigation.navigate, + (propsAndState: PropsAndState) => propsAndState.route.key, + ( + threadInfo: ResolvedThreadInfo | MinimallyEncodedResolvedThreadInfo, + parentThreadInfo: + | ?ResolvedThreadInfo + | ?MinimallyEncodedResolvedThreadInfo, + nameEditValue: ?string, + colorEditValue: string, + descriptionEditValue: ?string, + descriptionTextHeight: ?number, + canStartEditing: boolean, + navigate: ThreadSettingsNavigate, + routeKey: string, + ) => { + const canEditThreadAvatar = threadHasPermission( + threadInfo, + threadPermissions.EDIT_THREAD_AVATAR, + ); + const canEditThreadName = threadHasPermission( + threadInfo, + threadPermissions.EDIT_THREAD_NAME, + ); + const canEditThreadDescription = threadHasPermission( + threadInfo, + threadPermissions.EDIT_THREAD_DESCRIPTION, + ); + const canEditThreadColor = threadHasPermission( + threadInfo, + threadPermissions.EDIT_THREAD_COLOR, + ); - const canChangeAvatar = canEditThreadAvatar && canStartEditing; - const canChangeName = canEditThreadName && canStartEditing; - const canChangeDescription = canEditThreadDescription && canStartEditing; - const canChangeColor = canEditThreadColor && canStartEditing; - - const listData: ChatSettingsItem[] = []; - listData.push({ - itemType: 'header', - key: 'avatarHeader', - title: 'Channel Avatar', - categoryType: 'unpadded', - }); - listData.push({ - itemType: 'avatar', - key: 'avatar', - threadInfo, - canChangeSettings: canChangeAvatar, - }); - listData.push({ - itemType: 'footer', - key: 'avatarFooter', - categoryType: 'outline', - }); - - listData.push({ - itemType: 'header', - key: 'basicsHeader', - title: 'Basics', - categoryType: 'full', - }); - listData.push({ - itemType: 'name', - key: 'name', - threadInfo, - nameEditValue, - canChangeSettings: canChangeName, - }); - listData.push({ - itemType: 'color', - key: 'color', - threadInfo, - colorEditValue, - canChangeSettings: canChangeColor, - navigate, - threadSettingsRouteKey: routeKey, - }); - listData.push({ - itemType: 'footer', - key: 'basicsFooter', - categoryType: 'full', - }); + const canChangeAvatar = canEditThreadAvatar && canStartEditing; + const canChangeName = canEditThreadName && canStartEditing; + const canChangeDescription = + canEditThreadDescription && canStartEditing; + const canChangeColor = canEditThreadColor && canStartEditing; - if ( - (descriptionEditValue !== null && descriptionEditValue !== undefined) || - threadInfo.description || - canEditThreadDescription - ) { + const listData: ChatSettingsItem[] = []; + listData.push({ + itemType: 'header', + key: 'avatarHeader', + title: 'Channel Avatar', + categoryType: 'unpadded', + }); listData.push({ - itemType: 'description', - key: 'description', + itemType: 'avatar', + key: 'avatar', threadInfo, - descriptionEditValue, - descriptionTextHeight, - canChangeSettings: canChangeDescription, + canChangeSettings: canChangeAvatar, + }); + listData.push({ + itemType: 'footer', + key: 'avatarFooter', + categoryType: 'outline', }); - } - const isMember = viewerIsMember(threadInfo); - if (isMember) { listData.push({ itemType: 'header', - key: 'subscriptionHeader', - title: 'Subscription', + key: 'basicsHeader', + title: 'Basics', categoryType: 'full', }); listData.push({ - itemType: 'pushNotifs', - key: 'pushNotifs', + itemType: 'name', + key: 'name', + threadInfo, + nameEditValue, + canChangeSettings: canChangeName, + }); + listData.push({ + itemType: 'color', + key: 'color', threadInfo, + colorEditValue, + canChangeSettings: canChangeColor, + navigate, + threadSettingsRouteKey: routeKey, + }); + listData.push({ + itemType: 'footer', + key: 'basicsFooter', + categoryType: 'full', }); - if (threadInfo.type !== threadTypes.SIDEBAR) { + + if ( + (descriptionEditValue !== null && + descriptionEditValue !== undefined) || + threadInfo.description || + canEditThreadDescription + ) { + listData.push({ + itemType: 'description', + key: 'description', + threadInfo, + descriptionEditValue, + descriptionTextHeight, + canChangeSettings: canChangeDescription, + }); + } + + const isMember = viewerIsMember(threadInfo); + if (isMember) { + listData.push({ + itemType: 'header', + key: 'subscriptionHeader', + title: 'Subscription', + categoryType: 'full', + }); listData.push({ - itemType: 'homeNotifs', - key: 'homeNotifs', + itemType: 'pushNotifs', + key: 'pushNotifs', threadInfo, }); + if (threadInfo.type !== threadTypes.SIDEBAR) { + listData.push({ + itemType: 'homeNotifs', + key: 'homeNotifs', + threadInfo, + }); + } + listData.push({ + itemType: 'footer', + key: 'subscriptionFooter', + categoryType: 'full', + }); } + + listData.push({ + itemType: 'header', + key: 'privacyHeader', + title: 'Privacy', + categoryType: 'full', + }); + listData.push({ + itemType: 'visibility', + key: 'visibility', + threadInfo, + }); + listData.push({ + itemType: 'parent', + key: 'parent', + threadInfo, + parentThreadInfo, + }); listData.push({ itemType: 'footer', - key: 'subscriptionFooter', + key: 'privacyFooter', categoryType: 'full', }); - } - - listData.push({ - itemType: 'header', - key: 'privacyHeader', - title: 'Privacy', - categoryType: 'full', - }); - listData.push({ - itemType: 'visibility', - key: 'visibility', - threadInfo, - }); - listData.push({ - itemType: 'parent', - key: 'parent', - threadInfo, - parentThreadInfo, - }); - listData.push({ - itemType: 'footer', - key: 'privacyFooter', - categoryType: 'full', - }); - return listData; - }, - ); - - subchannelsListDataSelector = createSelector( - (propsAndState: PropsAndState) => propsAndState.threadInfo, - (propsAndState: PropsAndState) => propsAndState.navigation.navigate, - (propsAndState: PropsAndState) => propsAndState.childThreadInfos, - (propsAndState: PropsAndState) => propsAndState.numSubchannelsShowing, - ( - threadInfo: ResolvedThreadInfo | MinimallyEncodedResolvedThreadInfo, - navigate: ThreadSettingsNavigate, - childThreads: ?$ReadOnlyArray< - ResolvedThreadInfo | MinimallyEncodedResolvedThreadInfo, - >, - numSubchannelsShowing: number, - ) => { - const listData: ChatSettingsItem[] = []; - - const subchannels = childThreads?.filter(threadIsChannel) ?? []; - const canCreateSubchannels = threadHasPermission( - threadInfo, - threadPermissions.CREATE_SUBCHANNELS, - ); - if (subchannels.length === 0 && !canCreateSubchannels) { return listData; - } + }, + ); - listData.push({ - itemType: 'header', - key: 'subchannelHeader', - title: 'Subchannels', - categoryType: 'unpadded', - }); + subchannelsListDataSelector: PropsAndState => $ReadOnlyArray<ChatSettingsItem> = + createSelector( + (propsAndState: PropsAndState) => propsAndState.threadInfo, + (propsAndState: PropsAndState) => propsAndState.navigation.navigate, + (propsAndState: PropsAndState) => propsAndState.childThreadInfos, + (propsAndState: PropsAndState) => propsAndState.numSubchannelsShowing, + ( + threadInfo: ResolvedThreadInfo | MinimallyEncodedResolvedThreadInfo, + navigate: ThreadSettingsNavigate, + childThreads: ?$ReadOnlyArray< + ResolvedThreadInfo | MinimallyEncodedResolvedThreadInfo, + >, + numSubchannelsShowing: number, + ) => { + const listData: ChatSettingsItem[] = []; + + const subchannels = childThreads?.filter(threadIsChannel) ?? []; + const canCreateSubchannels = threadHasPermission( + threadInfo, + threadPermissions.CREATE_SUBCHANNELS, + ); + if (subchannels.length === 0 && !canCreateSubchannels) { + return listData; + } - if (canCreateSubchannels) { listData.push({ - itemType: 'addSubchannel', - key: 'addSubchannel', + itemType: 'header', + key: 'subchannelHeader', + title: 'Subchannels', + categoryType: 'unpadded', }); - } - const numItems = Math.min(numSubchannelsShowing, subchannels.length); - for (let i = 0; i < numItems; i++) { - const subchannelInfo = subchannels[i]; + if (canCreateSubchannels) { + listData.push({ + itemType: 'addSubchannel', + key: 'addSubchannel', + }); + } + + const numItems = Math.min(numSubchannelsShowing, subchannels.length); + for (let i = 0; i < numItems; i++) { + const subchannelInfo = subchannels[i]; + listData.push({ + itemType: 'childThread', + key: `childThread${subchannelInfo.id}`, + threadInfo: subchannelInfo, + firstListItem: i === 0 && !canCreateSubchannels, + lastListItem: i === numItems - 1 && numItems === subchannels.length, + }); + } + + if (numItems < subchannels.length) { + listData.push({ + itemType: 'seeMore', + key: 'seeMoreSubchannels', + onPress: this.onPressSeeMoreSubchannels, + }); + } + listData.push({ - itemType: 'childThread', - key: `childThread${subchannelInfo.id}`, - threadInfo: subchannelInfo, - firstListItem: i === 0 && !canCreateSubchannels, - lastListItem: i === numItems - 1 && numItems === subchannels.length, + itemType: 'footer', + key: 'subchannelFooter', + categoryType: 'unpadded', }); - } - if (numItems < subchannels.length) { + return listData; + }, + ); + + sidebarsListDataSelector: PropsAndState => $ReadOnlyArray<ChatSettingsItem> = + createSelector( + (propsAndState: PropsAndState) => propsAndState.navigation.navigate, + (propsAndState: PropsAndState) => propsAndState.childThreadInfos, + (propsAndState: PropsAndState) => propsAndState.numSidebarsShowing, + ( + navigate: ThreadSettingsNavigate, + childThreads: ?$ReadOnlyArray< + ResolvedThreadInfo | MinimallyEncodedResolvedThreadInfo, + >, + numSidebarsShowing: number, + ) => { + const listData: ChatSettingsItem[] = []; + + const sidebars = + childThreads?.filter( + childThreadInfo => childThreadInfo.type === threadTypes.SIDEBAR, + ) ?? []; + if (sidebars.length === 0) { + return listData; + } + listData.push({ - itemType: 'seeMore', - key: 'seeMoreSubchannels', - onPress: this.onPressSeeMoreSubchannels, + itemType: 'header', + key: 'sidebarHeader', + title: 'Threads', + categoryType: 'unpadded', + }); + + const numItems = Math.min(numSidebarsShowing, sidebars.length); + for (let i = 0; i < numItems; i++) { + const sidebarInfo = sidebars[i]; + listData.push({ + itemType: 'childThread', + key: `childThread${sidebarInfo.id}`, + threadInfo: sidebarInfo, + firstListItem: i === 0, + lastListItem: i === numItems - 1 && numItems === sidebars.length, + }); + } + + if (numItems < sidebars.length) { + listData.push({ + itemType: 'seeMore', + key: 'seeMoreSidebars', + onPress: this.onPressSeeMoreSidebars, + }); + } + + listData.push({ + itemType: 'footer', + key: 'sidebarFooter', + categoryType: 'unpadded', }); - } - listData.push({ - itemType: 'footer', - key: 'subchannelFooter', - categoryType: 'unpadded', - }); - - return listData; - }, - ); - - sidebarsListDataSelector = createSelector( - (propsAndState: PropsAndState) => propsAndState.navigation.navigate, - (propsAndState: PropsAndState) => propsAndState.childThreadInfos, - (propsAndState: PropsAndState) => propsAndState.numSidebarsShowing, - ( - navigate: ThreadSettingsNavigate, - childThreads: ?$ReadOnlyArray< - ResolvedThreadInfo | MinimallyEncodedResolvedThreadInfo, - >, - numSidebarsShowing: number, - ) => { - const listData: ChatSettingsItem[] = []; - - const sidebars = - childThreads?.filter( - childThreadInfo => childThreadInfo.type === threadTypes.SIDEBAR, - ) ?? []; - if (sidebars.length === 0) { return listData; - } + }, + ); - listData.push({ - itemType: 'header', - key: 'sidebarHeader', - title: 'Threads', - categoryType: 'unpadded', - }); + threadMembersListDataSelector: PropsAndState => $ReadOnlyArray<ChatSettingsItem> = + createSelector( + (propsAndState: PropsAndState) => propsAndState.threadInfo, + (propsAndState: PropsAndState) => !propsAndState.somethingIsSaving, + (propsAndState: PropsAndState) => propsAndState.navigation.navigate, + (propsAndState: PropsAndState) => propsAndState.route.key, + (propsAndState: PropsAndState) => propsAndState.numMembersShowing, + (propsAndState: PropsAndState) => propsAndState.verticalBounds, + ( + threadInfo: ResolvedThreadInfo | MinimallyEncodedResolvedThreadInfo, + canStartEditing: boolean, + navigate: ThreadSettingsNavigate, + routeKey: string, + numMembersShowing: number, + verticalBounds: ?VerticalBounds, + ) => { + const listData: ChatSettingsItem[] = []; + + const canAddMembers = threadHasPermission( + threadInfo, + threadPermissions.ADD_MEMBERS, + ); + if (threadInfo.members.length === 0 && !canAddMembers) { + return listData; + } - const numItems = Math.min(numSidebarsShowing, sidebars.length); - for (let i = 0; i < numItems; i++) { - const sidebarInfo = sidebars[i]; listData.push({ - itemType: 'childThread', - key: `childThread${sidebarInfo.id}`, - threadInfo: sidebarInfo, - firstListItem: i === 0, - lastListItem: i === numItems - 1 && numItems === sidebars.length, + itemType: 'header', + key: 'memberHeader', + title: 'Members', + categoryType: 'unpadded', }); - } - if (numItems < sidebars.length) { + if (canAddMembers) { + listData.push({ + itemType: 'addMember', + key: 'addMember', + }); + } + + const numItems = Math.min(numMembersShowing, threadInfo.members.length); + for (let i = 0; i < numItems; i++) { + const memberInfo = threadInfo.members[i]; + listData.push({ + itemType: 'member', + key: `member${memberInfo.id}`, + memberInfo, + threadInfo, + canEdit: canStartEditing, + navigate, + firstListItem: i === 0 && !canAddMembers, + lastListItem: + i === numItems - 1 && numItems === threadInfo.members.length, + verticalBounds, + threadSettingsRouteKey: routeKey, + }); + } + + if (numItems < threadInfo.members.length) { + listData.push({ + itemType: 'seeMore', + key: 'seeMoreMembers', + onPress: this.onPressSeeMoreMembers, + }); + } + listData.push({ - itemType: 'seeMore', - key: 'seeMoreSidebars', - onPress: this.onPressSeeMoreSidebars, + itemType: 'footer', + key: 'memberFooter', + categoryType: 'unpadded', }); - } - listData.push({ - itemType: 'footer', - key: 'sidebarFooter', - categoryType: 'unpadded', - }); - - return listData; - }, - ); - - threadMembersListDataSelector = createSelector( - (propsAndState: PropsAndState) => propsAndState.threadInfo, - (propsAndState: PropsAndState) => !propsAndState.somethingIsSaving, - (propsAndState: PropsAndState) => propsAndState.navigation.navigate, - (propsAndState: PropsAndState) => propsAndState.route.key, - (propsAndState: PropsAndState) => propsAndState.numMembersShowing, - (propsAndState: PropsAndState) => propsAndState.verticalBounds, - ( - threadInfo: ResolvedThreadInfo | MinimallyEncodedResolvedThreadInfo, - canStartEditing: boolean, - navigate: ThreadSettingsNavigate, - routeKey: string, - numMembersShowing: number, - verticalBounds: ?VerticalBounds, - ) => { - const listData: ChatSettingsItem[] = []; - - const canAddMembers = threadHasPermission( - threadInfo, - threadPermissions.ADD_MEMBERS, - ); - if (threadInfo.members.length === 0 && !canAddMembers) { return listData; - } + }, + ); - listData.push({ - itemType: 'header', - key: 'memberHeader', - title: 'Members', - categoryType: 'unpadded', - }); + mediaGalleryListDataSelector: PropsAndState => $ReadOnlyArray<ChatSettingsItem> = + createSelector( + (propsAndState: PropsAndState) => propsAndState.threadInfo, + (propsAndState: PropsAndState) => propsAndState.verticalBounds, + ( + threadInfo: ThreadInfo | MinimallyEncodedThreadInfo, + verticalBounds: ?VerticalBounds, + ) => { + const listData: ChatSettingsItem[] = []; + const limit = 6; - if (canAddMembers) { listData.push({ - itemType: 'addMember', - key: 'addMember', + itemType: 'actionHeader', + key: 'mediaGalleryHeader', + title: 'Media Gallery', + actionText: 'See more', + onPress: this.onPressSeeMoreMediaGallery, }); - } - const numItems = Math.min(numMembersShowing, threadInfo.members.length); - for (let i = 0; i < numItems; i++) { - const memberInfo = threadInfo.members[i]; listData.push({ - itemType: 'member', - key: `member${memberInfo.id}`, - memberInfo, + itemType: 'mediaGallery', + key: 'mediaGallery', threadInfo, - canEdit: canStartEditing, - navigate, - firstListItem: i === 0 && !canAddMembers, - lastListItem: - i === numItems - 1 && numItems === threadInfo.members.length, + limit, verticalBounds, - threadSettingsRouteKey: routeKey, }); - } - if (numItems < threadInfo.members.length) { listData.push({ - itemType: 'seeMore', - key: 'seeMoreMembers', - onPress: this.onPressSeeMoreMembers, + itemType: 'footer', + key: 'mediaGalleryFooter', + categoryType: 'outline', }); - } - listData.push({ - itemType: 'footer', - key: 'memberFooter', - categoryType: 'unpadded', - }); - - return listData; - }, - ); - - mediaGalleryListDataSelector = createSelector( - (propsAndState: PropsAndState) => propsAndState.threadInfo, - (propsAndState: PropsAndState) => propsAndState.verticalBounds, - ( - threadInfo: ThreadInfo | MinimallyEncodedThreadInfo, - verticalBounds: ?VerticalBounds, - ) => { - const listData: ChatSettingsItem[] = []; - const limit = 6; - - listData.push({ - itemType: 'actionHeader', - key: 'mediaGalleryHeader', - title: 'Media Gallery', - actionText: 'See more', - onPress: this.onPressSeeMoreMediaGallery, - }); - - listData.push({ - itemType: 'mediaGallery', - key: 'mediaGallery', - threadInfo, - limit, - verticalBounds, - }); - - listData.push({ - itemType: 'footer', - key: 'mediaGalleryFooter', - categoryType: 'outline', - }); - - return listData; - }, - ); - - actionsListDataSelector = createSelector( - (propsAndState: PropsAndState) => propsAndState.threadInfo, - (propsAndState: PropsAndState) => propsAndState.parentThreadInfo, - (propsAndState: PropsAndState) => propsAndState.navigation.navigate, - (propsAndState: PropsAndState) => propsAndState.styles, - (propsAndState: PropsAndState) => propsAndState.userInfos, - (propsAndState: PropsAndState) => propsAndState.viewerID, - ( - threadInfo: ResolvedThreadInfo | MinimallyEncodedResolvedThreadInfo, - parentThreadInfo: - | ?ResolvedThreadInfo - | ?MinimallyEncodedResolvedThreadInfo, - navigate: ThreadSettingsNavigate, - styles: typeof unboundStyles, - userInfos: UserInfos, - viewerID: ?string, - ) => { - const buttons = []; - - if (this.props.canPromoteSidebar) { - buttons.push({ - itemType: 'promoteSidebar', - key: 'promoteSidebar', - threadInfo, - navigate, - }); - } + return listData; + }, + ); - const canLeaveThread = threadHasPermission( - threadInfo, - threadPermissions.LEAVE_THREAD, - ); + actionsListDataSelector: PropsAndState => $ReadOnlyArray<ChatSettingsItem> = + createSelector( + (propsAndState: PropsAndState) => propsAndState.threadInfo, + (propsAndState: PropsAndState) => propsAndState.parentThreadInfo, + (propsAndState: PropsAndState) => propsAndState.navigation.navigate, + (propsAndState: PropsAndState) => propsAndState.styles, + (propsAndState: PropsAndState) => propsAndState.userInfos, + (propsAndState: PropsAndState) => propsAndState.viewerID, + ( + threadInfo: ResolvedThreadInfo | MinimallyEncodedResolvedThreadInfo, + parentThreadInfo: + | ?ResolvedThreadInfo + | ?MinimallyEncodedResolvedThreadInfo, + navigate: ThreadSettingsNavigate, + styles: typeof unboundStyles, + userInfos: UserInfos, + viewerID: ?string, + ) => { + const buttons = []; + + if (this.props.canPromoteSidebar) { + buttons.push({ + itemType: 'promoteSidebar', + key: 'promoteSidebar', + threadInfo, + navigate, + }); + } - if (viewerIsMember(threadInfo) && canLeaveThread) { - buttons.push({ - itemType: 'leaveThread', - key: 'leaveThread', + const canLeaveThread = threadHasPermission( threadInfo, - navigate, - }); - } + threadPermissions.LEAVE_THREAD, + ); - const canDeleteThread = threadHasPermission( - threadInfo, - threadPermissions.DELETE_THREAD, - ); - if (canDeleteThread) { - buttons.push({ - itemType: 'deleteThread', - key: 'deleteThread', + if (viewerIsMember(threadInfo) && canLeaveThread) { + buttons.push({ + itemType: 'leaveThread', + key: 'leaveThread', + threadInfo, + navigate, + }); + } + + const canDeleteThread = threadHasPermission( threadInfo, - navigate, - }); - } + threadPermissions.DELETE_THREAD, + ); + if (canDeleteThread) { + buttons.push({ + itemType: 'deleteThread', + key: 'deleteThread', + threadInfo, + navigate, + }); + } - const threadIsPersonal = threadInfo.type === threadTypes.PERSONAL; - if (threadIsPersonal && viewerID) { - const otherMemberID = getSingleOtherUser(threadInfo, viewerID); - if (otherMemberID) { - const otherUserInfo = userInfos[otherMemberID]; - const availableRelationshipActions = - getAvailableRelationshipButtons(otherUserInfo); - - for (const action of availableRelationshipActions) { - buttons.push({ - itemType: 'editRelationship', - key: action, - threadInfo, - navigate, - relationshipButton: action, + const threadIsPersonal = threadInfo.type === threadTypes.PERSONAL; + if (threadIsPersonal && viewerID) { + const otherMemberID = getSingleOtherUser(threadInfo, viewerID); + if (otherMemberID) { + const otherUserInfo = userInfos[otherMemberID]; + const availableRelationshipActions = + getAvailableRelationshipButtons(otherUserInfo); + + for (const action of availableRelationshipActions) { + buttons.push({ + itemType: 'editRelationship', + key: action, + threadInfo, + navigate, + relationshipButton: action, + }); + } + } + } + + const listData: ChatSettingsItem[] = []; + if (buttons.length === 0) { + return listData; + } + + listData.push({ + itemType: 'header', + key: 'actionsHeader', + title: 'Actions', + categoryType: 'unpadded', + }); + for (let i = 0; i < buttons.length; i++) { + // Necessary for Flow... + if (buttons[i].itemType === 'editRelationship') { + listData.push({ + ...buttons[i], + buttonStyle: [ + i === 0 ? null : styles.nonTopButton, + i === buttons.length - 1 ? styles.lastButton : null, + ], + }); + } else { + listData.push({ + ...buttons[i], + buttonStyle: [ + i === 0 ? null : styles.nonTopButton, + i === buttons.length - 1 ? styles.lastButton : null, + ], }); } } - } + listData.push({ + itemType: 'footer', + key: 'actionsFooter', + categoryType: 'unpadded', + }); - const listData: ChatSettingsItem[] = []; - if (buttons.length === 0) { return listData; - } + }, + ); - listData.push({ - itemType: 'header', - key: 'actionsHeader', - title: 'Actions', - categoryType: 'unpadded', - }); - for (let i = 0; i < buttons.length; i++) { - // Necessary for Flow... - if (buttons[i].itemType === 'editRelationship') { - listData.push({ - ...buttons[i], - buttonStyle: [ - i === 0 ? null : styles.nonTopButton, - i === buttons.length - 1 ? styles.lastButton : null, - ], - }); - } else { - listData.push({ - ...buttons[i], - buttonStyle: [ - i === 0 ? null : styles.nonTopButton, - i === buttons.length - 1 ? styles.lastButton : null, - ], - }); - } - } - listData.push({ - itemType: 'footer', - key: 'actionsFooter', - categoryType: 'unpadded', - }); - - return listData; - }, - ); - - listDataSelector = createSelector( - this.threadBasicsListDataSelector, - this.subchannelsListDataSelector, - this.sidebarsListDataSelector, - this.threadMembersListDataSelector, - this.mediaGalleryListDataSelector, - this.actionsListDataSelector, - ( - threadBasicsListData: ChatSettingsItem[], - subchannelsListData: ChatSettingsItem[], - sidebarsListData: ChatSettingsItem[], - threadMembersListData: ChatSettingsItem[], - mediaGalleryListData: ChatSettingsItem[], - actionsListData: ChatSettingsItem[], - ) => [ - ...threadBasicsListData, - ...subchannelsListData, - ...sidebarsListData, - ...threadMembersListData, - ...mediaGalleryListData, - ...actionsListData, - ], - ); - - get listData() { + listDataSelector: PropsAndState => $ReadOnlyArray<ChatSettingsItem> = + createSelector( + this.threadBasicsListDataSelector, + this.subchannelsListDataSelector, + this.sidebarsListDataSelector, + this.threadMembersListDataSelector, + this.mediaGalleryListDataSelector, + this.actionsListDataSelector, + ( + threadBasicsListData: $ReadOnlyArray<ChatSettingsItem>, + subchannelsListData: $ReadOnlyArray<ChatSettingsItem>, + sidebarsListData: $ReadOnlyArray<ChatSettingsItem>, + threadMembersListData: $ReadOnlyArray<ChatSettingsItem>, + mediaGalleryListData: $ReadOnlyArray<ChatSettingsItem>, + actionsListData: $ReadOnlyArray<ChatSettingsItem>, + ) => [ + ...threadBasicsListData, + ...subchannelsListData, + ...sidebarsListData, + ...threadMembersListData, + ...mediaGalleryListData, + ...actionsListData, + ], + ); + + get listData(): $ReadOnlyArray<ChatSettingsItem> { return this.listDataSelector({ ...this.props, ...this.state }); } - render() { + render(): React.Node { return ( <View style={this.props.styles.container} @@ -933,7 +942,7 @@ // ESLint doesn't recognize that invariant always throws // eslint-disable-next-line consistent-return - renderItem = (row: { item: ChatSettingsItem, ... }) => { + renderItem = (row: { +item: ChatSettingsItem, ... }): React.Node => { const item = row.item; if (item.itemType === 'header') { return ( diff --git a/native/chat/text-message-tooltip-button.react.js b/native/chat/text-message-tooltip-button.react.js --- a/native/chat/text-message-tooltip-button.react.js +++ b/native/chat/text-message-tooltip-button.react.js @@ -20,6 +20,7 @@ import SidebarInputBarHeightMeasurer from './sidebar-input-bar-height-measurer.react.js'; import { useAnimatedMessageTooltipButton } from './utils.js'; import EmojiKeyboard from '../components/emoji-keyboard.react.js'; +import type { EmojiSelection } from '../components/emoji-keyboard.react.js'; import type { AppNavigationProp } from '../navigation/app-navigator.react.js'; import { useSelector } from '../redux/redux-utils.js'; import { useTooltipActions } from '../tooltip/tooltip-hooks.js'; @@ -144,7 +145,7 @@ const { dismissTooltip } = useTooltipActions(navigation, tooltipRouteKey); const onEmojiSelected = React.useCallback( - emoji => { + (emoji: EmojiSelection) => { sendReaction(emoji.emoji); dismissTooltip(); }, diff --git a/native/chat/text-message-tooltip-modal.react.js b/native/chat/text-message-tooltip-modal.react.js --- a/native/chat/text-message-tooltip-modal.react.js +++ b/native/chat/text-message-tooltip-modal.react.js @@ -23,6 +23,7 @@ type TooltipMenuProps, } from '../tooltip/tooltip.react.js'; import type { ChatTextMessageInfoItemWithHeight } from '../types/chat-types.js'; +import type { TextStyle } from '../types/styles.js'; import { exitEditAlert } from '../utils/edit-messages-utils.js'; import { useNavigateToPinModal } from '../utils/toggle-pin-utils.js'; @@ -55,13 +56,13 @@ }); }, [inputState, navigateToThread, threadInfo, text]); const renderReplyIcon = React.useCallback( - style => <CommIcon name="reply" style={style} size={12} />, + (style: TextStyle) => <CommIcon name="reply" style={style} size={12} />, [], ); const onPressSidebar = useAnimatedNavigateToSidebar(route.params.item); const renderSidebarIcon = React.useCallback( - style => ( + (style: TextStyle) => ( <SWMansionIcon name="message-circle-lines" style={style} size={16} /> ), [], @@ -96,18 +97,24 @@ } }, [inputState, messageEditingContext, messageInfo, text]); const renderEditIcon = React.useCallback( - style => <SWMansionIcon name="edit-1" style={style} size={16} />, + (style: TextStyle) => ( + <SWMansionIcon name="edit-1" style={style} size={16} /> + ), [], ); const onPressTogglePin = useNavigateToPinModal(overlayContext, route); const renderPinIcon = React.useCallback( - style => <CommIcon name="pin-outline" style={style} size={16} />, + (style: TextStyle) => ( + <CommIcon name="pin-outline" style={style} size={16} /> + ), [], ); const renderUnpinIcon = React.useCallback( - style => <CommIcon name="unpin-outline" style={style} size={16} />, + (style: TextStyle) => ( + <CommIcon name="unpin-outline" style={style} size={16} /> + ), [], ); @@ -116,13 +123,15 @@ setTimeout(confirmCopy); }, [text]); const renderCopyIcon = React.useCallback( - style => <SWMansionIcon name="copy" style={style} size={16} />, + (style: TextStyle) => <SWMansionIcon name="copy" style={style} size={16} />, [], ); const onPressReport = useOnPressReport(route); const renderReportIcon = React.useCallback( - style => <SWMansionIcon name="warning-circle" style={style} size={16} />, + (style: TextStyle) => ( + <SWMansionIcon name="warning-circle" style={style} size={16} /> + ), [], ); diff --git a/native/chat/text-message.react.js b/native/chat/text-message.react.js --- a/native/chat/text-message.react.js +++ b/native/chat/text-message.react.js @@ -78,7 +78,7 @@ }; } - render() { + render(): React.Node { const { item, navigation, @@ -136,21 +136,21 @@ this.message = message; }; - canReply() { + canReply(): boolean { return threadHasPermission( this.props.item.threadInfo, threadPermissions.VOICED, ); } - canNavigateToSidebar() { + canNavigateToSidebar(): boolean { return ( - this.props.item.threadCreatedFromMessage || + !!this.props.item.threadCreatedFromMessage || this.props.canCreateSidebarFromMessage ); } - visibleEntryIDs() { + visibleEntryIDs(): $ReadOnlyArray<string> { const result = ['copy']; if (this.canReply()) { diff --git a/native/chat/thread-settings-button.react.js b/native/chat/thread-settings-button.react.js --- a/native/chat/thread-settings-button.react.js +++ b/native/chat/thread-settings-button.react.js @@ -28,7 +28,7 @@ }; class ThreadSettingsButton extends React.PureComponent<Props> { - render() { + render(): React.Node { return ( <Button onPress={this.onPress} androidBorderlessRipple={true}> <SWMansionIcon diff --git a/native/community-creation/community-configuration.react.js b/native/community-creation/community-configuration.react.js --- a/native/community-creation/community-configuration.react.js +++ b/native/community-creation/community-configuration.react.js @@ -61,7 +61,7 @@ const [announcementSetting, setAnnouncementSetting] = React.useState(false); const [errorMessage, setErrorMessage] = React.useState<?string>(); - const onChangePendingCommunityName = React.useCallback(newValue => { + const onChangePendingCommunityName = React.useCallback((newValue: string) => { setErrorMessage(); setPendingCommunityName(newValue); }, []); diff --git a/native/components/emoji-keyboard.react.js b/native/components/emoji-keyboard.react.js --- a/native/components/emoji-keyboard.react.js +++ b/native/components/emoji-keyboard.react.js @@ -31,7 +31,7 @@ return JSON.parse(recentlyUsedEmojis ?? '[]'); }; -const onStateChangeCallback = nextRecentlyUsedEmojis => +const onStateChangeCallback = (nextRecentlyUsedEmojis: $ReadOnlyArray<mixed>) => AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(nextRecentlyUsedEmojis)); const useRecentPicksPersistenceArgs = { diff --git a/native/components/full-screen-view-modal.react.js b/native/components/full-screen-view-modal.react.js --- a/native/components/full-screen-view-modal.react.js +++ b/native/components/full-screen-view-modal.react.js @@ -40,7 +40,7 @@ derivedDimensionsInfoSelector, } from '../selectors/dimensions-selectors.js'; import type { NativeMethods } from '../types/react-native.js'; -import type { AnimatedStyleObj } from '../types/styles.js'; +import type { AnimatedViewStyle, ViewStyle } from '../types/styles.js'; import type { UserProfileBottomSheetNavigationProp } from '../user-profile/user-profile-bottom-sheet-navigator.react.js'; import { clamp, @@ -175,16 +175,16 @@ closeButton: ?React.ElementRef<TouchableOpacityInstance>; mediaIconsContainer: ?React.ElementRef<typeof View>; - closeButtonX = new Value(-1); - closeButtonY = new Value(-1); - closeButtonWidth = new Value(0); - closeButtonHeight = new Value(0); - closeButtonLastState = new Value(1); - mediaIconsX = new Value(-1); - mediaIconsY = new Value(-1); - mediaIconsWidth = new Value(0); - mediaIconsHeight = new Value(0); - actionLinksLastState = new Value(1); + closeButtonX: Value = new Value(-1); + closeButtonY: Value = new Value(-1); + closeButtonWidth: Value = new Value(0); + closeButtonHeight: Value = new Value(0); + closeButtonLastState: Value = new Value(1); + mediaIconsX: Value = new Value(-1); + mediaIconsY: Value = new Value(-1); + mediaIconsWidth: Value = new Value(0); + mediaIconsHeight: Value = new Value(0); + actionLinksLastState: Value = new Value(1); centerX: Value; centerY: Value; @@ -959,7 +959,7 @@ return { width, height: safeAreaHeight }; } - get contentViewContainerStyle(): AnimatedStyleObj { + get contentViewContainerStyle(): AnimatedViewStyle { const { height, width } = this.props.contentDimensions; const { height: frameHeight, width: frameWidth } = this.frame; const top = (frameHeight - height) / 2 + this.props.dimensions.topInset; @@ -979,13 +979,13 @@ }; } - static isActive(props) { + static isActive(props: Props): boolean { const { overlayContext } = props; invariant(overlayContext, 'FullScreenViewModal should have OverlayContext'); return !overlayContext.isDismissing; } - get contentContainerStyle() { + get contentContainerStyle(): ViewStyle { const { verticalBounds } = this.props.route.params; const fullScreenHeight = this.props.dimensions.height; const top = verticalBounds.y; @@ -998,7 +998,7 @@ return [styles.contentContainer, verticalStyle]; } - render() { + render(): React.Node { const { children, saveContentCallback, copyContentCallback } = this.props; const statusBar = FullScreenViewModal.isActive(this.props) ? ( diff --git a/native/components/keyboard-avoiding-view.react.js b/native/components/keyboard-avoiding-view.react.js --- a/native/components/keyboard-avoiding-view.react.js +++ b/native/components/keyboard-avoiding-view.react.js @@ -120,7 +120,7 @@ } }; - get relativeKeyboardHeight() { + get relativeKeyboardHeight(): number { const { viewFrame, keyboardFrame } = this; if (!viewFrame || !keyboardFrame) { return 0; @@ -145,7 +145,7 @@ // ESLint doesn't recognize that invariant always throws // eslint-disable-next-line consistent-return - render() { + render(): React.Node { const { behavior, children, diff --git a/native/components/link-button.react.js b/native/components/link-button.react.js --- a/native/components/link-button.react.js +++ b/native/components/link-button.react.js @@ -29,7 +29,7 @@ +styles: typeof unboundStyles, }; class LinkButton extends React.PureComponent<Props> { - render() { + render(): React.Node { const disabledStyle = this.props.disabled ? this.props.styles.disabled : null; diff --git a/native/components/node-height-measurer.react.js b/native/components/node-height-measurer.react.js --- a/native/components/node-height-measurer.react.js +++ b/native/components/node-height-measurer.react.js @@ -495,7 +495,7 @@ const dummies = currentlyMeasuring.map(({ measureKey, dummy }) => { const { children } = dummy.props; const style = [dummy.props.style, styles.dummy]; - const onLayout = event => + const onLayout = (event: LayoutEvent) => this.onDummyLayout(measureKey, iteration, event); const node = React.cloneElement(dummy, { style, diff --git a/native/components/selectable-text-input.react.ios.js b/native/components/selectable-text-input.react.ios.js --- a/native/components/selectable-text-input.react.ios.js +++ b/native/components/selectable-text-input.react.ios.js @@ -15,7 +15,7 @@ import type { SelectionChangeEvent } from '../types/react-native.js'; const SelectableTextInput = React.forwardRef(function BaseSelectableTextInput( - props, + props: SelectableTextInputProps, ref: ReactRefSetter<SelectableTextInputRef>, ): React.Node { const { diff --git a/native/components/selectable-text-input.react.js b/native/components/selectable-text-input.react.js --- a/native/components/selectable-text-input.react.js +++ b/native/components/selectable-text-input.react.js @@ -13,7 +13,7 @@ import type { SelectionChangeEvent } from '../types/react-native.js'; const SelectableTextInput = React.forwardRef(function BaseSelectableTextInput( - props, + props: SelectableTextInputProps, ref: ReactRefSetter<SelectableTextInputRef>, ): React.Node { const { diff --git a/native/components/tag-input.react.js b/native/components/tag-input.react.js --- a/native/components/tag-input.react.js +++ b/native/components/tag-input.react.js @@ -377,7 +377,7 @@ } } - render() { + render(): React.Node { return ( <TouchableOpacity onPress={this.onPress} diff --git a/native/components/thread-list-thread.react.js b/native/components/thread-list-thread.react.js --- a/native/components/thread-list-thread.react.js +++ b/native/components/thread-list-thread.react.js @@ -43,7 +43,7 @@ +styles: typeof unboundStyles, }; class ThreadListThread extends React.PureComponent<Props> { - render() { + render(): React.Node { const { modalIosHighlightUnderlay: underlayColor } = this.props.colors; return ( diff --git a/native/components/thread-list.react.js b/native/components/thread-list.react.js --- a/native/components/thread-list.react.js +++ b/native/components/thread-list.react.js @@ -50,29 +50,30 @@ }; textInput: ?React.ElementRef<typeof TextInput>; - listDataSelector = createSelector( - (propsAndState: PropsAndState) => propsAndState.threadInfos, - (propsAndState: PropsAndState) => propsAndState.searchText, - (propsAndState: PropsAndState) => propsAndState.searchResults, - (propsAndState: PropsAndState) => propsAndState.itemStyle, - (propsAndState: PropsAndState) => propsAndState.itemTextStyle, - ( - threadInfos: $ReadOnlyArray<ThreadInfo>, - text: string, - searchResults: Set<string>, - ) => - text - ? threadInfos.filter(threadInfo => searchResults.has(threadInfo.id)) - : // We spread to make sure the result of this selector updates when - // any input param (namely itemStyle or itemTextStyle) changes - [...threadInfos], - ); - - get listData() { + listDataSelector: PropsAndState => $ReadOnlyArray<ThreadInfo> = + createSelector( + (propsAndState: PropsAndState) => propsAndState.threadInfos, + (propsAndState: PropsAndState) => propsAndState.searchText, + (propsAndState: PropsAndState) => propsAndState.searchResults, + (propsAndState: PropsAndState) => propsAndState.itemStyle, + (propsAndState: PropsAndState) => propsAndState.itemTextStyle, + ( + threadInfos: $ReadOnlyArray<ThreadInfo>, + text: string, + searchResults: Set<string>, + ): $ReadOnlyArray<ThreadInfo> => + text + ? threadInfos.filter(threadInfo => searchResults.has(threadInfo.id)) + : // We spread to make sure the result of this selector updates when + // any input param (namely itemStyle or itemTextStyle) changes + [...threadInfos], + ); + + get listData(): $ReadOnlyArray<ThreadInfo> { return this.listDataSelector({ ...this.props, ...this.state }); } - render() { + render(): React.Node { let searchBar = null; if (this.props.searchIndex) { searchBar = ( @@ -103,11 +104,11 @@ static keyExtractor = ( threadInfo: ThreadInfo | MinimallyEncodedThreadInfo, - ) => { + ): string => { return threadInfo.id; }; - renderItem = (row: { item: ThreadInfo, ... }) => { + renderItem = (row: { +item: ThreadInfo, ... }): React.Node => { return ( <ThreadListThread threadInfo={row.item} @@ -121,7 +122,7 @@ static getItemLayout = ( data: ?$ReadOnlyArray<ThreadInfo | MinimallyEncodedThreadInfo>, index: number, - ) => { + ): { length: number, offset: number, index: number } => { return { length: 24, offset: 24 * index, index }; }; diff --git a/native/components/user-list-user.react.js b/native/components/user-list-user.react.js --- a/native/components/user-list-user.react.js +++ b/native/components/user-list-user.react.js @@ -49,7 +49,7 @@ +styles: typeof unboundStyles, }; class UserListUser extends React.PureComponent<Props> { - render() { + render(): React.Node { const { userInfo } = this.props; let notice = null; if (userInfo.notice) { diff --git a/native/components/user-list.react.js b/native/components/user-list.react.js --- a/native/components/user-list.react.js +++ b/native/components/user-list.react.js @@ -21,7 +21,7 @@ +indicatorStyle: IndicatorStyle, }; class UserList extends React.PureComponent<Props> { - render() { + render(): React.Node { return ( <FlatList data={this.props.userInfos} @@ -36,11 +36,11 @@ ); } - static keyExtractor = (userInfo: UserListItem) => { + static keyExtractor = (userInfo: UserListItem): string => { return userInfo.id; }; - renderItem = (row: { item: UserListItem, ... }) => { + renderItem = (row: { +item: UserListItem, ... }): React.Node => { return ( <UserListUser userInfo={row.item} @@ -53,7 +53,7 @@ static getItemLayout = ( data: ?$ReadOnlyArray<UserListItem>, index: number, - ) => { + ): { length: number, offset: number, index: number } => { if (!data) { return { length: 0, offset: 0, index }; } diff --git a/native/crash.react.js b/native/crash.react.js --- a/native/crash.react.js +++ b/native/crash.react.js @@ -65,9 +65,9 @@ +doneWaiting: boolean, }; class Crash extends React.PureComponent<Props, State> { - errorTitle = _shuffle(errorTitles)[0]; + errorTitle: string = _shuffle(errorTitles)[0]; - constructor(props) { + constructor(props: Props) { super(props); this.state = { errorReportID: null, @@ -89,7 +89,7 @@ this.setState({ doneWaiting: true }); } - render() { + render(): React.Node { const errorText = [...this.props.errorData] .reverse() .map(errorData => errorData.error.message) diff --git a/native/input/input-state-container.react.js b/native/input/input-state-container.react.js --- a/native/input/input-state-container.react.js +++ b/native/input/input-state-container.react.js @@ -107,6 +107,7 @@ type PendingMultimediaUploads, type MultimediaProcessingStep, type MessagePendingUploads, + type InputState, } from './input-state.js'; import { encryptMedia } from '../media/encryption-utils.js'; import { disposeTempFile } from '../media/file-utils.js'; @@ -128,6 +129,7 @@ [localMessageID: string]: ?$ReadOnlySet<string>, }; type CompletedUploads = $ReadOnly<WritableCompletedUploads>; +type ActiveURI = { +count: number, +onClear: $ReadOnlyArray<() => mixed> }; type BaseProps = { +children: React.Node, @@ -165,16 +167,16 @@ pendingUploads: {}, }; sendCallbacks: Array<() => void> = []; - activeURIs = new Map(); + activeURIs: Map<string, ActiveURI> = new Map(); editInputBarCallbacks: Array< (params: EditInputBarMessageParameters) => void, > = []; scrollToMessageCallbacks: Array<(messageID: string) => void> = []; - pendingThreadCreations = new Map<string, Promise<string>>(); - pendingThreadUpdateHandlers = new Map< + pendingThreadCreations: Map<string, Promise<string>> = new Map(); + pendingThreadUpdateHandlers: Map< string, (ThreadInfo | MinimallyEncodedThreadInfo) => mixed, - >(); + > = new Map(); // TODO: flip the switch // Note that this enables Blob service for encrypted media only useBlobServiceUploads = false; @@ -183,7 +185,7 @@ // sidebar, the sidebar gets created right away, but the message needs to wait // for the uploads to complete before sending. We use this Set to track the // message localIDs that need sidebarCreation: true. - pendingSidebarCreationMessageLocalIDs = new Set<string>(); + pendingSidebarCreationMessageLocalIDs: Set<string> = new Set(); static getCompletedUploads(props: Props, state: State): CompletedUploads { const completedUploads: WritableCompletedUploads = {}; @@ -297,7 +299,9 @@ } } - async dispatchMultimediaMessageAction(messageInfo: RawMultimediaMessageInfo) { + async dispatchMultimediaMessageAction( + messageInfo: RawMultimediaMessageInfo, + ): Promise<void> { if (!threadIsPending(messageInfo.threadID)) { this.props.dispatchActionPromise( sendMultimediaMessageActionTypes, @@ -394,7 +398,7 @@ } } - inputStateSelector = createSelector( + inputStateSelector: State => InputState = createSelector( (state: State) => state.pendingUploads, (pendingUploads: PendingMultimediaUploads) => ({ pendingUploads, @@ -432,7 +436,7 @@ ); }; - uploadInProgress = () => { + uploadInProgress = (): boolean => { if (this.props.ongoingMessageCreation) { return true; } @@ -1305,7 +1309,7 @@ }); } - messageHasUploadFailure = (localMessageID: string) => { + messageHasUploadFailure = (localMessageID: string): boolean => { const pendingUploads = this.state.pendingUploads[localMessageID]; if (!pendingUploads) { return false; @@ -1352,7 +1356,7 @@ rawMessageInfo: RawMultimediaMessageInfo, localMessageID: string, threadInfo: ThreadInfo | MinimallyEncodedThreadInfo, - ) => { + ): Promise<void> => { const pendingUploads = this.state.pendingUploads[localMessageID] ?? {}; const now = Date.now(); @@ -1615,7 +1619,10 @@ } }; - waitForCaptureURIUnload(uri: string) { + waitForCaptureURIUnload(uri: string): Promise<{ + +steps: $ReadOnlyArray<MediaMissionStep>, + +result: ?string, + }> { const start = Date.now(); const path = pathFromURI(uri); if (!path) { @@ -1675,7 +1682,7 @@ } }; - render() { + render(): React.Node { const inputState = this.inputStateSelector(this.state); return ( <InputStateContext.Provider value={inputState}> diff --git a/native/keyboard/keyboard-input-host.react.js b/native/keyboard/keyboard-input-host.react.js --- a/native/keyboard/keyboard-input-host.react.js +++ b/native/keyboard/keyboard-input-host.react.js @@ -46,12 +46,12 @@ } } - static mediaGalleryOpen(props: Props) { + static mediaGalleryOpen(props: Props): boolean { const { keyboardState } = props; return !!(keyboardState && keyboardState.mediaGalleryOpen); } - render() { + render(): React.Node { const kbComponent = KeyboardInputHost.mediaGalleryOpen(this.props) ? mediaGalleryKeyboardName : null; diff --git a/native/media/camera-modal.react.js b/native/media/camera-modal.react.js --- a/native/media/camera-modal.react.js +++ b/native/media/camera-modal.react.js @@ -19,17 +19,21 @@ PinchGestureHandler, TapGestureHandler, State as GestureState, + type PinchGestureEvent, + type TapGestureEvent, } from 'react-native-gesture-handler'; import Orientation from 'react-native-orientation-locker'; import type { Orientations } from 'react-native-orientation-locker'; import Reanimated, { EasingNode as ReanimatedEasing, + type EventResult, } from 'react-native-reanimated'; import { SafeAreaView } from 'react-native-safe-area-context'; import { pathFromURI, filenameFromPathOrURI } from 'lib/media/file-utils.js'; import { useIsAppForegrounded } from 'lib/shared/lifecycle-utils.js'; import type { PhotoCapture } from 'lib/types/media-types.js'; +import type { ReactRef } from 'lib/types/react-types.js'; import type { Dispatch } from 'lib/types/redux-types.js'; import { useDispatch } from 'lib/utils/redux-utils.js'; @@ -219,6 +223,8 @@ ]); } +type RNCameraStatus = 'READY' | 'PENDING_AUTHORIZATION' | 'NOT_AUTHORIZED'; + type TouchableOpacityInstance = React.AbstractComponent< React.ElementConfig<typeof TouchableOpacity>, NativeMethods, @@ -260,47 +266,47 @@ class CameraModal extends React.PureComponent<Props, State> { camera: ?RNCamera; - pinchEvent; - pinchHandler = React.createRef(); - tapEvent; - tapHandler = React.createRef(); + pinchEvent: EventResult<PinchGestureEvent>; + pinchHandler: ReactRef<PinchGestureHandler> = React.createRef(); + tapEvent: EventResult<TapGestureEvent>; + tapHandler: ReactRef<TapGestureHandler> = React.createRef(); animationCode: Node; closeButton: ?React.ElementRef<TouchableOpacityInstance>; - closeButtonX = new Value(-1); - closeButtonY = new Value(-1); - closeButtonWidth = new Value(0); - closeButtonHeight = new Value(0); + closeButtonX: Value = new Value(-1); + closeButtonY: Value = new Value(-1); + closeButtonWidth: Value = new Value(0); + closeButtonHeight: Value = new Value(0); photoButton: ?React.ElementRef<TouchableOpacityInstance>; - photoButtonX = new Value(-1); - photoButtonY = new Value(-1); - photoButtonWidth = new Value(0); - photoButtonHeight = new Value(0); + photoButtonX: Value = new Value(-1); + photoButtonY: Value = new Value(-1); + photoButtonWidth: Value = new Value(0); + photoButtonHeight: Value = new Value(0); switchCameraButton: ?React.ElementRef<TouchableOpacityInstance>; - switchCameraButtonX = new Value(-1); - switchCameraButtonY = new Value(-1); - switchCameraButtonWidth = new Value(0); - switchCameraButtonHeight = new Value(0); + switchCameraButtonX: Value = new Value(-1); + switchCameraButtonY: Value = new Value(-1); + switchCameraButtonWidth: Value = new Value(0); + switchCameraButtonHeight: Value = new Value(0); flashButton: ?React.ElementRef<TouchableOpacityInstance>; - flashButtonX = new Value(-1); - flashButtonY = new Value(-1); - flashButtonWidth = new Value(0); - flashButtonHeight = new Value(0); + flashButtonX: Value = new Value(-1); + flashButtonY: Value = new Value(-1); + flashButtonWidth: Value = new Value(0); + flashButtonHeight: Value = new Value(0); - focusIndicatorX = new Value(-1); - focusIndicatorY = new Value(-1); - focusIndicatorScale = new Value(0); - focusIndicatorOpacity = new Value(0); + focusIndicatorX: Value = new Value(-1); + focusIndicatorY: Value = new Value(-1); + focusIndicatorScale: Value = new Value(0); + focusIndicatorOpacity: Value = new Value(0); - cancelIndicatorAnimation = new Value(0); + cancelIndicatorAnimation: Value = new Value(0); cameraIDsFetched = false; - stagingModeProgress = new Value(0); - sendButtonProgress = new Animated.Value(0); + stagingModeProgress: Value = new Value(0); + sendButtonProgress: Animated.Value = new Animated.Value(0); sendButtonStyle: ViewStyle; overlayStyle: AnimatedViewStyle; @@ -502,7 +508,7 @@ ); } - static isActive(props) { + static isActive(props: Props): boolean { const { overlayContext } = props; if (!overlayContext) { return true; @@ -582,7 +588,7 @@ } catch (e) {} } - get containerStyle() { + get containerStyle(): AnimatedViewStyle { const { overlayContext } = this.props; if (!overlayContext) { return styles.container; @@ -593,7 +599,7 @@ }; } - get focusIndicatorStyle() { + get focusIndicatorStyle(): AnimatedViewStyle { return { ...styles.focusIndicator, opacity: this.focusIndicatorOpacity, @@ -605,7 +611,14 @@ }; } - renderCamera = ({ camera, status }) => { + renderCamera = ({ + camera, + status, + }: { + +camera: RNCamera & { +_cameraHandle?: mixed, ... }, + status: RNCameraStatus, + ... + }): React.Node => { if (camera && camera._cameraHandle) { this.fetchCameraIDs(camera); } @@ -630,7 +643,7 @@ ); }; - renderStagingView() { + renderStagingView(): React.Node { let image = null; const { pendingPhotoCapture } = this.state; if (pendingPhotoCapture) { @@ -663,7 +676,7 @@ ); } - renderCameraContent(status) { + renderCameraContent(status: RNCameraStatus): React.Node { if (status === 'PENDING_AUTHORIZATION') { return <ContentLoading fillType="flex" colors={colors.dark} />; } else if (status === 'NOT_AUTHORIZED') { @@ -746,7 +759,7 @@ ); } - render() { + render(): React.Node { const statusBar = CameraModal.isActive(this.props) ? ( <ConnectedStatusBar hidden /> ) : null; diff --git a/native/media/media-gallery-keyboard.react.js b/native/media/media-gallery-keyboard.react.js --- a/native/media/media-gallery-keyboard.react.js +++ b/native/media/media-gallery-keyboard.react.js @@ -128,7 +128,7 @@ fetchingPhotos = false; flatList: ?FlatList<MediaLibrarySelection>; viewableIndices: number[] = []; - queueModeProgress = new Animated.Value(0); + queueModeProgress: Animated.Value = new Animated.Value(0); sendButtonStyle: ViewStyle; mediaSelected = false; @@ -153,7 +153,7 @@ }; } - static getDerivedStateFromProps(props: Props) { + static getDerivedStateFromProps(props: Props): Partial<State> { // We keep this in state since we pass this.state as // FlatList's extraData prop return { dimensions: props.dimensions }; @@ -161,7 +161,7 @@ componentDidMount() { this.mounted = true; - return this.fetchPhotos(); + this.fetchPhotos(); } componentWillUnmount() { @@ -236,7 +236,7 @@ } } - guardedSetState(change) { + guardedSetState(change: Partial<State>) { if (this.mounted) { this.setState(change); } @@ -427,11 +427,11 @@ return granted; } - get queueModeActive() { + get queueModeActive(): boolean { return !!this.state.queuedMediaURIs; } - renderItem = (row: { item: MediaLibrarySelection, ... }) => { + renderItem = (row: { +item: MediaLibrarySelection, ... }): React.Node => { const { containerHeight, queuedMediaURIs } = this.state; invariant(containerHeight, 'should be set'); const { uri } = row.item; @@ -452,15 +452,15 @@ ); }; - ItemSeparator = () => { + ItemSeparator = (): React.Node => { return <View style={this.props.styles.separator} />; }; - static keyExtractor = (item: MediaLibrarySelection) => { + static keyExtractor = (item: MediaLibrarySelection): string => { return item.uri; }; - GalleryHeader = () => ( + GalleryHeader = (): React.Node => ( <View style={this.props.styles.galleryHeader}> <Text style={this.props.styles.galleryHeaderTitle}>Photos</Text> <Button @@ -474,7 +474,7 @@ </View> ); - render() { + render(): React.Node { let content; const { selections, error, containerHeight } = this.state; const bottomOffsetStyle: ViewStyle = { diff --git a/native/media/media-utils.js b/native/media/media-utils.js --- a/native/media/media-utils.js +++ b/native/media/media-utils.js @@ -66,7 +66,7 @@ reportPromise: Promise<$ReadOnlyArray<MediaMissionStep>>, } { let resolveResult; - const sendResult = result => { + const sendResult = (result: MediaMissionFailure | MediaResult) => { if (resolveResult) { resolveResult(result); } diff --git a/native/media/multimedia.react.js b/native/media/multimedia.react.js --- a/native/media/multimedia.react.js +++ b/native/media/multimedia.react.js @@ -42,7 +42,7 @@ +departingSource: ?Source, }; class Multimedia extends React.PureComponent<Props, State> { - static defaultProps = { + static defaultProps: Partial<Props> = { spinnerColor: 'black', }; @@ -55,7 +55,7 @@ }; } - get inputState() { + get inputState(): InputState { const { inputState } = this.props; invariant(inputState, 'inputState should be set in Multimedia'); return inputState; @@ -101,7 +101,7 @@ } } - render() { + render(): React.Node { const images = []; const { currentSource, departingSource } = this.state; if (departingSource) { @@ -117,7 +117,7 @@ source: Source, invisibleLoad?: boolean = false, triggerOnLoad?: boolean = true, - ) { + ): React.Node { const onLoadProp = triggerOnLoad ? this.onLoad : undefined; if (source.kind === 'encrypted') { return ( diff --git a/native/media/remote-image.react.js b/native/media/remote-image.react.js --- a/native/media/remote-image.react.js +++ b/native/media/remote-image.react.js @@ -45,7 +45,7 @@ } } - render() { + render(): React.Node { const { style, spinnerColor, invisibleLoad, uri, placeholder } = this.props; const source = { uri }; diff --git a/native/media/save-media.js b/native/media/save-media.js --- a/native/media/save-media.js +++ b/native/media/save-media.js @@ -136,7 +136,7 @@ reportPromise: Promise<$ReadOnlyArray<MediaMissionStep>>, } { let resolveResult; - const sendResult = result => { + const sendResult = (result: MediaMissionResult) => { if (resolveResult) { resolveResult(result); } diff --git a/native/media/video-playback-modal.react.js b/native/media/video-playback-modal.react.js --- a/native/media/video-playback-modal.react.js +++ b/native/media/video-playback-modal.react.js @@ -75,6 +75,12 @@ +item: ChatMultimediaMessageInfoItem, }; +type ReactNativeVideoOnProgressData = { + +currentTime: number, + +playableDuration: number, + +seekableDuration: number, +}; + type Props = { +navigation: AppNavigationProp<'VideoPlaybackModal'>, +route: NavigationRoute<'VideoPlaybackModal'>, @@ -197,7 +203,7 @@ const controlsShowing = useValue(1); const outsideButtons = React.useCallback( - (x, y) => + (x: Animated.Value, y: Animated.Value) => and( or( eq(controlsShowing, 0), @@ -586,13 +592,16 @@ videoRef.current.seek(0); }, []); - const progressCallback = React.useCallback(res => { - setTimeElapsed(formatDuration(res.currentTime)); - setTotalDuration(formatDuration(res.seekableDuration)); - setPercentElapsed( - Math.ceil((res.currentTime / res.seekableDuration) * 100), - ); - }, []); + const progressCallback = React.useCallback( + (res: ReactNativeVideoOnProgressData) => { + setTimeElapsed(formatDuration(res.currentTime)); + setTotalDuration(formatDuration(res.seekableDuration)); + setPercentElapsed( + Math.ceil((res.currentTime / res.seekableDuration) * 100), + ); + }, + [], + ); const readyForDisplayCallback = React.useCallback(() => { setSpinnerVisible(false); diff --git a/native/navigation/community-drawer-content.react.js b/native/navigation/community-drawer-content.react.js --- a/native/navigation/community-drawer-content.react.js +++ b/native/navigation/community-drawer-content.react.js @@ -31,6 +31,7 @@ import { flattenDrawerItemsData, filterOutThreadAndDescendantIDs, + type CommunityDrawerItemDataFlattened, } from '../utils/drawer-utils.react.js'; const maxDepth = 2; @@ -118,7 +119,12 @@ const navigateToThread = useNavigateToThread(); const renderItem = React.useCallback( - ({ item }) => { + ({ + item, + }: { + +item: CommunityDrawerItemDataFlattened, + ... + }): React.Node => { const isCommunity = threadTypeIsCommunityRoot(item.threadInfo.type); return ( <CommunityDrawerItem diff --git a/native/navigation/nav-selectors.js b/native/navigation/nav-selectors.js --- a/native/navigation/nav-selectors.js +++ b/native/navigation/nav-selectors.js @@ -46,7 +46,9 @@ import { useSelector } from '../redux/redux-utils.js'; import type { NavPlusRedux } from '../types/selector-types.js'; -const baseCreateIsForegroundSelector = (routeName: string) => +const baseCreateIsForegroundSelector: ( + routeName: string, +) => (context: ?NavContextType) => boolean = (routeName: string) => createSelector( (context: ?NavContextType) => context && context.state, (navigationState: ?PossiblyStaleNavigationState) => { @@ -73,7 +75,9 @@ }, [navContext]); } -const baseCreateActiveTabSelector = (routeName: string) => +const baseCreateActiveTabSelector: ( + routeName: string, +) => (context: ?NavContextType) => boolean = (routeName: string) => createSelector( (context: ?NavContextType) => context && context.state, (navigationState: ?PossiblyStaleNavigationState) => { diff --git a/native/navigation/orientation-handler.react.js b/native/navigation/orientation-handler.react.js --- a/native/navigation/orientation-handler.react.js +++ b/native/navigation/orientation-handler.react.js @@ -33,7 +33,7 @@ } } - updateOrientation = orientation => { + updateOrientation = (orientation: Orientations) => { if (orientation !== this.props.deviceOrientation) { this.props.dispatch({ type: updateDeviceOrientationActionType, @@ -42,7 +42,7 @@ } }; - render() { + render(): React.Node { return null; } } diff --git a/native/navigation/root-navigator.react.js b/native/navigation/root-navigator.react.js --- a/native/navigation/root-navigator.react.js +++ b/native/navigation/root-navigator.react.js @@ -10,6 +10,8 @@ StackNavigationHelpers, StackNavigationProp, StackRouterOptions, + RouteProp, + StackCardInterpolationProps, } from '@react-navigation/core'; import { createNavigatorFactory, @@ -93,7 +95,10 @@ React.useState(true); const mergedScreenOptions = React.useMemo(() => { if (typeof screenOptions === 'function') { - return input => ({ + return (input: { + +route: RouteProp<>, + +navigation: RootNavigationHelpers<>, + }) => ({ ...screenOptions(input), keyboardHandlingEnabled, }); @@ -152,7 +157,7 @@ }); const transitionPreset = { ...baseTransitionPreset, - cardStyleInterpolator: interpolatorProps => { + cardStyleInterpolator: (interpolatorProps: StackCardInterpolationProps) => { const baseCardStyleInterpolator = baseTransitionPreset.cardStyleInterpolator(interpolatorProps); const overlayOpacity = interpolatorProps.current.progress.interpolate({ diff --git a/native/navigation/tab-bar.react.js b/native/navigation/tab-bar.react.js --- a/native/navigation/tab-bar.react.js +++ b/native/navigation/tab-bar.react.js @@ -37,7 +37,7 @@ const prevKeyboardState = prevKeyboardStateRef.current; const setTabBar = React.useCallback( - toValue => { + (toValue: number) => { const keyboardIsShowing = keyboardState && keyboardState.keyboardShowing; const keyboardWasShowing = prevKeyboardState && prevKeyboardState.keyboardShowing; @@ -68,7 +68,7 @@ const reduxTabBarHeight = useSelector(state => state.dimensions.tabBarHeight); const dispatch = useDispatch(); const setReduxTabBarHeight = React.useCallback( - height => { + (height: number) => { if (height === reduxTabBarHeight) { return; } diff --git a/native/navigation/tab-navigator.react.js b/native/navigation/tab-navigator.react.js --- a/native/navigation/tab-navigator.react.js +++ b/native/navigation/tab-navigator.react.js @@ -46,14 +46,14 @@ const calendarTabOptions = { tabBarLabel: 'Calendar', // eslint-disable-next-line react/display-name - tabBarIcon: ({ color }) => ( + tabBarIcon: ({ color }: { +color: string, ... }) => ( <SWMansionIcon name="calendar" style={[styles.icon, { color }]} /> ), }; const getChatTabOptions = (badge: number) => ({ tabBarLabel: 'Inbox', // eslint-disable-next-line react/display-name - tabBarIcon: ({ color }) => ( + tabBarIcon: ({ color }: { +color: string, ... }) => ( <SWMansionIcon name="message-square" style={[styles.icon, { color }]} /> ), tabBarBadge: badge ? badge : undefined, @@ -61,14 +61,14 @@ const profileTabOptions = { tabBarLabel: 'Profile', // eslint-disable-next-line react/display-name - tabBarIcon: ({ color }) => ( + tabBarIcon: ({ color }: { +color: string, ... }) => ( <SWMansionIcon name="user-2" style={[styles.icon, { color }]} /> ), }; const appsTabOptions = { tabBarLabel: 'Apps', // eslint-disable-next-line react/display-name - tabBarIcon: ({ color }) => ( + tabBarIcon: ({ color }: { +color: string, ... }) => ( <SWMansionIcon name="globe-1" style={[styles.icon, { color }]} /> ), }; diff --git a/native/profile/appearance-preferences.react.js b/native/profile/appearance-preferences.react.js --- a/native/profile/appearance-preferences.react.js +++ b/native/profile/appearance-preferences.react.js @@ -93,7 +93,7 @@ +colors: Colors, }; class AppearancePreferences extends React.PureComponent<Props> { - render() { + render(): React.Node { const { panelIosHighlightUnderlay: underlay } = this.props.colors; const options: Array<React.Node> = []; diff --git a/native/profile/backup-menu.react.js b/native/profile/backup-menu.react.js --- a/native/profile/backup-menu.react.js +++ b/native/profile/backup-menu.react.js @@ -48,7 +48,7 @@ }, [restoreBackupProtocol, userStore]); const onBackupToggled = React.useCallback( - value => { + (value: boolean) => { dispatch({ type: setLocalSettingsActionType, payload: { isBackupEnabled: value }, diff --git a/native/profile/custom-server-modal.react.js b/native/profile/custom-server-modal.react.js --- a/native/profile/custom-server-modal.react.js +++ b/native/profile/custom-server-modal.react.js @@ -77,7 +77,7 @@ }; } - render() { + render(): React.Node { return ( <Modal containerStyle={this.props.styles.container} diff --git a/native/profile/default-notifications-preferences.react.js b/native/profile/default-notifications-preferences.react.js --- a/native/profile/default-notifications-preferences.react.js +++ b/native/profile/default-notifications-preferences.react.js @@ -143,7 +143,7 @@ this.selectNotificationSetting(notificationTypes.BADGE_ONLY); }; - render() { + render(): React.Node { const { styles, selectedDefaultNotification } = this.props; return ( <ScrollView diff --git a/native/profile/dev-tools.react.js b/native/profile/dev-tools.react.js --- a/native/profile/dev-tools.react.js +++ b/native/profile/dev-tools.react.js @@ -102,7 +102,7 @@ +dispatch: Dispatch, }; class DevTools extends React.PureComponent<Props> { - render() { + render(): React.Node { const { panelIosHighlightUnderlay: underlay } = this.props.colors; const serverButtons: Array<React.Node> = []; @@ -206,7 +206,7 @@ ); } - onPressCrash = () => { + onPressCrash = (): empty => { throw new Error('User triggered crash through dev menu!'); }; diff --git a/native/profile/edit-password.react.js b/native/profile/edit-password.react.js --- a/native/profile/edit-password.react.js +++ b/native/profile/edit-password.react.js @@ -129,7 +129,7 @@ this.mounted = false; } - render() { + render(): React.Node { const buttonContent = this.props.loadingStatus === 'loading' ? ( <ActivityIndicator size="small" color="white" /> diff --git a/native/profile/emoji-user-avatar-creation.react.js b/native/profile/emoji-user-avatar-creation.react.js --- a/native/profile/emoji-user-avatar-creation.react.js +++ b/native/profile/emoji-user-avatar-creation.react.js @@ -5,6 +5,7 @@ import { EditUserAvatarContext } from 'lib/components/edit-user-avatar-provider.react.js'; import { savedEmojiAvatarSelectorForCurrentUser } from 'lib/selectors/user-selectors.js'; +import type { UpdateUserAvatarRequest } from 'lib/types/avatar-types.js'; import type { ProfileNavigationProp } from './profile.react.js'; import { useNativeSetUserAvatar } from '../avatars/avatar-hooks.js'; @@ -27,7 +28,7 @@ const nativeSetUserAvatar = useNativeSetUserAvatar(); const setAvatar = React.useCallback( - async avatarRequest => { + async (avatarRequest: UpdateUserAvatarRequest): Promise<void> => { const result = await nativeSetUserAvatar(avatarRequest); displayActionResultModal('Avatar updated!'); return result; diff --git a/native/profile/keyserver-selection-list.react.js b/native/profile/keyserver-selection-list.react.js --- a/native/profile/keyserver-selection-list.react.js +++ b/native/profile/keyserver-selection-list.react.js @@ -16,7 +16,12 @@ return `${item.keyserverAdminUserInfo.id}${item.keyserverInfo.urlPrefix}`; } -function renderKeyserverListItem({ item }) { +function renderKeyserverListItem({ + item, +}: { + +item: SelectedKeyserverInfo, + ... +}) { return <KeyserverSelectionListItem {...item} />; } diff --git a/native/profile/profile-screen.react.js b/native/profile/profile-screen.react.js --- a/native/profile/profile-screen.react.js +++ b/native/profile/profile-screen.react.js @@ -162,7 +162,7 @@ }; class ProfileScreen extends React.PureComponent<Props> { - get loggedOutOrLoggingOut() { + get loggedOutOrLoggingOut(): boolean { return ( !this.props.currentUserInfo || this.props.currentUserInfo.anonymous || @@ -170,7 +170,7 @@ ); } - render() { + render(): React.Node { let developerTools, defaultNotifications, keyserverSelection, diff --git a/native/profile/profile.react.js b/native/profile/profile.react.js --- a/native/profile/profile.react.js +++ b/native/profile/profile.react.js @@ -4,6 +4,7 @@ StackNavigationProp, StackNavigationHelpers, StackHeaderProps, + StackHeaderLeftButtonProps, } from '@react-navigation/core'; import { createStackNavigator } from '@react-navigation/stack'; import * as React from 'react'; @@ -107,7 +108,7 @@ const colors = useColors(); const headerLeftButton = React.useCallback( - headerProps => + (headerProps: StackHeaderLeftButtonProps) => headerProps.canGoBack ? ( <HeaderBackButton {...headerProps} /> ) : ( diff --git a/native/profile/relationship-list-item.react.js b/native/profile/relationship-list-item.react.js --- a/native/profile/relationship-list-item.react.js +++ b/native/profile/relationship-list-item.react.js @@ -10,6 +10,7 @@ } from 'lib/actions/relationship-actions.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; import type { LoadingStatus } from 'lib/types/loading-types.js'; +import type { ReactRef } from 'lib/types/react-types.js'; import { type RelationshipRequest, type RelationshipAction, @@ -118,9 +119,9 @@ +navigateToUserProfileBottomSheet: (userID: string) => mixed, }; class RelationshipListItem extends React.PureComponent<Props> { - editButton = React.createRef<React.ElementRef<typeof View>>(); + editButton: ReactRef<React.ElementRef<typeof View>> = React.createRef(); - render() { + render(): React.Node { const { lastListItem, removeUserLoadingStatus, @@ -230,7 +231,7 @@ this.props.onSelect({ id, username }); }; - visibleEntryIDs() { + visibleEntryIDs(): [string] { const { relationshipListRoute } = this.props; const id = { [FriendListRouteName]: 'unfriend', @@ -300,7 +301,9 @@ ); } - async updateFriendship(action: RelationshipAction) { + async updateFriendship( + action: RelationshipAction, + ): Promise<RelationshipErrors> { try { return await this.props.updateRelationships({ action, diff --git a/native/profile/relationship-list.react.js b/native/profile/relationship-list.react.js --- a/native/profile/relationship-list.react.js +++ b/native/profile/relationship-list.react.js @@ -96,7 +96,9 @@ const callSearchUsers = useServerCall(searchUsers); const userInfos = useSelector(state => state.userStore.userInfos); const searchUsersOnServer = React.useCallback( - async (usernamePrefix: string) => { + async ( + usernamePrefix: string, + ): Promise<$ReadOnlyArray<GlobalAccountUserInfo>> => { if (usernamePrefix.length === 0) { return []; } diff --git a/native/profile/toggle-report.react.js b/native/profile/toggle-report.react.js --- a/native/profile/toggle-report.react.js +++ b/native/profile/toggle-report.react.js @@ -17,7 +17,7 @@ const isReportEnabled = useIsReportEnabled(reportType); const onReportToggled = React.useCallback( - value => { + (value: boolean) => { dispatch({ type: updateReportsEnabledActionType, payload: { [(reportType: string)]: value }, diff --git a/native/push/push-handler.react.js b/native/push/push-handler.react.js --- a/native/push/push-handler.react.js +++ b/native/push/push-handler.react.js @@ -420,7 +420,7 @@ } } - requestAndroidNotificationsPermission = () => { + requestAndroidNotificationsPermission = (): Promise<boolean> => { if (!this.androidNotificationsPermissionPromise) { this.androidNotificationsPermissionPromise = (async () => { const notifPermission = @@ -675,7 +675,7 @@ handleAndroidNotificationIfActive = ( threadID: string, texts: { body: string, title: ?string }, - ) => { + ): boolean => { if (this.currentState !== 'active') { return false; } @@ -683,7 +683,7 @@ return true; }; - render() { + render(): React.Node { return ( <InAppNotification {...this.state.inAppNotifProps} diff --git a/native/redux/client-db-utils.js b/native/redux/client-db-utils.js --- a/native/redux/client-db-utils.js +++ b/native/redux/client-db-utils.js @@ -70,10 +70,13 @@ ); // Convert `rawThreadInfo`s to a map of `threadID` => `threadInfo`. - const threadIDToThreadInfo = rawThreadInfos.reduce((acc, threadInfo) => { - acc[threadInfo.id] = threadInfo; - return acc; - }, {}); + const threadIDToThreadInfo = rawThreadInfos.reduce( + (acc: { [string]: RawThreadInfo }, threadInfo: RawThreadInfo) => { + acc[threadInfo.id] = threadInfo; + return acc; + }, + {}, + ); // Apply `migrationFunc` to `threadInfo`s. const updatedThreadIDToThreadInfo: ThreadStoreThreadInfos = diff --git a/native/redux/connectivity-updater.react.js b/native/redux/connectivity-updater.react.js --- a/native/redux/connectivity-updater.react.js +++ b/native/redux/connectivity-updater.react.js @@ -8,12 +8,23 @@ import { updateConnectivityActiveType } from './action-types.js'; import { useSelector } from './redux-utils.js'; +type NetInfoStateType = + | 'none' + | 'unknown' + | 'cellular' + | 'wifi' + | 'bluetooth' + | 'ethernet' + | 'wimax' + | 'vpn' + | 'other'; + export default function ConnectivityUpdater(): null { const connectivity = useSelector(state => state.connectivity); const dispatch = useDispatch(); const onConnectionChange = React.useCallback( - ({ type }) => { + ({ type }: { +type: NetInfoStateType, ... }) => { const connected = type !== 'none' && type !== 'unknown'; const hasWiFi = type === 'wifi'; if ( diff --git a/native/redux/persist.js b/native/redux/persist.js --- a/native/redux/persist.js +++ b/native/redux/persist.js @@ -118,7 +118,7 @@ ...state, messageSentFromRoute: [], }), - [3]: state => ({ + [3]: (state: any) => ({ currentUserInfo: state.currentUserInfo, entryStore: state.entryStore, threadInfos: state.threadInfos, @@ -145,7 +145,7 @@ ...state, calendarFilters: defaultCalendarFilters, }), - [6]: state => ({ + [6]: (state: any) => ({ ...state, threadInfos: undefined, threadStore: { @@ -153,7 +153,7 @@ inconsistencyResponses: [], }, }), - [7]: state => ({ + [7]: (state: AppState) => ({ ...state, lastUserInteraction: undefined, sessionID: undefined, @@ -176,14 +176,14 @@ actualizedCalendarQuery: undefined, }, }), - [9]: state => ({ + [9]: (state: any) => ({ ...state, connection: { ...state.connection, lateResponses: [], }, }), - [10]: state => ({ + [10]: (state: any) => ({ ...state, nextLocalID: highestLocalIDSelector(state) + 1, connection: { @@ -211,7 +211,7 @@ deviceOrientation: Orientation.getInitialOrientation(), }), [14]: (state: AppState) => state, - [15]: state => ({ + [15]: (state: any) => ({ ...state, threadStore: { ...state.threadStore, @@ -229,7 +229,7 @@ }, queuedReports: [], }), - [16]: state => { + [16]: (state: any) => { const result = { ...state, messageSentFromRoute: undefined, @@ -243,7 +243,7 @@ } return result; }, - [17]: state => ({ + [17]: (state: any) => ({ ...state, userInfos: undefined, userStore: { @@ -251,14 +251,14 @@ inconsistencyResponses: [], }, }), - [18]: state => ({ + [18]: (state: AppState) => ({ ...state, userStore: { userInfos: state.userStore.userInfos, inconsistencyReports: [], }, }), - [19]: state => { + [19]: (state: any) => { const threadInfos: { [string]: RawThreadInfo } = {}; for (const threadID in state.threadStore.threadInfos) { const threadInfo = state.threadStore.threadInfos[threadID]; @@ -286,7 +286,7 @@ messageTypes.SIDEBAR_SOURCE, ]), }), - [22]: state => { + [22]: (state: any) => { for (const key in state.drafts) { const value = state.drafts[key]; try { @@ -302,19 +302,19 @@ drafts: undefined, }; }, - [23]: state => ({ + [23]: (state: AppState) => ({ ...state, globalThemeInfo: defaultGlobalThemeInfo, }), - [24]: state => ({ + [24]: (state: AppState) => ({ ...state, enabledApps: defaultEnabledApps, }), - [25]: state => ({ + [25]: (state: AppState) => ({ ...state, crashReportsEnabled: __DEV__, }), - [26]: state => { + [26]: (state: any) => { const { currentUserInfo } = state; if (currentUserInfo.anonymous) { return state; @@ -333,7 +333,7 @@ }, }; }, - [27]: state => ({ + [27]: (state: any) => ({ ...state, queuedReports: undefined, enabledReports: undefined, @@ -358,7 +358,7 @@ ], }, }), - [28]: state => { + [28]: (state: AppState) => { const threadParentToChildren: { [string]: string[] } = {}; for (const threadID in state.threadStore.threadInfos) { const threadInfo = state.threadStore.threadInfos[threadID]; @@ -463,7 +463,7 @@ }, [32]: (state: AppState) => unshimClientDB(state, [messageTypes.MULTIMEDIA]), [33]: (state: AppState) => unshimClientDB(state, [messageTypes.REACTION]), - [34]: state => { + [34]: (state: any) => { const { threadIDsToNotifIDs, ...stateSansThreadIDsToNotifIDs } = state; return stateSansThreadIDsToNotifIDs; }, @@ -523,7 +523,7 @@ // 8. Convert rawThreadInfos to a map of threadID to threadInfo const threadIDToThreadInfo = rawThreadInfosWithPinnedCount.reduce( - (acc, threadInfo) => { + (acc: { [string]: RawThreadInfo }, threadInfo: RawThreadInfo) => { acc[threadInfo.id] = threadInfo; return acc; }, @@ -568,7 +568,7 @@ return state; }, - [37]: state => { + [37]: (state: AppState) => { const operations = messageStoreOpsHandlers.convertOpsToClientDBOps([ { type: 'remove_all_threads', @@ -591,10 +591,10 @@ return state; }, - [38]: state => + [38]: (state: AppState) => updateClientDBThreadStoreThreadInfos(state, updateRolesAndPermissions), [39]: (state: AppState) => unshimClientDB(state, [messageTypes.EDIT_MESSAGE]), - [40]: state => + [40]: (state: AppState) => updateClientDBThreadStoreThreadInfos(state, updateRolesAndPermissions), [41]: (state: AppState) => { const queuedReports = state.reportStore.queuedReports.map(report => ({ @@ -624,7 +624,7 @@ } return state; }, - [43]: async state => { + [43]: async (state: any) => { const { messages, drafts, threads, messageStoreThreads } = await commCoreModule.getClientDBStore(); @@ -677,7 +677,7 @@ inviteLinksStore: convertInviteLinksStoreToNewIDSchema(inviteLinksStore), }; }, - [44]: async state => { + [44]: async (state: any) => { const { cookie, ...rest } = state; return { @@ -685,7 +685,7 @@ keyserverStore: { keyserverInfos: { [ashoatKeyserverID]: { cookie } } }, }; }, - [45]: async state => { + [45]: async (state: any) => { const { updatesCurrentAsOf, keyserverStore, ...rest } = state; return { @@ -702,7 +702,7 @@ }, }; }, - [46]: async state => { + [46]: async (state: AppState) => { const { currentAsOf } = state.messageStore; return { @@ -713,7 +713,7 @@ }, }; }, - [47]: async state => { + [47]: async (state: any) => { const { urlPrefix, keyserverStore, ...rest } = state; return { @@ -730,7 +730,7 @@ }, }; }, - [48]: async state => { + [48]: async (state: any) => { const { connection, keyserverStore, ...rest } = state; return { @@ -747,7 +747,7 @@ }, }; }, - [49]: async state => { + [49]: async (state: AppState) => { const { keyserverStore, ...rest } = state; const { connection, ...keyserverRest } = @@ -767,7 +767,7 @@ connection, }; }, - [50]: async state => { + [50]: async (state: any) => { const { connection, ...rest } = state; const { actualizedCalendarQuery, ...connectionRest } = connection; @@ -777,7 +777,7 @@ actualizedCalendarQuery, }; }, - [51]: async state => { + [51]: async (state: any) => { const { lastCommunicatedPlatformDetails, keyserverStore, ...rest } = state; return { @@ -794,14 +794,14 @@ }, }; }, - [52]: async state => ({ + [52]: async (state: AppState) => ({ ...state, integrityStore: { threadHashes: {}, threadHashingStatus: 'data_not_loaded', }, }), - [53]: state => { + [53]: (state: any) => { if (!state.userStore.inconsistencyReports) { return state; } @@ -833,7 +833,7 @@ }, }; }, - [54]: state => { + [54]: (state: any) => { let updatedMessageStoreThreads: MessageStoreThreads = {}; for (const threadID: string in state.messageStore.threads) { const { lastNavigatedTo, lastPruned, ...rest } = @@ -853,7 +853,7 @@ }, }; }, - [55]: async state => + [55]: async (state: AppState) => __DEV__ ? { ...state, @@ -869,7 +869,7 @@ }, } : state, - [56]: state => { + [56]: (state: any) => { const { deviceToken, keyserverStore, ...rest } = state; return { @@ -886,7 +886,7 @@ }, }; }, - [57]: async state => { + [57]: async (state: any) => { const { // eslint-disable-next-line no-unused-vars connection, @@ -926,15 +926,18 @@ } return state; }, - [59]: state => { + [59]: (state: AppState) => { const clientDBThreadInfos = commCoreModule.getAllThreadsSync(); const rawThreadInfos = clientDBThreadInfos.map( convertClientDBThreadInfoToRawThreadInfo, ); - const rawThreadInfosObject = rawThreadInfos.reduce((acc, threadInfo) => { - acc[threadInfo.id] = threadInfo; - return acc; - }, {}); + const rawThreadInfosObject = rawThreadInfos.reduce( + (acc: { [string]: RawThreadInfo }, threadInfo: RawThreadInfo) => { + acc[threadInfo.id] = threadInfo; + return acc; + }, + {}, + ); const migratedRawThreadInfos = persistMigrationToRemoveSelectRolePermissions(rawThreadInfosObject); @@ -964,7 +967,7 @@ return state; }, - [60]: state => + [60]: (state: AppState) => updateClientDBThreadStoreThreadInfos(state, updateRolesAndPermissions), }; diff --git a/native/redux/update-roles-and-permissions.js b/native/redux/update-roles-and-permissions.js --- a/native/redux/update-roles-and-permissions.js +++ b/native/redux/update-roles-and-permissions.js @@ -32,7 +32,7 @@ ]; } - const constructNodes = nodeID => ({ + const constructNodes = (nodeID: string): ThreadTraversalNode => ({ threadID: nodeID, children: parentThreadMap[nodeID]?.map(constructNodes) ?? null, }); diff --git a/native/roles/roles-navigator.react.js b/native/roles/roles-navigator.react.js --- a/native/roles/roles-navigator.react.js +++ b/native/roles/roles-navigator.react.js @@ -3,6 +3,7 @@ import type { StackNavigationProp, StackNavigationHelpers, + StackHeaderLeftButtonProps, } from '@react-navigation/core'; import { createStackNavigator } from '@react-navigation/stack'; import * as React from 'react'; @@ -34,7 +35,7 @@ const communityRolesScreenOptions = { headerTitle: 'Create role', // eslint-disable-next-line react/display-name - headerLeft: headerLeftProps => ( + headerLeft: (headerLeftProps: StackHeaderLeftButtonProps) => ( <CommunityRolesHeaderLeftButton {...headerLeftProps} /> ), }; diff --git a/native/search/message-search.react.js b/native/search/message-search.react.js --- a/native/search/message-search.react.js +++ b/native/search/message-search.react.js @@ -147,7 +147,7 @@ }, []); const renderItem = React.useCallback( - ({ item }) => { + ({ item }: { +item: ChatMessageItemWithHeight, ... }) => { if (item.itemType === 'loader') { return <ListLoadingIndicator />; } diff --git a/native/tooltip/tooltip.react.js b/native/tooltip/tooltip.react.js --- a/native/tooltip/tooltip.react.js +++ b/native/tooltip/tooltip.react.js @@ -36,7 +36,11 @@ type LayoutCoordinates, } from '../types/layout-types.js'; import type { LayoutEvent } from '../types/react-native.js'; -import { AnimatedView } from '../types/styles.js'; +import { + AnimatedView, + type ViewStyle, + type AnimatedViewStyle, +} from '../types/styles.js'; /* eslint-disable import/no-named-as-default-member */ const { Value, Node, Extrapolate, add, multiply, interpolateNode } = Animated; @@ -258,14 +262,14 @@ return 'below'; } - get opacityStyle() { + get opacityStyle(): AnimatedViewStyle { return { ...this.props.styles.backdrop, opacity: this.backdropOpacity, }; } - get contentContainerStyle() { + get contentContainerStyle(): ViewStyle { const { verticalBounds } = this.props.route.params; const fullScreenHeight = this.props.dimensions.height; const top = verticalBounds.y; @@ -278,7 +282,7 @@ }; } - get buttonStyle() { + get buttonStyle(): ViewStyle { const { params } = this.props.route; const { initialCoordinates, verticalBounds } = params; const { x, y, width, height } = initialCoordinates; @@ -290,14 +294,14 @@ }; } - get margin() { + get margin(): number { const customMargin = this.props.route.params.margin; return customMargin !== null && customMargin !== undefined ? customMargin : 20; } - get tooltipContainerStyle() { + get tooltipContainerStyle(): AnimatedViewStyle { const { dimensions, route } = this.props; const { initialCoordinates, verticalBounds, chatInputBarHeight } = route.params; @@ -356,7 +360,7 @@ return style; } - render() { + render(): React.Node { const { dimensions, overlayContext, @@ -473,7 +477,7 @@ ); } - getTooltipItem() { + getTooltipItem(): React.ComponentType<TooltipItemBaseProps> { const BoundTooltipItem = this.props.boundTooltipItem; return BoundTooltipItem; } @@ -483,7 +487,7 @@ this.props.tooltipContext.showActionSheet(); }; - renderMoreIcon = () => { + renderMoreIcon = (): React.Node => { const { styles } = this.props; return ( <SWMansionIcon name="menu-vertical" style={styles.icon} size={16} /> @@ -507,7 +511,12 @@ } }; } - function ConnectedTooltip(props) { + function ConnectedTooltip( + props: $ReadOnly<{ + ...BaseTooltipPropsType, + +hideTooltip: () => mixed, + }>, + ) { const dimensions = useSelector(state => state.dimensions); const overlayContext = React.useContext(OverlayContext); const chatContext = React.useContext(ChatContext); @@ -528,7 +537,7 @@ const styles = useStyles(unboundStyles); const boundTooltipItem = React.useCallback( - innerProps => { + (innerProps: TooltipItemBaseProps) => { const containerStyle = isFixed ? [styles.itemContainer, styles.itemContainerFixed] : styles.itemContainer; diff --git a/native/utils/typeahead-utils.js b/native/utils/typeahead-utils.js --- a/native/utils/typeahead-utils.js +++ b/native/utils/typeahead-utils.js @@ -18,20 +18,34 @@ `((^(.|\n)*\\s+)|^)@(${validChatNameRegexString})?$`, ); +type FocusAndUpdateTextAndSelection = ( + text: string, + selection: Selection, +) => void; + export type TypeaheadTooltipActionsParams<SuggestionItemType> = { +suggestions: $ReadOnlyArray<SuggestionItemType>, +textBeforeAtSymbol: string, +text: string, +query: string, - +focusAndUpdateTextAndSelection: (text: string, selection: Selection) => void, + +focusAndUpdateTextAndSelection: FocusAndUpdateTextAndSelection, +}; + +type MentionTypeaheadTooltipActionExecuteHandlerParams = { + +textBeforeAtSymbol: string, + +text: string, + +query: string, + +mentionText: string, + +focusAndUpdateTextAndSelection: FocusAndUpdateTextAndSelection, }; + function mentionTypeaheadTooltipActionExecuteHandler({ textBeforeAtSymbol, text, query, mentionText, focusAndUpdateTextAndSelection, -}) { +}: MentionTypeaheadTooltipActionExecuteHandlerParams) { const { newText, newSelectionStart } = getNewTextAndSelection( textBeforeAtSymbol, text, @@ -43,6 +57,7 @@ end: newSelectionStart, }); } + function mentionTypeaheadTooltipActions({ suggestions, textBeforeAtSymbol,