Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3248668
D3271.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
42 KB
Referenced Files
None
Subscribers
None
D3271.diff
View Options
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'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
Details
Attached
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)
Attached To
Mode
D3271: [web] [refactor] 30+ diff squash, remove prop drill of setModal
Attached
Detach File
Event Timeline
Log In to Comment