Reduce amount of requests done by the onboarding task list (#9194)
* Significantly reduce work of useUserOnboardingContext * Wrap UserOnboardingButton to avoid unnecessary work when it's not shown * Remove progress from user onboarding button
This commit is contained in:
parent
e8eefeb937
commit
94d292a6ec
4 changed files with 117 additions and 81 deletions
|
@ -15,54 +15,96 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { ClientEvent, IMyDevice, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { ClientEvent, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||
import { Notifier } from "../Notifier";
|
||||
import DMRoomMap from "../utils/DMRoomMap";
|
||||
import { useEventEmitter } from "./useEventEmitter";
|
||||
|
||||
export interface UserOnboardingContext {
|
||||
avatar: string | null;
|
||||
myDevice: string;
|
||||
devices: IMyDevice[];
|
||||
dmRooms: {[userId: string]: Room};
|
||||
hasAvatar: boolean;
|
||||
hasDevices: boolean;
|
||||
hasDmRooms: boolean;
|
||||
hasNotificationsEnabled: boolean;
|
||||
}
|
||||
|
||||
const USER_ONBOARDING_CONTEXT_INTERVAL = 5000;
|
||||
|
||||
/**
|
||||
* Returns a persistent, non-changing reference to a function
|
||||
* This function proxies all its calls to the current value of the given input callback
|
||||
*
|
||||
* This allows you to use the current value of e.g., a state in a callback that’s used by e.g., a useEventEmitter or
|
||||
* similar hook without re-registering the hook when the state changes
|
||||
* @param value changing callback
|
||||
*/
|
||||
function useRefOf<T extends any[], R>(value: (...values: T) => R): (...values: T) => R {
|
||||
const ref = useRef(value);
|
||||
ref.current = value;
|
||||
return useCallback(
|
||||
(...values: T) => ref.current(...values),
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
function useUserOnboardingContextValue<T>(defaultValue: T, callback: (cli: MatrixClient) => Promise<T>): T {
|
||||
const [value, setValue] = useState<T>(defaultValue);
|
||||
const cli = MatrixClientPeg.get();
|
||||
|
||||
const handler = useRefOf(callback);
|
||||
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
return;
|
||||
}
|
||||
|
||||
let handle: number | null = null;
|
||||
let enabled = true;
|
||||
const repeater = async () => {
|
||||
if (handle !== null) {
|
||||
clearTimeout(handle);
|
||||
handle = null;
|
||||
}
|
||||
setValue(await handler(cli));
|
||||
if (enabled) {
|
||||
handle = setTimeout(repeater, USER_ONBOARDING_CONTEXT_INTERVAL);
|
||||
}
|
||||
};
|
||||
repeater().catch(err => logger.warn("could not update user onboarding context", err));
|
||||
cli.on(ClientEvent.AccountData, repeater);
|
||||
return () => {
|
||||
enabled = false;
|
||||
cli.off(ClientEvent.AccountData, repeater);
|
||||
if (handle !== null) {
|
||||
clearTimeout(handle);
|
||||
handle = null;
|
||||
}
|
||||
};
|
||||
}, [cli, handler, value]);
|
||||
return value;
|
||||
}
|
||||
|
||||
export function useUserOnboardingContext(): UserOnboardingContext | null {
|
||||
const [context, setContext] = useState<UserOnboardingContext | null>(null);
|
||||
const hasAvatar = useUserOnboardingContextValue(false, async (cli) => {
|
||||
const profile = await cli.getProfileInfo(cli.getUserId());
|
||||
return Boolean(profile?.avatar_url);
|
||||
});
|
||||
const hasDevices = useUserOnboardingContextValue(false, async (cli) => {
|
||||
const myDevice = cli.getDeviceId();
|
||||
const devices = await cli.getDevices();
|
||||
return Boolean(devices.devices.find(device => device.device_id !== myDevice));
|
||||
});
|
||||
const hasDmRooms = useUserOnboardingContextValue(false, async () => {
|
||||
const dmRooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals() ?? {};
|
||||
return Boolean(Object.keys(dmRooms).length);
|
||||
});
|
||||
const hasNotificationsEnabled = useUserOnboardingContextValue(false, async () => {
|
||||
return Notifier.isPossible();
|
||||
});
|
||||
|
||||
const cli = MatrixClientPeg.get();
|
||||
const handler = useCallback(async () => {
|
||||
try {
|
||||
const profile = await cli.getProfileInfo(cli.getUserId());
|
||||
|
||||
const myDevice = cli.getDeviceId();
|
||||
const devices = await cli.getDevices();
|
||||
|
||||
const dmRooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals() ?? {};
|
||||
setContext({
|
||||
avatar: profile?.avatar_url ?? null,
|
||||
myDevice,
|
||||
devices: devices.devices,
|
||||
dmRooms: dmRooms,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.warn("Could not load context for user onboarding task list: ", e);
|
||||
setContext(null);
|
||||
}
|
||||
}, [cli]);
|
||||
|
||||
useEventEmitter(cli, ClientEvent.AccountData, handler);
|
||||
useEffect(() => {
|
||||
const handle = setInterval(handler, 2000);
|
||||
handler();
|
||||
return () => {
|
||||
if (handle) {
|
||||
clearInterval(handle);
|
||||
}
|
||||
};
|
||||
}, [handler]);
|
||||
|
||||
return context;
|
||||
return useMemo(
|
||||
() => ({ hasAvatar, hasDevices, hasDmRooms, hasNotificationsEnabled }),
|
||||
[hasAvatar, hasDevices, hasDmRooms, hasNotificationsEnabled],
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue