Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3332755
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rCOMM Comm
Attached
Detach File
Event Timeline
Log In to Comment