diff --git a/src/analytics/PosthogAnalytics.ts b/src/analytics/PosthogAnalytics.ts
index 0ca23fb3..9d4f3f5c 100644
--- a/src/analytics/PosthogAnalytics.ts
+++ b/src/analytics/PosthogAnalytics.ts
@@ -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);
});
}
diff --git a/src/e2ee/sharedKeyManagement.ts b/src/e2ee/sharedKeyManagement.ts
index 0a819066..651012c7 100644
--- a/src/e2ee/sharedKeyManagement.ts
+++ b/src/e2ee/sharedKeyManagement.ts
@@ -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];
+};
diff --git a/src/home/CallList.tsx b/src/home/CallList.tsx
index f93e2ff8..99296c5d 100644
--- a/src/home/CallList.tsx
+++ b/src/home/CallList.tsx
@@ -109,7 +109,7 @@ function CallTile({
);
diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx
index 2ab942d3..4c796e9f 100644
--- a/src/room/GroupCallView.tsx
+++ b/src/room/GroupCallView.tsx
@@ -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(() => {
diff --git a/src/settings/useSetting.ts b/src/settings/useSetting.ts
index c127554e..5d86a4b6 100644
--- a/src/settings/useSetting.ts
+++ b/src/settings/useSetting.ts
@@ -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, (value: T) => void];
type DisableableSetting = [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 = (name: string, defaultValue: T): Setting => {
const key = useMemo(() => getSettingKey(name), [name]);
- const [value, setValue] = useState(() => {
- 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 = (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 = (name: string, newValue: T) => {
- localStorage.setItem(getSettingKey(name), JSON.stringify(newValue));
- settingsBus.emit(name, newValue);
-};
+export const setSetting = (name: string, newValue: T) =>
+ setLocalStorageItem(getSettingKey(name), JSON.stringify(newValue));
const canEnableSpatialAudio = () => {
const { userAgent } = navigator;
diff --git a/src/useLocalStorage.ts b/src/useLocalStorage.ts
new file mode 100644
index 00000000..e7fe51ef
--- /dev/null
+++ b/src/useLocalStorage.ts
@@ -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;
+
+// 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(() =>
+ 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);
+};