Просмотр исходного кода

Merge pull request #11105 from overleaf/ii-shared-hooks-js-to-ts-conversion

[web] Shared React hooks JS to TS conversion

GitOrigin-RevId: 0ccdebff236c7424b1a73cd7d6646a9d01a20eb1
ilkin-overleaf 3 лет назад
Родитель
Сommit
d959dbc236

+ 0 - 0
services/web/frontend/js/shared/hooks/use-abort-controller.js → services/web/frontend/js/shared/hooks/use-abort-controller.ts


+ 5 - 5
services/web/frontend/js/shared/hooks/use-browser-window.js → services/web/frontend/js/shared/hooks/use-browser-window.ts

@@ -1,10 +1,10 @@
 import { useEffect, useState } from 'react'
 import { useEffect, useState } from 'react'
 
 
 let titleIsFlashing = false
 let titleIsFlashing = false
-let originalTitle
-let flashIntervalHandle
+let originalTitle = ''
+let flashIntervalHandle: ReturnType<typeof setInterval>
 
 
-function flashTitle(message) {
+function flashTitle(message: string) {
   if (document.hasFocus() || titleIsFlashing) {
   if (document.hasFocus() || titleIsFlashing) {
     return
     return
   }
   }
@@ -30,11 +30,11 @@ function stopFlashingTitle() {
 
 
   clearInterval(flashIntervalHandle)
   clearInterval(flashIntervalHandle)
   window.document.title = originalTitle
   window.document.title = originalTitle
-  originalTitle = undefined
+  originalTitle = ''
   titleIsFlashing = false
   titleIsFlashing = false
 }
 }
 
 
-function setTitle(title) {
+function setTitle(title: string) {
   if (titleIsFlashing) {
   if (titleIsFlashing) {
     originalTitle = title
     originalTitle = title
   } else {
   } else {

+ 4 - 4
services/web/frontend/js/shared/hooks/use-callback-handlers.js → services/web/frontend/js/shared/hooks/use-callback-handlers.ts

@@ -1,17 +1,17 @@
 import { useCallback, useRef } from 'react'
 import { useCallback, useRef } from 'react'
 
 
 export default function useCallbackHandlers() {
 export default function useCallbackHandlers() {
-  const handlersRef = useRef(new Set())
+  const handlersRef = useRef(new Set<(...arg: unknown[]) => void>())
 
 
-  const addHandler = useCallback(handler => {
+  const addHandler = useCallback((handler: (...args: unknown[]) => void) => {
     handlersRef.current.add(handler)
     handlersRef.current.add(handler)
   }, [])
   }, [])
 
 
-  const deleteHandler = useCallback(handler => {
+  const deleteHandler = useCallback((handler: (...args: unknown[]) => void) => {
     handlersRef.current.delete(handler)
     handlersRef.current.delete(handler)
   }, [])
   }, [])
 
 
-  const callHandlers = useCallback((...args) => {
+  const callHandlers = useCallback((...args: unknown[]) => {
     for (const handler of handlersRef.current) {
     for (const handler of handlersRef.current) {
       handler(...args)
       handler(...args)
     }
     }

+ 1 - 1
services/web/frontend/js/shared/hooks/use-debounce.js → services/web/frontend/js/shared/hooks/use-debounce.ts

@@ -6,7 +6,7 @@ import { useEffect, useState } from 'react'
  * @param {number} delay
  * @param {number} delay
  * @returns {T}
  * @returns {T}
  */
  */
-export default function useDebounce(value, delay = 0) {
+export default function useDebounce<T>(value: T, delay = 0) {
   const [debouncedValue, setDebouncedValue] = useState(value)
   const [debouncedValue, setDebouncedValue] = useState(value)
 
 
   useEffect(() => {
   useEffect(() => {

+ 5 - 2
services/web/frontend/js/shared/hooks/use-deep-compare-effect.js → services/web/frontend/js/shared/hooks/use-deep-compare-effect.ts

@@ -1,8 +1,11 @@
 import { useEffect, useRef } from 'react'
 import { useEffect, useRef } from 'react'
 import _ from 'lodash'
 import _ from 'lodash'
 
 
-export default function useDeepCompareEffect(callback, dependencies) {
-  const ref = useRef()
+export default function useDeepCompareEffect<T>(
+  callback: () => void,
+  dependencies: T[]
+) {
+  const ref = useRef<T[]>()
   return useEffect(() => {
   return useEffect(() => {
     if (_.isEqual(dependencies, ref.current)) {
     if (_.isEqual(dependencies, ref.current)) {
       return
       return

+ 15 - 8
services/web/frontend/js/shared/hooks/use-detach-action.js → services/web/frontend/js/shared/hooks/use-detach-action.ts

@@ -1,22 +1,27 @@
 import { useCallback, useEffect } from 'react'
 import { useCallback, useEffect } from 'react'
 import { useDetachContext } from '../context/detach-context'
 import { useDetachContext } from '../context/detach-context'
 import getMeta from '../../utils/meta'
 import getMeta from '../../utils/meta'
+import { DetachRole, DetachTargetRole, Message } from './use-detach-state'
 
 
 const debugPdfDetach = getMeta('ol-debugPdfDetach')
 const debugPdfDetach = getMeta('ol-debugPdfDetach')
 
 
-export default function useDetachAction(
-  actionName,
-  actionFunction,
-  senderRole,
-  targetRole
+function useDetachAction<
+  A,
+  S extends DetachRole,
+  T extends DetachTargetRole<S>
+>(
+  actionName: string,
+  actionFunction: (...args: A[]) => void,
+  senderRole: S,
+  targetRole: T
 ) {
 ) {
   const { role, broadcastEvent, addEventHandler, deleteEventHandler } =
   const { role, broadcastEvent, addEventHandler, deleteEventHandler } =
     useDetachContext()
     useDetachContext()
 
 
-  const eventName = `action-${actionName}`
+  const eventName: Message['event'] = `action-${actionName}`
 
 
   const triggerFn = useCallback(
   const triggerFn = useCallback(
-    (...args) => {
+    (...args: A[]) => {
       if (role === senderRole) {
       if (role === senderRole) {
         broadcastEvent(eventName, { args })
         broadcastEvent(eventName, { args })
       } else {
       } else {
@@ -27,7 +32,7 @@ export default function useDetachAction(
   )
   )
 
 
   const handleActionEvent = useCallback(
   const handleActionEvent = useCallback(
-    message => {
+    (message: Message<A>) => {
       if (message.event !== eventName) {
       if (message.event !== eventName) {
         return
         return
       }
       }
@@ -49,3 +54,5 @@ export default function useDetachAction(
 
 
   return triggerFn
   return triggerFn
 }
 }
+
+export default useDetachAction

+ 2 - 2
services/web/frontend/js/shared/hooks/use-detach-layout.js → services/web/frontend/js/shared/hooks/use-detach-layout.ts

@@ -24,7 +24,7 @@ export default function useDetachLayout() {
   // redundant
   // redundant
   const [isRedundant, setIsRedundant] = useState(false)
   const [isRedundant, setIsRedundant] = useState(false)
 
 
-  const uiTimeoutRef = useRef()
+  const uiTimeoutRef = useRef<ReturnType<typeof setTimeout>>()
 
 
   useEffect(() => {
   useEffect(() => {
     if (debugPdfDetach) {
     if (debugPdfDetach) {
@@ -153,7 +153,7 @@ export default function useDetachLayout() {
     message => {
     message => {
       if (role === 'detacher') {
       if (role === 'detacher') {
         if (message.role === 'detacher') {
         if (message.role === 'detacher') {
-          handleEventForDetacherFromDetacher(message)
+          handleEventForDetacherFromDetacher()
         } else if (message.role === 'detached') {
         } else if (message.role === 'detached') {
           handleEventForDetacherFromDetached(message)
           handleEventForDetacherFromDetached(message)
         }
         }

+ 0 - 22
services/web/frontend/js/shared/hooks/use-detach-state-watcher.js

@@ -1,22 +0,0 @@
-import { useEffect } from 'react'
-import useDetachState from './use-detach-state'
-
-export default function useDetachStateWatcher(
-  key,
-  stateValue,
-  senderRole,
-  targetRole
-) {
-  const [value, setValue] = useDetachState(
-    key,
-    stateValue,
-    senderRole,
-    targetRole
-  )
-
-  useEffect(() => {
-    setValue(stateValue)
-  }, [setValue, stateValue])
-
-  return [value, setValue]
-}

+ 32 - 0
services/web/frontend/js/shared/hooks/use-detach-state-watcher.ts

@@ -0,0 +1,32 @@
+import { useEffect } from 'react'
+import useDetachState, {
+  DetachRole,
+  DetachTargetRole,
+} from './use-detach-state'
+
+type UseDetachParams = Parameters<typeof useDetachState>
+
+function useDetachStateWatcher<
+  S extends DetachRole,
+  T extends DetachTargetRole<S>
+>(
+  key: UseDetachParams[0],
+  stateValue: UseDetachParams[1],
+  senderRole: S,
+  targetRole: T
+) {
+  const [value, setValue] = useDetachState(
+    key,
+    stateValue,
+    senderRole,
+    targetRole
+  )
+
+  useEffect(() => {
+    setValue(stateValue)
+  }, [setValue, stateValue])
+
+  return [value, setValue]
+}
+
+export default useDetachStateWatcher

+ 22 - 8
services/web/frontend/js/shared/hooks/use-detach-state.js → services/web/frontend/js/shared/hooks/use-detach-state.ts

@@ -4,12 +4,24 @@ import getMeta from '../../utils/meta'
 
 
 const debugPdfDetach = getMeta('ol-debugPdfDetach')
 const debugPdfDetach = getMeta('ol-debugPdfDetach')
 
 
-export default function useDetachState(
-  key,
-  defaultValue,
-  senderRole,
-  targetRole
-) {
+export type DetachRole = 'detacher' | 'detached'
+export type DetachTargetRole<T extends DetachRole> = T extends 'detacher'
+  ? 'detached'
+  : 'detacher'
+export type Message<DataArgs = unknown> = {
+  event: `${'action' | 'state'}-${string}`
+  data: {
+    args: DataArgs[]
+    value: unknown
+  }
+}
+
+function useDetachState<S extends DetachRole, T extends DetachTargetRole<S>>(
+  key: string,
+  defaultValue: unknown,
+  senderRole: S,
+  targetRole: T
+): [unknown, React.Dispatch<unknown>] {
   const [value, setValue] = useState(defaultValue)
   const [value, setValue] = useState(defaultValue)
 
 
   const {
   const {
@@ -20,7 +32,7 @@ export default function useDetachState(
     deleteEventHandler,
     deleteEventHandler,
   } = useDetachContext()
   } = useDetachContext()
 
 
-  const eventName = `state-${key}`
+  const eventName: Message['event'] = `state-${key}`
 
 
   // lastDetachedConnectedAt is added as a dependency in order to re-broadcast
   // lastDetachedConnectedAt is added as a dependency in order to re-broadcast
   // all states when a new detached tab connects
   // all states when a new detached tab connects
@@ -38,7 +50,7 @@ export default function useDetachState(
   ])
   ])
 
 
   const handleStateEvent = useCallback(
   const handleStateEvent = useCallback(
-    message => {
+    (message: Message) => {
       if (message.event !== eventName) {
       if (message.event !== eventName) {
         return
         return
       }
       }
@@ -60,3 +72,5 @@ export default function useDetachState(
 
 
   return [value, setValue]
   return [value, setValue]
 }
 }
+
+export default useDetachState

+ 1 - 1
services/web/frontend/js/shared/hooks/use-dropdown.js → services/web/frontend/js/shared/hooks/use-dropdown.ts

@@ -5,7 +5,7 @@ export default function useDropdown(defaultOpen = false) {
   const [open, setOpen] = useState(defaultOpen)
   const [open, setOpen] = useState(defaultOpen)
 
 
   // store the dropdown node for use in the "click outside" event listener
   // store the dropdown node for use in the "click outside" event listener
-  const ref = useRef(null)
+  const ref = useRef<ReturnType<typeof findDOMNode>>(null)
 
 
   // react-bootstrap v0.x passes `component` instead of `node` to the ref callback
   // react-bootstrap v0.x passes `component` instead of `node` to the ref callback
   const handleRef = useCallback(
   const handleRef = useCallback(

+ 7 - 4
services/web/frontend/js/shared/hooks/use-expand-collapse.js → services/web/frontend/js/shared/hooks/use-expand-collapse.ts

@@ -3,13 +3,16 @@ import classNames from 'classnames'
 
 
 function useExpandCollapse({
 function useExpandCollapse({
   initiallyExpanded = false,
   initiallyExpanded = false,
-  collapsedSize = '0',
+  collapsedSize = 0,
   dimension = 'height',
   dimension = 'height',
-  classes = {},
+  classes = { container: '', containerCollapsed: '' },
 } = {}) {
 } = {}) {
-  const ref = useRef()
+  const ref = useRef<{ scrollHeight: number; scrollWidth: number }>()
   const [isExpanded, setIsExpanded] = useState(initiallyExpanded)
   const [isExpanded, setIsExpanded] = useState(initiallyExpanded)
-  const [sizing, setSizing] = useState({
+  const [sizing, setSizing] = useState<{
+    size: number | null
+    needsExpandCollapse: boolean | null
+  }>({
     size: null,
     size: null,
     needsExpandCollapse: null,
     needsExpandCollapse: null,
   })
   })

+ 2 - 2
services/web/frontend/js/shared/hooks/use-previous-value.js → services/web/frontend/js/shared/hooks/use-previous-value.ts

@@ -1,7 +1,7 @@
 import { useEffect, useRef } from 'react'
 import { useEffect, useRef } from 'react'
 
 
-export default function usePreviousValue(value) {
-  const ref = useRef()
+export default function usePreviousValue<T>(value: T) {
+  const ref = useRef<T>()
   useEffect(() => {
   useEffect(() => {
     ref.current = value
     ref.current = value
   })
   })

+ 5 - 7
services/web/frontend/js/shared/hooks/use-scope-event-emitter.js → services/web/frontend/js/shared/hooks/use-scope-event-emitter.ts

@@ -1,16 +1,14 @@
 import { useCallback } from 'react'
 import { useCallback } from 'react'
 import { useIdeContext } from '../context/ide-context'
 import { useIdeContext } from '../context/ide-context'
 
 
-/**
- * @param {string} eventName
- * @param {boolean} [broadcast]
- * @returns function
- */
-export default function useScopeEventEmitter(eventName, broadcast = true) {
+export default function useScopeEventEmitter(
+  eventName: string,
+  broadcast = true
+) {
   const { $scope } = useIdeContext()
   const { $scope } = useIdeContext()
 
 
   return useCallback(
   return useCallback(
-    (...detail) => {
+    (...detail: unknown[]) => {
       if (broadcast) {
       if (broadcast) {
         $scope.$broadcast(eventName, ...detail)
         $scope.$broadcast(eventName, ...detail)
       } else {
       } else {

+ 4 - 5
services/web/frontend/js/shared/hooks/use-scope-event-listener.js → services/web/frontend/js/shared/hooks/use-scope-event-listener.ts

@@ -1,11 +1,10 @@
 import { useEffect } from 'react'
 import { useEffect } from 'react'
 import { useIdeContext } from '../context/ide-context'
 import { useIdeContext } from '../context/ide-context'
 
 
-/**
- * @param {string} eventName
- * @param {function} [listener]
- */
-export default function useScopeEventListener(eventName, listener) {
+export default function useScopeEventListener(
+  eventName: string,
+  listener: (...args: unknown[]) => void
+) {
   const { $scope } = useIdeContext()
   const { $scope } = useIdeContext()
 
 
   useEffect(() => {
   useEffect(() => {

+ 12 - 3
services/web/frontend/js/shared/hooks/use-stop-on-first-error.js → services/web/frontend/js/shared/hooks/use-stop-on-first-error.ts

@@ -3,14 +3,23 @@ import { useDetachCompileContext as useCompileContext } from '../context/detach-
 import { useProjectContext } from '../context/project-context'
 import { useProjectContext } from '../context/project-context'
 import * as eventTracking from '../../infrastructure/event-tracking'
 import * as eventTracking from '../../infrastructure/event-tracking'
 
 
-export function useStopOnFirstError(opts = {}) {
+type UseStopOnFirstErrorProps = {
+  eventSource?: string
+}
+
+export function useStopOnFirstError(opts: UseStopOnFirstErrorProps = {}) {
   const { eventSource } = opts
   const { eventSource } = opts
   const { stopOnFirstError, setStopOnFirstError } = useCompileContext()
   const { stopOnFirstError, setStopOnFirstError } = useCompileContext()
   const { _id: projectId } = useProjectContext()
   const { _id: projectId } = useProjectContext()
 
 
+  type Opts = {
+    projectId: string
+    source?: UseStopOnFirstErrorProps['eventSource']
+  }
+
   const enableStopOnFirstError = useCallback(() => {
   const enableStopOnFirstError = useCallback(() => {
     if (!stopOnFirstError) {
     if (!stopOnFirstError) {
-      const opts = { projectId }
+      const opts: Opts = { projectId }
       if (eventSource) {
       if (eventSource) {
         opts.source = eventSource
         opts.source = eventSource
       }
       }
@@ -20,7 +29,7 @@ export function useStopOnFirstError(opts = {}) {
   }, [eventSource, projectId, stopOnFirstError, setStopOnFirstError])
   }, [eventSource, projectId, stopOnFirstError, setStopOnFirstError])
 
 
   const disableStopOnFirstError = useCallback(() => {
   const disableStopOnFirstError = useCallback(() => {
-    const opts = { projectId }
+    const opts: Opts = { projectId }
     if (eventSource) {
     if (eventSource) {
       opts.source = eventSource
       opts.source = eventSource
     }
     }