Apply prettier formatting
This commit is contained in:
parent
1cac306093
commit
526645c791
1576 changed files with 65385 additions and 62478 deletions
|
@ -34,8 +34,7 @@ export function useRoomMemberProfile({
|
|||
|
||||
const member = useMemo(() => {
|
||||
const threadContexts = [TimelineRenderingType.ThreadsList, TimelineRenderingType.Thread];
|
||||
if ((!forceHistorical && useOnlyCurrentProfiles)
|
||||
|| threadContexts.includes(context.timelineRenderingType)) {
|
||||
if ((!forceHistorical && useOnlyCurrentProfiles) || threadContexts.includes(context.timelineRenderingType)) {
|
||||
const currentMember = context.room?.getMember(userId);
|
||||
if (currentMember) return currentMember;
|
||||
}
|
||||
|
|
|
@ -18,11 +18,7 @@ import { useEffect } from "react";
|
|||
|
||||
const DEBOUNCE_TIMEOUT = 100;
|
||||
|
||||
export function useDebouncedCallback<T extends any[]>(
|
||||
enabled: boolean,
|
||||
callback: (...params: T) => void,
|
||||
params: T,
|
||||
) {
|
||||
export function useDebouncedCallback<T extends any[]>(enabled: boolean, callback: (...params: T) => void, params: T) {
|
||||
useEffect(() => {
|
||||
let handle: number | null = null;
|
||||
const doSearch = () => {
|
||||
|
|
|
@ -25,11 +25,14 @@ export const useRecentSearches = (): [Room[], () => void] => {
|
|||
const [rooms, setRooms] = useState(() => {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const recents = SettingsStore.getValue("SpotlightSearch.recentSearches", null);
|
||||
return recents.map(r => cli.getRoom(r)).filter(Boolean);
|
||||
return recents.map((r) => cli.getRoom(r)).filter(Boolean);
|
||||
});
|
||||
|
||||
return [rooms, () => {
|
||||
SettingsStore.setValue("SpotlightSearch.recentSearches", null, SettingLevel.ACCOUNT, []);
|
||||
setRooms([]);
|
||||
}];
|
||||
return [
|
||||
rooms,
|
||||
() => {
|
||||
SettingsStore.setValue("SpotlightSearch.recentSearches", null, SettingLevel.ACCOUNT, []);
|
||||
setRooms([]);
|
||||
},
|
||||
];
|
||||
};
|
||||
|
|
|
@ -21,30 +21,36 @@ import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
|
|||
|
||||
import { useTypedEventEmitter } from "./useEventEmitter";
|
||||
|
||||
const tryGetContent = <T extends {}>(ev?: MatrixEvent) => ev ? ev.getContent<T>() : undefined;
|
||||
const tryGetContent = <T extends {}>(ev?: MatrixEvent) => (ev ? ev.getContent<T>() : undefined);
|
||||
|
||||
// Hook to simplify listening to Matrix account data
|
||||
export const useAccountData = <T extends {}>(cli: MatrixClient, eventType: string) => {
|
||||
const [value, setValue] = useState<T>(() => tryGetContent<T>(cli.getAccountData(eventType)));
|
||||
|
||||
const handler = useCallback((event) => {
|
||||
if (event.getType() !== eventType) return;
|
||||
setValue(event.getContent());
|
||||
}, [eventType]);
|
||||
const handler = useCallback(
|
||||
(event) => {
|
||||
if (event.getType() !== eventType) return;
|
||||
setValue(event.getContent());
|
||||
},
|
||||
[eventType],
|
||||
);
|
||||
useTypedEventEmitter(cli, ClientEvent.AccountData, handler);
|
||||
|
||||
return value || {} as T;
|
||||
return value || ({} as T);
|
||||
};
|
||||
|
||||
// Hook to simplify listening to Matrix room account data
|
||||
export const useRoomAccountData = <T extends {}>(room: Room, eventType: string) => {
|
||||
const [value, setValue] = useState<T>(() => tryGetContent<T>(room.getAccountData(eventType)));
|
||||
|
||||
const handler = useCallback((event) => {
|
||||
if (event.getType() !== eventType) return;
|
||||
setValue(event.getContent());
|
||||
}, [eventType]);
|
||||
const handler = useCallback(
|
||||
(event) => {
|
||||
if (event.getType() !== eventType) return;
|
||||
setValue(event.getContent());
|
||||
},
|
||||
[eventType],
|
||||
);
|
||||
useTypedEventEmitter(room, RoomEvent.AccountData, handler);
|
||||
|
||||
return value || {} as T;
|
||||
return value || ({} as T);
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { useState, useEffect, DependencyList } from 'react';
|
||||
import { useState, useEffect, DependencyList } from "react";
|
||||
|
||||
type Fn<T> = () => Promise<T>;
|
||||
|
||||
|
@ -22,7 +22,7 @@ export const useAsyncMemo = <T>(fn: Fn<T>, deps: DependencyList, initialValue?:
|
|||
const [value, setValue] = useState<T>(initialValue);
|
||||
useEffect(() => {
|
||||
let discard = false;
|
||||
fn().then(v => {
|
||||
fn().then((v) => {
|
||||
if (!discard) {
|
||||
setValue(v);
|
||||
}
|
||||
|
|
|
@ -25,9 +25,7 @@ interface State {
|
|||
device: MediaDeviceInfo | null;
|
||||
}
|
||||
|
||||
export const useAudioDeviceSelection = (
|
||||
onDeviceChanged?: (device: MediaDeviceInfo) => void,
|
||||
) => {
|
||||
export const useAudioDeviceSelection = (onDeviceChanged?: (device: MediaDeviceInfo) => void) => {
|
||||
const shouldRequestPermissionsRef = useRef<boolean>(true);
|
||||
const [state, setState] = useState<State>({
|
||||
devices: [],
|
||||
|
@ -40,15 +38,16 @@ export const useAudioDeviceSelection = (
|
|||
MediaDeviceHandler.getDevices().then(({ audioinput }) => {
|
||||
MediaDeviceHandler.getDefaultDevice(audioinput);
|
||||
const deviceFromSettings = MediaDeviceHandler.getAudioInput();
|
||||
const device = audioinput.find((d) => {
|
||||
return d.deviceId === deviceFromSettings;
|
||||
}) || audioinput[0];
|
||||
const device =
|
||||
audioinput.find((d) => {
|
||||
return d.deviceId === deviceFromSettings;
|
||||
}) || audioinput[0];
|
||||
setState({
|
||||
...state,
|
||||
devices: audioinput,
|
||||
device,
|
||||
});
|
||||
stream?.getTracks().forEach(t => t.stop());
|
||||
stream?.getTracks().forEach((t) => t.stop());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -37,14 +37,14 @@ export const useConnectionState = (call: Call): ConnectionState =>
|
|||
useTypedEventEmitterState(
|
||||
call,
|
||||
CallEvent.ConnectionState,
|
||||
useCallback(state => state ?? call.connectionState, [call]),
|
||||
useCallback((state) => state ?? call.connectionState, [call]),
|
||||
);
|
||||
|
||||
export const useParticipants = (call: Call): Map<RoomMember, Set<string>> =>
|
||||
useTypedEventEmitterState(
|
||||
call,
|
||||
CallEvent.Participants,
|
||||
useCallback(state => state ?? call.participants, [call]),
|
||||
useCallback((state) => state ?? call.participants, [call]),
|
||||
);
|
||||
|
||||
export const useParticipantCount = (call: Call): number => {
|
||||
|
@ -71,8 +71,9 @@ export const useParticipatingMembers = (call: Call): RoomMember[] => {
|
|||
};
|
||||
|
||||
export const useFull = (call: Call): boolean => {
|
||||
return useParticipantCount(call) >= (
|
||||
SdkConfig.get("element_call").participant_limit ?? DEFAULTS.element_call.participant_limit!
|
||||
return (
|
||||
useParticipantCount(call) >=
|
||||
(SdkConfig.get("element_call").participant_limit ?? DEFAULTS.element_call.participant_limit!)
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -89,5 +90,5 @@ export const useLayout = (call: ElementCall): Layout =>
|
|||
useTypedEventEmitterState(
|
||||
call,
|
||||
CallEvent.Layout,
|
||||
useCallback(state => state ?? call.layout, [call]),
|
||||
useCallback((state) => state ?? call.layout, [call]),
|
||||
);
|
||||
|
|
|
@ -21,10 +21,7 @@ import type { EventEmitter } from "events";
|
|||
|
||||
type Handler = (...args: any[]) => void;
|
||||
|
||||
export function useTypedEventEmitter<
|
||||
Events extends string,
|
||||
Arguments extends ListenerMap<Events>,
|
||||
>(
|
||||
export function useTypedEventEmitter<Events extends string, Arguments extends ListenerMap<Events>>(
|
||||
emitter: TypedEventEmitter<Events, Arguments>,
|
||||
eventName: Events,
|
||||
handler: Handler,
|
||||
|
@ -35,11 +32,7 @@ export function useTypedEventEmitter<
|
|||
/**
|
||||
* Hook to wrap an EventEmitter on and off in hook lifecycle
|
||||
*/
|
||||
export function useEventEmitter(
|
||||
emitter: EventEmitter | undefined,
|
||||
eventName: string | symbol,
|
||||
handler: Handler,
|
||||
): void {
|
||||
export function useEventEmitter(emitter: EventEmitter | undefined, eventName: string | symbol, handler: Handler): void {
|
||||
// Create a ref that stores handler
|
||||
const savedHandler = useRef(handler);
|
||||
|
||||
|
@ -70,11 +63,7 @@ export function useEventEmitter(
|
|||
|
||||
type Mapper<T> = (...args: any[]) => T;
|
||||
|
||||
export function useTypedEventEmitterState<
|
||||
T,
|
||||
Events extends string,
|
||||
Arguments extends ListenerMap<Events>,
|
||||
>(
|
||||
export function useTypedEventEmitterState<T, Events extends string, Arguments extends ListenerMap<Events>>(
|
||||
emitter: TypedEventEmitter<Events, Arguments>,
|
||||
eventName: Events,
|
||||
fn: Mapper<T>,
|
||||
|
@ -88,9 +77,12 @@ export function useEventEmitterState<T>(
|
|||
fn: Mapper<T>,
|
||||
): T {
|
||||
const [value, setValue] = useState<T>(fn);
|
||||
const handler = useCallback((...args: any[]) => {
|
||||
setValue(fn(...args));
|
||||
}, [fn]);
|
||||
const handler = useCallback(
|
||||
(...args: any[]) => {
|
||||
setValue(fn(...args));
|
||||
},
|
||||
[fn],
|
||||
);
|
||||
// re-run when the emitter changes
|
||||
useEffect(handler, [emitter]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
useEventEmitter(emitter, eventName, handler);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
|
@ -17,8 +16,7 @@ limitations under the License.
|
|||
|
||||
import { useState } from "react";
|
||||
|
||||
const favouriteMessageIds = JSON.parse(
|
||||
localStorage?.getItem("io_element_favouriteMessages")?? "[]") as string[];
|
||||
const favouriteMessageIds = JSON.parse(localStorage?.getItem("io_element_favouriteMessages") ?? "[]") as string[];
|
||||
|
||||
export default function useFavouriteMessages() {
|
||||
const [, setX] = useState<string[]>();
|
||||
|
@ -27,11 +25,12 @@ export default function useFavouriteMessages() {
|
|||
const isFavourite = (eventId: string): boolean => favouriteMessageIds.includes(eventId);
|
||||
|
||||
const toggleFavourite = (eventId: string) => {
|
||||
isFavourite(eventId) ? favouriteMessageIds.splice(favouriteMessageIds.indexOf(eventId), 1)
|
||||
isFavourite(eventId)
|
||||
? favouriteMessageIds.splice(favouriteMessageIds.indexOf(eventId), 1)
|
||||
: favouriteMessageIds.push(eventId);
|
||||
|
||||
//update the local storage
|
||||
localStorage.setItem('io_element_favouriteMessages', JSON.stringify(favouriteMessageIds));
|
||||
localStorage.setItem("io_element_favouriteMessages", JSON.stringify(favouriteMessageIds));
|
||||
|
||||
// This forces a re-render to account for changes in appearance in real-time when the favourite button is toggled
|
||||
setX([]);
|
||||
|
|
|
@ -16,8 +16,7 @@ limitations under the License.
|
|||
|
||||
import { useState } from "react";
|
||||
|
||||
export default function useFocus(
|
||||
): [boolean, {onFocus: () => void, onBlur: () => void}] {
|
||||
export default function useFocus(): [boolean, { onFocus: () => void; onBlur: () => void }] {
|
||||
const [focused, setFocused] = useState(false);
|
||||
|
||||
const props = {
|
||||
|
|
|
@ -18,7 +18,7 @@ import { useState } from "react";
|
|||
|
||||
export default function useHover(
|
||||
ignoreHover?: (ev: React.MouseEvent) => boolean,
|
||||
): [boolean, { onMouseOver: () => void, onMouseLeave: () => void, onMouseMove: (ev: React.MouseEvent) => void }] {
|
||||
): [boolean, { onMouseOver: () => void; onMouseLeave: () => void; onMouseMove: (ev: React.MouseEvent) => void }] {
|
||||
const [hovered, setHoverState] = useState(false);
|
||||
|
||||
const props = {
|
||||
|
|
|
@ -27,11 +27,14 @@ import { useTypedEventEmitter } from "./useEventEmitter";
|
|||
export function useIsEncrypted(cli: MatrixClient, room?: Room): boolean | undefined {
|
||||
const [isEncrypted, setIsEncrypted] = useState(room ? cli.isRoomEncrypted(room.roomId) : undefined);
|
||||
|
||||
const update = useCallback((event: MatrixEvent) => {
|
||||
if (room && event.getType() === EventType.RoomEncryption) {
|
||||
setIsEncrypted(cli.isRoomEncrypted(room.roomId));
|
||||
}
|
||||
}, [cli, room]);
|
||||
const update = useCallback(
|
||||
(event: MatrixEvent) => {
|
||||
if (room && event.getType() === EventType.RoomEncryption) {
|
||||
setIsEncrypted(cli.isRoomEncrypted(room.roomId));
|
||||
}
|
||||
},
|
||||
[cli, room],
|
||||
);
|
||||
useTypedEventEmitter(room?.currentState, RoomStateEvent.Events, update);
|
||||
|
||||
return isEncrypted;
|
||||
|
|
|
@ -20,16 +20,20 @@ import { useCallback, useRef } from "react";
|
|||
* Hook to prevent a slower response to an earlier query overwriting the result to a faster response of a later query
|
||||
* @param onResultChanged
|
||||
*/
|
||||
export const useLatestResult = <T, R>(onResultChanged: (result: R) => void):
|
||||
[(query: T | null) => void, (query: T | null, result: R) => void] => {
|
||||
export const useLatestResult = <T, R>(
|
||||
onResultChanged: (result: R) => void,
|
||||
): [(query: T | null) => void, (query: T | null, result: R) => void] => {
|
||||
const ref = useRef<T | null>(null);
|
||||
const setQuery = useCallback((query: T | null) => {
|
||||
ref.current = query;
|
||||
}, []);
|
||||
const setResult = useCallback((query: T | null, result: R) => {
|
||||
if (ref.current === query) {
|
||||
onResultChanged(result);
|
||||
}
|
||||
}, [onResultChanged]);
|
||||
const setResult = useCallback(
|
||||
(query: T | null, result: R) => {
|
||||
if (ref.current === query) {
|
||||
onResultChanged(result);
|
||||
}
|
||||
},
|
||||
[onResultChanged],
|
||||
);
|
||||
return [setQuery, setResult];
|
||||
};
|
||||
|
|
|
@ -35,10 +35,13 @@ export const useLocalStorageState = <T>(key: string, initialValue: T): [T, Dispa
|
|||
setValue(getValue(lsKey, initialValue));
|
||||
}, [lsKey, initialValue]);
|
||||
|
||||
const _setValue: Dispatch<SetStateAction<T>> = useCallback((v: T) => {
|
||||
window.localStorage.setItem(lsKey, JSON.stringify(v));
|
||||
setValue(v);
|
||||
}, [lsKey]);
|
||||
const _setValue: Dispatch<SetStateAction<T>> = useCallback(
|
||||
(v: T) => {
|
||||
window.localStorage.setItem(lsKey, JSON.stringify(v));
|
||||
setValue(v);
|
||||
},
|
||||
[lsKey],
|
||||
);
|
||||
|
||||
return [value, _setValue];
|
||||
};
|
||||
|
|
|
@ -36,30 +36,33 @@ export const useProfileInfo = () => {
|
|||
|
||||
const [updateQuery, updateResult] = useLatestResult<string, IProfileInfo | null>(setProfile);
|
||||
|
||||
const search = useCallback(async ({ query: term }: IProfileInfoOpts): Promise<boolean> => {
|
||||
updateQuery(term);
|
||||
if (!term?.length || !term.startsWith('@') || !term.includes(':')) {
|
||||
setProfile(null);
|
||||
return true;
|
||||
}
|
||||
const search = useCallback(
|
||||
async ({ query: term }: IProfileInfoOpts): Promise<boolean> => {
|
||||
updateQuery(term);
|
||||
if (!term?.length || !term.startsWith("@") || !term.includes(":")) {
|
||||
setProfile(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const result = await MatrixClientPeg.get().getProfileInfo(term);
|
||||
updateResult(term, {
|
||||
user_id: term,
|
||||
avatar_url: result.avatar_url,
|
||||
display_name: result.displayname,
|
||||
});
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("Could not fetch profile info for params", { term }, e);
|
||||
updateResult(term, null);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [updateQuery, updateResult]);
|
||||
setLoading(true);
|
||||
try {
|
||||
const result = await MatrixClientPeg.get().getProfileInfo(term);
|
||||
updateResult(term, {
|
||||
user_id: term,
|
||||
avatar_url: result.avatar_url,
|
||||
display_name: result.displayname,
|
||||
});
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("Could not fetch profile info for params", { term }, e);
|
||||
updateResult(term, null);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[updateQuery, updateResult],
|
||||
);
|
||||
|
||||
return {
|
||||
ready: true,
|
||||
|
|
|
@ -71,46 +71,47 @@ export const usePublicRoomDirectory = () => {
|
|||
}
|
||||
}
|
||||
|
||||
const search = useCallback(async ({
|
||||
limit = 20,
|
||||
query,
|
||||
roomTypes,
|
||||
}: IPublicRoomsOpts): Promise<boolean> => {
|
||||
const opts: IRoomDirectoryOptions = { limit };
|
||||
const search = useCallback(
|
||||
async ({ limit = 20, query, roomTypes }: IPublicRoomsOpts): Promise<boolean> => {
|
||||
const opts: IRoomDirectoryOptions = { limit };
|
||||
|
||||
if (config?.roomServer != MatrixClientPeg.getHomeserverName()) {
|
||||
opts.server = config?.roomServer;
|
||||
}
|
||||
if (config?.roomServer != MatrixClientPeg.getHomeserverName()) {
|
||||
opts.server = config?.roomServer;
|
||||
}
|
||||
|
||||
if (config?.instanceId === ALL_ROOMS) {
|
||||
opts.include_all_networks = true;
|
||||
} else if (config?.instanceId) {
|
||||
opts.third_party_instance_id = config.instanceId;
|
||||
}
|
||||
if (config?.instanceId === ALL_ROOMS) {
|
||||
opts.include_all_networks = true;
|
||||
} else if (config?.instanceId) {
|
||||
opts.third_party_instance_id = config.instanceId;
|
||||
}
|
||||
|
||||
if (query || roomTypes) {
|
||||
opts.filter = {
|
||||
generic_search_term: query,
|
||||
room_types: await MatrixClientPeg.get().doesServerSupportUnstableFeature(
|
||||
"org.matrix.msc3827.stable",
|
||||
) ? Array.from<RoomType | null>(roomTypes) : null,
|
||||
};
|
||||
}
|
||||
if (query || roomTypes) {
|
||||
opts.filter = {
|
||||
generic_search_term: query,
|
||||
room_types: (await MatrixClientPeg.get().doesServerSupportUnstableFeature(
|
||||
"org.matrix.msc3827.stable",
|
||||
))
|
||||
? Array.from<RoomType | null>(roomTypes)
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
updateQuery(opts);
|
||||
try {
|
||||
setLoading(true);
|
||||
const { chunk } = await MatrixClientPeg.get().publicRooms(opts);
|
||||
updateResult(opts, chunk);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("Could not fetch public rooms for params", opts, e);
|
||||
updateResult(opts, []);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [config, updateQuery, updateResult]);
|
||||
updateQuery(opts);
|
||||
try {
|
||||
setLoading(true);
|
||||
const { chunk } = await MatrixClientPeg.get().publicRooms(opts);
|
||||
updateResult(opts, chunk);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("Could not fetch public rooms for params", opts, e);
|
||||
updateResult(opts, []);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[config, updateQuery, updateResult],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
initProtocols();
|
||||
|
@ -128,18 +129,19 @@ export const usePublicRoomDirectory = () => {
|
|||
let roomServer: string = myHomeserver;
|
||||
if (
|
||||
SdkConfig.getObject("room_directory")?.get("servers")?.includes(lsRoomServer) ||
|
||||
SettingsStore.getValue("room_directory_servers")?.includes(lsRoomServer)
|
||||
SettingsStore.getValue("room_directory_servers")?.includes(lsRoomServer)
|
||||
) {
|
||||
roomServer = lsRoomServer;
|
||||
}
|
||||
|
||||
let instanceId: string | undefined = undefined;
|
||||
if (roomServer === myHomeserver && (
|
||||
lsInstanceId === ALL_ROOMS ||
|
||||
Object.values(protocols).some((p: IProtocol) => {
|
||||
p.instances.some(i => i.instance_id === lsInstanceId);
|
||||
})
|
||||
)) {
|
||||
if (
|
||||
roomServer === myHomeserver &&
|
||||
(lsInstanceId === ALL_ROOMS ||
|
||||
Object.values(protocols).some((p: IProtocol) => {
|
||||
p.instances.some((i) => i.instance_id === lsInstanceId);
|
||||
}))
|
||||
) {
|
||||
instanceId = lsInstanceId;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,18 +25,34 @@ import { useTypedEventEmitter } from "./useEventEmitter";
|
|||
// Hook to simplify watching Matrix Room joined members
|
||||
export const useRoomMembers = (room: Room, throttleWait = 250): RoomMember[] => {
|
||||
const [members, setMembers] = useState<RoomMember[]>(room.getJoinedMembers());
|
||||
useTypedEventEmitter(room.currentState, RoomStateEvent.Update, throttle(() => {
|
||||
setMembers(room.getJoinedMembers());
|
||||
}, throttleWait, { leading: true, trailing: true }));
|
||||
useTypedEventEmitter(
|
||||
room.currentState,
|
||||
RoomStateEvent.Update,
|
||||
throttle(
|
||||
() => {
|
||||
setMembers(room.getJoinedMembers());
|
||||
},
|
||||
throttleWait,
|
||||
{ leading: true, trailing: true },
|
||||
),
|
||||
);
|
||||
return members;
|
||||
};
|
||||
|
||||
// Hook to simplify watching Matrix Room joined member count
|
||||
export const useRoomMemberCount = (room: Room, throttleWait = 250): number => {
|
||||
const [count, setCount] = useState<number>(room.getJoinedMemberCount());
|
||||
useTypedEventEmitter(room.currentState, RoomStateEvent.Update, throttle(() => {
|
||||
setCount(room.getJoinedMemberCount());
|
||||
}, throttleWait, { leading: true, trailing: true }));
|
||||
useTypedEventEmitter(
|
||||
room.currentState,
|
||||
RoomStateEvent.Update,
|
||||
throttle(
|
||||
() => {
|
||||
setCount(room.getJoinedMemberCount());
|
||||
},
|
||||
throttleWait,
|
||||
{ leading: true, trailing: true },
|
||||
),
|
||||
);
|
||||
return count;
|
||||
};
|
||||
|
||||
|
|
|
@ -25,17 +25,12 @@ import { useEventEmitter } from "./useEventEmitter";
|
|||
|
||||
export const useNotificationState = (room: Room): [RoomNotifState, (state: RoomNotifState) => void] => {
|
||||
const echoChamber = useMemo(() => EchoChamber.forRoom(room), [room]);
|
||||
const [notificationState, setNotificationState] = useState<RoomNotifState>(
|
||||
echoChamber.notificationVolume,
|
||||
);
|
||||
const [notificationState, setNotificationState] = useState<RoomNotifState>(echoChamber.notificationVolume);
|
||||
useEventEmitter(echoChamber, PROPERTY_UPDATED, (key: CachedRoomKey) => {
|
||||
if (key === CachedRoomKey.NotificationVolume) {
|
||||
setNotificationState(echoChamber.notificationVolume);
|
||||
}
|
||||
});
|
||||
const setter = useCallback(
|
||||
(state: RoomNotifState) => echoChamber.notificationVolume = state,
|
||||
[echoChamber],
|
||||
);
|
||||
const setter = useCallback((state: RoomNotifState) => (echoChamber.notificationVolume = state), [echoChamber]);
|
||||
return [notificationState, setter];
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import SettingsStore from '../settings/SettingsStore';
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
|
||||
// Hook to fetch the value of a setting and dynamically update when it changes
|
||||
export const useSettingValue = <T>(settingName: string, roomId: string = null, excludeDefault = false) => {
|
||||
|
|
|
@ -32,50 +32,50 @@ export const useSlidingSyncRoomSearch = () => {
|
|||
const [loading, setLoading] = useState(false);
|
||||
const listIndex = SlidingSyncManager.instance.getOrAllocateListIndex(SlidingSyncManager.ListSearch);
|
||||
|
||||
const [updateQuery, updateResult] = useLatestResult<{ term: string, limit?: number }, Room[]>(setRooms);
|
||||
const [updateQuery, updateResult] = useLatestResult<{ term: string; limit?: number }, Room[]>(setRooms);
|
||||
|
||||
const search = useCallback(async ({
|
||||
limit = 100,
|
||||
query: term,
|
||||
}: SlidingSyncRoomSearchOpts): Promise<boolean> => {
|
||||
const opts = { limit, term };
|
||||
updateQuery(opts);
|
||||
const search = useCallback(
|
||||
async ({ limit = 100, query: term }: SlidingSyncRoomSearchOpts): Promise<boolean> => {
|
||||
const opts = { limit, term };
|
||||
updateQuery(opts);
|
||||
|
||||
if (!term?.length) {
|
||||
setRooms([]);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
await SlidingSyncManager.instance.ensureListRegistered(listIndex, {
|
||||
ranges: [[0, limit]],
|
||||
filters: {
|
||||
room_name_like: term,
|
||||
},
|
||||
});
|
||||
const rooms = [];
|
||||
const { roomIndexToRoomId } = SlidingSyncManager.instance.slidingSync.getListData(listIndex);
|
||||
let i = 0;
|
||||
while (roomIndexToRoomId[i]) {
|
||||
const roomId = roomIndexToRoomId[i];
|
||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||
if (room) {
|
||||
rooms.push(room);
|
||||
}
|
||||
i++;
|
||||
if (!term?.length) {
|
||||
setRooms([]);
|
||||
return true;
|
||||
}
|
||||
updateResult(opts, rooms);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("Could not fetch sliding sync rooms for params", { limit, term }, e);
|
||||
updateResult(opts, []);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
// TODO: delete the list?
|
||||
}
|
||||
}, [updateQuery, updateResult, listIndex]);
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
await SlidingSyncManager.instance.ensureListRegistered(listIndex, {
|
||||
ranges: [[0, limit]],
|
||||
filters: {
|
||||
room_name_like: term,
|
||||
},
|
||||
});
|
||||
const rooms = [];
|
||||
const { roomIndexToRoomId } = SlidingSyncManager.instance.slidingSync.getListData(listIndex);
|
||||
let i = 0;
|
||||
while (roomIndexToRoomId[i]) {
|
||||
const roomId = roomIndexToRoomId[i];
|
||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||
if (room) {
|
||||
rooms.push(room);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
updateResult(opts, rooms);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("Could not fetch sliding sync rooms for params", { limit, term }, e);
|
||||
updateResult(opts, []);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
// TODO: delete the list?
|
||||
}
|
||||
},
|
||||
[updateQuery, updateResult, listIndex],
|
||||
);
|
||||
|
||||
return {
|
||||
loading,
|
||||
|
|
|
@ -32,12 +32,8 @@ const debuglog = (...args: any[]) => {
|
|||
* @param targetValue Desired value to animate to (can be changed repeatedly to whatever is current at that time)
|
||||
* @param duration Duration that each animation should take, specify 0 to skip animating
|
||||
*/
|
||||
export function useSmoothAnimation(
|
||||
initialValue: number,
|
||||
targetValue: number,
|
||||
duration: number,
|
||||
): number {
|
||||
const state = useRef<{ timestamp: DOMHighResTimeStamp | null, value: number }>({
|
||||
export function useSmoothAnimation(initialValue: number, targetValue: number, duration: number): number {
|
||||
const state = useRef<{ timestamp: DOMHighResTimeStamp | null; value: number }>({
|
||||
timestamp: null,
|
||||
value: initialValue,
|
||||
});
|
||||
|
|
|
@ -55,13 +55,12 @@ export const useSpaceResults = (space?: Room, query?: string): [IHierarchyRoom[]
|
|||
const normalizedQuery = normalize(trimmedQuery);
|
||||
|
||||
const cli = MatrixClientPeg.get();
|
||||
return rooms?.filter(r => {
|
||||
return r.room_type !== RoomType.Space &&
|
||||
return rooms?.filter((r) => {
|
||||
return (
|
||||
r.room_type !== RoomType.Space &&
|
||||
cli.getRoom(r.room_id)?.getMyMembership() !== "join" &&
|
||||
(
|
||||
normalize(r.name || "").includes(normalizedQuery) ||
|
||||
(r.canonical_alias || "").includes(lcQuery)
|
||||
);
|
||||
(normalize(r.name || "").includes(normalizedQuery) || (r.canonical_alias || "").includes(lcQuery))
|
||||
);
|
||||
});
|
||||
}, [rooms, query]);
|
||||
|
||||
|
|
|
@ -21,9 +21,13 @@ export const useStateArray = <T>(initialSize: number, initialState: T | T[]): [T
|
|||
const [data, setData] = useState<T[]>(() => {
|
||||
return Array.isArray(initialState) ? initialState : new Array(initialSize).fill(initialState);
|
||||
});
|
||||
return [data, (index: number, value: T) => setData(data => {
|
||||
const copy = [...data];
|
||||
copy[index] = value;
|
||||
return copy;
|
||||
})];
|
||||
return [
|
||||
data,
|
||||
(index: number, value: T) =>
|
||||
setData((data) => {
|
||||
const copy = [...data];
|
||||
copy[index] = value;
|
||||
return copy;
|
||||
}),
|
||||
];
|
||||
};
|
||||
|
|
|
@ -59,7 +59,7 @@ export const useInterval = (handler: Handler, intervalMs: number) => {
|
|||
// Hook to simplify a variable counting down to 0, handler called when it reached 0
|
||||
export const useExpiringCounter = (handler: Handler, intervalMs: number, initialCount: number) => {
|
||||
const [count, setCount] = useState(initialCount);
|
||||
useInterval(() => setCount(c => c - 1), intervalMs);
|
||||
useInterval(() => setCount((c) => c - 1), intervalMs);
|
||||
if (count === 0) {
|
||||
handler();
|
||||
}
|
||||
|
|
|
@ -24,7 +24,10 @@ import { doesRoomHaveUnreadMessages } from "../Unread";
|
|||
import { EffectiveMembership, getEffectiveMembership } from "../utils/membership";
|
||||
import { useEventEmitter } from "./useEventEmitter";
|
||||
|
||||
export const useUnreadNotifications = (room: Room, threadId?: string): {
|
||||
export const useUnreadNotifications = (
|
||||
room: Room,
|
||||
threadId?: string,
|
||||
): {
|
||||
symbol: string | null;
|
||||
count: number;
|
||||
color: NotificationColor;
|
||||
|
@ -33,7 +36,9 @@ export const useUnreadNotifications = (room: Room, threadId?: string): {
|
|||
const [count, setCount] = useState<number>(0);
|
||||
const [color, setColor] = useState<NotificationColor>(0);
|
||||
|
||||
useEventEmitter(room, RoomEvent.UnreadNotifications,
|
||||
useEventEmitter(
|
||||
room,
|
||||
RoomEvent.UnreadNotifications,
|
||||
(unreadNotifications: NotificationCount, evtThreadId?: string) => {
|
||||
// Discarding all events not related to the thread if one has been setup
|
||||
if (threadId && threadId !== evtThreadId) return;
|
||||
|
|
|
@ -30,33 +30,36 @@ export const useUserDirectory = () => {
|
|||
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [updateQuery, updateResult] = useLatestResult<{ term: string, limit?: number }, DirectoryMember[]>(setUsers);
|
||||
const [updateQuery, updateResult] = useLatestResult<{ term: string; limit?: number }, DirectoryMember[]>(setUsers);
|
||||
|
||||
const search = useCallback(async ({
|
||||
limit = 20,
|
||||
query: term,
|
||||
}: IUserDirectoryOpts): Promise<boolean> => {
|
||||
const opts = { limit, term };
|
||||
updateQuery(opts);
|
||||
const search = useCallback(
|
||||
async ({ limit = 20, query: term }: IUserDirectoryOpts): Promise<boolean> => {
|
||||
const opts = { limit, term };
|
||||
updateQuery(opts);
|
||||
|
||||
if (!term?.length) {
|
||||
setUsers([]);
|
||||
return true;
|
||||
}
|
||||
if (!term?.length) {
|
||||
setUsers([]);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
const { results } = await MatrixClientPeg.get().searchUserDirectory(opts);
|
||||
updateResult(opts, results.map(user => new DirectoryMember(user)));
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("Could not fetch user in user directory for params", { limit, term }, e);
|
||||
updateResult(opts, []);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [updateQuery, updateResult]);
|
||||
try {
|
||||
setLoading(true);
|
||||
const { results } = await MatrixClientPeg.get().searchUserDirectory(opts);
|
||||
updateResult(
|
||||
opts,
|
||||
results.map((user) => new DirectoryMember(user)),
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("Could not fetch user in user directory for params", { limit, term }, e);
|
||||
updateResult(opts, []);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[updateQuery, updateResult],
|
||||
);
|
||||
|
||||
return {
|
||||
ready: true,
|
||||
|
|
|
@ -42,10 +42,7 @@ const USER_ONBOARDING_CONTEXT_INTERVAL = 5000;
|
|||
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),
|
||||
[],
|
||||
);
|
||||
return useCallback((...values: T) => ref.current(...values), []);
|
||||
}
|
||||
|
||||
function useUserOnboardingContextValue<T>(defaultValue: T, callback: (cli: MatrixClient) => Promise<T>): T {
|
||||
|
@ -71,7 +68,7 @@ function useUserOnboardingContextValue<T>(defaultValue: T, callback: (cli: Matri
|
|||
handle = window.setTimeout(repeater, USER_ONBOARDING_CONTEXT_INTERVAL);
|
||||
}
|
||||
};
|
||||
repeater().catch(err => logger.warn("could not update user onboarding context", err));
|
||||
repeater().catch((err) => logger.warn("could not update user onboarding context", err));
|
||||
cli.on(ClientEvent.AccountData, repeater);
|
||||
return () => {
|
||||
enabled = false;
|
||||
|
@ -93,7 +90,7 @@ export function useUserOnboardingContext(): UserOnboardingContext | null {
|
|||
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));
|
||||
return Boolean(devices.devices.find((device) => device.device_id !== myDevice));
|
||||
});
|
||||
const hasDmRooms = useUserOnboardingContextValue(false, async () => {
|
||||
const dmRooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals() ?? {};
|
||||
|
|
|
@ -49,7 +49,7 @@ interface InternalUserOnboardingTask extends UserOnboardingTask {
|
|||
|
||||
const onClickStartDm = (ev: ButtonEvent) => {
|
||||
PosthogTrackers.trackInteraction("WebUserOnboardingTaskSendDm", ev);
|
||||
defaultDispatcher.dispatch({ action: 'view_create_chat' });
|
||||
defaultDispatcher.dispatch({ action: "view_create_chat" });
|
||||
};
|
||||
|
||||
const tasks: InternalUserOnboardingTask[] = [
|
||||
|
@ -94,12 +94,14 @@ const tasks: InternalUserOnboardingTask[] = [
|
|||
},
|
||||
{
|
||||
id: "download-apps",
|
||||
title: () => _t("Download %(brand)s", {
|
||||
brand: SdkConfig.get("brand"),
|
||||
}),
|
||||
description: () => _t("Don’t miss a thing by taking %(brand)s with you", {
|
||||
brand: SdkConfig.get("brand"),
|
||||
}),
|
||||
title: () =>
|
||||
_t("Download %(brand)s", {
|
||||
brand: SdkConfig.get("brand"),
|
||||
}),
|
||||
description: () =>
|
||||
_t("Don’t miss a thing by taking %(brand)s with you", {
|
||||
brand: SdkConfig.get("brand"),
|
||||
}),
|
||||
completed: (ctx: UserOnboardingContext) => ctx.hasDevices,
|
||||
action: {
|
||||
label: _t("Download apps"),
|
||||
|
@ -143,10 +145,7 @@ const tasks: InternalUserOnboardingTask[] = [
|
|||
|
||||
export function useUserOnboardingTasks(context: UserOnboardingContext): [UserOnboardingTask[], UserOnboardingTask[]] {
|
||||
const useCase = useSettingValue<UseCase | null>("FTUE.useCaseSelection") ?? UseCase.Skip;
|
||||
const relevantTasks = useMemo(
|
||||
() => tasks.filter(it => !it.relevant || it.relevant.includes(useCase)),
|
||||
[useCase],
|
||||
);
|
||||
const completedTasks = relevantTasks.filter(it => context && it.completed(context));
|
||||
return [completedTasks, relevantTasks.filter(it => !completedTasks.includes(it))];
|
||||
const relevantTasks = useMemo(() => tasks.filter((it) => !it.relevant || it.relevant.includes(useCase)), [useCase]);
|
||||
const completedTasks = relevantTasks.filter((it) => context && it.completed(context));
|
||||
return [completedTasks, relevantTasks.filter((it) => !completedTasks.includes(it))];
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue