Page MenuHomePhabricator

D9771.id33096.diff
No OneTemporary

D9771.id33096.diff

diff --git a/web/account/siwe-login-form.react.js b/web/account/siwe-login-form.react.js
--- a/web/account/siwe-login-form.react.js
+++ b/web/account/siwe-login-form.react.js
@@ -17,7 +17,10 @@
import SWMansionIcon from 'lib/components/SWMansionIcon.react.js';
import stores from 'lib/facts/stores.js';
import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js';
-import type { LogInStartingPayload } from 'lib/types/account-types.js';
+import type {
+ LogInStartingPayload,
+ LogInExtraInfo,
+} from 'lib/types/account-types.js';
import type {
OLMIdentityKeys,
SignedIdentityKeysBlob,
@@ -92,7 +95,7 @@
useSignedIdentityKeysBlob();
const callSIWEAuthEndpoint = React.useCallback(
- async (message: string, signature: string, extraInfo) => {
+ async (message: string, signature: string, extraInfo: LogInExtraInfo) => {
invariant(
signedIdentityKeysBlob,
'signedIdentityKeysBlob must be set in attemptSIWEAuth',
diff --git a/web/account/traditional-login-form.react.js b/web/account/traditional-login-form.react.js
--- a/web/account/traditional-login-form.react.js
+++ b/web/account/traditional-login-form.react.js
@@ -45,20 +45,26 @@
}, []);
const [username, setUsername] = React.useState<string>('');
- const onUsernameChange = React.useCallback(e => {
- invariant(e.target instanceof HTMLInputElement, 'target not input');
- setUsername(e.target.value);
- }, []);
+ const onUsernameChange = React.useCallback(
+ (e: SyntheticEvent<HTMLInputElement>) => {
+ invariant(e.target instanceof HTMLInputElement, 'target not input');
+ setUsername(e.target.value);
+ },
+ [],
+ );
const onUsernameBlur = React.useCallback(() => {
setUsername(untrimmedUsername => untrimmedUsername.trim());
}, []);
const [password, setPassword] = React.useState<string>('');
- const onPasswordChange = React.useCallback(e => {
- invariant(e.target instanceof HTMLInputElement, 'target not input');
- setPassword(e.target.value);
- }, []);
+ const onPasswordChange = React.useCallback(
+ (e: SyntheticEvent<HTMLInputElement>) => {
+ invariant(e.target instanceof HTMLInputElement, 'target not input');
+ setPassword(e.target.value);
+ },
+ [],
+ );
const [errorMessage, setErrorMessage] = React.useState<string>('');
diff --git a/web/app.react.js b/web/app.react.js
--- a/web/app.react.js
+++ b/web/app.react.js
@@ -165,7 +165,7 @@
});
};
- render() {
+ render(): React.Node {
let content;
if (this.props.loggedIn) {
content = (
@@ -213,10 +213,11 @@
);
}
- onHeaderDoubleClick = () => electron?.doubleClickTopBar();
- stopDoubleClickPropagation = electron ? e => e.stopPropagation() : null;
+ onHeaderDoubleClick = (): void => electron?.doubleClickTopBar();
+ stopDoubleClickPropagation: ?(SyntheticEvent<HTMLAnchorElement>) => void =
+ electron ? e => e.stopPropagation() : null;
- renderLoginPage() {
+ renderLoginPage(): React.Node {
const { loginMethod } = this.props.navInfo;
if (loginMethod === 'qr-code') {
@@ -226,7 +227,7 @@
return <Splash />;
}
- renderMainContent() {
+ renderMainContent(): React.Node {
const mainContent = this.getMainContentWithSwitcher();
let navigationArrows = null;
@@ -284,7 +285,7 @@
);
}
- getMainContentWithSwitcher() {
+ getMainContentWithSwitcher(): React.Node {
const { tab, settingsSection } = this.props.navInfo;
let mainContent;
diff --git a/web/avatars/avatar-hooks.react.js b/web/avatars/avatar-hooks.react.js
--- a/web/avatars/avatar-hooks.react.js
+++ b/web/avatars/avatar-hooks.react.js
@@ -21,7 +21,7 @@
const callUploadMultimedia = useServerCall(uploadMultimedia);
const callBlobServiceUpload = useBlobServiceUpload();
const uploadAvatarMedia = React.useCallback(
- async file => {
+ async (file: File): Promise<UpdateUserAvatarRequest> => {
const validatedFile = await validateFile(file);
const { result } = validatedFile;
if (!result.success) {
diff --git a/web/avatars/edit-thread-avatar-menu.react.js b/web/avatars/edit-thread-avatar-menu.react.js
--- a/web/avatars/edit-thread-avatar-menu.react.js
+++ b/web/avatars/edit-thread-avatar-menu.react.js
@@ -57,8 +57,10 @@
const uploadAvatarMedia = useUploadAvatarMedia();
const onImageSelected = React.useCallback(
- async event => {
- const uploadResult = await uploadAvatarMedia(event.target.files[0]);
+ async (event: SyntheticEvent<HTMLInputElement>) => {
+ const { target } = event;
+ invariant(target instanceof HTMLInputElement, 'target not input');
+ const uploadResult = await uploadAvatarMedia(target.files[0]);
baseSetThreadAvatar(threadInfo.id, uploadResult);
},
[baseSetThreadAvatar, threadInfo.id, uploadAvatarMedia],
diff --git a/web/avatars/edit-user-avatar-menu.react.js b/web/avatars/edit-user-avatar-menu.react.js
--- a/web/avatars/edit-user-avatar-menu.react.js
+++ b/web/avatars/edit-user-avatar-menu.react.js
@@ -69,8 +69,10 @@
const uploadAvatarMedia = useUploadAvatarMedia();
const onImageSelected = React.useCallback(
- async event => {
- const uploadResult = await uploadAvatarMedia(event.target.files[0]);
+ async (event: SyntheticEvent<HTMLInputElement>) => {
+ const { target } = event;
+ invariant(target instanceof HTMLInputElement, 'target not input');
+ const uploadResult = await uploadAvatarMedia(target.files[0]);
baseSetUserAvatar(uploadResult);
},
[baseSetUserAvatar, uploadAvatarMedia],
diff --git a/web/avatars/emoji-avatar-selection-modal.react.js b/web/avatars/emoji-avatar-selection-modal.react.js
--- a/web/avatars/emoji-avatar-selection-modal.react.js
+++ b/web/avatars/emoji-avatar-selection-modal.react.js
@@ -53,10 +53,13 @@
[pendingAvatarColor, pendingAvatarEmoji],
);
- const onEmojiSelect = React.useCallback(selection => {
- setUpdateAvatarStatus();
- setPendingAvatarEmoji(selection.native);
- }, []);
+ const onEmojiSelect = React.useCallback(
+ (selection: { +native: string, ... }) => {
+ setUpdateAvatarStatus();
+ setPendingAvatarEmoji(selection.native);
+ },
+ [],
+ );
const onColorSelection = React.useCallback((hex: string) => {
setUpdateAvatarStatus();
diff --git a/web/calendar/calendar.react.js b/web/calendar/calendar.react.js
--- a/web/calendar/calendar.react.js
+++ b/web/calendar/calendar.react.js
@@ -41,6 +41,11 @@
import type { NavInfo } from '../types/nav-types.js';
import { canonicalURLFromReduxState } from '../url-utils.js';
+type StartAndEndDates = {
+ +startDate: string,
+ +endDate: string,
+};
+
type BaseProps = {
+url: string,
};
@@ -69,7 +74,7 @@
dayOfMonth: number,
monthInput: ?number = undefined,
yearInput: ?number = undefined,
- ) {
+ ): Date {
return getDate(
yearInput ? yearInput : this.props.year,
monthInput ? monthInput : this.props.month,
@@ -77,7 +82,7 @@
);
}
- prevMonthDates() {
+ prevMonthDates(): StartAndEndDates {
const { year, month } = this.props;
const lastMonthDate = getDate(year, month - 1, 1);
const prevYear = lastMonthDate.getFullYear();
@@ -88,7 +93,7 @@
};
}
- nextMonthDates() {
+ nextMonthDates(): StartAndEndDates {
const { year, month } = this.props;
const nextMonthDate = getDate(year, month + 1, 1);
const nextYear = nextMonthDate.getFullYear();
@@ -99,7 +104,7 @@
};
}
- render() {
+ render(): React.Node {
const { year, month } = this.props;
const monthName = dateFormat(getDate(year, month, 1), 'mmmm');
const prevURL = canonicalURLFromReduxState(
diff --git a/web/calendar/day.react.js b/web/calendar/day.react.js
--- a/web/calendar/day.react.js
+++ b/web/calendar/day.react.js
@@ -65,7 +65,7 @@
}
}
- render() {
+ render(): React.Node {
const now = new Date();
const isToday = dateString(now) === this.props.dayString;
const tdClasses = classNames(css.day, { [css.currentDay]: isToday });
diff --git a/web/calendar/filter-panel.react.js b/web/calendar/filter-panel.react.js
--- a/web/calendar/filter-panel.react.js
+++ b/web/calendar/filter-panel.react.js
@@ -77,7 +77,7 @@
return this.props.filteredCommunityThreadIDs.has(threadID);
}
- render() {
+ render(): React.Node {
const filterThreadInfos = this.state.query
? this.state.searchResults
: this.props.filterThreadInfos;
@@ -275,7 +275,7 @@
+selected: boolean,
};
class Item extends React.PureComponent<ItemProps> {
- render() {
+ render(): React.Node {
const threadInfo = this.props.filterThreadInfo.threadInfo;
const beforeCheckStyles = { borderColor: `#${threadInfo.color}` };
let afterCheck = null;
@@ -343,7 +343,7 @@
+selected: boolean,
};
class Category extends React.PureComponent<CategoryProps> {
- render() {
+ render(): React.Node {
const beforeCheckStyles = { borderColor: 'white' };
let afterCheck = null;
if (this.props.selected) {
diff --git a/web/chat/chat-input-bar.react.js b/web/chat/chat-input-bar.react.js
--- a/web/chat/chat-input-bar.react.js
+++ b/web/chat/chat-input-bar.react.js
@@ -166,7 +166,7 @@
static unassignedUploadIDs(
pendingUploads: $ReadOnlyArray<PendingMultimediaUpload>,
- ) {
+ ): Array<string> {
return pendingUploads
.filter(
(pendingUpload: PendingMultimediaUpload) => !pendingUpload.messageID,
@@ -199,7 +199,7 @@
this.props.inputState.removeReplyListener(this.focusAndUpdateText);
}
- render() {
+ render(): React.Node {
const isMember = viewerIsMember(this.props.threadInfo);
const canJoin = threadHasPermission(
this.props.threadInfo,
@@ -540,7 +540,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/web/chat/chat-message-list.react.js b/web/chat/chat-message-list.react.js
--- a/web/chat/chat-message-list.react.js
+++ b/web/chat/chat-message-list.react.js
@@ -107,7 +107,7 @@
this.props.removeScrollToMessageListener(this.scrollToMessage);
}
- getSnapshotBeforeUpdate(prevProps: Props) {
+ getSnapshotBeforeUpdate(prevProps: Props): ?Snapshot {
if (
ChatMessageList.hasNewMessage(this.props, prevProps) &&
this.messageContainer
@@ -118,7 +118,7 @@
return null;
}
- static hasNewMessage(props: Props, prevProps: Props) {
+ static hasNewMessage(props: Props, prevProps: Props): boolean {
const { messageListData } = props;
if (!messageListData || messageListData.length === 0) {
return false;
@@ -133,7 +133,7 @@
);
}
- componentDidUpdate(prevProps: Props, prevState, snapshot: ?Snapshot) {
+ componentDidUpdate(prevProps: Props, prevState: State, snapshot: ?Snapshot) {
const { messageListData } = this.props;
const prevMessageListData = prevProps.messageListData;
@@ -180,14 +180,14 @@
}
}
- static keyExtractor(item: ChatMessageItem) {
+ static keyExtractor(item: ChatMessageItem): string {
if (item.itemType === 'loader') {
return 'loader';
}
return messageKey(item.messageInfo);
}
- renderItem = item => {
+ renderItem = (item: ChatMessageItem): React.Node => {
if (item.itemType === 'loader') {
return (
<div key="loader" className={css.loading}>
@@ -271,7 +271,7 @@
return maxHeight;
};
- willMessageEditWindowOverflow(composedMessageID: string) {
+ willMessageEditWindowOverflow(composedMessageID: string): boolean {
const { messageContainer } = this;
if (!messageContainer) {
return false;
@@ -305,7 +305,7 @@
return messageBottom > containerBottom || messageTop < containerTop;
}
- render() {
+ render(): React.Node {
const { messageListData, threadInfo, inputState, isEditState } = this.props;
if (!messageListData) {
return <div className={css.container} />;
@@ -353,7 +353,7 @@
this.debounceEditModeAfterScrollToMessage();
};
- debounceEditModeAfterScrollToMessage = _debounce(() => {
+ debounceEditModeAfterScrollToMessage: () => void = _debounce(() => {
if (this.state.scrollingEndCallback) {
this.state.scrollingEndCallback();
}
diff --git a/web/chat/chat-thread-list-item-menu.react.js b/web/chat/chat-thread-list-item-menu.react.js
--- a/web/chat/chat-thread-list-item-menu.react.js
+++ b/web/chat/chat-thread-list-item-menu.react.js
@@ -21,7 +21,7 @@
const active = useThreadIsActive(threadInfo.id);
const [menuVisible, setMenuVisible] = React.useState(false);
const toggleMenu = React.useCallback(
- event => {
+ (event: SyntheticEvent<HTMLButtonElement>) => {
event.stopPropagation();
setMenuVisible(!menuVisible);
},
@@ -39,7 +39,7 @@
);
const onToggleUnreadStatusClicked = React.useCallback(
- event => {
+ (event: SyntheticEvent<HTMLButtonElement>) => {
event.stopPropagation();
toggleUnreadStatus();
},
diff --git a/web/chat/chat-thread-list.react.js b/web/chat/chat-thread-list.react.js
--- a/web/chat/chat-thread-list.react.js
+++ b/web/chat/chat-thread-list.react.js
@@ -41,7 +41,12 @@
}
};
-const renderItem = ({ index, data, style }) => {
+type RenderItemInput = {
+ +index: number,
+ +data: $ReadOnlyArray<Item>,
+ +style: CSSStyleDeclaration,
+};
+const renderItem: RenderItemInput => React.Node = ({ index, data, style }) => {
let item;
if (data[index].type === 'search') {
item = <ChatThreadListSearch />;
@@ -129,7 +134,7 @@
items.push({ type: 'empty' });
}
- const itemSize = index => {
+ const itemSize = (index: number) => {
if (items[index].type === 'search') {
return sizes.search;
} else if (items[index].type === 'empty') {
diff --git a/web/chat/edit-text-message.react.js b/web/chat/edit-text-message.react.js
--- a/web/chat/edit-text-message.react.js
+++ b/web/chat/edit-text-message.react.js
@@ -98,7 +98,7 @@
}, [background, updatePosition]);
const preventCloseTab = React.useCallback(
- event => {
+ (event: BeforeUnloadEvent) => {
if (!isMessageEdited) {
return null;
}
diff --git a/web/chat/failed-send.react.js b/web/chat/failed-send.react.js
--- a/web/chat/failed-send.react.js
+++ b/web/chat/failed-send.react.js
@@ -79,7 +79,7 @@
}
}
- render() {
+ render(): React.Node {
return (
<div className={css.failedSend}>
<span className={css.deliveryFailed}>Delivery failed.</span>
diff --git a/web/chat/message-tooltip.react.js b/web/chat/message-tooltip.react.js
--- a/web/chat/message-tooltip.react.js
+++ b/web/chat/message-tooltip.react.js
@@ -179,7 +179,7 @@
);
const onEmojiSelect = React.useCallback(
- emoji => {
+ (emoji: { +native: string, ... }) => {
const reactionInput = emoji.native;
sendReaction(reactionInput);
},
diff --git a/web/chat/multimedia-message.react.js b/web/chat/multimedia-message.react.js
--- a/web/chat/multimedia-message.react.js
+++ b/web/chat/multimedia-message.react.js
@@ -27,7 +27,7 @@
+inputState: ?InputState,
};
class MultimediaMessage extends React.PureComponent<Props> {
- render() {
+ render(): React.Node {
const { item, inputState } = this.props;
invariant(
item.messageInfo.type === messageTypes.IMAGES ||
diff --git a/web/chat/robotext-message.react.js b/web/chat/robotext-message.react.js
--- a/web/chat/robotext-message.react.js
+++ b/web/chat/robotext-message.react.js
@@ -108,7 +108,7 @@
+dispatch: Dispatch,
};
class InnerThreadEntity extends React.PureComponent<InnerThreadEntityProps> {
- render() {
+ render(): React.Node {
return <a onClick={this.onClickThread}>{this.props.name}</a>;
}
diff --git a/web/components/button.react.js b/web/components/button.react.js
--- a/web/components/button.react.js
+++ b/web/components/button.react.js
@@ -61,7 +61,7 @@
style = buttonThemes.standard;
}
- const wrappedChildren = React.Children.map(children, child => {
+ const wrappedChildren = React.Children.map(children, (child: React.Node) => {
if (typeof child === 'string' || typeof child === 'number') {
return <span>{child}</span>;
}
diff --git a/web/components/dropdown.react.js b/web/components/dropdown.react.js
--- a/web/components/dropdown.react.js
+++ b/web/components/dropdown.react.js
@@ -48,7 +48,7 @@
}, [disabled, isOpen]);
const handleSelection = React.useCallback(
- selection => {
+ (selection: DropdownOption) => {
setActiveSelection(selection.id);
setIsOpen(false);
},
diff --git a/web/components/menu.react.js b/web/components/menu.react.js
--- a/web/components/menu.react.js
+++ b/web/components/menu.react.js
@@ -102,7 +102,7 @@
}, [closeMenu]);
const onClickMenuCallback = React.useCallback(
- e => {
+ (e: SyntheticEvent<HTMLButtonElement>) => {
e.stopPropagation();
setCurrentOpenMenu(ourSymbol.current);
},
diff --git a/web/components/navigation-arrows.react.js b/web/components/navigation-arrows.react.js
--- a/web/components/navigation-arrows.react.js
+++ b/web/components/navigation-arrows.react.js
@@ -9,7 +9,8 @@
import electron from '../electron.js';
import history from '../router-history.js';
-const stopDoubleClickPropagation = e => e.stopPropagation();
+const stopDoubleClickPropagation = (e: SyntheticEvent<HTMLAnchorElement>) =>
+ e.stopPropagation();
function NavigationArrows(): React.Node {
const goBack = React.useCallback(
diff --git a/web/components/search.react.js b/web/components/search.react.js
--- a/web/components/search.react.js
+++ b/web/components/search.react.js
@@ -1,5 +1,6 @@
// @flow
+import invariant from 'invariant';
import * as React from 'react';
import SWMansionIcon from 'lib/components/SWMansionIcon.react.js';
@@ -30,8 +31,10 @@
}, [onChangeText, onClearText]);
const onChange = React.useCallback(
- event => {
- onChangeText(event.target.value);
+ (event: SyntheticEvent<HTMLInputElement>) => {
+ const { target } = event;
+ invariant(target instanceof HTMLInputElement, 'target not input');
+ onChangeText(target.value);
},
[onChangeText],
);
diff --git a/web/input/input-state-container.react.js b/web/input/input-state-container.react.js
--- a/web/input/input-state-container.react.js
+++ b/web/input/input-state-container.react.js
@@ -100,6 +100,9 @@
type PendingMultimediaUpload,
type TypeaheadState,
InputStateContext,
+ type BaseInputState,
+ type TypeaheadInputState,
+ type InputState,
} from './input-state.js';
import { encryptFile } from '../media/encryption-utils.js';
import { generateThumbHash } from '../media/image-utils.js';
@@ -172,7 +175,10 @@
},
};
replyCallbacks: Array<(message: string) => void> = [];
- pendingThreadCreations = new Map<string, Promise<string>>();
+ pendingThreadCreations: Map<string, Promise<string>> = new Map<
+ string,
+ Promise<string>,
+ >();
// TODO: flip the switch
// Note that this enables Blob service for encrypted media only
useBlobServiceUploads = false;
@@ -181,7 +187,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<string>();
static reassignToRealizedThreads<T>(
state: { +[threadID: string]: T },
@@ -200,7 +206,7 @@
return updated ? newState : null;
}
- static getDerivedStateFromProps(props: Props, state: State) {
+ static getDerivedStateFromProps(props: Props, state: State): ?Partial<State> {
const pendingUploads = InputStateContainer.reassignToRealizedThreads(
state.pendingUploads,
props,
@@ -224,7 +230,7 @@
return stateUpdate;
}
- static completedMessageIDs(state: State) {
+ static completedMessageIDs(state: State): Set<string> {
const completed = new Map();
for (const threadID in state.pendingUploads) {
const pendingUploads = state.pendingUploads[threadID];
@@ -404,7 +410,9 @@
return threadInfoInsideCommunity(threadInfo, commStaffCommunity.id);
}
- async sendMultimediaMessage(messageInfo: RawMultimediaMessageInfo) {
+ async sendMultimediaMessage(
+ messageInfo: RawMultimediaMessageInfo,
+ ): Promise<void> {
if (!threadIsPending(messageInfo.threadID)) {
this.props.dispatchActionPromise(
sendMultimediaMessageActionTypes,
@@ -564,74 +572,84 @@
return threadCreationPromise;
}
- inputBaseStateSelector = _memoize((threadID: ?string) =>
- createSelector(
- (propsAndState: PropsAndState) =>
- threadID ? propsAndState.pendingUploads[threadID] : null,
- (propsAndState: PropsAndState) =>
- threadID ? propsAndState.drafts[draftKeyFromThreadID(threadID)] : null,
- (propsAndState: PropsAndState) =>
- threadID ? propsAndState.textCursorPositions[threadID] : null,
- (
- pendingUploads: ?{ [localUploadID: string]: PendingMultimediaUpload },
- draft: ?string,
- textCursorPosition: ?number,
- ) => {
- let threadPendingUploads = [];
- const assignedUploads = {};
- if (pendingUploads) {
- const [uploadsWithMessageIDs, uploadsWithoutMessageIDs] =
- _partition('messageID')(pendingUploads);
- threadPendingUploads = _sortBy('localID')(uploadsWithoutMessageIDs);
- const threadAssignedUploads = _groupBy('messageID')(
- uploadsWithMessageIDs,
- );
- for (const messageID in threadAssignedUploads) {
- // lodash libdefs don't return $ReadOnlyArray
- assignedUploads[messageID] = [...threadAssignedUploads[messageID]];
+ inputBaseStateSelector: (?string) => PropsAndState => BaseInputState =
+ _memoize((threadID: ?string) =>
+ createSelector(
+ (propsAndState: PropsAndState) =>
+ threadID ? propsAndState.pendingUploads[threadID] : null,
+ (propsAndState: PropsAndState) =>
+ threadID
+ ? propsAndState.drafts[draftKeyFromThreadID(threadID)]
+ : null,
+ (propsAndState: PropsAndState) =>
+ threadID ? propsAndState.textCursorPositions[threadID] : null,
+ (
+ pendingUploads: ?{ [localUploadID: string]: PendingMultimediaUpload },
+ draft: ?string,
+ textCursorPosition: ?number,
+ ) => {
+ let threadPendingUploads = [];
+ const assignedUploads = {};
+ if (pendingUploads) {
+ const [uploadsWithMessageIDs, uploadsWithoutMessageIDs] =
+ _partition('messageID')(pendingUploads);
+ threadPendingUploads = _sortBy('localID')(uploadsWithoutMessageIDs);
+ const threadAssignedUploads = _groupBy('messageID')(
+ uploadsWithMessageIDs,
+ );
+ for (const messageID in threadAssignedUploads) {
+ // lodash libdefs don't return $ReadOnlyArray
+ assignedUploads[messageID] = [
+ ...threadAssignedUploads[messageID],
+ ];
+ }
}
- }
- return {
- pendingUploads: threadPendingUploads,
- assignedUploads,
- draft: draft ?? '',
- textCursorPosition: textCursorPosition ?? 0,
- appendFiles: (threadInfo: ThreadInfo, files: $ReadOnlyArray<File>) =>
- this.appendFiles(threadInfo, files),
- cancelPendingUpload: (localUploadID: string) =>
- this.cancelPendingUpload(threadID, localUploadID),
- sendTextMessage: (
- messageInfo: RawTextMessageInfo,
- threadInfo: ThreadInfo,
- parentThreadInfo: ?ThreadInfo,
- ) => this.sendTextMessage(messageInfo, threadInfo, parentThreadInfo),
- createMultimediaMessage: (localID: number, threadInfo: ThreadInfo) =>
- this.createMultimediaMessage(localID, threadInfo),
- setDraft: (newDraft: string) => this.setDraft(threadID, newDraft),
- setTextCursorPosition: (newPosition: number) =>
- this.setTextCursorPosition(threadID, newPosition),
- messageHasUploadFailure: (localMessageID: string) =>
- this.messageHasUploadFailure(assignedUploads[localMessageID]),
- retryMultimediaMessage: (
- localMessageID: string,
- threadInfo: ThreadInfo,
- ) =>
- this.retryMultimediaMessage(
- localMessageID,
- threadInfo,
- assignedUploads[localMessageID],
- ),
- addReply: (message: string) => this.addReply(message),
- addReplyListener: this.addReplyListener,
- removeReplyListener: this.removeReplyListener,
- registerSendCallback: this.props.registerSendCallback,
- unregisterSendCallback: this.props.unregisterSendCallback,
- };
- },
- ),
- );
+ return {
+ pendingUploads: threadPendingUploads,
+ assignedUploads,
+ draft: draft ?? '',
+ textCursorPosition: textCursorPosition ?? 0,
+ appendFiles: (
+ threadInfo: ThreadInfo,
+ files: $ReadOnlyArray<File>,
+ ) => this.appendFiles(threadInfo, files),
+ cancelPendingUpload: (localUploadID: string) =>
+ this.cancelPendingUpload(threadID, localUploadID),
+ sendTextMessage: (
+ messageInfo: RawTextMessageInfo,
+ threadInfo: ThreadInfo,
+ parentThreadInfo: ?ThreadInfo,
+ ) =>
+ this.sendTextMessage(messageInfo, threadInfo, parentThreadInfo),
+ createMultimediaMessage: (
+ localID: number,
+ threadInfo: ThreadInfo,
+ ) => this.createMultimediaMessage(localID, threadInfo),
+ setDraft: (newDraft: string) => this.setDraft(threadID, newDraft),
+ setTextCursorPosition: (newPosition: number) =>
+ this.setTextCursorPosition(threadID, newPosition),
+ messageHasUploadFailure: (localMessageID: string) =>
+ this.messageHasUploadFailure(assignedUploads[localMessageID]),
+ retryMultimediaMessage: (
+ localMessageID: string,
+ threadInfo: ThreadInfo,
+ ) =>
+ this.retryMultimediaMessage(
+ localMessageID,
+ threadInfo,
+ assignedUploads[localMessageID],
+ ),
+ addReply: (message: string) => this.addReply(message),
+ addReplyListener: this.addReplyListener,
+ removeReplyListener: this.removeReplyListener,
+ registerSendCallback: this.props.registerSendCallback,
+ unregisterSendCallback: this.props.unregisterSendCallback,
+ };
+ },
+ ),
+ );
- typeaheadStateSelector = createSelector(
+ typeaheadStateSelector: PropsAndState => TypeaheadInputState = createSelector(
(propsAndState: PropsAndState) => propsAndState.typeaheadState,
(typeaheadState: TypeaheadState) => ({
typeaheadState,
@@ -639,7 +657,10 @@
}),
);
- inputStateSelector = createSelector(
+ inputStateSelector: ({
+ +inputBaseState: BaseInputState,
+ +typeaheadState: TypeaheadInputState,
+ }) => InputState = createSelector(
state => state.inputBaseState,
state => state.typeaheadState,
(inputBaseState, typeaheadState) => ({
@@ -816,7 +837,7 @@
uploadFiles(
threadID: string,
uploads: $ReadOnlyArray<PendingMultimediaUpload>,
- ) {
+ ): Promise<mixed> {
return Promise.all(
uploads.map(upload => this.uploadFile(threadID, upload)),
);
@@ -1450,7 +1471,7 @@
messageHasUploadFailure(
pendingUploads: ?$ReadOnlyArray<PendingMultimediaUpload>,
- ) {
+ ): boolean {
if (!pendingUploads) {
return false;
}
@@ -1566,7 +1587,7 @@
);
};
- render() {
+ render(): React.Node {
const { activeChatThreadID } = this.props;
// we're going with two selectors as we want to avoid
diff --git a/web/input/input-state.js b/web/input/input-state.js
--- a/web/input/input-state.js
+++ b/web/input/input-state.js
@@ -55,15 +55,13 @@
+accept: ?() => void,
};
-// This type represents the input state for a particular thread
-export type InputState = {
+export type BaseInputState = {
+pendingUploads: $ReadOnlyArray<PendingMultimediaUpload>,
+assignedUploads: {
[messageID: string]: $ReadOnlyArray<PendingMultimediaUpload>,
},
+draft: string,
+textCursorPosition: number,
- +typeaheadState: TypeaheadState,
+appendFiles: (
threadInfo: ThreadInfo,
files: $ReadOnlyArray<File>,
@@ -77,7 +75,6 @@
+createMultimediaMessage: (localID: number, threadInfo: ThreadInfo) => void,
+setDraft: (draft: string) => void,
+setTextCursorPosition: (newPosition: number) => void,
- +setTypeaheadState: ($Shape<TypeaheadState>) => void,
+messageHasUploadFailure: (localMessageID: string) => boolean,
+retryMultimediaMessage: (
localMessageID: string,
@@ -90,6 +87,17 @@
+unregisterSendCallback: (() => mixed) => void,
};
+export type TypeaheadInputState = {
+ +typeaheadState: TypeaheadState,
+ +setTypeaheadState: ($Shape<TypeaheadState>) => void,
+};
+
+// This type represents the input state for a particular thread
+export type InputState = {
+ ...BaseInputState,
+ ...TypeaheadInputState,
+};
+
const InputStateContext: React.Context<?InputState> =
React.createContext<?InputState>(null);
diff --git a/web/modals/history/history-entry.react.js b/web/modals/history/history-entry.react.js
--- a/web/modals/history/history-entry.react.js
+++ b/web/modals/history/history-entry.react.js
@@ -17,6 +17,7 @@
type RestoreEntryInfo,
type RestoreEntryResult,
type CalendarQuery,
+ type RestoreEntryPayload,
} from 'lib/types/entry-types.js';
import type { LoadingStatus } from 'lib/types/loading-types.js';
import type { ResolvedThreadInfo } from 'lib/types/thread-types.js';
@@ -49,7 +50,7 @@
};
class HistoryEntry extends React.PureComponent<Props> {
- render() {
+ render(): React.Node {
let deleted = null;
if (this.props.entryInfo.deleted) {
let restore = null;
@@ -133,7 +134,7 @@
this.props.onClick(entryID);
};
- async restoreEntryAction() {
+ async restoreEntryAction(): Promise<RestoreEntryPayload> {
const entryID = this.props.entryInfo.id;
invariant(entryID, 'entry should have ID');
const result = await this.props.restoreEntry({
diff --git a/web/modals/history/history-modal.react.js b/web/modals/history/history-modal.react.js
--- a/web/modals/history/history-modal.react.js
+++ b/web/modals/history/history-modal.react.js
@@ -71,7 +71,7 @@
+revisions: $ReadOnlyArray<HistoryRevisionInfo>,
};
class HistoryModal extends React.PureComponent<Props, State> {
- static defaultProps = { currentEntryID: null };
+ static defaultProps: Partial<Props> = { currentEntryID: null };
constructor(props: Props) {
super(props);
@@ -91,7 +91,7 @@
}
}
- render() {
+ render(): React.Node {
let allHistoryButton = null;
if (this.state.mode === 'entry') {
allHistoryButton = (
diff --git a/web/modals/search/message-search-modal.react.js b/web/modals/search/message-search-modal.react.js
--- a/web/modals/search/message-search-modal.react.js
+++ b/web/modals/search/message-search-modal.react.js
@@ -3,6 +3,7 @@
import * as React from 'react';
import { useModalContext } from 'lib/components/modal-provider.react.js';
+import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
import type { ThreadInfo } from 'lib/types/thread-types.js';
import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js';
@@ -40,7 +41,7 @@
}, [setQuery, input, searchMessages, threadInfo.id]);
const onKeyDown = React.useCallback(
- event => {
+ (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
onPressSearch();
}
@@ -76,7 +77,7 @@
}, [clearTooltip, possiblyLoadMoreMessages]);
const renderItem = React.useCallback(
- item => (
+ (item: ChatMessageInfoItem) => (
<MessageResult
key={item.messageInfo.id}
item={item}
diff --git a/web/modals/threads/gallery/thread-settings-media-gallery.react.js b/web/modals/threads/gallery/thread-settings-media-gallery.react.js
--- a/web/modals/threads/gallery/thread-settings-media-gallery.react.js
+++ b/web/modals/threads/gallery/thread-settings-media-gallery.react.js
@@ -1,5 +1,6 @@
// @flow
+import invariant from 'invariant';
import * as React from 'react';
import { useFetchThreadMedia } from 'lib/actions/thread-actions.js';
@@ -142,8 +143,9 @@
}, [tab, mediaInfos, onClick]);
const handleScroll = React.useCallback(
- async event => {
+ async (event: SyntheticEvent<HTMLDivElement>) => {
const container = event.target;
+ invariant(container instanceof HTMLDivElement, 'target not div');
// Load more data when the user is within 1000 pixels of the end
const buffer = 1000;
diff --git a/web/modals/threads/members/add-members-modal.react.js b/web/modals/threads/members/add-members-modal.react.js
--- a/web/modals/threads/members/add-members-modal.react.js
+++ b/web/modals/threads/members/add-members-modal.react.js
@@ -78,7 +78,7 @@
const userSearchResultsWithENSNames = useENSNames(userSearchResults);
const onSwitchUser = React.useCallback(
- userID =>
+ (userID: string) =>
setPendingUsersToAdd(users => {
const newUsers = new Set(users);
if (newUsers.has(userID)) {
diff --git a/web/modals/threads/members/member.react.js b/web/modals/threads/members/member.react.js
--- a/web/modals/threads/members/member.react.js
+++ b/web/modals/threads/members/member.react.js
@@ -44,7 +44,7 @@
const roleName = roles.get(memberInfo.id)?.name;
const onMenuChange = React.useCallback(
- menuOpen => {
+ (menuOpen: boolean) => {
if (menuOpen) {
setOpenMenu(() => memberInfo.id);
} else {
diff --git a/web/modals/threads/sidebars/sidebar.react.js b/web/modals/threads/sidebars/sidebar.react.js
--- a/web/modals/threads/sidebars/sidebar.react.js
+++ b/web/modals/threads/sidebars/sidebar.react.js
@@ -31,7 +31,7 @@
const navigateToThread = useOnClickThread(threadInfo);
const onClickThread = React.useCallback(
- event => {
+ (event: SyntheticEvent<HTMLButtonElement>) => {
popModal();
navigateToThread(event);
},
diff --git a/web/modals/threads/subchannels/subchannel.react.js b/web/modals/threads/subchannels/subchannel.react.js
--- a/web/modals/threads/subchannels/subchannel.react.js
+++ b/web/modals/threads/subchannels/subchannel.react.js
@@ -40,7 +40,7 @@
const navigateToThread = useOnClickThread(threadInfo);
const onClickThread = React.useCallback(
- event => {
+ (event: SyntheticEvent<HTMLButtonElement>) => {
popModal();
navigateToThread(event);
},
diff --git a/web/modals/update-modal.react.js b/web/modals/update-modal.react.js
--- a/web/modals/update-modal.react.js
+++ b/web/modals/update-modal.react.js
@@ -73,7 +73,7 @@
React.useEffect(
() =>
- electron?.onNewVersionAvailable?.(version => {
+ electron?.onNewVersionAvailable?.((version: string) => {
// On these versions we want to update immediately because there's
// an issue if the user decides to update 10min after showing the modal
if (electron?.version === '1.0.0' || electron?.version === '2.0.0') {
diff --git a/web/push-notif/push-notifs-handler.js b/web/push-notif/push-notifs-handler.js
--- a/web/push-notif/push-notifs-handler.js
+++ b/web/push-notif/push-notifs-handler.js
@@ -28,7 +28,7 @@
React.useEffect(
() =>
- electron?.onDeviceTokenRegistered?.(token => {
+ electron?.onDeviceTokenRegistered?.((token: ?string) => {
dispatchActionPromise(
setDeviceTokenActionTypes,
callSetDeviceToken(token),
@@ -41,20 +41,22 @@
React.useEffect(
() =>
- electron?.onNotificationClicked?.(({ threadID }) => {
- const convertedThreadID = convertNonPendingIDToNewSchema(
- threadID,
- ashoatKeyserverID,
- );
-
- const payload = {
- chatMode: 'view',
- activeChatThreadID: convertedThreadID,
- tab: 'chat',
- };
-
- dispatch({ type: updateNavInfoActionType, payload });
- }),
+ electron?.onNotificationClicked?.(
+ ({ threadID }: { +threadID: string }) => {
+ const convertedThreadID = convertNonPendingIDToNewSchema(
+ threadID,
+ ashoatKeyserverID,
+ );
+
+ const payload = {
+ chatMode: 'view',
+ activeChatThreadID: convertedThreadID,
+ tab: 'chat',
+ };
+
+ dispatch({ type: updateNavInfoActionType, payload });
+ },
+ ),
[dispatch],
);
}
diff --git a/web/redux/focus-handler.react.js b/web/redux/focus-handler.react.js
--- a/web/redux/focus-handler.react.js
+++ b/web/redux/focus-handler.react.js
@@ -35,7 +35,7 @@
const dispatch = useDispatch();
const curWindowActive = useSelector(state => state.windowActive);
const updateRedux = React.useCallback(
- windowActive => {
+ (windowActive: boolean) => {
if (windowActive === curWindowActive) {
return;
}
diff --git a/web/redux/initial-state-gate.js b/web/redux/initial-state-gate.js
--- a/web/redux/initial-state-gate.js
+++ b/web/redux/initial-state-gate.js
@@ -61,7 +61,7 @@
const childFunction = React.useCallback(
// This argument is passed from `PersistGate`. It means that the state is
// rehydrated and we can start fetching the initial info.
- bootstrapped => {
+ (bootstrapped: boolean) => {
if (bootstrapped && initialStateLoaded) {
return children;
} else {
diff --git a/web/redux/persist.js b/web/redux/persist.js
--- a/web/redux/persist.js
+++ b/web/redux/persist.js
@@ -41,7 +41,7 @@
declare var keyserverURL: string;
const migrations = {
- [1]: async state => {
+ [1]: async (state: any) => {
const {
primaryIdentityPublicKey,
...stateWithoutPrimaryIdentityPublicKey
@@ -56,7 +56,7 @@
},
};
},
- [2]: async state => {
+ [2]: async (state: AppState) => {
return state;
},
[3]: async (state: AppState) => {
@@ -90,7 +90,7 @@
return newState;
},
- [4]: async state => {
+ [4]: async (state: any) => {
const { lastCommunicatedPlatformDetails, keyserverStore, ...rest } = state;
return {
@@ -107,7 +107,7 @@
},
};
},
- [5]: async state => {
+ [5]: async (state: any) => {
const databaseModule = await getDatabaseModule();
const isDatabaseSupported = await databaseModule.isDatabaseSupported();
if (!isDatabaseSupported) {
@@ -135,7 +135,7 @@
return state;
},
- [6]: async state => ({
+ [6]: async (state: AppState) => ({
...state,
integrityStore: { threadHashes: {}, threadHashingStatus: 'starting' },
}),
@@ -166,11 +166,11 @@
},
};
},
- [8]: async state => ({
+ [8]: async (state: AppState) => ({
...state,
globalThemeInfo: defaultGlobalThemeInfo,
}),
- [9]: async state => ({
+ [9]: async (state: AppState) => ({
...state,
keyserverStore: {
...state.keyserverStore,
diff --git a/web/redux/visibility-handler.react.js b/web/redux/visibility-handler.react.js
--- a/web/redux/visibility-handler.react.js
+++ b/web/redux/visibility-handler.react.js
@@ -11,9 +11,12 @@
function VisibilityHandler(): React.Node {
const visibility = useVisibility();
const [visible, setVisible] = React.useState(!visibility.hidden());
- const onVisibilityChange = React.useCallback((event, state: string) => {
- setVisible(state === 'visible');
- }, []);
+ const onVisibilityChange = React.useCallback(
+ (event: mixed, state: string) => {
+ setVisible(state === 'visible');
+ },
+ [],
+ );
React.useEffect(() => {
const listener = visibility.change(onVisibilityChange);
return () => {
@@ -24,7 +27,7 @@
const dispatch = useDispatch();
const curForeground = useIsAppForegrounded();
const updateRedux = React.useCallback(
- foreground => {
+ (foreground: boolean) => {
if (foreground === curForeground) {
return;
}
diff --git a/web/settings/password-change-modal.js b/web/settings/password-change-modal.js
--- a/web/settings/password-change-modal.js
+++ b/web/settings/password-change-modal.js
@@ -56,7 +56,7 @@
this.newPasswordInput.focus();
}
- render() {
+ render(): React.Node {
let errorMsg;
if (this.state.errorMessage) {
errorMsg = (
@@ -188,7 +188,7 @@
);
};
- async changeUserSettingsAction() {
+ async changeUserSettingsAction(): Promise<void> {
try {
await this.props.changeUserPassword({
updatedFields: {
diff --git a/web/settings/tunnelbroker-test.react.js b/web/settings/tunnelbroker-test.react.js
--- a/web/settings/tunnelbroker-test.react.js
+++ b/web/settings/tunnelbroker-test.react.js
@@ -25,7 +25,7 @@
const messageInput = React.useRef(null);
const onSubmit = React.useCallback(
- async event => {
+ async (event: SyntheticEvent<HTMLButtonElement>) => {
event.preventDefault();
setLoading(true);
diff --git a/web/utils/tooltip-action-utils.js b/web/utils/tooltip-action-utils.js
--- a/web/utils/tooltip-action-utils.js
+++ b/web/utils/tooltip-action-utils.js
@@ -410,7 +410,7 @@
}, [messageTimestamp, tooltipActions]);
const createMessageTooltip = React.useCallback(
- tooltipPositionStyle => (
+ (tooltipPositionStyle: TooltipPositionStyle) => (
<MessageTooltip
actions={tooltipActions}
messageTimestamp={messageTimestamp}
diff --git a/web/utils/typeahead-utils.js b/web/utils/typeahead-utils.js
--- a/web/utils/typeahead-utils.js
+++ b/web/utils/typeahead-utils.js
@@ -75,14 +75,21 @@
caretLeftOffset,
};
}
-export type GetTypeaheadTooltipActionsParams<SuggestionItemType> = {
+type MentionTypeaheadSharedParams = {
+inputStateDraft: string,
+inputStateSetDraft: (draft: string) => mixed,
+inputStateSetTextCursorPosition: (newPosition: number) => mixed,
- +suggestions: $ReadOnlyArray<SuggestionItemType>,
+textBeforeAtSymbol: string,
+query: string,
};
+export type GetTypeaheadTooltipActionsParams<SuggestionItemType> = {
+ ...MentionTypeaheadSharedParams,
+ +suggestions: $ReadOnlyArray<SuggestionItemType>,
+};
+export type MentionTypeaheadTooltipActionExecuteHandlerParams = {
+ ...MentionTypeaheadSharedParams,
+ +mentionText: string,
+};
function mentionTypeaheadTooltipActionExecuteHandler({
textBeforeAtSymbol,
inputStateDraft,
@@ -90,7 +97,7 @@
mentionText,
inputStateSetDraft,
inputStateSetTextCursorPosition,
-}) {
+}: MentionTypeaheadTooltipActionExecuteHandlerParams) {
const { newText, newSelectionStart } = getNewTextAndSelection(
textBeforeAtSymbol,
inputStateDraft,

File Metadata

Mime Type
text/plain
Expires
Thu, Dec 26, 7:43 AM (11 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2704347
Default Alt Text
D9771.id33096.diff (42 KB)

Event Timeline