very hacky account switcher poc
This commit is contained in:
parent
fd79ea4b9b
commit
c3be616cc7
8 changed files with 154 additions and 17 deletions
|
@ -5,12 +5,14 @@ import initMatrix from '../../../client/initMatrix';
|
|||
import cons from '../../../client/state/cons';
|
||||
import settings from '../../../client/state/settings';
|
||||
import navigation from '../../../client/state/navigation';
|
||||
import { openReusableDialog } from '../../../client/action/navigation'
|
||||
import {
|
||||
toggleSystemTheme, toggleMarkdown, toggleMembershipEvents, toggleNickAvatarEvents,
|
||||
toggleNotifications, toggleNotificationSounds,
|
||||
} from '../../../client/action/settings';
|
||||
import logout from '../../../client/action/logout';
|
||||
import { usePermission } from '../../hooks/usePermission';
|
||||
import colorMXID from '../../../util/colorMXID';
|
||||
|
||||
import Text from '../../atoms/text/Text';
|
||||
import IconButton from '../../atoms/button/IconButton';
|
||||
|
@ -25,6 +27,7 @@ import SettingTile from '../../molecules/setting-tile/SettingTile';
|
|||
import ImportE2ERoomKeys from '../../molecules/import-export-e2e-room-keys/ImportE2ERoomKeys';
|
||||
import ExportE2ERoomKeys from '../../molecules/import-export-e2e-room-keys/ExportE2ERoomKeys';
|
||||
import { ImagePackUser, ImagePackGlobal } from '../../molecules/image-pack/ImagePack';
|
||||
import PeopleSelector from '../../molecules/people-selector/PeopleSelector';
|
||||
|
||||
import ProfileEditor from '../profile-editor/ProfileEditor';
|
||||
import CrossSigning from './CrossSigning';
|
||||
|
@ -38,6 +41,7 @@ import BellIC from '../../../../public/res/ic/outlined/bell.svg';
|
|||
import InfoIC from '../../../../public/res/ic/outlined/info.svg';
|
||||
import PowerIC from '../../../../public/res/ic/outlined/power.svg';
|
||||
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
|
||||
import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg';
|
||||
|
||||
import CinnySVG from '../../../../public/res/svg/cinny.svg';
|
||||
import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
|
||||
|
@ -322,6 +326,50 @@ function Settings() {
|
|||
logout();
|
||||
}
|
||||
};
|
||||
const handleSwitchUsers = async () => {
|
||||
const loggedInUsers = JSON.parse(window.localStorage.getItem("loggedInUsers"));
|
||||
|
||||
const renderUserSwitcher = () => (
|
||||
<div>
|
||||
{
|
||||
loggedInUsers.map((userId) => {
|
||||
let mx = initMatrix.matrixClient;
|
||||
let user = mx.getUser(userId);
|
||||
let avatarUrl = user.avatarUrl ? mx.mxcUrlToHttp(user.avatarUrl, 80, 80, 'crop') : null;
|
||||
return (
|
||||
<PeopleSelector
|
||||
key={userId}
|
||||
onClick={() => {
|
||||
if (userId != window.localStorage.getItem("currentUser")) {
|
||||
window.localStorage.setItem("currentUser", userId);
|
||||
window.location.reload();
|
||||
}
|
||||
}}
|
||||
name={user.displayName}
|
||||
avatarSrc={avatarUrl}
|
||||
color={colorMXID(userId)}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
<Button
|
||||
variant="caution"
|
||||
iconSrc={AddUserIC}
|
||||
onClick={() => {
|
||||
window.localStorage.removeItem("currentUser");
|
||||
window.location.reload();
|
||||
}}
|
||||
>
|
||||
Add user
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
await openReusableDialog(
|
||||
<Text variant="s1" weight="medium">Switch users</Text>,
|
||||
renderUserSwitcher,
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<PopupWindow
|
||||
|
@ -330,6 +378,9 @@ function Settings() {
|
|||
title={<Text variant="s1" weight="medium" primary>Settings</Text>}
|
||||
contentOptions={(
|
||||
<>
|
||||
<Button variant="caution" onClick={handleSwitchUsers}>
|
||||
Switch users
|
||||
</Button>
|
||||
<Button variant="danger" iconSrc={PowerIC} onClick={handleLogout}>
|
||||
Logout
|
||||
</Button>
|
||||
|
|
|
@ -9,6 +9,7 @@ import * as auth from '../../../client/action/auth';
|
|||
import cons from '../../../client/state/cons';
|
||||
import { Debounce, getUrlPrams } from '../../../util/common';
|
||||
import { getBaseUrl } from '../../../util/matrixUtil';
|
||||
import colorMXID from '../../../util/colorMXID';
|
||||
|
||||
import Text from '../../atoms/text/Text';
|
||||
import Button from '../../atoms/button/Button';
|
||||
|
@ -25,6 +26,7 @@ import EyeIC from '../../../../public/res/ic/outlined/eye.svg';
|
|||
import EyeBlindIC from '../../../../public/res/ic/outlined/eye-blind.svg';
|
||||
import CinnySvg from '../../../../public/res/svg/cinny.svg';
|
||||
import SSOButtons from '../../molecules/sso-buttons/SSOButtons';
|
||||
import PeopleSelector from '../../molecules/people-selector/PeopleSelector';
|
||||
|
||||
const LOCALPART_SIGNUP_REGEX = /^[a-z0-9_\-.=/]+$/;
|
||||
const BAD_LOCALPART_ERROR = 'Username can only contain characters a-z, 0-9, or \'=_-./\'';
|
||||
|
@ -539,11 +541,11 @@ function Auth() {
|
|||
|
||||
useEffect(async () => {
|
||||
if (!loginToken) return;
|
||||
if (localStorage.getItem(cons.secretKey.BASE_URL) === undefined) {
|
||||
if (window.userLocalStorage.getItem(cons.secretKey.BASE_URL) === undefined) {
|
||||
setLoginToken(null);
|
||||
return;
|
||||
}
|
||||
const baseUrl = localStorage.getItem(cons.secretKey.BASE_URL);
|
||||
const baseUrl = window.userLocalStorage.getItem(cons.secretKey.BASE_URL);
|
||||
try {
|
||||
await auth.loginWithToken(baseUrl, loginToken);
|
||||
|
||||
|
@ -567,6 +569,24 @@ function Auth() {
|
|||
<Text variant="h2" weight="medium">Cinny</Text>
|
||||
</TitleWrapper>
|
||||
</Header>
|
||||
{(() => {
|
||||
const loggedInUsers = JSON.parse(window.localStorage.getItem("loggedInUsers"));
|
||||
return (
|
||||
loggedInUsers.map((userId) => {
|
||||
return (
|
||||
<PeopleSelector
|
||||
key={userId}
|
||||
onClick={() => {
|
||||
window.localStorage.setItem("currentUser", userId);
|
||||
window.location.reload();
|
||||
}}
|
||||
name={userId}
|
||||
color={colorMXID(userId)}
|
||||
/>
|
||||
);
|
||||
})
|
||||
);
|
||||
})()}
|
||||
<div className="auth-card__content">
|
||||
<AuthCard />
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
import * as sdk from 'matrix-js-sdk';
|
||||
import cons from '../state/cons';
|
||||
import NamespacedStorage from '../state/NamespacedStorage';
|
||||
|
||||
function updateLocalStore(accessToken, deviceId, userId, baseUrl) {
|
||||
localStorage.setItem(cons.secretKey.ACCESS_TOKEN, accessToken);
|
||||
localStorage.setItem(cons.secretKey.DEVICE_ID, deviceId);
|
||||
localStorage.setItem(cons.secretKey.USER_ID, userId);
|
||||
localStorage.setItem(cons.secretKey.BASE_URL, baseUrl);
|
||||
window.localStorage.setItem("currentUser", userId);
|
||||
let loggedInUsers = new Set(JSON.parse(window.localStorage.getItem("loggedInUsers")));
|
||||
loggedInUsers.add(userId);
|
||||
window.localStorage.setItem("loggedInUsers", JSON.stringify(Array.from(loggedInUsers)));
|
||||
|
||||
window.userLocalStorage = new NamespacedStorage(userId, window.localStorage);
|
||||
|
||||
window.userLocalStorage.setItem(cons.secretKey.ACCESS_TOKEN, accessToken);
|
||||
window.userLocalStorage.setItem(cons.secretKey.DEVICE_ID, deviceId);
|
||||
window.userLocalStorage.setItem(cons.secretKey.USER_ID, userId);
|
||||
window.userLocalStorage.setItem(cons.secretKey.BASE_URL, baseUrl);
|
||||
}
|
||||
|
||||
function createTemporaryClient(baseUrl) {
|
||||
|
@ -14,7 +22,7 @@ function createTemporaryClient(baseUrl) {
|
|||
|
||||
async function startSsoLogin(baseUrl, type, idpId) {
|
||||
const client = createTemporaryClient(baseUrl);
|
||||
localStorage.setItem(cons.secretKey.BASE_URL, client.baseUrl);
|
||||
window.userLocalStorage.setItem(cons.secretKey.BASE_URL, client.baseUrl);
|
||||
window.location.href = client.getSsoLoginUrl(window.location.href, type, idpId);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,14 @@ async function logout() {
|
|||
// ignore if failed to logout
|
||||
}
|
||||
mx.clearStores();
|
||||
window.localStorage.clear();
|
||||
|
||||
window.userLocalStorage.clear();
|
||||
let currentUser = window.localStorage.getItem("currentUser");
|
||||
let loggedInUsers = new Set(JSON.parse(window.localStorage.getItem("loggedInUsers")));
|
||||
loggedInUsers.delete(currentUser);
|
||||
window.localStorage.setItem("loggedInUsers", JSON.stringify(loggedInUsers));
|
||||
window.localStorage.removeItem("currentUser");
|
||||
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import RoomsInput from './state/RoomsInput';
|
|||
import Notifications from './state/Notifications';
|
||||
import { cryptoCallbacks } from './state/secretStorageKeys';
|
||||
import navigation from './state/navigation';
|
||||
import NamespacedStorage from './state/NamespacedStorage';
|
||||
|
||||
global.Olm = require('@matrix-org/olm');
|
||||
|
||||
|
@ -22,16 +23,19 @@ class InitMatrix extends EventEmitter {
|
|||
}
|
||||
|
||||
async init() {
|
||||
await this.startClient();
|
||||
const user = window.localStorage.getItem("currentUser");
|
||||
window.userLocalStorage = new NamespacedStorage(user, window.localStorage);
|
||||
|
||||
await this.startClient(user, window.userLocalStorage);
|
||||
this.setupSync();
|
||||
this.listenEvents();
|
||||
}
|
||||
|
||||
async startClient() {
|
||||
async startClient(user, storage) {
|
||||
const indexedDBStore = new sdk.IndexedDBStore({
|
||||
indexedDB: global.indexedDB,
|
||||
localStorage: global.localStorage,
|
||||
dbName: 'web-sync-store',
|
||||
localStorage: storage,
|
||||
dbName: `${user}.web-sync-store`,
|
||||
});
|
||||
await indexedDBStore.startup();
|
||||
|
||||
|
@ -40,7 +44,7 @@ class InitMatrix extends EventEmitter {
|
|||
accessToken: secret.accessToken,
|
||||
userId: secret.userId,
|
||||
store: indexedDBStore,
|
||||
cryptoStore: new sdk.IndexedDBCryptoStore(global.indexedDB, 'crypto-store'),
|
||||
cryptoStore: new sdk.IndexedDBCryptoStore(global.indexedDB, `${user}.crypto-store`),
|
||||
deviceId: secret.deviceId,
|
||||
timelineSupport: true,
|
||||
cryptoCallbacks,
|
||||
|
@ -98,7 +102,7 @@ class InitMatrix extends EventEmitter {
|
|||
this.matrixClient.on('Session.logged_out', () => {
|
||||
this.matrixClient.stopClient();
|
||||
this.matrixClient.clearStores();
|
||||
window.localStorage.clear();
|
||||
window.userLocalStorage.clear();
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
|
44
src/client/state/NamespacedStorage.js
Normal file
44
src/client/state/NamespacedStorage.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
class NamespacedStorage {
|
||||
#namespace;
|
||||
#storage;
|
||||
#cache;
|
||||
|
||||
constructor(namespace, storage) {
|
||||
this.#namespace = namespace;
|
||||
this.#storage = storage;
|
||||
|
||||
this.#cache = new Set();
|
||||
for (let i = 0; i < this.#storage.length; i++) {
|
||||
let key = this.#storage.key(i);
|
||||
if (key.startsWith(`${this.#namespace}.`)) {
|
||||
this.#cache.add(key.replace(`${this.#namespace}.`, ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
key(n) {
|
||||
return this.#cache[n];
|
||||
}
|
||||
|
||||
getItem(key) {
|
||||
return this.#storage.getItem(`${this.#namespace}.${key}`);
|
||||
}
|
||||
|
||||
setItem(key, value) {
|
||||
this.#storage.setItem(`${this.#namespace}.${key}`, value);
|
||||
this.#cache.add(key);
|
||||
}
|
||||
|
||||
removeItem(key) {
|
||||
this.#storage.removeItem(`${this.#namespace}.${key}`);
|
||||
this.#cache.delete(key);
|
||||
}
|
||||
|
||||
clear() {
|
||||
for (let key of this.#cache) {
|
||||
this.removeItem(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default NamespacedStorage;
|
|
@ -1,7 +1,10 @@
|
|||
import cons from './cons';
|
||||
import NamespacedStorage from './NamespacedStorage';
|
||||
|
||||
function getSecret(key) {
|
||||
return localStorage.getItem(key);
|
||||
const user = global.localStorage.getItem("currentUser");
|
||||
const storage = new NamespacedStorage(user, global.localStorage);
|
||||
return storage.getItem(key);
|
||||
}
|
||||
|
||||
const isAuthenticated = () => getSecret(cons.secretKey.ACCESS_TOKEN) !== null;
|
||||
|
|
|
@ -4,7 +4,7 @@ import appDispatcher from '../dispatcher';
|
|||
import cons from './cons';
|
||||
|
||||
function getSettings() {
|
||||
const settings = localStorage.getItem('settings');
|
||||
const settings = window.localStorage.getItem('settings');
|
||||
if (settings === null) return null;
|
||||
return JSON.parse(settings);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ function setSettings(key, value) {
|
|||
let settings = getSettings();
|
||||
if (settings === null) settings = {};
|
||||
settings[key] = value;
|
||||
localStorage.setItem('settings', JSON.stringify(settings));
|
||||
window.localStorage.setItem('settings', JSON.stringify(settings));
|
||||
}
|
||||
|
||||
class Settings extends EventEmitter {
|
||||
|
|
Loading…
Reference in a new issue