Skip to content

Commit 632e895

Browse files
author
Loïc Mangeonjean
committed
fix: inline small not maintained commonjs library to prevent issues
1 parent f408d99 commit 632e895

File tree

3 files changed

+159
-10
lines changed

3 files changed

+159
-10
lines changed

package-lock.json

Lines changed: 0 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
"dependencies": {
2626
"@codingame/monaco-languageclient-wrapper": "^5.0.4",
2727
"@rehooks/local-storage": "^2.4.5",
28-
"activity-detector": "^3.0.0",
2928
"react": ">=18.2.0",
3029
"uuid": "^9.0.1"
3130
},

src/hooks/useIsUserActive.ts

Lines changed: 159 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,172 @@
11
import { useEffect, useState } from 'react'
2-
import createActivityDetector from 'activity-detector'
2+
3+
enum State {
4+
idle = 'idle',
5+
active = 'active'
6+
}
7+
8+
const DEFAULT_INITIAL_STATE = State.active
9+
10+
const DEFAULT_ACTIVITY_EVENTS = [
11+
'click',
12+
'mousemove',
13+
'keydown',
14+
'DOMMouseScroll',
15+
'mousewheel',
16+
'mousedown',
17+
'touchstart',
18+
'touchmove',
19+
'focus'
20+
]
21+
22+
const DEFAULT_INACTIVITY_EVENTS = ['blur', 'visibilitychange']
23+
24+
const DEFAULT_IGNORED_EVENTS_WHEN_IDLE = ['mousemove']
25+
26+
let hidden: 'hidden' | undefined, visibilityChangeEvent: 'visibilitychange' | undefined
27+
if (typeof document.hidden !== 'undefined') {
28+
hidden = 'hidden'
29+
visibilityChangeEvent = 'visibilitychange'
30+
} else {
31+
const prefixes = ['webkit', 'moz', 'ms']
32+
for (let i = 0; i < prefixes.length; i++) {
33+
const prefix = prefixes[i]
34+
if (typeof (document as unknown as Record<string, unknown>)[`${prefix}Hidden`] !== 'undefined') {
35+
hidden = `${prefix}Hidden` as 'hidden'
36+
visibilityChangeEvent = `${prefix}visibilitychange` as 'visibilitychange'
37+
break
38+
}
39+
}
40+
}
41+
42+
interface ActivityDetectorParams {
43+
/**
44+
* Events which force a transition to 'active'
45+
*/
46+
activityEvents?: string[]
47+
/**
48+
* Events which force a transition to 'idle'
49+
*/
50+
inactivityEvents?: string[]
51+
/**
52+
* Events that are ignored in 'idle' state
53+
*/
54+
ignoredEventsWhenIdle?: string[]
55+
/**
56+
* Inactivity time in ms to transition to 'idle'
57+
*/
58+
timeToIdle?: number
59+
/**
60+
* One of 'active' or 'idle'
61+
*/
62+
initialState?: State
63+
autoInit?: boolean
64+
}
65+
function createActivityDetector ({
66+
activityEvents = DEFAULT_ACTIVITY_EVENTS,
67+
inactivityEvents = DEFAULT_INACTIVITY_EVENTS,
68+
ignoredEventsWhenIdle = DEFAULT_IGNORED_EVENTS_WHEN_IDLE,
69+
timeToIdle = 30000,
70+
initialState = DEFAULT_INITIAL_STATE,
71+
autoInit = true
72+
}: ActivityDetectorParams = {}) {
73+
const listeners: Record<State, (() => void)[]> = { [State.active]: [], [State.idle]: [] }
74+
let state: State
75+
let timer: number
76+
77+
const setState = (newState: State) => {
78+
clearTimeout(timer)
79+
if (newState === State.active) {
80+
timer = window.setTimeout(() => setState(State.idle), timeToIdle)
81+
}
82+
if (state !== newState) {
83+
state = newState
84+
listeners[state].forEach(l => l())
85+
}
86+
}
87+
88+
const handleUserActivityEvent = (event: Event) => {
89+
if (state === State.active || ignoredEventsWhenIdle.indexOf(event.type) < 0) {
90+
setState(State.active)
91+
}
92+
}
93+
94+
const handleUserInactivityEvent = () => {
95+
setState(State.idle)
96+
}
97+
98+
const handleVisibilityChangeEvent = () => {
99+
setState(document[hidden!] ? State.idle : State.active)
100+
}
101+
102+
/**
103+
* Starts the activity detector with the given state.
104+
*/
105+
const init = (firstState = DEFAULT_INITIAL_STATE) => {
106+
setState(firstState === State.active ? State.active : State.idle)
107+
activityEvents.forEach(eventName =>
108+
window.addEventListener(eventName, handleUserActivityEvent))
109+
110+
inactivityEvents.filter(eventName => eventName !== 'visibilitychange')
111+
.forEach(eventName =>
112+
window.addEventListener(eventName, handleUserInactivityEvent))
113+
114+
if (inactivityEvents.indexOf('visibilitychange') >= 0 && visibilityChangeEvent != null) {
115+
document.addEventListener(visibilityChangeEvent, handleVisibilityChangeEvent)
116+
}
117+
}
118+
119+
/**
120+
* Register an event listener for the required event
121+
*/
122+
const on = (eventName: State, listener: () => void) => {
123+
listeners[eventName].push(listener)
124+
const off = () => {
125+
const index = listeners[eventName].indexOf(listener)
126+
if (index >= 0) {
127+
listeners[eventName].splice(index, 1)
128+
}
129+
}
130+
return off
131+
}
132+
133+
/**
134+
* Stops the activity detector and clean the listeners
135+
*/
136+
const stop = () => {
137+
listeners[State.active] = []
138+
listeners[State.idle] = []
139+
140+
clearTimeout(timer)
141+
142+
activityEvents.forEach(eventName =>
143+
window.removeEventListener(eventName, handleUserActivityEvent))
144+
145+
inactivityEvents.forEach(eventName =>
146+
window.removeEventListener(eventName, handleUserInactivityEvent))
147+
148+
if (visibilityChangeEvent != null) {
149+
document.removeEventListener(visibilityChangeEvent, handleVisibilityChangeEvent)
150+
}
151+
}
152+
153+
if (autoInit) {
154+
init(initialState)
155+
}
156+
157+
return { on, stop, init }
158+
}
3159

4160
export default function useIsUserActive (timeToIdle: number): boolean {
5161
const [active, setActive] = useState(true)
6162
useEffect(() => {
7163
const activityDetector = createActivityDetector({
8164
timeToIdle
9165
})
10-
activityDetector.on('idle', () => {
166+
activityDetector.on(State.idle, () => {
11167
setActive(false)
12168
})
13-
activityDetector.on('active', () => {
169+
activityDetector.on(State.active, () => {
14170
setActive(true)
15171
})
16172
return () => {

0 commit comments

Comments
 (0)