Skip to content

Commit a97a3e5

Browse files
authored
Merge pull request #33 from CodinGame/fix-esm-strict
Fix esm strict
2 parents f408d99 + bef4210 commit a97a3e5

File tree

7 files changed

+171
-16
lines changed

7 files changed

+171
-16
lines changed

.eslintrc.cjs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,11 @@ module.exports = {
66
extends: [
77
'@codingame/eslint-config',
88
'@codingame/eslint-config-react'
9-
]
9+
],
10+
rules: {
11+
'import/extensions': [
12+
'error',
13+
'always'
14+
]
15+
}
1016
}

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/LanguageClient.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { createLanguageClientManager, LanguageClientId, StatusChangeEvent as Wra
33
import { useLocalStorage, writeStorage } from '@rehooks/local-storage'
44
import { v4 as uuidv4 } from 'uuid'
55
import { initializePromise } from '@codingame/monaco-editor-wrapper'
6-
import useIsUserActive from './hooks/useIsUserActive'
7-
import useShouldShutdownLanguageClient from './hooks/useShouldShutdownLanguageClient'
8-
import { useLastVersion } from './hooks/useLastVersion'
6+
import useIsUserActive from './hooks/useIsUserActive.js'
7+
import useShouldShutdownLanguageClient from './hooks/useShouldShutdownLanguageClient.js'
8+
import { useLastVersion } from './hooks/useLastVersion.js'
99

1010
export interface StatusChangeEvent {
1111
status: WrapperStatusChangeEvent['status'] | 'inactivityShutdown'

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

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CodinGameInfrastructure, Infrastructure, LanguageClientId, loadExtensionConfigurations, registerLanguageClient, WorkspaceFolder } from '@codingame/monaco-languageclient-wrapper'
2-
import LanguageClient, { LanguageClientProps, StatusChangeEvent } from './LanguageClient'
2+
import LanguageClient, { LanguageClientProps, StatusChangeEvent } from './LanguageClient.js'
33

44
export default LanguageClient
55
export {

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"sourceMap": true,
2121
"target": "ES2022"
2222
},
23-
"include": ["src"],
23+
"include": ["src", ".eslintrc.cjs"],
2424
"exclude": [
2525
"dist",
2626
"node_modules"

0 commit comments

Comments
 (0)