Page MenuHomePhabricator

No OneTemporary

diff --git a/web/account/log-in-form.react.js b/web/account/log-in-form.react.js
index 27c4d76c6..7fccadb6f 100644
--- a/web/account/log-in-form.react.js
+++ b/web/account/log-in-form.react.js
@@ -1,100 +1,129 @@
// @flow
import olm from '@matrix-org/olm';
import { useConnectModal } from '@rainbow-me/rainbowkit';
import * as React from 'react';
import { useDispatch } from 'react-redux';
+import uuid from 'uuid';
import { useSigner } from 'wagmi';
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 OrBreak from '../components/or-break.react.js';
import {
setPrimaryIdentityKeys,
setNotificationIdentityKeys,
+ setPickledPrimaryAccount,
+ setPickledNotificationAccount,
} from '../redux/crypto-store-reducer.js';
import { useSelector } from '../redux/redux-utils.js';
function LoginForm(): React.Node {
const { openConnectModal } = useConnectModal();
const { data: signer } = useSigner();
const dispatch = useDispatch();
const primaryIdentityPublicKeys = useSelector(
state => state.cryptoStore.primaryIdentityKeys,
);
const notificationIdentityPublicKeys = useSelector(
state => state.cryptoStore.notificationIdentityKeys,
);
React.useEffect(() => {
(async () => {
if (
primaryIdentityPublicKeys !== null &&
primaryIdentityPublicKeys !== undefined &&
notificationIdentityPublicKeys !== null &&
notificationIdentityPublicKeys !== undefined
) {
return;
}
await olm.init();
const identityAccount = new olm.Account();
identityAccount.create();
const { ed25519: identityED25519, curve25519: identityCurve25519 } =
JSON.parse(identityAccount.identity_keys());
dispatch({
type: setPrimaryIdentityKeys,
payload: { ed25519: identityED25519, curve25519: identityCurve25519 },
});
+ const identityAccountPicklingKey = uuid.v4();
+ const pickledIdentityAccount = identityAccount.pickle(
+ identityAccountPicklingKey,
+ );
+
+ dispatch({
+ type: setPickledPrimaryAccount,
+ payload: {
+ picklingKey: identityAccountPicklingKey,
+ pickledAccount: pickledIdentityAccount,
+ },
+ });
+
const notificationAccount = new olm.Account();
notificationAccount.create();
const {
ed25519: notificationED25519,
curve25519: notificationCurve25519,
} = JSON.parse(notificationAccount.identity_keys());
dispatch({
type: setNotificationIdentityKeys,
payload: {
ed25519: notificationED25519,
curve25519: notificationCurve25519,
},
});
+
+ const notificationAccountPicklingKey = uuid.v4();
+ const pickledNotificationAccount = notificationAccount.pickle(
+ notificationAccountPicklingKey,
+ );
+
+ dispatch({
+ type: setPickledNotificationAccount,
+ payload: {
+ picklingKey: notificationAccountPicklingKey,
+ pickledAccount: pickledNotificationAccount,
+ },
+ });
})();
}, [dispatch, notificationIdentityPublicKeys, primaryIdentityPublicKeys]);
const [siweAuthFlowSelected, setSIWEAuthFlowSelected] =
React.useState<boolean>(false);
const onSIWEButtonClick = React.useCallback(() => {
setSIWEAuthFlowSelected(true);
openConnectModal && openConnectModal();
}, [openConnectModal]);
const cancelSIWEAuthFlow = React.useCallback(() => {
setSIWEAuthFlowSelected(false);
}, []);
if (siweAuthFlowSelected && signer) {
return (
<div className={css.modal_body}>
<SIWELoginForm cancelSIWEAuthFlow={cancelSIWEAuthFlow} />
</div>
);
}
return (
<div className={css.modal_body}>
<TraditionalLoginForm />
<OrBreak />
<SIWEButton onSIWEButtonClick={onSIWEButtonClick} />
</div>
);
}
export default LoginForm;
diff --git a/web/package.json b/web/package.json
index 037ab09b0..49a93c5b1 100644
--- a/web/package.json
+++ b/web/package.json
@@ -1,99 +1,100 @@
{
"name": "web",
"version": "0.0.1",
"type": "module",
"private": true,
"license": "BSD-3-Clause",
"scripts": {
"clean": "rm -rf dist/ && rm -rf node_modules/",
"dev": "yarn concurrently --names=\"NODESSR,BROWSER\" -c \"bgBlue.bold,bgMagenta.bold\" \"yarn webpack --config webpack.config.cjs --config-name=server --watch\" \"yarn webpack-dev-server --config webpack.config.cjs --config-name=browser\"",
"prod": "yarn webpack --config webpack.config.cjs --env prod --progress",
"test": "jest"
},
"devDependencies": {
"@babel/core": "^7.13.14",
"@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8",
"@babel/plugin-proposal-object-rest-spread": "^7.13.8",
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
"@babel/plugin-transform-react-constant-elements": "^7.13.13",
"@babel/plugin-transform-runtime": "^7.13.10",
"@babel/preset-env": "^7.13.12",
"@babel/preset-flow": "^7.13.13",
"@babel/preset-react": "^7.13.13",
"assets-webpack-plugin": "^7.1.1",
"babel-jest": "^26.6.3",
"babel-plugin-transform-remove-console": "^6.9.4",
"concurrently": "^5.3.0",
"copy-webpack-plugin": "^11.0.0",
"flow-bin": "^0.182.0",
"flow-typed": "^3.2.1",
"identity-obj-proxy": "^3.0.0",
"jest": "^26.6.3",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1"
},
"dependencies": {
"@babel/runtime": "^7.13.10",
"@emoji-mart/data": "^1.1.2",
"@emoji-mart/react": "^1.1.1",
"@fortawesome/fontawesome-svg-core": "1.2.25",
"@fortawesome/free-regular-svg-icons": "5.11.2",
"@fortawesome/free-solid-svg-icons": "5.11.2",
"@fortawesome/react-fontawesome": "0.1.5",
"@rainbow-me/rainbowkit": "^0.8.1",
"basscss": "8.0.2",
"classnames": "^2.2.5",
"core-js": "^3.6.5",
"dateformat": "^3.0.3",
"detect-browser": "^4.0.4",
"emoji-mart": "^5.5.2",
"exif-js": "^2.3.0",
"history": "^4.6.3",
"ethers": "^5.7.2",
"invariant": "^2.2.4",
"is-svg": "^4.3.0",
"isomorphic-fetch": "^3.0.0",
"lib": "0.0.1",
"lodash": "^4.17.21",
"@matrix-org/olm": "3.2.4",
"react": "18.1.0",
"react-circular-progressbar": "^2.0.2",
"react-color": "^2.13.0",
"react-dnd": "^11.1.3",
"react-dnd-html5-backend": "^11.1.3",
"react-dom": "18.1.0",
"react-feather": "^2.0.3",
"react-icomoon": "^2.5.7",
"react-icons": "^4.4.0",
"react-redux": "^7.1.1",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-switch": "^7.0.0",
"react-timeago": "^5.2.0",
"redux": "^4.0.4",
"redux-devtools-extension": "^2.13.2",
"redux-persist": "^6.0.0",
"redux-thunk": "^2.2.0",
"reselect": "^4.0.0",
"simple-markdown": "^0.7.2",
"siwe": "^1.1.6",
"tinycolor2": "^1.4.1",
+ "uuid": "^3.3.3",
"visibilityjs": "^2.0.2",
"wagmi": "^0.8.10"
},
"jest": {
"roots": [
"<rootDir>/utils"
],
"transform": {
"\\.js$": "babel-jest"
},
"transformIgnorePatterns": [
"/node_modules/(?!@babel/runtime)"
],
"moduleNameMapper": {
"\\.(css)$": "identity-obj-proxy"
}
}
}
diff --git a/web/redux/crypto-store-reducer.js b/web/redux/crypto-store-reducer.js
index 4d0430dba..8bd53dd40 100644
--- a/web/redux/crypto-store-reducer.js
+++ b/web/redux/crypto-store-reducer.js
@@ -1,46 +1,60 @@
// @flow
import {
logOutActionTypes,
deleteAccountActionTypes,
} from 'lib/actions/user-actions.js';
import type { CryptoStore } from 'lib/types/crypto-types.js';
import { setNewSessionActionType } from 'lib/utils/action-utils.js';
import type { Action } from './redux-setup.js';
const setPrimaryIdentityKeys = 'SET_PRIMARY_IDENTITY_KEYS';
const setNotificationIdentityKeys = 'SET_NOTIFICATION_IDENTITY_KEYS';
+const setPickledPrimaryAccount = 'SET_PICKLED_PRIMARY_ACCOUNT';
+const setPickledNotificationAccount = 'SET_PICKLED_NOTIFICATION_ACCOUNT';
function reduceCryptoStore(state: CryptoStore, action: Action): CryptoStore {
if (action.type === setPrimaryIdentityKeys) {
return {
...state,
primaryIdentityKeys: action.payload,
};
} else if (action.type === setNotificationIdentityKeys) {
return {
...state,
notificationIdentityKeys: action.payload,
};
+ } else if (action.type === setPickledPrimaryAccount) {
+ return {
+ ...state,
+ primaryAccount: action.payload,
+ };
+ } else if (action.type === setPickledNotificationAccount) {
+ return {
+ ...state,
+ notificationAccount: action.payload,
+ };
} else if (
action.type === logOutActionTypes.success ||
action.type === deleteAccountActionTypes.success ||
(action.type === setNewSessionActionType &&
action.payload.sessionChange.cookieInvalidated)
) {
return {
primaryAccount: null,
primaryIdentityKeys: null,
notificationAccount: null,
notificationIdentityKeys: null,
};
}
return state;
}
export {
setPrimaryIdentityKeys,
setNotificationIdentityKeys,
+ setPickledPrimaryAccount,
+ setPickledNotificationAccount,
reduceCryptoStore,
};
diff --git a/web/redux/redux-setup.js b/web/redux/redux-setup.js
index d56380638..aa7b7be12 100644
--- a/web/redux/redux-setup.js
+++ b/web/redux/redux-setup.js
@@ -1,305 +1,315 @@
// @flow
import invariant from 'invariant';
import type { PersistState } from 'redux-persist/es/types.js';
import {
logOutActionTypes,
deleteAccountActionTypes,
} from 'lib/actions/user-actions.js';
import baseReducer from 'lib/reducers/master-reducer.js';
import { nonThreadCalendarFilters } from 'lib/selectors/calendar-filter-selectors.js';
import { mostRecentlyReadThreadSelector } from 'lib/selectors/thread-selectors.js';
import { isLoggedIn } from 'lib/selectors/user-selectors.js';
import { invalidSessionDowngrade } from 'lib/shared/account-utils.js';
import type { Shape } from 'lib/types/core.js';
-import type { CryptoStore, OLMIdentityKeys } from 'lib/types/crypto-types.js';
+import type {
+ CryptoStore,
+ OLMIdentityKeys,
+ PickledOLMAccount,
+} from 'lib/types/crypto-types.js';
import type { DraftStore } from 'lib/types/draft-types.js';
import type { EnabledApps } from 'lib/types/enabled-apps.js';
import type { EntryStore } from 'lib/types/entry-types.js';
import {
type CalendarFilter,
calendarThreadFilterTypes,
} from 'lib/types/filter-types.js';
import type { LifecycleState } from 'lib/types/lifecycle-state-types.js';
import type { LoadingStatus } from 'lib/types/loading-types.js';
import type { MessageStore } from 'lib/types/message-types.js';
import type { UserPolicies } from 'lib/types/policy-types.js';
import type { BaseAction } from 'lib/types/redux-types.js';
import type { ReportStore } from 'lib/types/report-types.js';
import type { ConnectionInfo } from 'lib/types/socket-types.js';
import type { ThreadStore } from 'lib/types/thread-types.js';
import type { CurrentUserInfo, UserStore } from 'lib/types/user-types.js';
import { setNewSessionActionType } from 'lib/utils/action-utils.js';
import {
updateWindowActiveActionType,
setDeviceIDActionType,
updateNavInfoActionType,
updateWindowDimensionsActionType,
updateCalendarCommunityFilter,
clearCalendarCommunityFilter,
} from './action-types.js';
import {
reduceCryptoStore,
setPrimaryIdentityKeys,
setNotificationIdentityKeys,
+ setPickledNotificationAccount,
+ setPickledPrimaryAccount,
} from './crypto-store-reducer.js';
import { reduceDeviceID } from './device-id-reducer.js';
import reduceNavInfo from './nav-reducer.js';
import { getVisibility } from './visibility.js';
import { filterThreadIDsBelongingToCommunity } from '../selectors/calendar-selectors.js';
import { activeThreadSelector } from '../selectors/nav-selectors.js';
import { type NavInfo } from '../types/nav-types.js';
export type WindowDimensions = { width: number, height: number };
export type AppState = {
navInfo: NavInfo,
deviceID: ?string,
currentUserInfo: ?CurrentUserInfo,
draftStore: DraftStore,
sessionID: ?string,
entryStore: EntryStore,
threadStore: ThreadStore,
userStore: UserStore,
messageStore: MessageStore,
updatesCurrentAsOf: number,
loadingStatuses: { [key: string]: { [idx: number]: LoadingStatus } },
calendarFilters: $ReadOnlyArray<CalendarFilter>,
calendarPickedCommunityID: ?string,
urlPrefix: string,
windowDimensions: WindowDimensions,
cookie?: void,
deviceToken?: void,
baseHref: string,
connection: ConnectionInfo,
watchedThreadIDs: $ReadOnlyArray<string>,
lifecycleState: LifecycleState,
enabledApps: EnabledApps,
reportStore: ReportStore,
nextLocalID: number,
dataLoaded: boolean,
windowActive: boolean,
userPolicies: UserPolicies,
cryptoStore: CryptoStore,
_persist: ?PersistState,
};
export type Action =
| BaseAction
| { type: 'UPDATE_NAV_INFO', payload: Shape<NavInfo> }
| {
type: 'UPDATE_WINDOW_DIMENSIONS',
payload: WindowDimensions,
}
| {
type: 'UPDATE_WINDOW_ACTIVE',
payload: boolean,
}
| {
type: 'SET_DEVICE_ID',
payload: string,
}
| { +type: 'SET_PRIMARY_IDENTITY_KEYS', payload: ?OLMIdentityKeys }
| { +type: 'SET_NOTIFICATION_IDENTITY_KEYS', payload: ?OLMIdentityKeys }
+ | { +type: 'SET_PICKLED_PRIMARY_ACCOUNT', payload: ?PickledOLMAccount }
+ | { +type: 'SET_PICKLED_NOTIFICATION_ACCOUNT', payload: ?PickledOLMAccount }
| {
+type: 'UPDATE_CALENDAR_COMMUNITY_FILTER',
+payload: string,
}
| {
+type: 'CLEAR_CALENDAR_COMMUNITY_FILTER',
+payload: void,
};
export function reducer(oldState: AppState | void, action: Action): AppState {
invariant(oldState, 'should be set');
let state = oldState;
if (action.type === updateWindowDimensionsActionType) {
return validateState(oldState, {
...state,
windowDimensions: action.payload,
});
} else if (action.type === updateWindowActiveActionType) {
return validateState(oldState, {
...state,
windowActive: action.payload,
});
} else if (action.type === updateCalendarCommunityFilter) {
const nonThreadFilters = nonThreadCalendarFilters(state.calendarFilters);
const threadIDs = Array.from(
filterThreadIDsBelongingToCommunity(
action.payload,
state.threadStore.threadInfos,
),
);
return {
...state,
calendarFilters: [
...nonThreadFilters,
{
type: calendarThreadFilterTypes.THREAD_LIST,
threadIDs,
},
],
calendarPickedCommunityID: action.payload,
};
} else if (action.type === clearCalendarCommunityFilter) {
const nonThreadFilters = nonThreadCalendarFilters(state.calendarFilters);
return {
...state,
calendarFilters: nonThreadFilters,
calendarPickedCommunityID: null,
};
} else if (action.type === setNewSessionActionType) {
if (
invalidSessionDowngrade(
oldState,
action.payload.sessionChange.currentUserInfo,
action.payload.preRequestUserState,
)
) {
return oldState;
}
state = {
...state,
sessionID: action.payload.sessionChange.sessionID,
};
} else if (
(action.type === logOutActionTypes.success &&
invalidSessionDowngrade(
oldState,
action.payload.currentUserInfo,
action.payload.preRequestUserState,
)) ||
(action.type === deleteAccountActionTypes.success &&
invalidSessionDowngrade(
oldState,
action.payload.currentUserInfo,
action.payload.preRequestUserState,
))
) {
return oldState;
}
if (
action.type !== updateNavInfoActionType &&
action.type !== setDeviceIDActionType &&
action.type !== setPrimaryIdentityKeys &&
- action.type !== setNotificationIdentityKeys
+ action.type !== setNotificationIdentityKeys &&
+ action.type !== setPickledPrimaryAccount &&
+ action.type !== setPickledNotificationAccount
) {
state = baseReducer(state, action).state;
}
state = {
...state,
navInfo: reduceNavInfo(
state.navInfo,
action,
state.threadStore.threadInfos,
),
deviceID: reduceDeviceID(state.deviceID, action),
cryptoStore: reduceCryptoStore(state.cryptoStore, action),
};
return validateState(oldState, state);
}
function validateState(oldState: AppState, state: AppState): AppState {
if (
(state.navInfo.activeChatThreadID &&
!state.navInfo.pendingThread &&
!state.threadStore.threadInfos[state.navInfo.activeChatThreadID]) ||
(!state.navInfo.activeChatThreadID && isLoggedIn(state))
) {
// Makes sure the active thread always exists
state = {
...state,
navInfo: {
...state.navInfo,
activeChatThreadID: mostRecentlyReadThreadSelector(state),
},
};
}
const activeThread = activeThreadSelector(state);
if (
activeThread &&
!state.navInfo.pendingThread &&
state.threadStore.threadInfos[activeThread].currentUser.unread &&
getVisibility().hidden()
) {
console.warn(
`thread ${activeThread} is active and unread, ` +
'but visibilityjs reports the window is not visible',
);
}
if (
activeThread &&
!state.navInfo.pendingThread &&
state.threadStore.threadInfos[activeThread].currentUser.unread &&
typeof document !== 'undefined' &&
document &&
'hasFocus' in document &&
!document.hasFocus()
) {
console.warn(
`thread ${activeThread} is active and unread, ` +
'but document.hasFocus() is false',
);
}
if (
activeThread &&
!getVisibility().hidden() &&
typeof document !== 'undefined' &&
document &&
'hasFocus' in document &&
document.hasFocus() &&
!state.navInfo.pendingThread &&
state.threadStore.threadInfos[activeThread].currentUser.unread
) {
// Makes sure a currently focused thread is never unread
state = {
...state,
threadStore: {
...state.threadStore,
threadInfos: {
...state.threadStore.threadInfos,
[activeThread]: {
...state.threadStore.threadInfos[activeThread],
currentUser: {
...state.threadStore.threadInfos[activeThread].currentUser,
unread: false,
},
},
},
},
};
}
const oldActiveThread = activeThreadSelector(oldState);
if (
activeThread &&
oldActiveThread !== activeThread &&
state.messageStore.threads[activeThread]
) {
// Update messageStore.threads[activeThread].lastNavigatedTo
state = {
...state,
messageStore: {
...state.messageStore,
threads: {
...state.messageStore.threads,
[activeThread]: {
...state.messageStore.threads[activeThread],
lastNavigatedTo: Date.now(),
},
},
},
};
}
return state;
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 1:25 AM (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2559302
Default Alt Text
(19 KB)

Event Timeline