Page MenuHomePhabricator

D3271.diff
No OneTemporary

D3271.diff

diff --git a/web/app.react.js b/web/app.react.js
--- a/web/app.react.js
+++ b/web/app.react.js
@@ -35,6 +35,7 @@
import Chat from './chat/chat.react';
import InputStateContainer from './input/input-state-container.react';
import LoadingIndicator from './loading-indicator.react';
+import { ModalProvider, useModalContext } from './modals/modal-provider.react';
import DisconnectedBar from './redux/disconnected-bar';
import DisconnectedBarVisibilityHandler from './redux/disconnected-bar-visibility-handler';
import FocusHandler from './redux/focus-handler.react';
@@ -83,15 +84,9 @@
+activeThreadCurrentlyUnread: boolean,
// Redux dispatch functions
+dispatch: Dispatch,
-};
-type State = {
+modal: ?React.Node,
};
-class App extends React.PureComponent<Props, State> {
- state: State = {
- modal: null,
- };
-
+class App extends React.PureComponent<Props> {
componentDidMount() {
const {
navInfo,
@@ -136,16 +131,14 @@
if (this.props.loggedIn) {
content = this.renderMainContent();
} else {
- content = (
- <Splash setModal={this.setModal} currentModal={this.state.modal} />
- );
+ content = <Splash />;
}
return (
<DndProvider backend={HTML5Backend}>
<FocusHandler />
<VisibilityHandler />
{content}
- {this.state.modal}
+ {this.props.modal}
</DndProvider>
);
}
@@ -153,11 +146,9 @@
renderMainContent() {
let mainContent;
if (this.props.navInfo.tab === 'calendar') {
- mainContent = (
- <Calendar setModal={this.setModal} url={this.props.location.pathname} />
- );
+ mainContent = <Calendar url={this.props.location.pathname} />;
} else if (this.props.navInfo.tab === 'chat') {
- mainContent = <Chat setModal={this.setModal} />;
+ mainContent = <Chat />;
}
return (
@@ -177,23 +168,15 @@
</div>
</div>
</header>
- <InputStateContainer setModal={this.setModal}>
+ <InputStateContainer>
<div className={css['main-content-container']}>
<div className={css['main-content']}>{mainContent}</div>
</div>
</InputStateContainer>
- <LeftLayoutAside setModal={this.setModal} />
+ <LeftLayoutAside />
</div>
);
}
-
- setModal = (modal: ?React.Node) => {
- this.setState({ modal });
- };
-
- clearModal() {
- this.setModal(null);
- }
}
const fetchEntriesLoadingStatusSelector = createLoadingStatusSelector(
@@ -230,6 +213,7 @@
);
const dispatch = useDispatch();
+ const modalContext = useModalContext();
return (
<App
@@ -240,9 +224,18 @@
mostRecentReadThread={mostRecentReadThread}
activeThreadCurrentlyUnread={activeThreadCurrentlyUnread}
dispatch={dispatch}
+ modal={modalContext.modal}
/>
);
},
);
-export default ConnectedApp;
+function AppWithProvider(props: BaseProps): React.Node {
+ return (
+ <ModalProvider>
+ <ConnectedApp {...props} />
+ </ModalProvider>
+ );
+}
+
+export default AppWithProvider;
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
@@ -44,7 +44,6 @@
import FilterPanel from './filter-panel.react';
type BaseProps = {
- +setModal: (modal: ?React.Node) => void,
+url: string,
};
type Props = {
@@ -149,7 +148,6 @@
<Day
dayString={dayString}
entryInfos={entries}
- setModal={this.props.setModal}
key={curDayOfMonth}
startingTabIndex={tabIndex}
/>,
@@ -166,7 +164,7 @@
let calendarContentStyle = null;
let filterButtonStyle = null;
if (this.state.filterPanelOpen) {
- filterPanel = <FilterPanel setModal={this.props.setModal} />;
+ filterPanel = <FilterPanel />;
calendarContentStyle = { marginLeft: '300px' };
filterButtonStyle = { backgroundColor: 'rgba(0,0,0,0.67)' };
}
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
@@ -23,6 +23,7 @@
import LogInFirstModal from '../modals/account/log-in-first-modal.react';
import HistoryModal from '../modals/history/history-modal.react';
+import { useModalContext } from '../modals/modal-provider.react';
import { useSelector } from '../redux/redux-utils';
import { htmlTargetFromEvent } from '../vector-utils';
import { AddVector, HistoryVector } from '../vectors.react';
@@ -34,7 +35,6 @@
type BaseProps = {
+dayString: string,
+entryInfos: $ReadOnlyArray<EntryInfo>,
- +setModal: (modal: ?React.Node) => void,
+startingTabIndex: number,
};
type Props = {
@@ -45,6 +45,7 @@
+nextLocalID: number,
+timeZone: ?string,
+dispatch: Dispatch,
+ +setModal: (modal: ?React.Node) => void,
};
type State = {
+pickerOpen: boolean,
@@ -107,7 +108,6 @@
<Entry
entryInfo={entryInfo}
focusOnFirstEntryNewerThan={this.focusOnFirstEntryNewerThan}
- setModal={this.props.setModal}
tabIndex={this.props.startingTabIndex + i}
key={key}
innerRef={this.entryRef}
@@ -216,12 +216,7 @@
createNewEntry = (threadID: string) => {
if (!this.props.loggedIn) {
- this.props.setModal(
- <LogInFirstModal
- inOrderTo="edit this calendar"
- setModal={this.props.setModal}
- />,
- );
+ this.props.setModal(<LogInFirstModal inOrderTo="edit this calendar" />);
return;
}
const viewerID = this.props.viewerID;
@@ -240,11 +235,7 @@
onHistory = (event: SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
this.props.setModal(
- <HistoryModal
- mode="day"
- dayString={this.props.dayString}
- onClose={this.clearModal}
- />,
+ <HistoryModal mode="day" dayString={this.props.dayString} />,
);
};
@@ -258,10 +249,6 @@
entry.focus();
}
};
-
- clearModal = () => {
- this.props.setModal(null);
- };
}
const ConnectedDay: React.ComponentType<BaseProps> = React.memo<BaseProps>(
@@ -275,6 +262,7 @@
const nextLocalID = useSelector(state => state.nextLocalID);
const timeZone = useSelector(state => state.timeZone);
const dispatch = useDispatch();
+ const modalContext = useModalContext();
return (
<Day
@@ -285,6 +273,7 @@
nextLocalID={nextLocalID}
timeZone={timeZone}
dispatch={dispatch}
+ setModal={modalContext.setModal}
/>
);
},
diff --git a/web/calendar/entry.react.js b/web/calendar/entry.react.js
--- a/web/calendar/entry.react.js
+++ b/web/calendar/entry.react.js
@@ -45,6 +45,7 @@
import LogInFirstModal from '../modals/account/log-in-first-modal.react';
import ConcurrentModificationModal from '../modals/concurrent-modification-modal.react';
import HistoryModal from '../modals/history/history-modal.react';
+import { useModalContext } from '../modals/modal-provider.react';
import { useSelector } from '../redux/redux-utils';
import { nonThreadCalendarQuery } from '../selectors/nav-selectors';
import { HistoryVector, DeleteVector } from '../vectors.react';
@@ -54,7 +55,6 @@
+innerRef: (key: string, me: Entry) => void,
+entryInfo: EntryInfo,
+focusOnFirstEntryNewerThan: (time: number) => void,
- +setModal: (modal: ?React.Node) => void,
+tabIndex: number,
};
type Props = {
@@ -68,6 +68,8 @@
+createEntry: (info: CreateEntryInfo) => Promise<CreateEntryPayload>,
+saveEntry: (info: SaveEntryInfo) => Promise<SaveEntryResult>,
+deleteEntry: (info: DeleteEntryInfo) => Promise<DeleteEntryResult>,
+ +setModal: (modal: ?React.Node) => void,
+ +clearModal: () => void,
};
type State = {
+focused: boolean,
@@ -257,12 +259,7 @@
onChange: (event: SyntheticEvent<HTMLTextAreaElement>) => void = event => {
if (!this.props.loggedIn) {
- this.props.setModal(
- <LogInFirstModal
- inOrderTo="edit this calendar"
- setModal={this.props.setModal}
- />,
- );
+ this.props.setModal(<LogInFirstModal inOrderTo="edit this calendar" />);
return;
}
const target = event.target;
@@ -389,13 +386,10 @@
type: concurrentModificationResetActionType,
payload: { id: entryID, dbText: e.payload.db },
});
- this.clearModal();
+ this.props.clearModal();
};
this.props.setModal(
- <ConcurrentModificationModal
- onClose={this.clearModal}
- onRefresh={onRefresh}
- />,
+ <ConcurrentModificationModal onRefresh={onRefresh} />,
);
}
throw e;
@@ -405,12 +399,7 @@
onDelete: (event: SyntheticEvent<HTMLAnchorElement>) => void = event => {
event.preventDefault();
if (!this.props.loggedIn) {
- this.props.setModal(
- <LogInFirstModal
- inOrderTo="edit this calendar"
- setModal={this.props.setModal}
- />,
- );
+ this.props.setModal(<LogInFirstModal inOrderTo="edit this calendar" />);
return;
}
this.dispatchDelete(this.props.entryInfo.id, true);
@@ -459,15 +448,10 @@
this.props.entryInfo.month,
this.props.entryInfo.day,
)}
- onClose={this.clearModal}
currentEntryID={this.props.entryInfo.id}
/>,
);
};
-
- clearModal: () => void = () => {
- this.props.setModal(null);
- };
}
export type InnerEntry = Entry;
@@ -492,6 +476,8 @@
const dispatchActionPromise = useDispatchActionPromise();
const dispatch = useDispatch();
+ const modalContext = useModalContext();
+
return (
<Entry
{...props}
@@ -504,6 +490,8 @@
deleteEntry={callDeleteEntry}
dispatchActionPromise={dispatchActionPromise}
dispatch={dispatch}
+ setModal={modalContext.setModal}
+ clearModal={modalContext.clearModal}
/>
);
},
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
@@ -26,6 +26,7 @@
} from 'lib/types/filter-types';
import type { Dispatch } from 'lib/types/redux-types';
+import { useModalContext } from '../modals/modal-provider.react';
import ThreadSettingsModal from '../modals/threads/thread-settings-modal.react';
import { useSelector } from '../redux/redux-utils';
import {
@@ -35,16 +36,13 @@
import { MagnifyingGlass } from '../vectors.react';
import css from './filter-panel.css';
-type BaseProps = {
- +setModal: (modal: ?React.Node) => void,
-};
type Props = {
- ...BaseProps,
+filterThreadInfos: () => $ReadOnlyArray<FilterThreadInfo>,
+filterThreadSearchIndex: () => SearchIndex,
+filteredThreadIDs: ?$ReadOnlySet<string>,
+includeDeleted: boolean,
+dispatch: Dispatch,
+ +setModal: (modal: ?React.Node) => void,
};
type State = {
+query: string,
@@ -204,9 +202,7 @@
}
onClickSettings = (threadID: string) => {
- this.props.setModal(
- <ThreadSettingsModal threadID={threadID} onClose={this.clearModal} />,
- );
+ this.props.setModal(<ThreadSettingsModal threadID={threadID} />);
};
onChangeQuery = (event: SyntheticEvent<HTMLInputElement>) => {
@@ -238,10 +234,6 @@
},
});
};
-
- clearModal = () => {
- this.props.setModal(null);
- };
}
type ItemProps = {
@@ -369,22 +361,23 @@
};
}
-const ConnectedFilterPanel: React.ComponentType<BaseProps> = React.memo<BaseProps>(
- function ConnectedFilterPanel(props) {
+const ConnectedFilterPanel: React.ComponentType<{}> = React.memo<{}>(
+ function ConnectedFilterPanel(): React.Node {
const filteredThreadIDs = useSelector(filteredThreadIDsSelector);
const filterThreadInfos = useSelector(webFilterThreadInfos);
const filterThreadSearchIndex = useSelector(webFilterThreadSearchIndex);
const includeDeleted = useSelector(includeDeletedSelector);
const dispatch = useDispatch();
+ const modalContext = useModalContext();
return (
<FilterPanel
- {...props}
filteredThreadIDs={filteredThreadIDs}
filterThreadInfos={filterThreadInfos}
filterThreadSearchIndex={filterThreadSearchIndex}
includeDeleted={includeDeleted}
dispatch={dispatch}
+ setModal={modalContext.setModal}
/>
);
},
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
@@ -47,11 +47,7 @@
import RelationshipPrompt from './relationship-prompt/relationship-prompt';
import ThreadTopBar from './thread-top-bar.react';
-type BaseProps = {
- +setModal: (modal: ?React.Node) => void,
-};
type PassedProps = {
- ...BaseProps,
// Redux state
+activeChatThreadID: ?string,
+threadInfo: ?ThreadInfo,
@@ -191,7 +187,7 @@
</div>
);
}
- const { threadInfo, setModal } = this.props;
+ const { threadInfo } = this.props;
invariant(threadInfo, 'ThreadInfo should be set if messageListData is');
return (
<Message
@@ -199,7 +195,6 @@
threadInfo={threadInfo}
setMouseOverMessagePosition={this.setMouseOverMessagePosition}
mouseOverMessagePosition={this.state.mouseOverMessagePosition}
- setModal={setModal}
timeZone={this.props.timeZone}
key={ChatMessageList.keyExtractor(item)}
/>
@@ -383,9 +378,8 @@
registerFetchKey(fetchMessagesBeforeCursorActionTypes);
registerFetchKey(fetchMostRecentMessagesActionTypes);
-
-const ConnectedChatMessageList: React.ComponentType<BaseProps> = React.memo<BaseProps>(
- function ConnectedChatMessageList(props) {
+const ConnectedChatMessageList: React.ComponentType<{}> = React.memo<{}>(
+ function ConnectedChatMessageList(): React.Node {
const userAgent = useSelector(state => state.userAgent);
const supportsReverseFlex = React.useMemo(() => {
const browser = detectBrowser(userAgent);
@@ -475,7 +469,6 @@
return (
<MessageListContext.Provider value={messageListContext}>
<ChatMessageList
- {...props}
activeChatThreadID={activeChatThreadID}
threadInfo={threadInfo}
messageListData={messageListData}
diff --git a/web/chat/chat-tabs.react.js b/web/chat/chat-tabs.react.js
--- a/web/chat/chat-tabs.react.js
+++ b/web/chat/chat-tabs.react.js
@@ -11,10 +11,7 @@
import ChatThreadTab from './chat-thread-tab.react';
import { ThreadListContext } from './thread-list-provider';
-type Props = {
- +setModal: (modal: ?React.Node) => void,
-};
-function ChatTabs(props: Props): React.Node {
+function ChatTabs(): React.Node {
let backgroundTitle = 'Background';
const unreadBackgroundCountVal = useSelector(unreadBackgroundCount);
if (unreadBackgroundCountVal) {
@@ -52,7 +49,7 @@
/>
</div>
<div className={css.threadList}>
- <ChatThreadList setModal={props.setModal} />
+ <ChatThreadList />
</div>
</div>
);
diff --git a/web/chat/chat-thread-list-item.react.js b/web/chat/chat-thread-list-item.react.js
--- a/web/chat/chat-thread-list-item.react.js
+++ b/web/chat/chat-thread-list-item.react.js
@@ -21,10 +21,9 @@
type Props = {
+item: ChatThreadItem,
- +setModal: (modal: ?React.Node) => void,
};
function ChatThreadListItem(props: Props): React.Node {
- const { item, setModal } = props;
+ const { item } = props;
const {
threadInfo,
lastUpdatedTimeIncludingSidebars,
@@ -105,7 +104,6 @@
threadInfo={item.threadInfo}
unread={sidebarItem.unread}
showingSidebarsInline={sidebarItem.showingSidebarsInline}
- setModal={setModal}
key="seeMore"
/>
);
diff --git a/web/chat/chat-thread-list-see-more-sidebars.react.js b/web/chat/chat-thread-list-see-more-sidebars.react.js
--- a/web/chat/chat-thread-list-see-more-sidebars.react.js
+++ b/web/chat/chat-thread-list-see-more-sidebars.react.js
@@ -6,21 +6,20 @@
import type { ThreadInfo } from 'lib/types/thread-types';
import SidebarListModal from '../modals/chat/sidebar-list-modal.react';
+import { useModalContext } from '../modals/modal-provider.react';
import css from './chat-thread-list.css';
type Props = {
+threadInfo: ThreadInfo,
+unread: boolean,
+showingSidebarsInline: boolean,
- +setModal: (modal: ?React.Node) => void,
};
function ChatThreadListSeeMoreSidebars(props: Props): React.Node {
- const { unread, showingSidebarsInline, setModal, threadInfo } = props;
+ const { unread, showingSidebarsInline, threadInfo } = props;
+ const { setModal } = useModalContext();
+
const onClick = React.useCallback(
- () =>
- setModal(
- <SidebarListModal setModal={setModal} threadInfo={threadInfo} />,
- ),
+ () => setModal(<SidebarListModal threadInfo={threadInfo} />),
[setModal, threadInfo],
);
const buttonText = showingSidebarsInline ? 'See more...' : 'See sidebars...';
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
@@ -10,12 +10,7 @@
import { ThreadListContext } from './thread-list-provider';
import ThreadListSearch from './thread-list-search.react';
-type Props = {
- +setModal: (modal: ?React.Node) => void,
-};
-
-function ChatThreadList(props: Props): React.Node {
- const { setModal } = props;
+function ChatThreadList(): React.Node {
const threadListContext = React.useContext(ThreadListContext);
invariant(
threadListContext,
@@ -31,17 +26,13 @@
const threadComponents: React.Node[] = React.useMemo(() => {
const threads = threadList.map(item => (
- <ChatThreadListItem
- item={item}
- key={item.threadInfo.id}
- setModal={setModal}
- />
+ <ChatThreadListItem item={item} key={item.threadInfo.id} />
));
if (threads.length === 0 && isBackground) {
threads.push(<EmptyItem key="emptyItem" />);
}
return threads;
- }, [threadList, isBackground, setModal]);
+ }, [threadList, isBackground]);
return (
<div className={css.threadListContainer}>
diff --git a/web/chat/chat.react.js b/web/chat/chat.react.js
--- a/web/chat/chat.react.js
+++ b/web/chat/chat.react.js
@@ -6,16 +6,13 @@
import ChatTabs from './chat-tabs.react';
import { ThreadListProvider } from './thread-list-provider';
-type Props = {
- +setModal: (modal: ?React.Node) => void,
-};
-function Chat(props: Props): React.Node {
+function Chat(): React.Node {
return (
<>
<ThreadListProvider>
- <ChatTabs setModal={props.setModal} />
+ <ChatTabs />
</ThreadListProvider>
- <ChatMessageList setModal={props.setModal} />
+ <ChatMessageList />
</>
);
}
diff --git a/web/chat/message.react.js b/web/chat/message.react.js
--- a/web/chat/message.react.js
+++ b/web/chat/message.react.js
@@ -24,7 +24,6 @@
messagePositionInfo: MessagePositionInfo,
) => void,
+mouseOverMessagePosition: ?OnMessagePositionWithContainerInfo,
- +setModal: (modal: ?React.Node) => void,
+timeZone: ?string,
};
function Message(props: Props): React.Node {
@@ -58,7 +57,6 @@
threadInfo={props.threadInfo}
setMouseOverMessagePosition={props.setMouseOverMessagePosition}
mouseOverMessagePosition={props.mouseOverMessagePosition}
- setModal={props.setModal}
/>
);
} else {
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
@@ -24,7 +24,6 @@
messagePositionInfo: MessagePositionInfo,
) => void,
+mouseOverMessagePosition: ?OnMessagePositionWithContainerInfo,
- +setModal: (modal: ?React.Node) => void,
};
type Props = {
...BaseProps,
@@ -33,7 +32,7 @@
};
class MultimediaMessage extends React.PureComponent<Props> {
render() {
- const { item, setModal, inputState } = this.props;
+ const { item, inputState } = this.props;
invariant(
item.messageInfo.type === messageTypes.IMAGES ||
item.messageInfo.type === messageTypes.MULTIMEDIA,
@@ -52,7 +51,6 @@
<Multimedia
uri={singleMedia.uri}
pendingUpload={pendingUpload}
- setModal={setModal}
multimediaCSSClass={css.multimedia}
multimediaImageCSSClass={css.multimediaImage}
key={singleMedia.id}
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
@@ -73,13 +73,13 @@
import { validateFile, preloadImage } from '../media/media-utils';
import InvalidUploadModal from '../modals/chat/invalid-upload.react';
+import { useModalContext } from '../modals/modal-provider.react';
import { useSelector } from '../redux/redux-utils';
import { nonThreadCalendarQuery } from '../selectors/nav-selectors';
import { type PendingMultimediaUpload, InputStateContext } from './input-state';
type BaseProps = {
+children: React.Node,
- +setModal: (modal: ?React.Node) => void,
};
type Props = {
...BaseProps,
@@ -108,6 +108,7 @@
text: string,
) => Promise<SendMessageResult>,
+newThread: (request: ClientNewThreadRequest) => Promise<NewThreadResult>,
+ +setModal: (modal: ?React.Node) => void,
};
type State = {
+pendingUploads: {
@@ -518,7 +519,7 @@
);
if (appendResults.some(({ result }) => !result.success)) {
- setModal(<InvalidUploadModal setModal={setModal} />);
+ setModal(<InvalidUploadModal />);
const time = Date.now() - selectionTime;
const reports = [];
@@ -1245,6 +1246,7 @@
const callNewThread = useServerCall(newThread);
const dispatch = useDispatch();
const dispatchActionPromise = useDispatchActionPromise();
+ const modalContext = useModalContext();
return (
<InputStateContainer
@@ -1262,6 +1264,7 @@
newThread={callNewThread}
dispatch={dispatch}
dispatchActionPromise={dispatchActionPromise}
+ setModal={modalContext.setModal}
/>
);
},
diff --git a/web/media/multimedia-modal.react.js b/web/media/multimedia-modal.react.js
--- a/web/media/multimedia-modal.react.js
+++ b/web/media/multimedia-modal.react.js
@@ -4,12 +4,18 @@
import * as React from 'react';
import { XCircle as XCircleIcon } from 'react-feather';
+import { useModalContext } from '../modals/modal-provider.react';
import css from './media.css';
-type Props = {
+type BaseProps = {
+uri: string,
- +setModal: (modal: ?React.Node) => void,
};
+
+type Props = {
+ ...BaseProps,
+ +clearModal: (modal: ?React.Node) => void,
+};
+
class MultimediaModal extends React.PureComponent<Props> {
overlay: ?HTMLDivElement;
@@ -29,7 +35,7 @@
>
<img src={this.props.uri} />
<XCircleIcon
- onClick={this.close}
+ onClick={this.props.clearModal}
className={css.closeMultimediaModal}
/>
</div>
@@ -44,7 +50,7 @@
event: SyntheticEvent<HTMLDivElement>,
) => void = event => {
if (event.target === this.overlay) {
- this.close();
+ this.props.clearModal();
}
};
@@ -52,13 +58,15 @@
event: SyntheticKeyboardEvent<HTMLDivElement>,
) => void = event => {
if (event.keyCode === 27) {
- this.close();
+ this.props.clearModal();
}
};
+}
- close: () => void = () => {
- this.props.setModal(null);
- };
+function ConnectedMultiMediaModal(props: BaseProps): React.Node {
+ const modalContext = useModalContext();
+
+ return <MultimediaModal {...props} clearModal={modalContext.clearModal} />;
}
-export default MultimediaModal;
+export default ConnectedMultiMediaModal;
diff --git a/web/media/multimedia.react.js b/web/media/multimedia.react.js
--- a/web/media/multimedia.react.js
+++ b/web/media/multimedia.react.js
@@ -11,17 +11,22 @@
} from 'react-feather';
import { type PendingMultimediaUpload } from '../input/input-state';
+import { useModalContext } from '../modals/modal-provider.react';
import css from './media.css';
import MultimediaModal from './multimedia-modal.react';
+type BaseProps = {
+ +uri: string,
+ +pendingUpload?: ?PendingMultimediaUpload,
+ +remove?: (uploadID: string) => void,
+ +multimediaCSSClass: string,
+ +multimediaImageCSSClass: string,
+};
type Props = {
- uri: string,
- pendingUpload?: ?PendingMultimediaUpload,
- remove?: (uploadID: string) => void,
- setModal?: (modal: ?React.Node) => void,
- multimediaCSSClass: string,
- multimediaImageCSSClass: string,
+ ...BaseProps,
+ +setModal: (modal: ?React.Node) => void,
};
+
class Multimedia extends React.PureComponent<Props> {
componentDidUpdate(prevProps: Props) {
const { uri, pendingUpload } = this.props;
@@ -108,8 +113,14 @@
const { setModal, uri } = this.props;
invariant(setModal, 'should be set');
- setModal(<MultimediaModal uri={uri} setModal={setModal} />);
+ setModal(<MultimediaModal uri={uri} />);
};
}
-export default Multimedia;
+function ConnectedMultimediaContainer(props: BaseProps): React.Node {
+ const modalContext = useModalContext();
+
+ return <Multimedia {...props} setModal={modalContext.setModal} />;
+}
+
+export default ConnectedMultimediaContainer;
diff --git a/web/modals/account/log-in-first-modal.react.js b/web/modals/account/log-in-first-modal.react.js
--- a/web/modals/account/log-in-first-modal.react.js
+++ b/web/modals/account/log-in-first-modal.react.js
@@ -2,18 +2,25 @@
import * as React from 'react';
+import { useModalContext } from '../../modals/modal-provider.react';
import css from '../../style.css';
import Modal from '../modal.react';
import LogInModal from './log-in-modal.react';
-type Props = {
+type BaseProps = {
+inOrderTo: string,
+};
+
+type Props = {
+ ...BaseProps,
+setModal: (modal: ?React.Node) => void,
+ +clearModal: () => void,
};
+
class LogInFirstModal extends React.PureComponent<Props> {
render(): React.Node {
return (
- <Modal name="Log in" onClose={this.clearModal}>
+ <Modal name="Log in" onClose={this.props.clearModal}>
<div className={css['modal-body']}>
<p>
{`In order to ${this.props.inOrderTo}, you'll first need to `}
@@ -31,14 +38,22 @@
);
}
- clearModal: () => void = () => {
- this.props.setModal(null);
- };
-
onClickLogIn: (event: SyntheticEvent<HTMLAnchorElement>) => void = event => {
event.preventDefault();
- this.props.setModal(<LogInModal setModal={this.props.setModal} />);
+ this.props.setModal(<LogInModal />);
};
}
-export default LogInFirstModal;
+function ConnectedLoginFirstModal(props: BaseProps): React.Node {
+ const modalContext = useModalContext();
+
+ return (
+ <LogInFirstModal
+ {...props}
+ setModal={modalContext.setModal}
+ clearModal={modalContext.clearModal}
+ />
+ );
+}
+
+export default ConnectedLoginFirstModal;
diff --git a/web/modals/account/log-in-modal.react.js b/web/modals/account/log-in-modal.react.js
--- a/web/modals/account/log-in-modal.react.js
+++ b/web/modals/account/log-in-modal.react.js
@@ -25,18 +25,16 @@
import { useSelector } from '../../redux/redux-utils';
import { webLogInExtraInfoSelector } from '../../selectors/account-selectors';
import Input from '../input.react';
+import { useModalContext } from '../modal-provider.react';
import Modal from '../modal.react';
import css from './user-settings-modal.css';
-type BaseProps = {
- +setModal: (modal: ?React.Node) => void,
-};
type Props = {
- ...BaseProps,
+inputDisabled: boolean,
+logInExtraInfo: () => LogInExtraInfo,
+dispatchActionPromise: DispatchActionPromise,
+logIn: (logInInfo: LogInInfo) => Promise<LogInResult>,
+ +clearModal: () => void,
};
type State = {
+username: string,
@@ -63,7 +61,7 @@
render() {
return (
- <Modal name="Log in" onClose={this.clearModal}>
+ <Modal name="Log in" onClose={this.props.clearModal}>
<div className={css['modal-body']}>
<form method="POST">
<div>
@@ -175,7 +173,7 @@
password: this.state.password,
...extraInfo,
});
- this.clearModal();
+ this.props.clearModal();
return result;
} catch (e) {
if (e.message === 'invalid_parameters') {
@@ -216,28 +214,24 @@
throw e;
}
}
-
- clearModal = () => {
- this.props.setModal(null);
- };
}
const loadingStatusSelector = createLoadingStatusSelector(logInActionTypes);
-
-const ConnectedLoginModal: React.ComponentType<BaseProps> = React.memo<BaseProps>(
- function ConnectedLoginModal(props) {
+const ConnectedLoginModal: React.ComponentType<{}> = React.memo<{}>(
+ function ConnectedLoginModal(): React.Node {
const inputDisabled = useSelector(loadingStatusSelector) === 'loading';
const loginExtraInfo = useSelector(webLogInExtraInfoSelector);
const callLogIn = useServerCall(logIn);
const dispatchActionPromise = useDispatchActionPromise();
+ const modalContext = useModalContext();
return (
<LogInModal
- {...props}
inputDisabled={inputDisabled}
logInExtraInfo={loginExtraInfo}
logIn={callLogIn}
dispatchActionPromise={dispatchActionPromise}
+ clearModal={modalContext.clearModal}
/>
);
},
diff --git a/web/modals/account/user-settings-modal.react.js b/web/modals/account/user-settings-modal.react.js
--- a/web/modals/account/user-settings-modal.react.js
+++ b/web/modals/account/user-settings-modal.react.js
@@ -29,6 +29,7 @@
import Button from '../../components/button.react';
import { useSelector } from '../../redux/redux-utils';
import Input from '../input.react';
+import { useModalContext } from '../modal-provider.react';
import Modal from '../modal.react';
import css from './user-settings-modal.css';
@@ -58,11 +59,7 @@
};
}
-type BaseProps = {
- +setModal: (modal: ?React.Node) => void,
-};
type Props = {
- ...BaseProps,
+currentUserInfo: ?CurrentUserInfo,
+preRequestUserState: PreRequestUserState,
+inputDisabled: boolean,
@@ -73,6 +70,7 @@
) => Promise<LogOutResult>,
+changeUserPassword: (passwordUpdate: PasswordUpdate) => Promise<void>,
+logOut: (preRequestUserState: PreRequestUserState) => Promise<LogOutResult>,
+ +clearModal: () => void,
};
type State = {
+newPassword: string,
@@ -115,7 +113,7 @@
logOut = async () => {
await this.props.logOut(this.props.preRequestUserState);
- this.clearModal();
+ this.props.clearModal();
};
render() {
@@ -206,7 +204,7 @@
}
return (
- <Modal name="Edit account" onClose={this.clearModal} size="large">
+ <Modal name="Edit account" onClose={this.props.clearModal} size="large">
<ul className={css['tab-panel']}>
<Tab
name="General"
@@ -328,7 +326,7 @@
},
currentPassword: this.state.currentPassword,
});
- this.clearModal();
+ this.props.clearModal();
} catch (e) {
if (e.message === 'invalid_credentials') {
this.setState(
@@ -377,7 +375,7 @@
this.state.currentPassword,
this.props.preRequestUserState,
);
- this.clearModal();
+ this.props.clearModal();
return response;
} catch (e) {
const errorMessage =
@@ -400,10 +398,6 @@
throw e;
}
}
-
- clearModal = () => {
- this.props.setModal(null);
- };
}
const deleteAccountLoadingStatusSelector = createLoadingStatusSelector(
@@ -412,9 +406,8 @@
const changeUserPasswordLoadingStatusSelector = createLoadingStatusSelector(
changeUserPasswordActionTypes,
);
-
-const ConnectedUserSettingsModal: React.ComponentType<BaseProps> = React.memo<BaseProps>(
- function ConnectedUserSettingsModal(props) {
+const ConnectedUserSettingsModal: React.ComponentType<{}> = React.memo<{}>(
+ function ConnectedUserSettingsModal(): React.Node {
const currentUserInfo = useSelector(state => state.currentUserInfo);
const preRequestUserState = useSelector(preRequestUserStateSelector);
const inputDisabled = useSelector(
@@ -427,9 +420,10 @@
const dispatchActionPromise = useDispatchActionPromise();
const boundLogOut = useServerCall(logOut);
+ const modalContext = useModalContext();
+
return (
<UserSettingsModal
- {...props}
currentUserInfo={currentUserInfo}
preRequestUserState={preRequestUserState}
inputDisabled={inputDisabled}
@@ -437,6 +431,7 @@
changeUserPassword={callChangeUserPassword}
dispatchActionPromise={dispatchActionPromise}
logOut={boundLogOut}
+ clearModal={modalContext.clearModal}
/>
);
},
diff --git a/web/modals/chat/invalid-upload.react.js b/web/modals/chat/invalid-upload.react.js
--- a/web/modals/chat/invalid-upload.react.js
+++ b/web/modals/chat/invalid-upload.react.js
@@ -2,26 +2,29 @@
import * as React from 'react';
+import { useModalContext } from '../../modals/modal-provider.react';
import css from '../../style.css';
import Modal from '../modal.react';
type Props = {
- +setModal: (modal: ?React.Node) => void,
+ +clearModal: () => void,
};
class InvalidUploadModal extends React.PureComponent<Props> {
render(): React.Node {
return (
- <Modal name="Invalid upload" onClose={this.clearModal}>
+ <Modal name="Invalid upload" onClose={this.props.clearModal}>
<div className={css['modal-body']}>
<p>We don&apos;t support that file type yet :(</p>
</div>
</Modal>
);
}
+}
+
+function ConnectedInvalidUploadModal(): React.Node {
+ const modalContext = useModalContext();
- clearModal: () => void = () => {
- this.props.setModal(null);
- };
+ return <InvalidUploadModal clearModal={modalContext.clearModal} />;
}
-export default InvalidUploadModal;
+export default ConnectedInvalidUploadModal;
diff --git a/web/modals/chat/sidebar-list-modal.react.js b/web/modals/chat/sidebar-list-modal.react.js
--- a/web/modals/chat/sidebar-list-modal.react.js
+++ b/web/modals/chat/sidebar-list-modal.react.js
@@ -15,22 +15,20 @@
import { useSelector } from '../../redux/redux-utils';
import globalCSS from '../../style.css';
import { MagnifyingGlass } from '../../vectors.react';
+import { useModalContext } from '../modal-provider.react';
import Modal from '../modal.react';
type Props = {
- +setModal: (modal: ?React.Node) => void,
+threadInfo: ThreadInfo,
};
+
function SidebarListModal(props: Props): React.Node {
- const { setModal, threadInfo } = props;
+ const { threadInfo } = props;
const [searchState, setSearchState] = React.useState({
text: '',
results: new Set<string>(),
});
-
- const clearModal = React.useCallback(() => {
- setModal(null);
- }, [setModal]);
+ const { clearModal } = useModalContext();
const sidebarInfos = useSelector(
state => sidebarInfoSelector(state)[threadInfo.id] ?? [],
diff --git a/web/modals/concurrent-modification-modal.react.js b/web/modals/concurrent-modification-modal.react.js
--- a/web/modals/concurrent-modification-modal.react.js
+++ b/web/modals/concurrent-modification-modal.react.js
@@ -3,16 +3,18 @@
import * as React from 'react';
import css from '../style.css';
+import { useModalContext } from './modal-provider.react';
import Modal from './modal.react';
type Props = {
+onRefresh: () => void,
- +onClose: () => void,
};
export default function ConcurrentModificationModal(props: Props): React.Node {
+ const modalContext = useModalContext();
+
return (
- <Modal name="Concurrent modification" onClose={props.onClose}>
+ <Modal name="Concurrent modification" onClose={modalContext.clearModal}>
<div className={css['modal-body']}>
<p>
It looks like somebody is attempting to modify that field at the same
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
@@ -33,6 +33,7 @@
import { dateFromString } from 'lib/utils/date-utils';
import LoadingIndicator from '../../loading-indicator.react';
+import { useModalContext } from '../../modals/modal-provider.react';
import { useSelector } from '../../redux/redux-utils';
import { allDaysToEntries } from '../../selectors/entry-selectors';
import Modal from '../modal.react';
@@ -43,7 +44,6 @@
type BaseProps = {
+mode: HistoryMode,
+dayString: string,
- +onClose: () => void,
+currentEntryID?: ?string,
};
type Props = {
@@ -59,6 +59,7 @@
+fetchRevisionsForEntry: (
entryID: string,
) => Promise<$ReadOnlyArray<HistoryRevisionInfo>>,
+ +onClose: () => void,
};
type State = {
+mode: HistoryMode,
@@ -262,6 +263,7 @@
const callFetchEntries = useServerCall(fetchEntries);
const callFetchRevisionsForEntry = useServerCall(fetchRevisionsForEntry);
const dispatchActionPromise = useDispatchActionPromise();
+ const modalContext = useModalContext();
return (
<HistoryModal
@@ -273,6 +275,7 @@
fetchEntries={callFetchEntries}
fetchRevisionsForEntry={callFetchRevisionsForEntry}
dispatchActionPromise={dispatchActionPromise}
+ onClose={modalContext.clearModal}
/>
);
},
diff --git a/web/modals/threads/thread-settings-modal.react.js b/web/modals/threads/thread-settings-modal.react.js
--- a/web/modals/threads/thread-settings-modal.react.js
+++ b/web/modals/threads/thread-settings-modal.react.js
@@ -36,6 +36,7 @@
} from 'lib/utils/action-utils';
import { firstLine } from 'lib/utils/string-utils';
+import { useModalContext } from '../../modals/modal-provider.react';
import { useSelector } from '../../redux/redux-utils';
import css from '../../style.css';
import Modal from '../modal.react';
@@ -71,7 +72,6 @@
type BaseProps = {
+threadID: string,
- +onClose: () => void,
};
type Props = {
...BaseProps,
@@ -87,6 +87,7 @@
+changeThreadSettings: (
update: UpdateThreadRequest,
) => Promise<ChangeThreadSettingsPayload>,
+ +onClose: () => void,
};
type State = {
+queuedChanges: ThreadChanges,
@@ -564,9 +565,11 @@
const threadInfo: ?ThreadInfo = useSelector(
state => threadInfoSelector(state)[props.threadID],
);
+ const modalContext = useModalContext();
+
if (!threadInfo) {
return (
- <Modal onClose={props.onClose} name="Invalid thread">
+ <Modal onClose={modalContext.clearModal} name="Invalid thread">
<div className={css['modal-body']}>
<p>You no longer have permission to view this thread</p>
</div>
@@ -584,6 +587,7 @@
deleteThread={callDeleteThread}
changeThreadSettings={callChangeThreadSettings}
dispatchActionPromise={dispatchActionPromise}
+ onClose={modalContext.clearModal}
/>
);
},
diff --git a/web/sidebar/community-picker.react.js b/web/sidebar/community-picker.react.js
--- a/web/sidebar/community-picker.react.js
+++ b/web/sidebar/community-picker.react.js
@@ -4,16 +4,15 @@
import Button from '../components/button.react';
import UserSettingsModal from '../modals/account/user-settings-modal.react.js';
+import { useModalContext } from '../modals/modal-provider.react';
import SWMansionIcon from '../SWMansionIcon.react';
import css from './community-picker.css';
-type Props = { +setModal: (modal: ?React.Node) => void };
-
-function CommunityPicker(props: Props): React.Node {
- const { setModal } = props;
+function CommunityPicker(): React.Node {
+ const { setModal } = useModalContext();
const setModalToUserSettings = React.useCallback(() => {
- setModal(<UserSettingsModal setModal={setModal} />);
+ setModal(<UserSettingsModal />);
}, [setModal]);
return (
diff --git a/web/sidebar/left-layout-aside.react.js b/web/sidebar/left-layout-aside.react.js
--- a/web/sidebar/left-layout-aside.react.js
+++ b/web/sidebar/left-layout-aside.react.js
@@ -5,15 +5,11 @@
import AppSwitcher from './app-switcher.react';
import CommunityPicker from './community-picker.react';
import css from './left-layout-aside.css';
-type Props = {
- +setModal: (modal: ?React.Node) => void,
-};
-function LeftLayoutAside(props: Props): React.Node {
- const { setModal } = props;
+function LeftLayoutAside(): React.Node {
return (
<aside className={css.container}>
- <CommunityPicker setModal={setModal} />
+ <CommunityPicker />
<AppSwitcher />
</aside>
);
diff --git a/web/splash/splash.react.js b/web/splash/splash.react.js
--- a/web/splash/splash.react.js
+++ b/web/splash/splash.react.js
@@ -20,20 +20,18 @@
import LoadingIndicator from '../loading-indicator.react';
import LogInModal from '../modals/account/log-in-modal.react';
+import { useModalContext } from '../modals/modal-provider.react';
import { useSelector } from '../redux/redux-utils';
import css from './splash.css';
const defaultRequestAccessScrollHeight = 390;
-type BaseProps = {
- +setModal: (modal: ?React.Node) => void,
- +currentModal: ?React.Node,
-};
type Props = {
- ...BaseProps,
+loadingStatus: LoadingStatus,
+dispatchActionPromise: DispatchActionPromise,
+requestAccess: (accessRequest: AccessRequest) => Promise<void>,
+ +setModal: (modal: React.Node) => void,
+ +modal: ?React.Node,
};
type State = {
+platform: DeviceType,
@@ -178,7 +176,7 @@
</div>
</div>
</div>
- {this.props.currentModal}
+ {this.props.modal}
</React.Fragment>
);
}
@@ -201,7 +199,7 @@
onClickLogIn = (event: SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
- this.props.setModal(<LogInModal setModal={this.props.setModal} />);
+ this.props.setModal(<LogInModal />);
};
onClickRequestAccess = (event: SyntheticEvent<HTMLAnchorElement>) => {
@@ -260,18 +258,22 @@
const loadingStatusSelector = createLoadingStatusSelector(
requestAccessActionTypes,
);
-const ConnectedSplash: React.ComponentType<BaseProps> = React.memo<BaseProps>(
- function ConnectedSplash(props) {
+
+const ConnectedSplash: React.ComponentType<{}> = React.memo<{}>(
+ function ConnectedSplash(): React.Node {
const loadingStatus = useSelector(loadingStatusSelector);
const callRequestAccess = useServerCall(requestAccess);
const dispatchActionPromise = useDispatchActionPromise();
+ const modalContext = useModalContext();
+
return (
<Splash
- {...props}
loadingStatus={loadingStatus}
requestAccess={callRequestAccess}
dispatchActionPromise={dispatchActionPromise}
+ setModal={modalContext.setModal}
+ modal={modalContext.modal}
/>
);
},

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 16, 10:31 AM (22 h, 19 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2497431
Default Alt Text
D3271.diff (42 KB)

Event Timeline