mirror of
https://github.com/outline/outline.git
synced 2026-06-13 03:14:59 +03:00
0139b91b5d
* chore: Replace lodash with es-toolkit Migrate all direct lodash imports to es-toolkit/compat for a smaller, faster, lodash-compatible utility library. Transitive lodash usage from other packages remains unchanged. * fix: Restore isPlainObject semantics in CanCan policy The lodash migration aliased `isObject` to `lodash/isPlainObject` and the codemod incorrectly mapped the local name to es-toolkit's `isObject`, which also returns true for arrays and functions. This caused condition objects in policy definitions to be skipped, breaking authorization checks across the codebase. * fix: Restore unicode-aware length counting in validators es-toolkit/compat's size() returns string.length, while lodash's _.size() counts unicode code points. Switch to [...value].length to preserve the previous behavior so multi-byte characters like emoji count as one.
65 lines
1.5 KiB
TypeScript
65 lines
1.5 KiB
TypeScript
import { throttle } from "es-toolkit/compat";
|
|
import { useState, useRef, useCallback, useEffect } from "react";
|
|
import { Minute } from "@shared/utils/time";
|
|
import useIsMounted from "./useIsMounted";
|
|
|
|
const activityEvents = [
|
|
"click",
|
|
"mousemove",
|
|
"keydown",
|
|
"DOMMouseScroll",
|
|
"mousewheel",
|
|
"mousedown",
|
|
"touchstart",
|
|
"touchmove",
|
|
"focus",
|
|
];
|
|
|
|
/**
|
|
* Hook to detect user idle state.
|
|
*
|
|
* @param timeToIdle The time in ms until idle
|
|
* @param events The events to listen to
|
|
* @returns boolean if the user is idle
|
|
*/
|
|
export default function useIdle(
|
|
timeToIdle: number = 3 * Minute.ms,
|
|
events = activityEvents
|
|
) {
|
|
const isMounted = useIsMounted();
|
|
const [isIdle, setIsIdle] = useState(false);
|
|
const timeout = useRef<ReturnType<typeof setTimeout>>();
|
|
|
|
const onActivity = useCallback(() => {
|
|
if (timeout.current) {
|
|
clearTimeout(timeout.current);
|
|
}
|
|
|
|
timeout.current = setTimeout(() => {
|
|
if (isMounted()) {
|
|
setIsIdle(true);
|
|
}
|
|
}, timeToIdle);
|
|
}, [isMounted, timeToIdle]);
|
|
|
|
useEffect(() => {
|
|
const handleUserActivityEvent = throttle(() => {
|
|
if (isMounted()) {
|
|
setIsIdle(false);
|
|
onActivity();
|
|
}
|
|
}, 1000);
|
|
|
|
events.forEach((eventName) =>
|
|
window.addEventListener(eventName, handleUserActivityEvent)
|
|
);
|
|
return () => {
|
|
events.forEach((eventName) =>
|
|
window.removeEventListener(eventName, handleUserActivityEvent)
|
|
);
|
|
};
|
|
}, [events, isMounted, onActivity]);
|
|
|
|
return isIdle;
|
|
}
|