`);
const statePromises = {
navInfo: navInfoPromise,
currentUserInfo: currentUserInfoPromise,
sessionID: sessionIDPromise,
serverVerificationResult: serverVerificationResultPromise,
entryStore: entryStorePromise,
threadStore: threadStorePromise,
userStore: userStorePromise,
messageStore: messageStorePromise,
updatesCurrentAsOf: initialTime,
loadingStatuses: {},
calendarFilters: defaultCalendarFilters,
// We can use paths local to the
on web
urlPrefix: '',
windowDimensions: { width: 0, height: 0 },
baseHref,
connection: {
...defaultConnectionInfo('web', viewer.timeZone),
actualizedCalendarQuery: calendarQuery,
},
watchedThreadIDs: [],
foreground: true,
nextLocalID: 0,
queuedReports: [],
timeZone: viewer.timeZone,
userAgent: viewer.userAgent,
cookie: undefined,
deviceToken: undefined,
dataLoaded: viewer.loggedIn,
windowActive: true,
};
const stateResult = await promiseAll(statePromises);
const state: AppState = { ...stateResult };
const store: Store
= createStore(reducer, state);
const routerContext = {};
const reactStream = renderToNodeStream(
,
);
if (routerContext.url) {
throw new ServerError('URL modified during server render!');
}
reactStream.pipe(res, { end: false });
await waitForStream(reactStream);
res.write(html`
`);
}
async function handleVerificationRequest(
viewer: Viewer,
code: ?string,
): Promise {
if (!code) {
return null;
}
try {
return await handleCodeVerificationRequest(viewer, code);
} catch (e) {
if (e instanceof ServerError && e.message === 'invalid_code') {
return { success: false };
}
throw e;
}
}
export { websiteResponder };
diff --git a/web/app.react.js b/web/app.react.js
index 8c0dd96b7..7b3aa5140 100644
--- a/web/app.react.js
+++ b/web/app.react.js
@@ -1,375 +1,380 @@
// @flow
import { config as faConfig } from '@fortawesome/fontawesome-svg-core';
import { faCalendar, faComments } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classNames from 'classnames';
import invariant from 'invariant';
import _isEqual from 'lodash/fp/isEqual';
import * as React from 'react';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { useDispatch } from 'react-redux';
import {
fetchEntriesActionTypes,
updateCalendarQueryActionTypes,
} from 'lib/actions/entry-actions';
import {
createLoadingStatusSelector,
combineLoadingStatuses,
} from 'lib/selectors/loading-selectors';
import {
mostRecentReadThreadSelector,
unreadCount,
} from 'lib/selectors/thread-selectors';
import { isLoggedIn } from 'lib/selectors/user-selectors';
import type { LoadingStatus } from 'lib/types/loading-types';
import type { Dispatch } from 'lib/types/redux-types';
import {
verifyField,
type ServerVerificationResult,
} from 'lib/types/verify-types';
import { registerConfig } from 'lib/utils/config';
import AccountBar from './account-bar.react';
import Calendar from './calendar/calendar.react';
import Chat from './chat/chat.react';
import InputStateContainer from './input/input-state-container.react';
import LoadingIndicator from './loading-indicator.react';
import ResetPasswordModal from './modals/account/reset-password-modal.react';
import VerificationModal from './modals/account/verification-modal.react';
import FocusHandler from './redux/focus-handler.react';
import { type NavInfo, updateNavInfoActionType } from './redux/redux-setup';
import { useSelector } from './redux/redux-utils';
import VisibilityHandler from './redux/visibility-handler.react';
import history from './router-history';
import Splash from './splash/splash.react';
import css from './style.css';
+import getTitle from './title/getTitle';
import { canonicalURLFromReduxState, navInfoFromURL } from './url-utils';
// We want Webpack's css-loader and style-loader to handle the Fontawesome CSS,
// so we disable the autoAddCss logic and import the CSS file. Otherwise every
// icon flashes huge for a second before the CSS is loaded.
import '@fortawesome/fontawesome-svg-core/styles.css';
faConfig.autoAddCss = false;
registerConfig({
// We can't securely cache credentials on web, so we have no way to recover
// from a cookie invalidation
resolveInvalidatedCookie: null,
// We use httponly cookies on web to protect against XSS attacks, so we have
// no access to the cookies from JavaScript
setCookieOnRequest: false,
setSessionIDOnRequest: true,
// Never reset the calendar range
calendarRangeInactivityLimit: null,
platformDetails: { platform: 'web' },
});
type BaseProps = {|
+location: {
+pathname: string,
...
},
|};
type Props = {|
...BaseProps,
// Redux state
+navInfo: NavInfo,
+serverVerificationResult: ?ServerVerificationResult,
+entriesLoadingStatus: LoadingStatus,
+loggedIn: boolean,
+mostRecentReadThread: ?string,
+activeThreadCurrentlyUnread: boolean,
+viewerID: ?string,
+unreadCount: number,
// Redux dispatch functions
+dispatch: Dispatch,
|};
type State = {|
+currentModal: ?React.Node,
|};
class App extends React.PureComponent