Page MenuHomePhabricator

D9532.diff
No OneTemporary

D9532.diff

diff --git a/lib/types/crypto-types.js b/lib/types/crypto-types.js
--- a/lib/types/crypto-types.js
+++ b/lib/types/crypto-types.js
@@ -32,6 +32,10 @@
+notificationIdentityKeys: OLMIdentityKeys,
};
+export type CryptoStoreContextType = {
+ +getInitializedCryptoStore: () => Promise<CryptoStore>,
+};
+
export type IdentityKeysBlob = {
+primaryIdentityPublicKeys: OLMIdentityKeys,
+notificationIdentityPublicKeys: OLMIdentityKeys,
diff --git a/web/account/account-hooks.js b/web/account/account-hooks.js
--- a/web/account/account-hooks.js
+++ b/web/account/account-hooks.js
@@ -1,35 +1,161 @@
// @flow
+import olm from '@commapp/olm';
+import invariant from 'invariant';
import * as React from 'react';
+import { useDispatch } from 'react-redux';
+import uuid from 'uuid';
-import type { SignedIdentityKeysBlob } from 'lib/types/crypto-types.js';
+import type {
+ SignedIdentityKeysBlob,
+ CryptoStore,
+ IdentityKeysBlob,
+ CryptoStoreContextType,
+} from 'lib/types/crypto-types.js';
+import { initOlm } from '../olm/olm-utils.js';
+import { setCryptoStore } from '../redux/crypto-store-reducer.js';
import { useSelector } from '../redux/redux-utils.js';
-import { getSignedIdentityKeysBlobSelector } from '../selectors/socket-selectors.js';
-function useSignedIdentityKeysBlob(): ?SignedIdentityKeysBlob {
- const getSignedIdentityKeysBlob: ?() => Promise<SignedIdentityKeysBlob> =
- useSelector(getSignedIdentityKeysBlobSelector);
+const CryptoStoreContext: React.Context<?CryptoStoreContextType> =
+ React.createContext(null);
- const [signedIdentityKeysBlob, setSignedIdentityKeysBlob] =
- React.useState<?SignedIdentityKeysBlob>(null);
+type Props = {
+ +children: React.Node,
+};
- React.useEffect(() => {
- (async () => {
- if (
- getSignedIdentityKeysBlob === null ||
- getSignedIdentityKeysBlob === undefined
- ) {
- setSignedIdentityKeysBlob(null);
- return;
+function GetOrCreateCryptoStoreProvider(props: Props): React.Node {
+ const dispatch = useDispatch();
+ const createCryptoStore = React.useCallback(async () => {
+ await initOlm();
+
+ const identityAccount = new olm.Account();
+ identityAccount.create();
+ const { ed25519: identityED25519, curve25519: identityCurve25519 } =
+ JSON.parse(identityAccount.identity_keys());
+
+ const identityAccountPicklingKey = uuid.v4();
+ const pickledIdentityAccount = identityAccount.pickle(
+ identityAccountPicklingKey,
+ );
+
+ const notificationAccount = new olm.Account();
+ notificationAccount.create();
+ const { ed25519: notificationED25519, curve25519: notificationCurve25519 } =
+ JSON.parse(notificationAccount.identity_keys());
+
+ const notificationAccountPicklingKey = uuid.v4();
+ const pickledNotificationAccount = notificationAccount.pickle(
+ notificationAccountPicklingKey,
+ );
+
+ const newCryptoStore = {
+ primaryAccount: {
+ picklingKey: identityAccountPicklingKey,
+ pickledAccount: pickledIdentityAccount,
+ },
+ primaryIdentityKeys: {
+ ed25519: identityED25519,
+ curve25519: identityCurve25519,
+ },
+ notificationAccount: {
+ picklingKey: notificationAccountPicklingKey,
+ pickledAccount: pickledNotificationAccount,
+ },
+ notificationIdentityKeys: {
+ ed25519: notificationED25519,
+ curve25519: notificationCurve25519,
+ },
+ };
+
+ dispatch({ type: setCryptoStore, payload: newCryptoStore });
+ return newCryptoStore;
+ }, [dispatch]);
+
+ const currentCryptoStore = useSelector(state => state.cryptoStore);
+ const createCryptoStorePromiseRef = React.useRef<?Promise<CryptoStore>>(null);
+ const getCryptoStorePromise = React.useCallback(() => {
+ if (currentCryptoStore) {
+ return Promise.resolve(currentCryptoStore);
+ }
+
+ const currentCreateCryptoStorePromiseRef =
+ createCryptoStorePromiseRef.current;
+ if (currentCreateCryptoStorePromiseRef) {
+ return currentCreateCryptoStorePromiseRef;
+ }
+
+ const newCreateCryptoStorePromise = (async () => {
+ try {
+ return await createCryptoStore();
+ } catch (e) {
+ createCryptoStorePromiseRef.current = undefined;
+ throw e;
}
- const resolvedSignedIdentityKeysBlob: SignedIdentityKeysBlob =
- await getSignedIdentityKeysBlob();
- setSignedIdentityKeysBlob(resolvedSignedIdentityKeysBlob);
})();
- }, [getSignedIdentityKeysBlob]);
- return signedIdentityKeysBlob;
+ createCryptoStorePromiseRef.current = newCreateCryptoStorePromise;
+ return newCreateCryptoStorePromise;
+ }, [createCryptoStore, currentCryptoStore]);
+
+ const isCryptoStoreSet = !!currentCryptoStore;
+ React.useEffect(() => {
+ if (!isCryptoStoreSet) {
+ createCryptoStorePromiseRef.current = undefined;
+ }
+ }, [isCryptoStoreSet]);
+
+ const contextValue = React.useMemo(
+ () => ({
+ getInitializedCryptoStore: getCryptoStorePromise,
+ }),
+ [getCryptoStorePromise],
+ );
+
+ return (
+ <CryptoStoreContext.Provider value={contextValue}>
+ {props.children}
+ </CryptoStoreContext.Provider>
+ );
+}
+
+function useGetOrCreateCryptoStore(): () => Promise<CryptoStore> {
+ const context = React.useContext(CryptoStoreContext);
+ invariant(context, 'CryptoStoreContext not found');
+ return context.getInitializedCryptoStore;
+}
+
+function useGetSignedIdentityKeysBlob(): () => Promise<SignedIdentityKeysBlob> {
+ const getOrCreateCryptoStore = useGetOrCreateCryptoStore();
+
+ return React.useCallback(async () => {
+ const { primaryAccount, primaryIdentityKeys, notificationIdentityKeys } =
+ await getOrCreateCryptoStore();
+
+ await initOlm();
+ const primaryOLMAccount = new olm.Account();
+ primaryOLMAccount.unpickle(
+ primaryAccount.picklingKey,
+ primaryAccount.pickledAccount,
+ );
+
+ const identityKeysBlob: IdentityKeysBlob = {
+ primaryIdentityPublicKeys: primaryIdentityKeys,
+ notificationIdentityPublicKeys: notificationIdentityKeys,
+ };
+
+ const payloadToBeSigned: string = JSON.stringify(identityKeysBlob);
+ const signedIdentityKeysBlob: SignedIdentityKeysBlob = {
+ payload: payloadToBeSigned,
+ signature: primaryOLMAccount.sign(payloadToBeSigned),
+ };
+
+ return signedIdentityKeysBlob;
+ }, [getOrCreateCryptoStore]);
}
-export { useSignedIdentityKeysBlob };
+export {
+ useGetSignedIdentityKeysBlob,
+ useGetOrCreateCryptoStore,
+ GetOrCreateCryptoStoreProvider,
+};
diff --git a/web/account/log-in-form.react.js b/web/account/log-in-form.react.js
--- a/web/account/log-in-form.react.js
+++ b/web/account/log-in-form.react.js
@@ -1,84 +1,31 @@
// @flow
-import olm from '@commapp/olm';
import { useConnectModal } from '@rainbow-me/rainbowkit';
import * as React from 'react';
import { useDispatch } from 'react-redux';
-import uuid from 'uuid';
import { useWalletClient } from 'wagmi';
import { isDev } from 'lib/utils/dev-utils.js';
+import { useGetOrCreateCryptoStore } from './account-hooks.js';
import css from './log-in-form.css';
import SIWEButton from './siwe-button.react.js';
import SIWELoginForm from './siwe-login-form.react.js';
import TraditionalLoginForm from './traditional-login-form.react.js';
import Button from '../components/button.react.js';
import OrBreak from '../components/or-break.react.js';
-import { initOlm } from '../olm/olm-utils.js';
import { updateNavInfoActionType } from '../redux/action-types.js';
-import { setCryptoStore } from '../redux/crypto-store-reducer.js';
-import { useSelector } from '../redux/redux-utils.js';
function LoginForm(): React.Node {
const { openConnectModal } = useConnectModal();
const { data: signer } = useWalletClient();
const dispatch = useDispatch();
- const cryptoStore = useSelector(state => state.cryptoStore);
+ const getOrCreateCryptoStore = useGetOrCreateCryptoStore();
React.useEffect(() => {
- (async () => {
- if (cryptoStore !== null && cryptoStore !== undefined) {
- return;
- }
- await initOlm();
-
- const identityAccount = new olm.Account();
- identityAccount.create();
- const { ed25519: identityED25519, curve25519: identityCurve25519 } =
- JSON.parse(identityAccount.identity_keys());
-
- const identityAccountPicklingKey = uuid.v4();
- const pickledIdentityAccount = identityAccount.pickle(
- identityAccountPicklingKey,
- );
-
- const notificationAccount = new olm.Account();
- notificationAccount.create();
- const {
- ed25519: notificationED25519,
- curve25519: notificationCurve25519,
- } = JSON.parse(notificationAccount.identity_keys());
-
- const notificationAccountPicklingKey = uuid.v4();
- const pickledNotificationAccount = notificationAccount.pickle(
- notificationAccountPicklingKey,
- );
-
- dispatch({
- type: setCryptoStore,
- payload: {
- primaryAccount: {
- picklingKey: identityAccountPicklingKey,
- pickledAccount: pickledIdentityAccount,
- },
- primaryIdentityKeys: {
- ed25519: identityED25519,
- curve25519: identityCurve25519,
- },
- notificationAccount: {
- picklingKey: notificationAccountPicklingKey,
- pickledAccount: pickledNotificationAccount,
- },
- notificationIdentityKeys: {
- ed25519: notificationED25519,
- curve25519: notificationCurve25519,
- },
- },
- });
- })();
- }, [dispatch, cryptoStore]);
+ getOrCreateCryptoStore();
+ }, [getOrCreateCryptoStore]);
const onQRCodeLoginButtonClick = React.useCallback(() => {
dispatch({
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
@@ -19,10 +19,7 @@
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 {
- OLMIdentityKeys,
- SignedIdentityKeysBlob,
-} from 'lib/types/crypto-types.js';
+import type { OLMIdentityKeys } from 'lib/types/crypto-types.js';
import {
useDispatchActionPromise,
useServerCall,
@@ -34,7 +31,7 @@
siweMessageSigningExplanationStatements,
} from 'lib/utils/siwe-utils.js';
-import { useSignedIdentityKeysBlob } from './account-hooks.js';
+import { useGetSignedIdentityKeysBlob } from './account-hooks.js';
import HeaderSeparator from './header-separator.react.js';
import css from './siwe.css';
import Button from '../components/button.react.js';
@@ -88,11 +85,11 @@
state => state.cryptoStore?.primaryIdentityKeys,
);
- const signedIdentityKeysBlob: ?SignedIdentityKeysBlob =
- useSignedIdentityKeysBlob();
+ const getSignedIdentityKeysBlob = useGetSignedIdentityKeysBlob();
const callSIWEAuthEndpoint = React.useCallback(
async (message: string, signature: string, extraInfo) => {
+ const signedIdentityKeysBlob = await getSignedIdentityKeysBlob();
invariant(
signedIdentityKeysBlob,
'signedIdentityKeysBlob must be set in attemptSIWEAuth',
@@ -115,7 +112,7 @@
throw e;
}
},
- [signedIdentityKeysBlob, siweAuthCall],
+ [getSignedIdentityKeysBlob, siweAuthCall],
);
const attemptSIWEAuth = React.useCallback(
@@ -186,8 +183,7 @@
if (
siweAuthLoadingStatus === 'loading' ||
!siweNonce ||
- !primaryIdentityPublicKeys ||
- !signedIdentityKeysBlob
+ !primaryIdentityPublicKeys
) {
return (
<div className={css.loadingIndicator}>
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
@@ -15,10 +15,9 @@
LogInStartingPayload,
} from 'lib/types/account-types.js';
import { logInActionSources } from 'lib/types/account-types.js';
-import type { SignedIdentityKeysBlob } from 'lib/types/crypto-types.js';
import { useDispatchActionPromise } from 'lib/utils/action-utils.js';
-import { useSignedIdentityKeysBlob } from './account-hooks.js';
+import { useGetSignedIdentityKeysBlob } from './account-hooks.js';
import HeaderSeparator from './header-separator.react.js';
import css from './log-in-form.css';
import PasswordInput from './password-input.react.js';
@@ -36,8 +35,7 @@
const dispatchActionPromise = useDispatchActionPromise();
const modalContext = useModalContext();
- const signedIdentityKeysBlob: ?SignedIdentityKeysBlob =
- useSignedIdentityKeysBlob();
+ const getSignedIdentityKeysBlob = useGetSignedIdentityKeysBlob();
const usernameInputRef = React.useRef();
React.useEffect(() => {
@@ -64,6 +62,7 @@
const logInAction = React.useCallback(
async (extraInfo: LogInExtraInfo) => {
+ const signedIdentityKeysBlob = await getSignedIdentityKeysBlob();
try {
invariant(
signedIdentityKeysBlob,
@@ -91,7 +90,7 @@
throw e;
}
},
- [callLogIn, modalContext, password, signedIdentityKeysBlob, username],
+ [callLogIn, modalContext, password, getSignedIdentityKeysBlob, username],
);
const onSubmit = React.useCallback(
@@ -171,11 +170,7 @@
<Button
variant="filled"
type="submit"
- disabled={
- signedIdentityKeysBlob === null ||
- signedIdentityKeysBlob === undefined ||
- inputDisabled
- }
+ disabled={inputDisabled}
onClick={onSubmit}
buttonColor={signInButtonColor}
>
diff --git a/web/root.js b/web/root.js
--- a/web/root.js
+++ b/web/root.js
@@ -12,6 +12,7 @@
import IntegrityHandler from 'lib/components/integrity-handler.react.js';
import { reduxLoggerMiddleware } from 'lib/utils/action-logger.js';
+import { GetOrCreateCryptoStoreProvider } from './account/account-hooks.js';
import App from './app.react.js';
import { SQLiteDataHandler } from './database/sqlite-data-handler.js';
import { localforageConfig } from './database/utils/constants.js';
@@ -37,12 +38,14 @@
<Provider store={store}>
<ErrorBoundary>
<InitialReduxStateGate persistor={persistor}>
- <Router history={history.getHistoryObject()}>
- <Route path="*" component={App} />
- </Router>
- <Socket />
- <SQLiteDataHandler />
- <IntegrityHandler />
+ <GetOrCreateCryptoStoreProvider>
+ <Router history={history.getHistoryObject()}>
+ <Route path="*" component={App} />
+ </Router>
+ <Socket />
+ <SQLiteDataHandler />
+ <IntegrityHandler />
+ </GetOrCreateCryptoStoreProvider>
</InitialReduxStateGate>
</ErrorBoundary>
</Provider>
diff --git a/web/selectors/socket-selectors.js b/web/selectors/socket-selectors.js
--- a/web/selectors/socket-selectors.js
+++ b/web/selectors/socket-selectors.js
@@ -1,6 +1,5 @@
// @flow
-import olm from '@commapp/olm';
import _memoize from 'lodash/memoize.js';
import { createSelector } from 'reselect';
@@ -14,11 +13,7 @@
sessionStateFuncSelector,
} from 'lib/selectors/socket-selectors.js';
import { createOpenSocketFunction } from 'lib/shared/socket-utils.js';
-import type {
- SignedIdentityKeysBlob,
- IdentityKeysBlob,
- CryptoStore,
-} from 'lib/types/crypto-types.js';
+import type { SignedIdentityKeysBlob } from 'lib/types/crypto-types.js';
import type {
ClientServerRequest,
ClientClientResponse,
@@ -29,7 +24,6 @@
} from 'lib/types/session-types.js';
import type { OneTimeKeyGenerator } from 'lib/types/socket-types.js';
-import { initOlm } from '../olm/olm-utils.js';
import type { AppState } from '../redux/redux-setup.js';
const baseOpenSocketSelector: (
@@ -64,58 +58,31 @@
baseSessionIdentificationSelector,
);
-const getSignedIdentityKeysBlobSelector: (
- state: AppState,
-) => ?() => Promise<SignedIdentityKeysBlob> = createSelector(
- (state: AppState) => state.cryptoStore,
- (cryptoStore: ?CryptoStore) => {
- if (!cryptoStore) {
- return null;
- }
-
- return async () => {
- await initOlm();
- const { primaryAccount, primaryIdentityKeys, notificationIdentityKeys } =
- cryptoStore;
- const primaryOLMAccount = new olm.Account();
- primaryOLMAccount.unpickle(
- primaryAccount.picklingKey,
- primaryAccount.pickledAccount,
- );
-
- const identityKeysBlob: IdentityKeysBlob = {
- primaryIdentityPublicKeys: primaryIdentityKeys,
- notificationIdentityPublicKeys: notificationIdentityKeys,
- };
-
- const payloadToBeSigned: string = JSON.stringify(identityKeysBlob);
- const signedIdentityKeysBlob: SignedIdentityKeysBlob = {
- payload: payloadToBeSigned,
- signature: primaryOLMAccount.sign(payloadToBeSigned),
- };
-
- return signedIdentityKeysBlob;
- };
- },
-);
+type WebGetClientResponsesSelectorInputType = {
+ +state: AppState,
+ +getSignedIdentityKeysBlob: () => Promise<SignedIdentityKeysBlob>,
+};
const webGetClientResponsesSelector: (
- state: AppState,
+ input: WebGetClientResponsesSelectorInputType,
) => (
serverRequests: $ReadOnlyArray<ClientServerRequest>,
) => Promise<$ReadOnlyArray<ClientClientResponse>> = createSelector(
- getClientResponsesSelector,
- getSignedIdentityKeysBlobSelector,
- (state: AppState) => state.navInfo.tab === 'calendar',
+ (input: WebGetClientResponsesSelectorInputType) =>
+ getClientResponsesSelector(input.state),
+ (input: WebGetClientResponsesSelectorInputType) =>
+ input.getSignedIdentityKeysBlob,
+ (input: WebGetClientResponsesSelectorInputType) =>
+ input.state.navInfo.tab === 'calendar',
(
getClientResponsesFunc: (
calendarActive: boolean,
oneTimeKeyGenerator: ?OneTimeKeyGenerator,
- getSignedIdentityKeysBlob: ?() => Promise<SignedIdentityKeysBlob>,
+ getSignedIdentityKeysBlob: () => Promise<SignedIdentityKeysBlob>,
getInitialNotificationsEncryptedMessage: ?() => Promise<string>,
serverRequests: $ReadOnlyArray<ClientServerRequest>,
) => Promise<$ReadOnlyArray<ClientClientResponse>>,
- getSignedIdentityKeysBlob: ?() => Promise<SignedIdentityKeysBlob>,
+ getSignedIdentityKeysBlob: () => Promise<SignedIdentityKeysBlob>,
calendarActive: boolean,
) =>
(serverRequests: $ReadOnlyArray<ClientServerRequest>) =>
@@ -151,7 +118,6 @@
export {
openSocketSelector,
sessionIdentificationSelector,
- getSignedIdentityKeysBlobSelector,
webGetClientResponsesSelector,
webSessionStateFuncSelector,
};
diff --git a/web/socket.react.js b/web/socket.react.js
--- a/web/socket.react.js
+++ b/web/socket.react.js
@@ -16,6 +16,7 @@
import { useDispatchActionPromise } from 'lib/utils/action-utils.js';
import { ashoatKeyserverID } from 'lib/utils/validation-utils.js';
+import { useGetSignedIdentityKeysBlob } from './account/account-hooks.js';
import { useSelector } from './redux/redux-utils.js';
import {
activeThreadSelector,
@@ -51,7 +52,10 @@
const preRequestUserState = useSelector(
preRequestUserStateForSingleKeyserverSelector(ashoatKeyserverID),
);
- const getClientResponses = useSelector(webGetClientResponsesSelector);
+ const getSignedIdentityKeysBlob = useGetSignedIdentityKeysBlob();
+ const getClientResponses = useSelector(state =>
+ webGetClientResponsesSelector({ state, getSignedIdentityKeysBlob }),
+ );
const sessionStateFunc = useSelector(
webSessionStateFuncSelector(ashoatKeyserverID),
);

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 30, 5:01 PM (21 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2601704
Default Alt Text
D9532.diff (19 KB)

Event Timeline