Apply prettier formatting

This commit is contained in:
Michael Weimann 2022-12-12 12:24:14 +01:00
parent 1cac306093
commit 526645c791
No known key found for this signature in database
GPG key ID: 53F535A266BB9584
1576 changed files with 65385 additions and 62478 deletions

View file

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

View file

@ -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 = () => {

View file

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

View file

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

View file

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

View file

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

View file

@ -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]),
);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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("Dont miss a thing by taking %(brand)s with you", {
brand: SdkConfig.get("brand"),
}),
title: () =>
_t("Download %(brand)s", {
brand: SdkConfig.get("brand"),
}),
description: () =>
_t("Dont 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))];
}