diff --git a/@types/internal-nav-helper/index.d.ts b/@types/internal-nav-helper/index.d.ts
new file mode 100644
index 000000000..21f2ea17a
--- /dev/null
+++ b/@types/internal-nav-helper/index.d.ts
@@ -0,0 +1,4 @@
+declare module 'internal-nav-helper' {
+ export function getNavHelper(onInternalNav: (path: string) => void): (e: MouseEvent) => void
+ export function findAnchorTag(el: HTMLElement): HTMLElement | null
+}
diff --git a/@types/loadable__component/index.d.ts b/@types/loadable__component/index.d.ts
new file mode 100644
index 000000000..403dd04a1
--- /dev/null
+++ b/@types/loadable__component/index.d.ts
@@ -0,0 +1,15 @@
+declare module '@loadable/component' {
+ import React from 'react'
+
+ interface LoadableOptions {
+ fallback?: React.ReactNode
+ ssr?: boolean
+ }
+
+ function Loadable
(
+ loadFn: () => Promise<{ default: React.ComponentType
}>,
+ options?: LoadableOptions
+ ): React.ComponentType
+
+ export default Loadable
+}
diff --git a/@types/react-helmet/index.d.ts b/@types/react-helmet/index.d.ts
new file mode 100644
index 000000000..7130799e3
--- /dev/null
+++ b/@types/react-helmet/index.d.ts
@@ -0,0 +1,19 @@
+declare module 'react-helmet' {
+ import React from 'react'
+
+ export interface HelmetProps {
+ children?: React.ReactNode
+ title?: string
+ meta?: Array<{
+ name?: string
+ content?: string
+ property?: string
+ }>
+ link?: Array<{
+ rel?: string
+ href?: string
+ }>
+ }
+
+ export class Helmet extends React.Component {}
+}
diff --git a/@types/react-joyride/index.d.ts b/@types/react-joyride/index.d.ts
new file mode 100644
index 000000000..7caaad488
--- /dev/null
+++ b/@types/react-joyride/index.d.ts
@@ -0,0 +1,24 @@
+import { Step } from 'react-joyride'
+
+declare module 'react-joyride' {
+ import type { TFunction } from 'i18next'
+ import type { Trans } from 'react-i18next'
+
+ /**
+ * Custom Tour interface that extends the official types
+ * to include a getSteps function with translation support
+ */
+ export interface CustomTour {
+ getSteps: (props: {
+ t: TFunction
+ Trans?: typeof Trans
+ }) => Step[]
+ styles: {
+ options?: Record
+ tooltip?: Record
+ tooltipContent?: Record
+ tooltipFooter?: Record
+ [key: string]: any
+ }
+ }
+}
diff --git a/@types/redux-bundler-react/index.d.ts b/@types/redux-bundler-react/index.d.ts
new file mode 100644
index 000000000..93581c710
--- /dev/null
+++ b/@types/redux-bundler-react/index.d.ts
@@ -0,0 +1,28 @@
+declare module 'redux-bundler-react' {
+ import type { ComponentType, ReactNode } from 'react'
+
+ // Overloaded function signatures for connect
+ export function connect>(
+ ...selectors: string[]
+ ): >(component: T) => T
+
+ export function connect>(
+ selectors: string[],
+ component: ComponentType
+ ): ComponentType
+
+ export function connect>(
+ selector: string,
+ component: ComponentType
+ ): ComponentType
+
+ // Handle multiple selectors with a component
+ export function connect>(
+ ...args: [...string[], ComponentType]
+ ): ComponentType
+
+ export class Provider extends ComponentType<{
+ store: Record
+ children: ReactNode
+ }> {}
+}
diff --git a/src/bundles/files/actions.js b/src/bundles/files/actions.js
index c5ed8bf01..381bd3fca 100644
--- a/src/bundles/files/actions.js
+++ b/src/bundles/files/actions.js
@@ -14,7 +14,7 @@ import { IGNORED_FILES, ACTIONS } from './consts.js'
/**
* @typedef {import('ipfs').IPFSService} IPFSService
- * @typedef {import('../../lib/files').FileStream} FileStream
+ * @typedef {import('../../files/types').FileStream} FileStream
* @typedef {import('./utils').Info} Info
* @typedef {import('ipfs').Pin} Pin
*/
diff --git a/src/bundles/files/utils.js b/src/bundles/files/utils.js
index a466586e4..bedf575d7 100644
--- a/src/bundles/files/utils.js
+++ b/src/bundles/files/utils.js
@@ -5,7 +5,7 @@ import { debouncedProvide } from '../../lib/files.js'
/**
* @typedef {import('ipfs').IPFSService} IPFSService
- * @typedef {import('../../lib/files').FileStream} FileStream
+ * @typedef {import('../../files/types').FileStream} FileStream
* @typedef {import('./actions').Ext} Ext
* @typedef {import('./actions').Extra} Extra
* @typedef {import('multiformats/cid').CID} CID
diff --git a/src/components/about-ipfs/AboutIpfs.js b/src/components/about-ipfs/AboutIpfs.js
index db7e8b9c8..e7fb064af 100644
--- a/src/components/about-ipfs/AboutIpfs.js
+++ b/src/components/about-ipfs/AboutIpfs.js
@@ -2,6 +2,10 @@ import React from 'react'
import { withTranslation, Trans } from 'react-i18next'
import Box from '../box/Box.js'
+/**
+ * @param {Object} props
+ * @param {import('i18next').TFunction} props.t
+ */
export const AboutIpfs = ({ t }) => {
return (
diff --git a/src/components/about-webui/AboutWebUI.js b/src/components/about-webui/AboutWebUI.js
index 8bcb81a01..06a942324 100644
--- a/src/components/about-webui/AboutWebUI.js
+++ b/src/components/about-webui/AboutWebUI.js
@@ -2,6 +2,10 @@ import React from 'react'
import { withTranslation, Trans } from 'react-i18next'
import Box from '../box/Box.js'
+/**
+ * @param {Object} props
+ * @param {import('i18next').TFunction} props.t
+ */
export const AboutWebUI = ({ t }) => {
return (
diff --git a/src/components/box/Box.js b/src/components/box/Box.js
index 7d044f1af..58bdd361e 100644
--- a/src/components/box/Box.js
+++ b/src/components/box/Box.js
@@ -1,11 +1,16 @@
import React from 'react'
import ErrorBoundary from '../error/error-boundary.js'
+/**
+ * @param {Object} props
+ * @param {string} [props.className]
+ * @param {React.CSSProperties} [props.style]
+ * @param {React.ReactNode} props.children
+ */
export const Box = ({
className = 'pa4',
style,
- children,
- ...props
+ children
}) => {
return (
diff --git a/src/components/is-connected/IsConnected.js b/src/components/is-connected/IsConnected.js
index ca47f5c84..4748caafd 100644
--- a/src/components/is-connected/IsConnected.js
+++ b/src/components/is-connected/IsConnected.js
@@ -3,6 +3,10 @@ import { withTranslation } from 'react-i18next'
import Box from '../box/Box.js'
import GlyphTick from '../../icons/GlyphTick.js'
+/**
+ * @param {Object} props
+ * @param {import('i18next').TFunction} props.t
+ */
export const IsConnected = ({ t }) => {
return (
diff --git a/src/components/is-not-connected/IsNotConnected.js b/src/components/is-not-connected/IsNotConnected.js
index 1c6b7851a..e9e06a1e0 100644
--- a/src/components/is-not-connected/IsNotConnected.js
+++ b/src/components/is-not-connected/IsNotConnected.js
@@ -13,7 +13,14 @@ const TABS = {
WINDOWS: 'windowsCMD'
}
-const IsNotConnected = ({ t, apiUrl, connected, sameOrigin, ipfsApiAddress, doUpdateIpfsApiAddress }) => {
+/**
+ * @param {Object} props
+ * @param {import('i18next').TFunction} props.t
+ * @param {string} props.apiUrl
+ * @param {boolean} props.connected
+ * @param {boolean} props.sameOrigin
+ */
+const IsNotConnected = ({ t, apiUrl: _apiUrl, connected: _connected, sameOrigin }) => {
const [activeTab, setActiveTab] = useState(TABS.UNIX)
const defaultDomains = ['http://localhost:3000', 'http://127.0.0.1:5001', 'https://webui.ipfs.io']
const origin = window.location.origin
@@ -79,10 +86,7 @@ const IsNotConnected = ({ t, apiUrl, connected, sameOrigin, ipfsApiAddress, doUp
Is your Kubo RPC on a port other than 5001? If your node is configured with a custom RPC API address, enter it here.
-
+
)
diff --git a/src/components/shell/Shell.js b/src/components/shell/Shell.js
index e9dca171d..3b50accc3 100644
--- a/src/components/shell/Shell.js
+++ b/src/components/shell/Shell.js
@@ -1,6 +1,12 @@
import React from 'react'
import classNames from 'classnames'
+/**
+ * @param {Object} props
+ * @param {string} [props.title]
+ * @param {React.ReactNode} props.children
+ * @param {string} [props.className]
+ */
const Shell = ({
title = 'Shell',
children,
diff --git a/src/components/tour/withTour.js b/src/components/tour/withTour.js
index fc27289eb..e2316d822 100644
--- a/src/components/tour/withTour.js
+++ b/src/components/tour/withTour.js
@@ -2,13 +2,20 @@ import React from 'react'
import { connect } from 'redux-bundler-react'
import { STATUS } from 'react-joyride'
+/**
+ * @param {React.ComponentType} WrappedComponent
+ * @returns {React.ComponentType}
+ */
const withTour = WrappedComponent => {
class WithTour extends React.Component {
+ /**
+ * @param {import('react-joyride').CallBackProps} data
+ */
handleJoyrideCallback = (data) => {
const { doDisableTours } = this.props
const { action, status } = data
- if (action === 'close' || [STATUS.FINISHED].includes(status)) {
+ if (action === 'close' || status === STATUS.FINISHED) {
doDisableTours()
}
}
diff --git a/src/constants/pinning.js b/src/constants/pinning.js
index cbac5e46e..7e1d48819 100644
--- a/src/constants/pinning.js
+++ b/src/constants/pinning.js
@@ -17,7 +17,16 @@ const complianceReportsHomepage = 'https://ipfs-shipyard.github.io/pinning-servi
*/
/**
- * @type {PinningServiceTemplate[]}
+ * @typedef {object} PinningServiceTemplateWithCompliance
+ * @property {string} name
+ * @property {string} icon
+ * @property {string} apiEndpoint
+ * @property {string} visitServiceUrl
+ * @property {string} [complianceReportUrl]
+ */
+
+/**
+ * @type {PinningServiceTemplateWithCompliance[]}
*/
const pinningServiceTemplates = [
{
@@ -45,14 +54,18 @@ const pinningServiceTemplates = [
visitServiceUrl: 'https://docs.4everland.org/storage/4ever-pin/pinning-services-api'
}
].map((service) => {
+ let complianceReportUrl
try {
const domain = new URL(service.apiEndpoint).hostname
- service.complianceReportUrl = `${complianceReportsHomepage}/${domain}.html`
+ complianceReportUrl = `${complianceReportsHomepage}/${domain}.html`
} catch (e) {
// if apiEndpoint is not a valid URL, don't add complianceReportUrl
// TODO: fix support for template apiEndpoints
}
- return { service, sort: Math.random() }
+ return {
+ service: { ...service, complianceReportUrl },
+ sort: Math.random()
+ }
}).sort((a, b) => a.sort - b.sort).map(({ service }) => service)
export {
diff --git a/src/contexts/identity-context.tsx b/src/contexts/identity-context.tsx
index 9b93fffd6..45acc1b0c 100644
--- a/src/contexts/identity-context.tsx
+++ b/src/contexts/identity-context.tsx
@@ -151,7 +151,7 @@ const IdentityProviderImpl: React.FC = ({ children }) =>
}, [ipfsConnected, fetchIdentity, state.isLoading, state.identity, state.lastSuccess])
useEffect(() => {
- if (!shouldPoll || !ipfsConnected || !state.lastSuccess) return
+ if (!shouldPoll || !ipfsConnected || !state.lastSuccess) return () => {}
const REFRESH_INTERVAL = 5000
const timeSinceLastSuccess = Date.now() - state.lastSuccess
@@ -162,6 +162,7 @@ const IdentityProviderImpl: React.FC = ({ children }) =>
} else {
fetchIdentity()
}
+ return () => {}
}, [shouldPoll, ipfsConnected, state.lastSuccess, fetchIdentity])
const contextValue: IdentityContextValue = useMemo(() => ({
diff --git a/src/files/explore-form/files-explore-form.tsx b/src/files/explore-form/files-explore-form.tsx
index d45f1c236..fd1b37b7f 100644
--- a/src/files/explore-form/files-explore-form.tsx
+++ b/src/files/explore-form/files-explore-form.tsx
@@ -8,10 +8,12 @@ import './files-explore-form.css'
// @ts-expect-error - need to fix types for ipfs-webui since we are a CJS consumer...
import { useExplore } from 'ipld-explorer-components/providers'
-/**
- * @type {React.FC<{ onBrowse: (evt: { path: string }) => void }>} *
- */
-const FilesExploreForm = ({ onBrowse: onBrowseProp }) => {
+interface FilesExploreFormProps {
+ // this prop is being passed as the `doFilesNavigateTo` action from the `files` bundle in App.js
+ onBrowse: ({ path, cid }: {path: string, cid?: string}) => void
+}
+
+const FilesExploreForm: React.FC = ({ onBrowse: onBrowseProp }) => {
const [path, setPath] = useState('')
const { doExploreUserProvidedPath } = useExplore()
const { t } = useTranslation('files')
@@ -36,15 +38,15 @@ const FilesExploreForm = ({ onBrowse: onBrowseProp }) => {
}
}, [trimmedPath, isValid])
- const onChange = (evt) => {
+ const onChange: React.ChangeEventHandler = (evt) => {
setPath(evt.target.value)
}
- const onKeyDown = (evt) => {
+ const onKeyDown: React.KeyboardEventHandler = (evt) => {
if (evt.key === 'Enter') {
onBrowse(evt)
}
}
- const onInspect = useCallback((evt) => {
+ const onInspect = useCallback((evt: React.MouseEvent) => {
evt.preventDefault()
if (isValid) {
@@ -53,16 +55,18 @@ const FilesExploreForm = ({ onBrowse: onBrowseProp }) => {
}
}, [doExploreUserProvidedPath, isValid, trimmedPath])
- const onBrowse = useCallback((evt) => {
+ const onBrowse = useCallback((evt: React.KeyboardEvent | React.MouseEvent) => {
evt.preventDefault()
if (isValid) {
let browsePath = trimmedPath
+ let cid
if (isIPFS.cid(trimmedPath)) {
browsePath = `/ipfs/${trimmedPath}`
+ cid = trimmedPath
}
- onBrowseProp({ path: browsePath })
+ onBrowseProp({ path: browsePath, cid })
setPath('')
}
}, [isValid, trimmedPath, onBrowseProp])
diff --git a/src/files/file-preview/file-thumbnail.tsx b/src/files/file-preview/file-thumbnail.tsx
index 33008ed19..fc1d691de 100644
--- a/src/files/file-preview/file-thumbnail.tsx
+++ b/src/files/file-preview/file-thumbnail.tsx
@@ -1,6 +1,5 @@
import { CID } from 'multiformats/cid'
import React, { useState, useEffect, useCallback, type FC } from 'react'
-// @ts-expect-error - redux-bundler-react is not typed
import { connect } from 'redux-bundler-react'
import typeFromExt from '../type-from-ext/index.js'
import './file-thumbnail.css'
diff --git a/src/files/files-grid/files-grid.tsx b/src/files/files-grid/files-grid.tsx
index d70d66392..b01554d6f 100644
--- a/src/files/files-grid/files-grid.tsx
+++ b/src/files/files-grid/files-grid.tsx
@@ -2,13 +2,12 @@ import React, { useRef, useState, useEffect, useCallback, type FC, type MouseEve
import { Trans, withTranslation } from 'react-i18next'
import { useDrop } from 'react-dnd'
import { NativeTypes } from 'react-dnd-html5-backend'
-import { ExtendedFile, FileStream, normalizeFiles } from '../../lib/files.js'
+import { normalizeFiles } from '../../lib/files.js'
import GridFile from './grid-file.jsx'
-// @ts-expect-error - redux-bundler-react is not typed
import { connect } from 'redux-bundler-react'
import './files-grid.css'
import { TFunction } from 'i18next'
-import type { ContextMenuFile } from 'src/files/types.js'
+import type { ContextMenuFile, ExtendedFile, FileStream } from '../types'
import type { CID } from 'multiformats/cid'
export interface FilesGridProps {
diff --git a/src/files/files-grid/grid-file.tsx b/src/files/files-grid/grid-file.tsx
index 3a1f96c03..e2a9bfb01 100644
--- a/src/files/files-grid/grid-file.tsx
+++ b/src/files/files-grid/grid-file.tsx
@@ -1,11 +1,10 @@
import React, { useRef, useState, useEffect, type FC } from 'react'
import { withTranslation } from 'react-i18next'
import { useDrag, useDrop, type DropTargetMonitor } from 'react-dnd'
-import { FileStream, humanSize, normalizeFiles } from '../../lib/files.js'
+import { humanSize, normalizeFiles } from '../../lib/files.js'
import { CID } from 'multiformats/cid'
import { isBinary } from 'istextorbinary'
import FileIcon from '../file-icon/FileIcon.js'
-// @ts-expect-error - redux-bundler-react is not typed
import { connect } from 'redux-bundler-react'
import FileThumbnail from '../file-preview/file-thumbnail.js'
import PinIcon from '../pin-icon/PinIcon.js'
@@ -15,7 +14,7 @@ import { NativeTypes } from 'react-dnd-html5-backend'
import { join, basename } from 'path'
import './grid-file.css'
import { TFunction } from 'i18next'
-import { ContextMenuFile } from '../types.js'
+import type { ContextMenuFile, FileStream } from '../types'
type SetPinningProps = { cid: CID, pinned: boolean }
diff --git a/src/files/modals/bulk-import-modal/bulk-import-modal.tsx b/src/files/modals/bulk-import-modal/bulk-import-modal.tsx
index e1e0d21cf..ccb9daacc 100644
--- a/src/files/modals/bulk-import-modal/bulk-import-modal.tsx
+++ b/src/files/modals/bulk-import-modal/bulk-import-modal.tsx
@@ -1,18 +1,26 @@
import React, { useState, useRef } from 'react'
-import Button from '../../../components/button/button.tsx'
+import Button from '../../../components/button/button'
import { Modal, ModalActions, ModalBody } from '../../../components/modal/modal'
import { useTranslation } from 'react-i18next'
import * as isIPFS from 'is-ipfs'
import Icon from '../../../icons/StrokeDocument.js'
import { normalizeFiles } from '../../../lib/files.js'
+import { FileStream } from '../../types'
-const BulkImportModal = ({ onCancel, className = '', onBulkCidImport, ...props }) => {
+interface BulkImportModalProps {
+ onCancel: () => void
+ className?: string
+ onBulkCidImport: (files: FileStream[]) => Promise
+ [key: string]: any
+}
+
+const BulkImportModal: React.FC = ({ onCancel, className = '', onBulkCidImport, ...props }) => {
const [selectedFile, setSelectedFile] = useState(null)
const [validationError, setValidationError] = useState(undefined)
const bulkCidInputRef = useRef(null)
const { t } = useTranslation('files')
- const validateFileContents = async (file) => {
+ const validateFileContents = async (file: File) => {
try {
const text = await file.text()
const lines = text.split('\n').filter(line => line.trim())
@@ -30,8 +38,8 @@ const BulkImportModal = ({ onCancel, className = '', onBulkCidImport, ...props }
}
}
- const onChange = async (event) => {
- const file = event.target.files[0]
+ const onChange: React.ChangeEventHandler = async (event) => {
+ const file = event.target.files?.[0]
if (!file) return
const validation = await validateFileContents(file)
diff --git a/src/files/pin-icon/PinIcon.js b/src/files/pin-icon/PinIcon.js
index 3c0299f37..19655b271 100644
--- a/src/files/pin-icon/PinIcon.js
+++ b/src/files/pin-icon/PinIcon.js
@@ -5,7 +5,7 @@ import GlyphPinCloud from '../../icons/GlyphPinCloud.js'
import '../PendingAnimation.css'
/**
- * @param {{ t: (key: string) => string, isFailedPin: boolean, isPendingPin: boolean, isRemotePin: boolean, pinned: boolean }} props
+ * @param {{ t: import('i18next').TFunction, isFailedPin: boolean, isPendingPin: boolean, isRemotePin: boolean, pinned: boolean }} props
* @returns {React.ReactElement}
*/
const PinningIcon = ({ t, isFailedPin, isPendingPin, isRemotePin, pinned }) => {
diff --git a/src/files/types.ts b/src/files/types.ts
index 34dcaee08..d9cbe3e26 100644
--- a/src/files/types.ts
+++ b/src/files/types.ts
@@ -8,3 +8,16 @@ export interface ContextMenuFile {
path: string
pinned: boolean
}
+
+export interface FileStream {
+ path: string
+ content: Blob
+ size: number
+}
+
+export interface FileExt {
+ filepath?: string
+ webkitRelativePath?: string
+}
+
+export type ExtendedFile = File & FileExt
diff --git a/src/helpers/Portal.js b/src/helpers/Portal.js
index 72f56477f..ed75a7cd5 100644
--- a/src/helpers/Portal.js
+++ b/src/helpers/Portal.js
@@ -1,6 +1,12 @@
import { memo, useEffect, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
+/**
+ * @param {Object} props
+ * @param {string} props.id
+ * @param {React.ReactNode} props.children
+ * @param {string|number} [props.zIndex]
+ */
const Portal = ({ id, children, zIndex }) => {
const el = useRef(document.getElementById(id) || document.createElement('div'))
const [dynamic] = useState(!el.current.parentElement)
@@ -8,7 +14,7 @@ const Portal = ({ id, children, zIndex }) => {
useEffect(() => {
if (dynamic) {
el.current.id = id
- zIndex && (el.current.style.zIndex = zIndex)
+ zIndex && (el.current.style.zIndex = String(zIndex))
document.body.appendChild(el.current)
}
return () => {
diff --git a/src/helpers/connected-context-provider.tsx b/src/helpers/connected-context-provider.tsx
index 6da32956f..66de9f84e 100644
--- a/src/helpers/connected-context-provider.tsx
+++ b/src/helpers/connected-context-provider.tsx
@@ -2,7 +2,6 @@
* @see {@link ./REDUX-BUNDLER-MIGRATION-GUIDE.md} for more information
*/
import React, { createContext, useContext, useMemo, ReactNode } from 'react'
-// @ts-expect-error - redux-bundler-react is not typed
import { connect } from 'redux-bundler-react'
/**
diff --git a/src/helpers/i8n.js b/src/helpers/i8n.js
index 4b18b3db0..f39ce3545 100644
--- a/src/helpers/i8n.js
+++ b/src/helpers/i8n.js
@@ -1,3 +1,6 @@
+/**
+ * @param {import('i18next').TFunction} translate
+ */
export const getJoyrideLocales = (translate) => ({
back: translate('tour.back'),
close: translate('tour.close'),
diff --git a/src/i18n-decorator.js b/src/i18n-decorator.js
index 8379e526c..d720cf737 100644
--- a/src/i18n-decorator.js
+++ b/src/i18n-decorator.js
@@ -2,6 +2,9 @@ import React from 'react'
import { I18nextProvider } from 'react-i18next'
import i18n from './i18n.js'
+/**
+ * @param {() => React.ReactElement} fn
+ */
export default function i18nDecorator (fn) {
return (
diff --git a/src/i18n.js b/src/i18n.js
index e405600ec..ba3febf73 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -16,7 +16,7 @@ i18n
.use(ICU)
.use(Backend)
.use(LanguageDetector)
- .init({
+ .init(/** @type {import('i18next').InitOptions} */ ({
load: 'currentOnly', // see https://github.com/i18next/i18next-http-backend/issues/61
backend: {
backends: [
@@ -29,7 +29,7 @@ i18n
expirationTime: (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') ? 1 : 7 * 24 * 60 * 60 * 1000
},
{ // HttpBackend
- loadPath: (lngs, namespaces) => {
+ loadPath: (/** @type {string[]} */ lngs, /** @type {string[]} */ namespaces) => {
const locale = getValidLocaleCode({ i18n, localeCode: lngs[0], languages: locales })
// ensure a relative path is used to look up the locales, so it works when loaded from /ipfs/
return `locales/${locale}/${namespaces}.json`
@@ -56,6 +56,6 @@ i18n
bindStore: 'added removed',
nsMode: 'default'
}
- })
+ }))
export default i18n
diff --git a/src/lib/files.js b/src/lib/files.js
index b4fabc6f4..4fe6d92a5 100644
--- a/src/lib/files.js
+++ b/src/lib/files.js
@@ -6,19 +6,13 @@ import filesize from 'filesize'
*/
/**
- * @typedef {Object} FileExt
- * @property {string} [filepath]
- * @property {string} [webkitRelativePath]
+ * @typedef {import('../files/types').FileExt} FileExt
*
- * @typedef {FileExt & File} ExtendedFile
+ * @typedef {import('../files/types').ExtendedFile} ExtendedFile
*
- * @typedef {Object} FileStream
- * @property {string} path
- * @property {Blob} content
- * @property {number} size
*
* @param {ExtendedFile[]} files
- * @returns {FileStream[]}
+ * @returns {import('../files/types').FileStream[]}
*/
export function normalizeFiles (files) {
const streams = []
diff --git a/src/lib/hofs/functions.test.js b/src/lib/hofs/functions.test.js
index 56fc54003..29359fb56 100644
--- a/src/lib/hofs/functions.test.js
+++ b/src/lib/hofs/functions.test.js
@@ -29,10 +29,12 @@ describe('hofFns', function () {
})
it('should throw an error if the passed fn is not a function', function () {
+ // @ts-expect-error - we want to test the error case
expect(() => after('not a function', 3)).toThrow(TypeError)
})
it('should throw an error if times is not a number', function () {
+ // @ts-expect-error - we want to test the error case
expect(() => after(jest.fn(), 'not a number')).toThrow(TypeError)
})
})
@@ -50,6 +52,7 @@ describe('hofFns', function () {
})
it('should throw an error if the passed fn is not a function', function () {
+ // @ts-expect-error - we want to test the error case
expect(() => once('not a function')).toThrow(TypeError)
})
})
@@ -71,6 +74,7 @@ describe('hofFns', function () {
})
it('should throw an error if the passed fn is not a function', function () {
+ // @ts-expect-error - we want to test the error case
expect(() => debounce('not a function')).toThrow(TypeError)
})
})
@@ -104,10 +108,12 @@ describe('hofFns', function () {
})
it('should throw an error if the passed fn is not a function', function () {
+ // @ts-expect-error - we want to test the error case
expect(() => onlyOnceAfter('not a function', 2)).toThrow(TypeError)
})
it('should throw an error if nth is not a number', function () {
+ // @ts-expect-error - we want to test the error case
expect(() => onlyOnceAfter(jest.fn(), 'not a number')).toThrow(TypeError)
})
})
diff --git a/src/lib/i18n-localeParser.js b/src/lib/i18n-localeParser.js
index 4ff8fce82..4d230d96b 100644
--- a/src/lib/i18n-localeParser.js
+++ b/src/lib/i18n-localeParser.js
@@ -13,7 +13,7 @@ export default function getValidLocaleCode ({ i18n, localeCode, languages }) {
if (info != null) {
return localeCode
}
-
+ // @ts-expect-error - fallbackLng is not typed correctly.
const fallbackLanguages = i18n.options.fallbackLng[localeCode]
if (info == null && fallbackLanguages != null) {
/**
diff --git a/src/lib/tours.js b/src/lib/tours.js
index 8f155826e..76c8992f0 100644
--- a/src/lib/tours.js
+++ b/src/lib/tours.js
@@ -1,5 +1,8 @@
import React from 'react'
+/**
+ * @type {import('react-joyride').CustomTour}
+ */
export const appTour = {
getSteps: ({ t }) => [{
content:
@@ -22,8 +25,11 @@ export const appTour = {
}
}
+/**
+ * @type {import('react-joyride').CustomTour}
+ */
export const welcomeTour = {
- getSteps: ({ t, Trans }) => [
+ getSteps: ({ t }) => [
{
content:
{t('tour.step1.title')}
@@ -45,6 +51,9 @@ export const welcomeTour = {
}
}
+/**
+ * @type {import('react-joyride').CustomTour}
+ */
export const statusTour = {
getSteps: ({ t, Trans }) => [
{
@@ -105,6 +114,9 @@ export const statusTour = {
}
}
+/**
+ * @type {import('react-joyride').CustomTour}
+ */
export const filesTour = {
getSteps: ({ t, Trans }) => [
{
@@ -180,6 +192,9 @@ export const filesTour = {
}
}
+/**
+ * @type {import('react-joyride').CustomTour}
+ */
export const peersTour = {
getSteps: ({ t }) => [
{
@@ -219,6 +234,9 @@ export const peersTour = {
}
}
+/**
+ * @type {import('react-joyride').CustomTour}
+ */
export const settingsTour = {
getSteps: ({ t, Trans }) => [
{
diff --git a/src/loader/ComponentLoader.js b/src/loader/ComponentLoader.js
index 7d5fd14b4..e7ffc765d 100644
--- a/src/loader/ComponentLoader.js
+++ b/src/loader/ComponentLoader.js
@@ -1,6 +1,10 @@
import React from 'react'
import './ComponentLoader.css'
+/**
+ * @param {Object} props
+ * @param {React.CSSProperties} [props.style]
+ */
const ComponentLoader = (props) => (