very hacky account switcher poc

This commit is contained in:
ash lea 2022-08-22 16:45:18 -04:00
parent fd79ea4b9b
commit c3be616cc7
8 changed files with 154 additions and 17 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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();
});
}

View 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;

View file

@ -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;

View file

@ -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 {