Save room shared keys to local storage

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
Šimon Brandner
2023-08-09 13:29:45 +02:00
parent 80f97cba32
commit c4e5e1afb1
6 changed files with 112 additions and 43 deletions

View File

@@ -20,7 +20,7 @@ import { MatrixClient } from "matrix-js-sdk";
import { Buffer } from "buffer";
import { widget } from "../widget";
import { getSetting, setSetting, settingsBus } from "../settings/useSetting";
import { getSetting, setSetting, getSettingKey } from "../settings/useSetting";
import {
CallEndedTracker,
CallStartedTracker,
@@ -34,6 +34,7 @@ import {
} from "./PosthogEvents";
import { Config } from "../config/Config";
import { getUrlParams } from "../UrlParams";
import { localStorageBus } from "../useLocalStorage";
/* Posthog analytics tracking.
*
@@ -413,7 +414,7 @@ export class PosthogAnalytics {
// * When the user changes their preferences on this device
// Note that for new accounts, pseudonymousAnalyticsOptIn won't be set, so updateAnonymityFromSettings
// won't be called (i.e. this.anonymity will be left as the default, until the setting changes)
settingsBus.on("opt-in-analytics", (optInAnalytics) => {
localStorageBus.on(getSettingKey("opt-in-analytics"), (optInAnalytics) => {
this.updateAnonymityAndIdentifyUser(optInAnalytics);
});
}

View File

@@ -14,7 +14,27 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { useSetting } from "../settings/useSetting";
import { useEffect, useMemo } from "react";
import { randomString } from "matrix-js-sdk/src/randomstring";
export const useRoomSharedKey = (roomId: string, initialValue?: string) =>
useSetting(`room-shared-key-${roomId}`, initialValue);
import { useEnableE2EE } from "../settings/useSetting";
import { useLocalStorage } from "../useLocalStorage";
const getRoomSharedKeyLocalStorageKey = (roomId: string): string =>
`room-shared-key-${roomId}`;
export const useRoomSharedKey = (
roomId: string
): [string | null, ((value: string) => void) | null] => {
const key = useMemo(() => getRoomSharedKeyLocalStorageKey(roomId), [roomId]);
const [e2eeEnabled] = useEnableE2EE();
const [roomSharedKey, setRoomSharedKey] = useLocalStorage(key);
useEffect(() => {
if ((roomSharedKey && roomSharedKey !== "") || !e2eeEnabled) return;
setRoomSharedKey(randomString(32));
}, [roomSharedKey, e2eeEnabled, setRoomSharedKey]);
return e2eeEnabled ? [roomSharedKey, setRoomSharedKey] : [null, null];
};

View File

@@ -109,7 +109,7 @@ function CallTile({
<CopyButton
className={styles.copyButton}
variant="icon"
value={getRoomUrl(roomAlias, roomSharedKey)}
value={getRoomUrl(roomAlias, roomSharedKey ?? undefined)}
/>
</div>
);

View File

@@ -245,14 +245,13 @@ export function GroupCallView({
}, [groupCall, state, leave]);
const [e2eeSharedKey, setE2EESharedKey] = useRoomSharedKey(
groupCall.room.roomId,
password ?? undefined
groupCall.room.roomId
);
useEffect(() => {
if (!password || password === e2eeSharedKey) return;
if (!password || password === "" || password === e2eeSharedKey) return;
setE2EESharedKey(password);
setE2EESharedKey?.(password);
}, [password, e2eeSharedKey, setE2EESharedKey]);
useEffect(() => {

View File

@@ -1,5 +1,5 @@
/*
Copyright 2022 New Vector Ltd
Copyright 2022 - 2023 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,59 +14,49 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { EventEmitter } from "events";
import { useMemo, useState, useEffect, useCallback } from "react";
import { useCallback, useMemo } from "react";
import { isE2EESupported } from "livekit-client";
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
import {
getLocalStorageItem,
setLocalStorageItem,
useLocalStorage,
} from "../useLocalStorage";
type Setting<T> = [T, (value: T) => void];
type DisableableSetting<T> = [T, ((value: T) => void) | null];
// Bus to notify other useSetting consumers when a setting is changed
export const settingsBus = new EventEmitter();
const getSettingKey = (name: string): string => {
export const getSettingKey = (name: string): string => {
return `matrix-setting-${name}`;
};
// Like useState, but reads from and persists the value to localStorage
export const useSetting = <T>(name: string, defaultValue: T): Setting<T> => {
const key = useMemo(() => getSettingKey(name), [name]);
const [value, setValue] = useState<T>(() => {
const item = localStorage.getItem(key);
return item == null ? defaultValue : JSON.parse(item);
});
const [item, setItem] = useLocalStorage(key);
useEffect(() => {
settingsBus.on(name, setValue);
return () => {
settingsBus.off(name, setValue);
};
}, [name, setValue]);
const value = useMemo(
() => (item == null ? defaultValue : JSON.parse(item)),
[item, defaultValue]
);
const setValue = useCallback(
(value: T) => {
setItem(JSON.stringify(value));
},
[setItem]
);
return [
value,
useCallback(
(newValue: T) => {
setValue(newValue);
localStorage.setItem(key, JSON.stringify(newValue));
settingsBus.emit(name, newValue);
},
[name, key, setValue]
),
];
return [value, setValue];
};
export const getSetting = <T>(name: string, defaultValue: T): T => {
const item = localStorage.getItem(getSettingKey(name));
const item = getLocalStorageItem(getSettingKey(name));
return item === null ? defaultValue : JSON.parse(item);
};
export const setSetting = <T>(name: string, newValue: T) => {
localStorage.setItem(getSettingKey(name), JSON.stringify(newValue));
settingsBus.emit(name, newValue);
};
export const setSetting = <T>(name: string, newValue: T) =>
setLocalStorageItem(getSettingKey(name), JSON.stringify(newValue));
const canEnableSpatialAudio = () => {
const { userAgent } = navigator;

59
src/useLocalStorage.ts Normal file
View File

@@ -0,0 +1,59 @@
/*
Copyright 2023 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import EventEmitter from "events";
import { useCallback, useEffect, useState } from "react";
type LocalStorageItem = ReturnType<typeof localStorage.getItem>;
// Bus to notify other useLocalStorage consumers when an item is changed
export const localStorageBus = new EventEmitter();
// Like useState, but reads from and persists the value to localStorage
export const useLocalStorage = (
key: string
): [LocalStorageItem, (value: string) => void] => {
const [value, setValue] = useState<LocalStorageItem>(() =>
localStorage.getItem(key)
);
useEffect(() => {
localStorageBus.on(key, setValue);
return () => {
localStorageBus.off(key, setValue);
};
}, [key, setValue]);
return [
value,
useCallback(
(newValue: string) => {
setValue(newValue);
localStorage.setItem(key, newValue);
localStorageBus.emit(key, newValue);
},
[key, setValue]
),
];
};
export const getLocalStorageItem = (key: string): LocalStorageItem =>
localStorage.getItem(key);
export const setLocalStorageItem = (key: string, value: string): void => {
localStorage.setItem(key, value);
localStorageBus.emit(key, value);
};