lint: resolve lint issues

This commit is contained in:
EnixCoda 2022-05-16 00:23:20 +08:00
parent 850ea5a64a
commit 77d9db6043
24 changed files with 101 additions and 84 deletions

View file

@ -96,6 +96,9 @@
},
"eslintConfig": {
"root": true,
"env": {
"browser": true
},
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
@ -105,7 +108,10 @@
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended"
]
],
"rules": {
"@typescript-eslint/ban-types": "off"
}
},
"resolutions": {
"@types/react": "^18.0.9",

View file

@ -25,13 +25,13 @@ export function Clippy({ codeSnippetElement }: Props) {
React.useEffect(() => {
const element = elementRef.current
if (element) {
function onClippyClick() {
const onClippyClick = () =>
setState(copyElementContent(codeSnippetElement) ? 'success' : 'fail')
}
element.addEventListener('click', onClippyClick)
return () => element.removeEventListener('click', onClippyClick)
}
}, [])
}, [codeSnippetElement])
return (
<div className={className}>

View file

@ -2,7 +2,7 @@ import * as React from 'react'
import { Icon } from '../Icon'
export function DiffStatText({
diff: { status, changes, additions, deletions },
diff: { status, additions, deletions },
}: {
diff: Required<TreeNode>['diff']
}) {

View file

@ -44,7 +44,7 @@ export function ListView({
const goToCurrentItem = React.useCallback(() => {
const targetPath = platform.getCurrentPath(metaData.branchName)
if (targetPath) expandTo(targetPath)
}, [metaData.branchName])
}, [metaData.branchName, expandTo])
useOnLocationChange(goToCurrentItem)
useOnPJAXDone(goToCurrentItem)
@ -52,7 +52,7 @@ export function ListView({
const { compactFileTree } = useConfigs().value
return (
<FixedSizeList
<FixedSizeList<NodeRendererContext>
ref={listRef}
itemKey={(index, { visibleNodes }) => visibleNodes?.nodes[index]?.path}
itemData={nodeRendererContext}

View file

@ -47,6 +47,7 @@ export function Node({
style={{ ...style, paddingLeft: `${10 + (compact ? 10 : 20) * depth}px` }}
title={node.path}
target={node.type === 'commit' ? '_blank' : undefined}
rel="noopener noreferrer"
{...platform.delegatePJAXProps?.({ node })}
>
<div className={'node-item-label'}>
@ -71,7 +72,7 @@ const NodeItemIcon = React.memo(function NodeItemIcon({
const src = React.useMemo(
() => (node.type === 'tree' ? getFolderIconURL(node, open) : getFileIconURL(node)),
[open],
[node, open],
)
if (icons === 'native') return <Icon type={getIconType(node)} />

View file

@ -14,7 +14,7 @@ export function useNodeRenderers(allRenderers: (NodeRenderer | null | undefined)
? (node: TreeNode) =>
renderers.map((render, i) => <React.Fragment key={i}>{render(node)}</React.Fragment>)
: undefined
}, allRenderers)
}, allRenderers) // eslint-disable-line react-hooks/exhaustive-deps
}
export function useRenderFileStatus() {

View file

@ -15,6 +15,8 @@ export function useVisibleNodesGeneratorMethods(
const goTo = useGoTo(visibleNodesGenerator, updateSearchKey, expandTo)
const toggleExpansion = useToggleExpansion(visibleNodesGenerator)
const focusNode = useFocusNode(visibleNodesGenerator)
// Only run when visibleNodesGenerator changes
useEffect(() => {
if (!visibleNodesGenerator) return
@ -26,7 +28,7 @@ export function useVisibleNodesGeneratorMethods(
const targetPath = getCurrentPath()
if (targetPath) goTo(targetPath)
}
}, [visibleNodesGenerator])
}, [visibleNodesGenerator]) // eslint-disable-line react-hooks/exhaustive-deps
return {
expandTo,

View file

@ -17,6 +17,7 @@ export function useVisibleNodesGenerator(metaData: MetaData) {
const accessToken = config.accessToken
const setStateContext = useLoadedContext(SideBarStateContext).onChange
// Only run when metadata or accessToken changes
useSequentialEffect(
useCallback(
checker => {
@ -54,7 +55,7 @@ export function useVisibleNodesGenerator(metaData: MetaData) {
setStateContext('tree-rendered')
})
},
[metaData, accessToken],
[metaData, accessToken], // eslint-disable-line react-hooks/exhaustive-deps
),
)

View file

@ -116,9 +116,9 @@ export function FileExplorer({ freeze, metaData }: Props) {
)}
</>
)}
<SizeObserver className={'files'}>
{({ width = 0, height = 0 }) => (
<div className={'magic-size-container'}>
<SizeObserver<HTMLDivElement>>
{({ width = 0, height = 0 }, ref) => (
<div className={'files'} ref={ref}>
<ListView
height={height}
width={width}

View file

@ -1,8 +0,0 @@
import * as React from 'react'
// I tried to install `react-iifc` but that causes TS build errors for unknown reason
// So here the duplicated code is
export function IIFC({ children }: { children(): React.ReactNode }) {
return <>{children()}</>
}

View file

@ -16,7 +16,7 @@ export function SelectInput<T>({
onChange={e => {
const key = e.target.value
const option = options.find(option => option.key === key)
onChange(option!?.value)
if (option) onChange(option.value)
}}
value={options.find(option => option.value === value)?.key}
{...selectProps}

View file

@ -31,9 +31,8 @@ export function SideBar() {
const accessToken = configContext.value.accessToken || ''
const [baseSize] = React.useState(() => configContext.value.sideBarWidth)
const $showSettings = useStateIO(false)
const showSettings = $showSettings.value
const toggleShowSettings = React.useCallback(() => $showSettings.onChange(show => !show), [])
const [showSettings, setShowSettings] = React.useState(false)
const toggleShowSettings = React.useCallback(() => setShowSettings(show => !show), [])
const $logoContainerElement = useStateIO<HTMLElement | null>(null)
@ -41,12 +40,12 @@ export function SideBar() {
React.useEffect(() => {
if (hasMetaData) {
DOMHelper.markGitakoReadyState(true)
$showSettings.onChange(false)
setShowSettings(false)
$logoContainerElement.onChange(DOMHelper.insertLogoMountPoint())
} else {
DOMHelper.markGitakoReadyState(false)
}
}, [hasMetaData])
}, [hasMetaData]) // eslint-disable-line react-hooks/exhaustive-deps
React.useEffect(() => {
if (detectBrowser() === 'Safari') DOMHelper.markGitakoSafariFlag()
@ -79,7 +78,7 @@ export function SideBar() {
if (intelligentToggle !== null) {
configContext.onChange({ intelligentToggle: shouldShow })
}
}, [shouldShow, intelligentToggle])
}, [shouldShow, intelligentToggle]) // eslint-disable-line react-hooks/exhaustive-deps
const error = useLoadedContext(SideBarErrorContext).value
// Lock shouldShow on error
@ -87,25 +86,25 @@ export function SideBar() {
if (error && shouldShow) {
$shouldShow.onChange(false)
}
}, [error])
}, [error]) // eslint-disable-line react-hooks/exhaustive-deps
const setShowSideBar = React.useCallback(
(show: typeof $shouldShow.value) => {
(show: boolean) => {
if (!error) $shouldShow.onChange(show)
},
[error],
[error], // eslint-disable-line react-hooks/exhaustive-deps
)
const toggleShowSideBar = React.useCallback(() => {
if (!error) $shouldShow.onChange(show => !show)
}, [error])
}, [error]) // eslint-disable-line react-hooks/exhaustive-deps
useToggleSideBarWithKeyboard(state, configContext, toggleShowSideBar)
const updateSideBarVisibility = React.useCallback(() => {
if (intelligentToggle === null && sidebarToggleMode === 'persistent') {
setShowSideBar(platform.shouldShow())
}
}, [intelligentToggle, sidebarToggleMode])
}, [intelligentToggle, sidebarToggleMode, setShowSideBar])
useOnPJAXDone(updateSideBarVisibility)
@ -120,7 +119,7 @@ export function SideBar() {
if (hideSidebarOnInvalidToken) {
setShowSideBar(false)
}
}, [hideSidebarOnInvalidToken])
}, [hideSidebarOnInvalidToken, setShowSideBar])
return (
<Theme>

View file

@ -40,7 +40,7 @@ export function SideBarBodyWrapper({
const heightForSafari = useConditionalHook(
() => detectBrowser() === 'Safari',
() => useWindowSize().height,
() => useWindowSize().height, // eslint-disable-line react-hooks/rules-of-hooks
)
React.useEffect(() => {
@ -55,7 +55,10 @@ export function SideBarBodyWrapper({
const bodyWrapperRef = React.useRef<HTMLDivElement | null>(null)
useDebounce(() => configContext.onChange({ sideBarWidth: size }), 100, [size])
function apply(sizeVariableMountPoint: HTMLElement | undefined, size: number) {
const applySizeToCSSVariables = React.useCallback(function apply(
sizeVariableMountPoint: HTMLElement | undefined,
size: number,
) {
if (sizeVariableMountPoint)
setCSSVariable(
'--gitako-width',
@ -69,12 +72,13 @@ export function SideBarBodyWrapper({
sizeVariableMountPoint ? undefined : `${size}px`,
bodyWrapperRef.current,
)
}
},
[])
// Update size using useEffect would cause delay
const onResize = React.useMemo(() => {
let sizeToApply: number,
applied = true
let sizeToApply: number
let applied = true
return ([size]: number[]) => {
// do NOT merge this with the above similar effect, side bar will jump otherwise
sizeToApply = getSafeSize(size, width)
@ -84,15 +88,15 @@ export function SideBarBodyWrapper({
applied = false
requestAnimationFrame(() => {
applied = true
apply(sizeVariableMountPoint, sizeToApply)
applySizeToCSSVariables(sizeVariableMountPoint, sizeToApply)
})
}
}
}, [width, sizeVariableMountPoint])
}, [width, sizeVariableMountPoint, applySizeToCSSVariables])
React.useEffect(() => {
apply(sizeVariableMountPoint, size)
}, [sizeVariableMountPoint])
applySizeToCSSVariables(sizeVariableMountPoint, size)
}, [sizeVariableMountPoint, size, applySizeToCSSVariables])
const onMouseLeave = React.useCallback(
<E extends HTMLElement>(e: React.MouseEvent<E>) => {
@ -116,7 +120,7 @@ export function SideBarBodyWrapper({
onResize={onResize}
onResetSize={() => {
setSize(defaultConfigs.sideBarWidth)
apply(sizeVariableMountPoint, defaultConfigs.sideBarWidth)
applySizeToCSSVariables(sizeVariableMountPoint, defaultConfigs.sideBarWidth)
}}
onResizeStateChange={state => {
blockLeaveRef.current = state === 'resizing'

View file

@ -33,7 +33,12 @@ export function SimpleToggleField<Key extends keyof Config>({ field, onChange }:
<>
{field.label}{' '}
{field.wikiLink ? (
<a href={field.wikiLink} title={field.tooltip} target={'_blank'}>
<a
href={field.wikiLink}
title={field.tooltip}
target="_blank"
rel="noopener noreferrer"
>
(?)
</a>
) : field.description ? (

View file

@ -6,16 +6,12 @@ type Size = {
height: number
}
type Props = Override<
React.HTMLAttributes<HTMLElement>,
{
type?: string | React.ComponentType
children(size: Partial<Size>): React.ReactNode
}
>
type Props<R extends Element> = {
children(size: Partial<Size>, ref: React.MutableRefObject<R | null>): React.ReactNode
}
export function SizeObserver({ type = 'div', children, ...rest }: Props) {
const ref = React.useRef<any>()
export function SizeObserver<R extends Element>({ children }: Props<R>) {
const ref = React.useRef<R | null>(null)
const [size, setSize] = React.useState<Partial<Size>>({
width: undefined,
@ -42,7 +38,5 @@ export function SizeObserver({ type = 'div', children, ...rest }: Props) {
}
}, [])
const props: any = { ...rest, ref } // :)
return React.createElement(type, props, children(size))
return <>{children(size, ref)}</>
}

View file

@ -41,7 +41,7 @@ export function ToggleShowButton({ error, className, onClick, onHover }: Props)
if (ref.current) {
ref.current.style.top = distance + 'px'
}
}, [height])
}, [height]) // eslint-disable-line react-hooks/exhaustive-deps
// And this repositions on drag
const { onPointerDown } = useResizeHandler(

View file

@ -1,10 +1,12 @@
/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { fuzzyMode } from './fuzzyMode'
type TreeNodeSource = {
[key: string]: true | TreeNodeSource
}
function createTreeNode(source: TreeNodeSource, name: string = '', paths: string[] = []): TreeNode {
function createTreeNode(source: TreeNodeSource, name = '', paths: string[] = []): TreeNode {
const subPaths = paths.concat(name)
return {
name,

View file

@ -57,7 +57,7 @@ function fuzzyMatch(input: string, sample: string) {
}
}
function fuzzyMatchIndexes(input: string, sample: string, shift: number = 0) {
function fuzzyMatchIndexes(input: string, sample: string, shift = 0) {
const indexes: number[] = []
let i = 0,
j = 0

View file

@ -10,31 +10,28 @@ import { SettingsSection } from './SettingsSection'
const ACCESS_TOKEN_REGEXP = /^([0-9a-fA-F]+|gh[pousr]_[A-Za-z0-9_]+)$/
type Props = {}
export function AccessTokenSettings(props: React.PropsWithChildren<Props>) {
export function AccessTokenSettings() {
const configContext = useConfigs()
const hasAccessToken = Boolean(configContext.value.accessToken)
const $useAccessToken = useStateIO('')
const [accessToken, setAccessToken] = React.useState('')
const useAccessTokenHint = useStateIO<React.ReactNode>('')
const focusInput = useStateIO(false)
const { value: accessTokenHint } = useAccessTokenHint
const { value: accessToken } = $useAccessToken
React.useEffect(() => {
// clear input when access token updates
$useAccessToken.onChange('')
setAccessToken('')
}, [configContext.value.accessToken])
const onInputAccessToken = React.useCallback(
({ currentTarget: { value } }: React.FormEvent<HTMLInputElement>) => {
$useAccessToken.onChange(value)
setAccessToken(value)
useAccessTokenHint.onChange(
ACCESS_TOKEN_REGEXP.test(value) ? '' : 'Gitako does not recognize the token.',
)
},
[],
[], // eslint-disable-line react-hooks/exhaustive-deps
)
const saveToken = React.useCallback(
@ -50,11 +47,11 @@ export function AccessTokenSettings(props: React.PropsWithChildren<Props>) {
) => {
if (accessToken) {
configContext.onChange({ accessToken })
$useAccessToken.onChange('')
setAccessToken('')
useAccessTokenHint.onChange(hint)
}
},
[accessToken],
[accessToken], // eslint-disable-line react-hooks/exhaustive-deps
)
const onPressAccessToken = React.useCallback(
@ -73,6 +70,7 @@ export function AccessTokenSettings(props: React.PropsWithChildren<Props>) {
href={wikiLinks.createAccessToken}
title="A token is required to access private repositories or bypass API rate limits"
target="_blank"
rel="noopener noreferrer"
>
(?)
</a>

View file

@ -38,9 +38,7 @@ const recursiveToggleFolderOptions: Option<Config['recursiveToggleFolder']>[] =
},
]
type Props = {}
export function FileTreeSettings(props: React.PropsWithChildren<Props>) {
export function FileTreeSettings() {
const configContext = useConfigs()
return (
<SettingsSection title={'File Tree'}>

View file

@ -15,8 +15,9 @@ export function Footer(props: Props) {
<Link
className={'version'}
href={wikiLinks.changeLog}
target={'_blank'}
title={'Check out new features!'}
target="_blank"
rel="noopener noreferrer"
>
{VERSION}
</Link>

View file

@ -72,11 +72,19 @@ export function SettingsBarContent({ toggleShow }: { toggleShow: () => void }) {
</SettingsSection>
)}
<SettingsSection title={'Talk to the author'}>
<a href="https://github.com/EnixCoda/Gitako/issues" target="_blank">
<a
href="https://github.com/EnixCoda/Gitako/issues"
target="_blank"
rel="noopener noreferrer"
>
Report bug
</a>
{' / '}
<a href="https://github.com/EnixCoda/Gitako/discussions" target="_blank">
<a
href="https://github.com/EnixCoda/Gitako/discussions"
target="_blank"
rel="noopener noreferrer"
>
Discuss feature
</a>
</SettingsSection>

View file

@ -8,9 +8,7 @@ import * as keyHelper from 'utils/keyHelper'
import { Field } from './Field'
import { SettingsSection } from './SettingsSection'
type Props = {}
export function SidebarSettings(props: React.PropsWithChildren<Props>) {
export function SidebarSettings() {
const configContext = useConfigs()
const useToggleShowSideBarShortcut = useStateIO(configContext.value.shortcut)
const { value: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut
@ -18,7 +16,7 @@ export function SidebarSettings(props: React.PropsWithChildren<Props>) {
React.useEffect(() => {
useToggleShowSideBarShortcut.onChange(configContext.value.shortcut)
}, [configContext.value.shortcut])
}, [configContext.value.shortcut]) // eslint-disable-line react-hooks/exhaustive-deps
return (
<SettingsSection title={'Sidebar'}>
@ -38,7 +36,7 @@ export function SidebarSettings(props: React.PropsWithChildren<Props>) {
// Clear shortcut with backspace
const shortcut = e.key === 'Backspace' ? '' : keyHelper.parseEvent(e)
useToggleShowSideBarShortcut.onChange(shortcut)
}, [])}
}, [])} // eslint-disable-line react-hooks/exhaustive-deps
readOnly
/>
{configContext.value.shortcut === toggleShowSideBarShortcut ? (

View file

@ -24,11 +24,19 @@ export function GiteeAccessDeniedError({ hasToken }: { hasToken: boolean }) {
) : (
<p>
Gitako needs access token to read this project due to{' '}
<a href="https://developer.github.com/v3/#rate-limiting" target="_blank">
<a
href="https://developer.github.com/v3/#rate-limiting"
target="_blank"
rel="noopener noreferrer"
>
GitHub rate limiting
</a>{' '}
and{' '}
<a href="https://developer.github.com/v3/#authentication" target="_blank">
<a
href="https://developer.github.com/v3/#authentication"
target="_blank"
rel="noopener noreferrer"
>
auth needs
</a>
. Please setup access token in the settings panel below.