mirror of
https://github.com/EnixCoda/Gitako.git
synced 2026-03-11 08:54:44 +00:00
refactor: no import * as from react
This commit is contained in:
parent
c3de9ad835
commit
03e3454afc
73 changed files with 250 additions and 251 deletions
|
|
@ -2,7 +2,7 @@ import { useConfigs } from 'containers/ConfigsContext'
|
|||
import { GITHUB_OAUTH } from 'env'
|
||||
import { platform } from 'platforms'
|
||||
import { GitHub } from 'platforms/GitHub'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
|
||||
export function AccessDeniedDescription() {
|
||||
const { accessToken } = useConfigs().value
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { cx } from 'utils/cx'
|
||||
import { copyElementContent } from 'utils/DOMHelper'
|
||||
|
||||
|
|
@ -10,8 +10,8 @@ const className = 'clippy-wrapper'
|
|||
export const ClippyClassName = className
|
||||
|
||||
export function Clippy({ codeSnippetElement }: Props) {
|
||||
const [state, setState] = React.useState<'normal' | 'success' | 'fail'>('normal')
|
||||
React.useEffect(() => {
|
||||
const [state, setState] = useState<'normal' | 'success' | 'fail'>('normal')
|
||||
useEffect(() => {
|
||||
const timer = window.setTimeout(() => {
|
||||
setState('normal')
|
||||
}, 1000)
|
||||
|
|
@ -21,8 +21,8 @@ export function Clippy({ codeSnippetElement }: Props) {
|
|||
// Temporary fix:
|
||||
// React moved root node of event delegation since v17
|
||||
// onClick on <a /> won't work when rendered with `renderReact`
|
||||
const elementRef = React.useRef<HTMLButtonElement | null>(null)
|
||||
React.useEffect(() => {
|
||||
const elementRef = useRef<HTMLButtonElement | null>(null)
|
||||
useEffect(() => {
|
||||
const element = elementRef.current
|
||||
if (element) {
|
||||
const onClippyClick = () =>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
DiffRemovedIcon,
|
||||
DiffRenamedIcon,
|
||||
} from '@primer/octicons-react'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { resolveDiffGraphMeta } from 'utils/general'
|
||||
import { Icon } from '../Icon'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { Icon } from '../Icon'
|
||||
|
||||
const iconMap = {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import { platform } from 'platforms'
|
||||
import * as React from 'react'
|
||||
import React, { useMemo, useRef } from 'react'
|
||||
import { cx } from 'utils/cx'
|
||||
import { cancelEvent } from 'utils/DOMHelper'
|
||||
import { getFileIconURL, getFolderIconURL } from 'utils/parseIconMapCSV'
|
||||
|
|
@ -43,7 +43,7 @@ export const Node = React.memo(function Node({
|
|||
onFocus,
|
||||
}: Props) {
|
||||
const { compactFileTree: compact } = useConfigs().value
|
||||
const ref = React.useRef<HTMLDivElement>(null)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
return (
|
||||
<a
|
||||
href={node.url}
|
||||
|
|
@ -87,11 +87,11 @@ const NodeItemIcon = React.memo(function NodeItemIcon({
|
|||
}) {
|
||||
const { icons } = useConfigs().value
|
||||
|
||||
const src = React.useMemo(
|
||||
const src = useMemo(
|
||||
() => (node.type === 'tree' ? getFolderIconURL(node, open) : getFileIconURL(node)),
|
||||
[node, open],
|
||||
)
|
||||
const iconType = React.useMemo(() => getIconType(node), [node])
|
||||
const iconType = useMemo(() => getIconType(node), [node])
|
||||
|
||||
if (icons === 'native') return <Icon type={iconType} />
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { VisibleNodesGenerator } from 'utils/VisibleNodesGenerator'
|
||||
|
||||
export function useExpandTo(visibleNodesGenerator: VisibleNodesGenerator) {
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
async (currentPath: string[]) => {
|
||||
const nodeExpandedTo = await visibleNodesGenerator.expandTo(currentPath.join('/'))
|
||||
if (nodeExpandedTo) visibleNodesGenerator.focusNode(nodeExpandedTo)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { VisibleNodesGenerator } from 'utils/VisibleNodesGenerator'
|
||||
|
||||
export function useFocusNode(visibleNodesGenerator: VisibleNodesGenerator) {
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
(node: TreeNode | null) => visibleNodesGenerator.focusNode(node),
|
||||
[visibleNodesGenerator],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { VisibleNodesGenerator } from 'utils/VisibleNodesGenerator'
|
||||
import { useExpandTo } from './useExpandTo'
|
||||
|
||||
|
|
@ -7,7 +7,7 @@ export function useGoTo(
|
|||
updateSearchKey: React.Dispatch<React.SetStateAction<string>>,
|
||||
expandTo: ReturnType<typeof useExpandTo>,
|
||||
) {
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
(path: string[]) => {
|
||||
updateSearchKey('')
|
||||
visibleNodesGenerator.search(null)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { SidebarContext } from 'components/SidebarContext'
|
||||
import * as React from 'react'
|
||||
import React, { useCallback, useContext } from 'react'
|
||||
import * as DOMHelper from 'utils/DOMHelper'
|
||||
import { OperatingSystems, os } from 'utils/general'
|
||||
import { loadWithFastRedirect } from 'utils/hooks/useFastRedirect'
|
||||
|
|
@ -32,9 +32,9 @@ export function useHandleKeyDown(
|
|||
searched: boolean,
|
||||
setAlignMode: (mode: AlignMode) => void,
|
||||
) {
|
||||
const { pendingFocusTarget } = React.useContext(SidebarContext)
|
||||
const { pendingFocusTarget } = useContext(SidebarContext)
|
||||
const setPendingFocusTarget = pendingFocusTarget.onChange
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
(event: React.KeyboardEvent<HTMLElement>) => {
|
||||
const { nodes, focusedNode, expandedNodes } = visibleNodes
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { ActionList, AnchoredOverlay } from '@primer/react'
|
|||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import { PortalContext } from 'containers/PortalContext'
|
||||
import { platform } from 'platforms'
|
||||
import * as React from 'react'
|
||||
import React, { useCallback, useContext, useMemo, useState } from 'react'
|
||||
import { useCopyToClipboard } from 'react-use'
|
||||
import { cx } from 'utils/cx'
|
||||
import { cancelEvent, onEnterKeyDown } from 'utils/DOMHelper'
|
||||
|
|
@ -22,7 +22,7 @@ import { VisibleNodesGeneratorMethods } from './useVisibleNodesGeneratorMethods'
|
|||
export type NodeRenderer = (node: TreeNode) => React.ReactNode
|
||||
|
||||
export function useNodeRenderers(allRenderers: (NodeRenderer | null | undefined)[]) {
|
||||
return React.useMemo(() => {
|
||||
return useMemo(() => {
|
||||
const renderers: NodeRenderer[] = allRenderers.filter(is.not.nil)
|
||||
return renderers.length
|
||||
? (node: TreeNode) =>
|
||||
|
|
@ -33,7 +33,7 @@ export function useNodeRenderers(allRenderers: (NodeRenderer | null | undefined)
|
|||
|
||||
export function useRenderFileStatus() {
|
||||
const { showDiffInText } = useConfigs().value
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
function renderFileStatus({ diff }: TreeNode) {
|
||||
return (
|
||||
diff && (
|
||||
|
|
@ -64,10 +64,10 @@ function NodeContextMenu({
|
|||
node: TreeNode
|
||||
visibleNodesGeneratorMethods: VisibleNodesGeneratorMethods
|
||||
}) {
|
||||
const [isOpen, setIsOpen] = React.useState(false)
|
||||
const [copied, setCopied] = React.useState<string | null>(null)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [copied, setCopied] = useState<string | null>(null)
|
||||
const [copyState, copyToClipboard] = useCopyToClipboard()
|
||||
const portalName = React.useContext(PortalContext)
|
||||
const portalName = useContext(PortalContext)
|
||||
const actionElements = {
|
||||
copyPermalink:
|
||||
node.permalink &&
|
||||
|
|
@ -233,14 +233,14 @@ export function useRenderFileCommentAmounts() {
|
|||
) : null
|
||||
}
|
||||
const { commentToggle } = useConfigs().value
|
||||
return React.useMemo(() => (commentToggle ? renderFileCommentAmounts : null), [commentToggle])
|
||||
return useMemo(() => (commentToggle ? renderFileCommentAmounts : null), [commentToggle])
|
||||
}
|
||||
|
||||
export function useRenderFindInFolderButton(
|
||||
onSearch: (searchKey: string, searchMode: SearchMode) => void,
|
||||
) {
|
||||
const { searchMode } = useConfigs().value
|
||||
return React.useMemo(
|
||||
return useMemo(
|
||||
() =>
|
||||
searchMode === 'fuzzy'
|
||||
? function renderFindInFolderButton(node: TreeNode) {
|
||||
|
|
@ -260,7 +260,7 @@ export function useRenderFindInFolderButton(
|
|||
}
|
||||
|
||||
export function useRenderGoToButton(searched: boolean, goTo: (path: string[]) => void) {
|
||||
return React.useMemo(
|
||||
return useMemo(
|
||||
() =>
|
||||
searched
|
||||
? function renderGoToButton(node: TreeNode): React.ReactNode {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import * as React from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { isOpenInNewWindowClick } from 'utils/general'
|
||||
import { loadWithFastRedirect } from 'utils/hooks/useFastRedirect'
|
||||
import { AlignMode } from '../useVirtualScroll'
|
||||
|
|
@ -10,7 +10,7 @@ export function useHandleNodeClick(
|
|||
setAlignMode: (mode: AlignMode) => void,
|
||||
) {
|
||||
const { recursiveToggleFolder } = useConfigs().value
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
(event: React.MouseEvent<HTMLElement, MouseEvent>, node: TreeNode) => {
|
||||
setAlignMode('lazy')
|
||||
switch (node.type) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { VisibleNodesGenerator } from 'utils/VisibleNodesGenerator'
|
||||
import { SearchMode, searchModes } from '../../searchModes'
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ export function useOnSearch(
|
|||
visibleNodesGenerator: VisibleNodesGenerator,
|
||||
) {
|
||||
const { restoreExpandedFolders } = useConfigs().value
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
(searchKey: string, searchMode: SearchMode) => {
|
||||
updateSearchKey(searchKey)
|
||||
visibleNodesGenerator.search(
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { searchModes } from '../../searchModes'
|
||||
|
||||
export function useRenderLabelText(searchKey: string) {
|
||||
const { searchMode } = useConfigs().value
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
(node: TreeNode) => searchModes[searchMode].renderNodeLabelText(node, searchKey),
|
||||
[searchKey, searchMode],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { VisibleNodesGenerator } from 'utils/VisibleNodesGenerator'
|
||||
|
||||
export function useToggleExpansion(visibleNodesGenerator: VisibleNodesGenerator) {
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
async (
|
||||
node: TreeNode,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,10 +3,18 @@ import { useFocusOnPendingTarget } from 'components/FocusTarget'
|
|||
import { LoadingIndicator } from 'components/LoadingIndicator'
|
||||
import { SearchBar } from 'components/SearchBar'
|
||||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import { useInspector } from 'containers/Inspector'
|
||||
import { PortalContext } from 'containers/PortalContext'
|
||||
import { RepoContext } from 'containers/RepoContext'
|
||||
import { useInspector } from 'containers/Inspector'
|
||||
import * as React from 'react'
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { usePrevious, useUpdateEffect } from 'react-use'
|
||||
import { cx } from 'utils/cx'
|
||||
import { run } from 'utils/general'
|
||||
|
|
@ -46,7 +54,7 @@ export type NodeRendererContext = {
|
|||
}
|
||||
|
||||
export function FileExplorer() {
|
||||
const metaData = React.useContext(RepoContext)
|
||||
const metaData = useContext(RepoContext)
|
||||
const visibleNodesGenerator = useVisibleNodesGenerator(metaData)
|
||||
const visibleNodes = useVisibleNodes(visibleNodesGenerator)
|
||||
const state = useLoadedContext(SideBarStateContext).value
|
||||
|
|
@ -88,7 +96,7 @@ function LoadedFileExplorer({
|
|||
}) {
|
||||
const config = useConfigs().value
|
||||
|
||||
const [searchKey, updateSearchKey] = React.useState('')
|
||||
const [searchKey, updateSearchKey] = useState('')
|
||||
const searched = !!searchKey
|
||||
const onSearch = useOnSearch(updateSearchKey, visibleNodesGenerator)
|
||||
const { focusedNode, nodes, expandedNodes, depths, loading } = visibleNodes
|
||||
|
|
@ -117,8 +125,8 @@ function LoadedFileExplorer({
|
|||
overScan: 10,
|
||||
})
|
||||
|
||||
const portalName = React.useMemo(() => `${Math.random()}`, [])
|
||||
React.useEffect(() => {
|
||||
const portalName = useMemo(() => `${Math.random()}`, [])
|
||||
useEffect(() => {
|
||||
const current = scrollElementRef.current
|
||||
if (current) registerPortalRoot(current, portalName)
|
||||
}, [scrollElementRef, portalName])
|
||||
|
|
@ -133,18 +141,18 @@ function LoadedFileExplorer({
|
|||
// - "lazy"
|
||||
// - navigate with keyboard
|
||||
// - "lazy"
|
||||
const [alignMode, setAlignMode] = React.useState<AlignMode>('top')
|
||||
const [alignMode, setAlignMode] = useState<AlignMode>('top')
|
||||
|
||||
const index = React.useMemo(
|
||||
const index = useMemo(
|
||||
() => (focusedNode?.path ? nodes.findIndex(node => node.path === focusedNode.path) : -1),
|
||||
[focusedNode?.path, nodes],
|
||||
)
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
useLayoutEffect(() => {
|
||||
if (index !== -1) scrollToItem(index, alignMode)
|
||||
}, [index, scrollToItem, alignMode])
|
||||
const prevSearchKey = usePrevious(searchKey)
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
// when start searching or stop searching
|
||||
if (!prevSearchKey !== !searchKey) scrollToItem(0, alignMode)
|
||||
}, [prevSearchKey, searchKey, scrollToItem, alignMode])
|
||||
|
|
@ -170,7 +178,7 @@ function LoadedFileExplorer({
|
|||
])
|
||||
const renderLabelText = useRenderLabelText(searchKey)
|
||||
|
||||
const goToCurrentItem = React.useCallback(() => {
|
||||
const goToCurrentItem = useCallback(() => {
|
||||
const targetPath = getCurrentPath()
|
||||
if (targetPath) expandTo(targetPath)
|
||||
}, [getCurrentPath, expandTo])
|
||||
|
|
@ -178,14 +186,14 @@ function LoadedFileExplorer({
|
|||
useOnLocationChange(goToCurrentItem)
|
||||
useAfterRedirect(goToCurrentItem)
|
||||
|
||||
const [currentPath, setCurrentPath] = React.useState(() => getCurrentPath())
|
||||
useAfterRedirect(React.useCallback(() => setCurrentPath(getCurrentPath()), [getCurrentPath]))
|
||||
const [currentPath, setCurrentPath] = useState(() => getCurrentPath())
|
||||
useAfterRedirect(useCallback(() => setCurrentPath(getCurrentPath()), [getCurrentPath]))
|
||||
useInspector('CurrentPath', currentPath)
|
||||
|
||||
const ref = React.useRef<HTMLDivElement | null>(null)
|
||||
const ref = useRef<HTMLDivElement | null>(null)
|
||||
useFocusOnPendingTarget(
|
||||
'files',
|
||||
React.useCallback(() => ref.current?.focus(), []),
|
||||
useCallback(() => ref.current?.focus(), []),
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { VisibleNodesGeneratorMethods } from './hooks/useVisibleNodesGeneratorMethods'
|
||||
import { AlignMode } from './useVirtualScroll'
|
||||
|
||||
|
|
@ -6,7 +6,7 @@ export function useHandleNodeFocus(
|
|||
{ focusNode }: VisibleNodesGeneratorMethods,
|
||||
setAlignMode: (mode: AlignMode) => void,
|
||||
) {
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
(event: React.FocusEvent<HTMLElement, Element>, node: TreeNode) => {
|
||||
setAlignMode('lazy')
|
||||
focusNode(node)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
|
||||
function useLatestValueRef<T>(value: T) {
|
||||
const ref = React.useRef(value)
|
||||
React.useEffect(() => {
|
||||
const ref = useRef(value)
|
||||
useEffect(() => {
|
||||
ref.current = value
|
||||
})
|
||||
return ref
|
||||
|
|
@ -11,5 +11,5 @@ export function useCallbackRef<Args extends AnyArray, R>(
|
|||
callback: (...args: Args) => R,
|
||||
): (...args: Args) => R {
|
||||
const ref = useLatestValueRef(callback)
|
||||
return React.useCallback((...args: Args) => ref.current(...args), [ref])
|
||||
return useCallback((...args: Args) => ref.current(...args), [ref])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
||||
import { useCallbackRef } from './useLatestValueRef'
|
||||
|
||||
function memoize<Args extends AnyArray, R>(
|
||||
|
|
@ -29,14 +29,14 @@ export function useVirtualScroll<E extends HTMLElement>({
|
|||
}) {
|
||||
const totalHeight = totalAmount * rowHeight
|
||||
|
||||
const ref = React.useRef<E | null>(null) // TODO: compare DOM native event listener
|
||||
const [scrollTop, setScrollTop] = React.useState(0)
|
||||
const ref = useRef<E | null>(null) // TODO: compare DOM native event listener
|
||||
const [scrollTop, setScrollTop] = useState(0)
|
||||
|
||||
const onScroll = React.useCallback((e: React.UIEvent<E, UIEvent>) => {
|
||||
const onScroll = useCallback((e: React.UIEvent<E, UIEvent>) => {
|
||||
setScrollTop(e.currentTarget.scrollTop)
|
||||
}, [])
|
||||
|
||||
const [startRenderIndex, endRenderIndex] = React.useMemo(() => {
|
||||
const [startRenderIndex, endRenderIndex] = useMemo(() => {
|
||||
const viewportLastItemOverflow = viewportHeight % rowHeight
|
||||
const visibleRowCount = (viewportHeight - viewportLastItemOverflow) / rowHeight
|
||||
const inViewIndexFirst = (Math.min(scrollTop, totalHeight - viewportHeight) / rowHeight) >> 0
|
||||
|
|
@ -46,14 +46,14 @@ export function useVirtualScroll<E extends HTMLElement>({
|
|||
return [renderIndexFirst, renderIndexLast]
|
||||
}, [scrollTop, viewportHeight, overScan, rowHeight, totalAmount, totalHeight])
|
||||
|
||||
const indexes = React.useMemo(() => {
|
||||
const indexes = useMemo(() => {
|
||||
const indexes: number[] = []
|
||||
let i = startRenderIndex
|
||||
while (i < endRenderIndex) indexes.push(i++)
|
||||
return indexes
|
||||
}, [startRenderIndex, endRenderIndex])
|
||||
|
||||
const mapStyles = React.useCallback(
|
||||
const mapStyles = useCallback(
|
||||
(row: number): React.CSSProperties => ({
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
|
|
@ -63,9 +63,9 @@ export function useVirtualScroll<E extends HTMLElement>({
|
|||
}),
|
||||
[rowHeight],
|
||||
)
|
||||
const memoizedStyler = React.useMemo(() => memoize(mapStyles, row => row), [mapStyles])
|
||||
const memoizedStyler = useMemo(() => memoize(mapStyles, row => row), [mapStyles])
|
||||
|
||||
const visibleRows: { row: number; style: React.CSSProperties }[] = React.useMemo(
|
||||
const visibleRows: { row: number; style: React.CSSProperties }[] = useMemo(
|
||||
() =>
|
||||
indexes.map(row => ({
|
||||
row,
|
||||
|
|
@ -74,7 +74,7 @@ export function useVirtualScroll<E extends HTMLElement>({
|
|||
[indexes, memoizedStyler],
|
||||
)
|
||||
|
||||
const containerStyle: React.CSSProperties = React.useMemo(
|
||||
const containerStyle: React.CSSProperties = useMemo(
|
||||
() => ({
|
||||
height: totalHeight,
|
||||
position: 'relative',
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import * as React from 'react'
|
||||
import { useContext, useEffect } from 'react'
|
||||
import { SidebarContext } from './SidebarContext'
|
||||
|
||||
export type FocusTarget = 'files' | 'search' | null
|
||||
|
||||
export function useFocusOnPendingTarget(target: FocusTarget, method: () => void) {
|
||||
const { pendingFocusTarget } = React.useContext(SidebarContext)
|
||||
React.useEffect(() => {
|
||||
const { pendingFocusTarget } = useContext(SidebarContext)
|
||||
useEffect(() => {
|
||||
if (pendingFocusTarget.value === target) {
|
||||
method()
|
||||
pendingFocusTarget.onChange(null)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { GearIcon, SyncIcon } from '@primer/octicons-react'
|
|||
import { Link } from '@primer/react'
|
||||
import { ReloadContext } from 'containers/ReloadContext'
|
||||
import { VERSION } from 'env'
|
||||
import * as React from 'react'
|
||||
import React, { useContext } from 'react'
|
||||
import { RoundIconButton } from './RoundIconButton'
|
||||
import { wikiLinks } from './settings/SettingsBar'
|
||||
|
||||
|
|
@ -12,7 +12,7 @@ type Props = {
|
|||
|
||||
export function Footer(props: Props) {
|
||||
const { toggleShowSettings } = props
|
||||
const reload = React.useContext(ReloadContext)
|
||||
const reload = useContext(ReloadContext)
|
||||
return (
|
||||
<div className={'gitako-footer'}>
|
||||
<div className="gitako-footer-section">
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { SideBar } from 'components/SideBar'
|
|||
import { ConfigsContextWrapper } from 'containers/ConfigsContext'
|
||||
import { InspectorContextWrapper } from 'containers/Inspector'
|
||||
import { ReloadContextWrapper } from 'containers/ReloadContext'
|
||||
import * as React from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import { StyleSheetManager } from 'styled-components'
|
||||
import { insertMountPoint } from 'utils/DOMHelper'
|
||||
import { ErrorBoundary } from '../containers/ErrorBoundary'
|
||||
|
|
@ -12,7 +12,7 @@ import { RepoContextWrapper } from '../containers/RepoContext'
|
|||
import { StateBarStateContextWrapper } from '../containers/SideBarState'
|
||||
|
||||
export function Gitako() {
|
||||
const mountPoint = React.useMemo(() => insertMountPoint(), [])
|
||||
const mountPoint = useMemo(() => insertMountPoint(), [])
|
||||
return (
|
||||
<StyleSheetManager target={mountPoint}>
|
||||
<ReloadContextWrapper>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import { getIsSupportedRegex } from './searchModes/regexMode'
|
||||
|
||||
export const Highlight = function Highlight({ text, match }: { text: string; match?: RegExp }) {
|
||||
const $match = React.useMemo(
|
||||
const $match = useMemo(
|
||||
() =>
|
||||
match instanceof RegExp
|
||||
? match.flags.includes('g')
|
||||
|
|
@ -12,7 +12,7 @@ export const Highlight = function Highlight({ text, match }: { text: string; mat
|
|||
[match],
|
||||
)
|
||||
|
||||
const chunks = React.useMemo(() => getChunks(text, $match), [text, $match])
|
||||
const chunks = useMemo(() => getChunks(text, $match), [text, $match])
|
||||
|
||||
return <>{chunks.map(([type, text], key) => React.createElement(type, { key }, text))}</>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
|
||||
export function HighlightOnIndexes({ text, indexes = [] }: { text: string; indexes?: number[] }) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -3,16 +3,15 @@ import {
|
|||
ChevronRightIcon as ChevronRight,
|
||||
ClockIcon as Clock,
|
||||
CommentIcon as Comment,
|
||||
DiffAddedIcon as DiffAdded,
|
||||
DiffIcon as Diff,
|
||||
DiffAddedIcon as DiffAdded,
|
||||
DiffIgnoredIcon as DiffIgnored,
|
||||
DiffModifiedIcon as DiffModified,
|
||||
DiffRemovedIcon as DiffRemoved,
|
||||
DiffRenamedIcon as DiffRenamed,
|
||||
FileCodeIcon as FileCode,
|
||||
FileIcon as File,
|
||||
FileCodeIcon as FileCode,
|
||||
FileMediaIcon as FileMedia,
|
||||
FileSubmoduleIcon as Submodule,
|
||||
FileZipIcon as FileZip,
|
||||
GearIcon as Gear,
|
||||
GrabberIcon as Grabber,
|
||||
|
|
@ -22,10 +21,11 @@ import {
|
|||
PinIcon as Pin,
|
||||
ReplyIcon as Reply,
|
||||
SearchIcon as Search,
|
||||
FileSubmoduleIcon as Submodule,
|
||||
TabIcon as Tab,
|
||||
XIcon as X,
|
||||
} from '@primer/octicons-react'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { cx } from 'utils/cx'
|
||||
|
||||
const iconToComponentMap = {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Checkbox as PrimerCheckbox, CheckboxProps, FormControl } from '@primer/react'
|
||||
import * as React from 'react'
|
||||
import { CheckboxProps, FormControl, Checkbox as PrimerCheckbox } from '@primer/react'
|
||||
import React from 'react'
|
||||
|
||||
export function Checkbox({
|
||||
label,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { FormControl, Select, SelectProps } from '@primer/react'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
|
||||
export type Option<T> = {
|
||||
key: string
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { HourglassIcon } from '@primer/octicons-react'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
|
||||
type Props = {
|
||||
text: React.ReactNode
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ import { GitBranchIcon } from '@primer/octicons-react'
|
|||
import { Box, BranchName, Breadcrumbs, Text } from '@primer/react'
|
||||
import { RepoContext } from 'containers/RepoContext'
|
||||
import { platform } from 'platforms'
|
||||
import * as React from 'react'
|
||||
import React, { useContext } from 'react'
|
||||
import { createAnchorClickHandler } from 'utils/createAnchorClickHandler'
|
||||
|
||||
export function MetaBar() {
|
||||
const metaData = React.useContext(RepoContext)
|
||||
const metaData = useContext(RepoContext)
|
||||
if (!metaData) return null
|
||||
|
||||
const { userName, repoName, branchName } = metaData
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import * as ReactDOM from 'react-dom'
|
||||
|
||||
type Props = {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { GrabberIcon } from '@primer/octicons-react'
|
||||
import { Icon } from 'components/Icon'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { ResizeHandlerOptions, useResizeHandler } from '../utils/hooks/useResizeHandler'
|
||||
import { Size2D } from './Size'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { AlertIcon, SearchIcon, XIcon } from '@primer/octicons-react'
|
||||
import { Popover, Text, TextInput, TextInputProps } from '@primer/react'
|
||||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import * as React from 'react'
|
||||
import React, { useCallback, useMemo, useRef } from 'react'
|
||||
import { formatWithShortcut, isValidRegexpSource } from 'utils/general'
|
||||
import { useFocusOnPendingTarget } from './FocusTarget'
|
||||
import { SearchMode } from './searchModes'
|
||||
|
|
@ -13,10 +13,10 @@ type Props = {
|
|||
} & Required<Pick<TextInputProps, 'onFocus'>>
|
||||
|
||||
export function SearchBar({ onSearch, onFocus, value }: Props) {
|
||||
const ref = React.useRef<HTMLInputElement | null>(null)
|
||||
const ref = useRef<HTMLInputElement | null>(null)
|
||||
useFocusOnPendingTarget(
|
||||
'search',
|
||||
React.useCallback(() => ref.current?.focus(), []),
|
||||
useCallback(() => ref.current?.focus(), []),
|
||||
)
|
||||
|
||||
const configs = useConfigs()
|
||||
|
|
@ -27,7 +27,7 @@ export function SearchBar({ onSearch, onFocus, value }: Props) {
|
|||
? 'Match file name with regular expression.'
|
||||
: `Match file path sequence with plain input.`
|
||||
|
||||
const isInputValid = React.useMemo(
|
||||
const isInputValid = useMemo(
|
||||
() =>
|
||||
({
|
||||
regex: isValidRegexpSource(value),
|
||||
|
|
@ -35,7 +35,7 @@ export function SearchBar({ onSearch, onFocus, value }: Props) {
|
|||
}[searchMode]),
|
||||
[value, searchMode],
|
||||
)
|
||||
const isSupportedRegex = React.useMemo(
|
||||
const isSupportedRegex = useMemo(
|
||||
() => !(searchMode === 'regex' && !getIsSupportedRegex(value)),
|
||||
[value, searchMode],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { Portal } from 'components/Portal'
|
|||
import { ToggleShowButton } from 'components/ToggleShowButton'
|
||||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import { platform, platformName } from 'platforms'
|
||||
import * as React from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { IIFC } from 'react-iifc'
|
||||
import { useWindowSize } from 'react-use'
|
||||
import { Config } from 'utils/config/helper'
|
||||
|
|
@ -50,9 +50,9 @@ export function SideBar() {
|
|||
|
||||
const configContext = useConfigs()
|
||||
|
||||
const blockLeaveRef = React.useRef(false)
|
||||
const blockLeaveRef = useRef(false)
|
||||
const { sidebarToggleMode, shortcut, focusSearchInputShortcut } = configContext.value
|
||||
const onResizeStateChange = React.useCallback((state: ResizeState) => {
|
||||
const onResizeStateChange = useCallback((state: ResizeState) => {
|
||||
blockLeaveRef.current = state === 'resizing'
|
||||
}, [])
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ export function SideBar() {
|
|||
() => useWindowSize().height, // eslint-disable-line react-hooks/rules-of-hooks
|
||||
)
|
||||
|
||||
const sidebarContextValue = React.useMemo(() => ({ pendingFocusTarget }), [pendingFocusTarget])
|
||||
const sidebarContextValue = useMemo(() => ({ pendingFocusTarget }), [pendingFocusTarget])
|
||||
|
||||
const placement = configContext.value.sidebarPlacement
|
||||
|
||||
|
|
@ -146,15 +146,12 @@ export function SideBar() {
|
|||
</div>
|
||||
<IIFC>
|
||||
{() => {
|
||||
const [showSettings, setShowSettings] = React.useState(false)
|
||||
const toggleShowSettings = React.useCallback(
|
||||
() => setShowSettings(show => !show),
|
||||
[],
|
||||
)
|
||||
const [showSettings, setShowSettings] = useState(false)
|
||||
const toggleShowSettings = useCallback(() => setShowSettings(show => !show), [])
|
||||
|
||||
useOnShortcutPressed(
|
||||
focusSearchInputShortcut,
|
||||
React.useCallback(() => setShowSettings(false), []),
|
||||
useCallback(() => setShowSettings(false), []),
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
@ -201,19 +198,19 @@ function ToggleShowButtonWrapper({
|
|||
}
|
||||
|
||||
function useFocusSidebarOnExpand(shouldExpand: boolean) {
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
// prevent keeping focus within Gitako
|
||||
if (!shouldExpand) document.body.focus()
|
||||
}, [shouldExpand])
|
||||
}
|
||||
|
||||
function useMarkGitakoGlobalAttributes() {
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const detach = DOMHelper.attachStickyGitakoPlatform()
|
||||
DOMHelper.markGitakoPlatform()
|
||||
return () => detach()
|
||||
}, [])
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const detach = DOMHelper.attachStickyGitakoReadyState()
|
||||
DOMHelper.markGitakoReadyState(true)
|
||||
return () => {
|
||||
|
|
@ -224,8 +221,8 @@ function useMarkGitakoGlobalAttributes() {
|
|||
}
|
||||
|
||||
function useLogoContainerElement() {
|
||||
const [logoContainerElement, setLogoContainerElement] = React.useState<HTMLElement | null>(null)
|
||||
React.useEffect(() => {
|
||||
const [logoContainerElement, setLogoContainerElement] = useState<HTMLElement | null>(null)
|
||||
useEffect(() => {
|
||||
setLogoContainerElement(DOMHelper.insertLogoMountPoint())
|
||||
}, [])
|
||||
return logoContainerElement
|
||||
|
|
@ -233,7 +230,7 @@ function useLogoContainerElement() {
|
|||
|
||||
function useUpdateBodyIndentOnStateUpdate(shouldExpand: boolean) {
|
||||
const { sidebarToggleMode, sidebarPlacement } = useConfigs().value
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!(sidebarToggleMode === 'persistent' && shouldExpand)) return
|
||||
|
||||
const detach = DOMHelper.attachStickyBodyIndent()
|
||||
|
|
@ -257,7 +254,7 @@ const getDerivedExpansion = ({
|
|||
|
||||
function useGetDerivedExpansion() {
|
||||
const { intelligentToggle, sidebarToggleMode } = useConfigs().value
|
||||
return React.useCallback(
|
||||
return useCallback(
|
||||
() => getDerivedExpansion({ intelligentToggle, sidebarToggleMode }),
|
||||
[intelligentToggle, sidebarToggleMode],
|
||||
)
|
||||
|
|
@ -266,7 +263,7 @@ function useGetDerivedExpansion() {
|
|||
function useUpdateBodyIndentAfterRedirect(update: (shouldExpand: boolean) => void) {
|
||||
const { intelligentToggle, sidebarToggleMode, sidebarPlacement } = useConfigs().value
|
||||
useAfterRedirect(
|
||||
React.useCallback(() => {
|
||||
useCallback(() => {
|
||||
// check and update expand state if pinned and auto-expand checked
|
||||
if (sidebarToggleMode === 'persistent') {
|
||||
const shouldExpand = getDerivedExpansion({ intelligentToggle, sidebarToggleMode })
|
||||
|
|
@ -282,7 +279,7 @@ function useUpdateBodyIndentAfterRedirect(update: (shouldExpand: boolean) => voi
|
|||
function useSaveExpandStateOnToggle(shouldExpand: boolean) {
|
||||
const configContext = useConfigs()
|
||||
const { intelligentToggle } = configContext.value
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (intelligentToggle !== null) configContext.onChange({ intelligentToggle: shouldExpand })
|
||||
}, [shouldExpand, intelligentToggle]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}
|
||||
|
|
@ -297,7 +294,7 @@ function useCollapseOnNoPermissionWhenTokenHasBeenSet(
|
|||
intelligentToggle === null &&
|
||||
!!accessToken &&
|
||||
state === 'error-due-to-auth'
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (hideSidebarOnInvalidToken) setShowSideBar(false)
|
||||
}, [hideSidebarOnInvalidToken, setShowSideBar])
|
||||
}
|
||||
|
|
@ -305,11 +302,8 @@ function useCollapseOnNoPermissionWhenTokenHasBeenSet(
|
|||
function useShouldExpand() {
|
||||
const getDerivedExpansion = useGetDerivedExpansion()
|
||||
const error = useLoadedContext(SideBarErrorContext).value
|
||||
const [shouldExpand, setShouldExpand] = React.useState(getDerivedExpansion)
|
||||
const toggleShowSideBar = React.useCallback(
|
||||
() => setShouldExpand(show => !show),
|
||||
[setShouldExpand],
|
||||
)
|
||||
const [shouldExpand, setShouldExpand] = useState(getDerivedExpansion)
|
||||
const toggleShowSideBar = useCallback(() => setShouldExpand(show => !show), [setShouldExpand])
|
||||
|
||||
const $shouldExpand = error ? false : shouldExpand
|
||||
|
||||
|
|
@ -331,7 +325,7 @@ function useShowSidebarKeyboard(
|
|||
|
||||
useOnShortcutPressed(
|
||||
config.shortcut,
|
||||
React.useCallback(
|
||||
useCallback(
|
||||
e => {
|
||||
DOMHelper.cancelEvent(e)
|
||||
toggleShowSideBar()
|
||||
|
|
@ -343,7 +337,7 @@ function useShowSidebarKeyboard(
|
|||
|
||||
useOnShortcutPressed(
|
||||
config.focusSearchInputShortcut,
|
||||
React.useCallback(
|
||||
useCallback(
|
||||
e => {
|
||||
DOMHelper.cancelEvent(e)
|
||||
if (!shouldExpand) setShouldExpand(true)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import * as React from 'react'
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react'
|
||||
import { useDebounce, useLatest, useWindowSize } from 'react-use'
|
||||
import { getDefaultConfigs } from 'utils/config/helper'
|
||||
import * as DOMHelper from 'utils/DOMHelper'
|
||||
|
|
@ -18,24 +18,24 @@ function useSidebarWidth() {
|
|||
// width => --gitako-width // layout effect
|
||||
// resize event => width
|
||||
// resize event => --gitako-width // rAF
|
||||
const [width, setWidth] = React.useState(configContext.value.sideBarWidth)
|
||||
const [width, setWidth] = useState(configContext.value.sideBarWidth)
|
||||
const { width: windowWidth } = useWindowSize()
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const safeSize = getSafeWidth(width, windowWidth)
|
||||
if (safeSize !== width) setWidth(safeSize)
|
||||
}, [windowWidth, width])
|
||||
useDebounce(() => configContext.onChange({ sideBarWidth: width }), 100, [width])
|
||||
|
||||
React.useLayoutEffect(() => DOMHelper.setGitakoWidthCSSVariable(width), [width])
|
||||
useLayoutEffect(() => DOMHelper.setGitakoWidthCSSVariable(width), [width])
|
||||
|
||||
const widthRef = useLatest(width)
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const detach = DOMHelper.attachStickyGitakoWidthCSSVariable(() => widthRef.current)
|
||||
return () => detach()
|
||||
}, [widthRef])
|
||||
|
||||
// Keep variable when directing from PR to repo home via meta bar
|
||||
useAfterRedirect(React.useCallback(() => DOMHelper.setGitakoWidthCSSVariable(width), [width]))
|
||||
useAfterRedirect(useCallback(() => DOMHelper.setGitakoWidthCSSVariable(width), [width]))
|
||||
|
||||
return [width, setWidth] as const
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ export function SideBarResizeHandler({
|
|||
}: Pick<ResizeHandlerOptions, 'onResizeStateChange'>) {
|
||||
const [width, setWidth] = useSidebarWidth()
|
||||
const { width: windowWidth } = useWindowSize()
|
||||
const onResize = React.useMemo(() => {
|
||||
const onResize = useMemo(() => {
|
||||
let widthToApply: Size
|
||||
let pending = false
|
||||
return ([width]: Size2D) => {
|
||||
|
|
@ -65,12 +65,9 @@ export function SideBarResizeHandler({
|
|||
}
|
||||
}, [windowWidth, setWidth])
|
||||
|
||||
const onResetSize = React.useCallback(
|
||||
() => setWidth(getDefaultConfigs().sideBarWidth),
|
||||
[setWidth],
|
||||
)
|
||||
const onResetSize = useCallback(() => setWidth(getDefaultConfigs().sideBarWidth), [setWidth])
|
||||
|
||||
const dummySize: Size2D = React.useMemo(() => [width, 0], [width])
|
||||
const dummySize: Size2D = useMemo(() => [width, 0], [width])
|
||||
|
||||
const placement = useConfigs().value.sidebarPlacement
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import iconURL from 'assets/icons/Gitako.png'
|
|||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import { SideBarErrorContext } from 'containers/ErrorContext'
|
||||
import { ReloadContext } from 'containers/ReloadContext'
|
||||
import * as React from 'react'
|
||||
import React, { useContext, useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
import { useDebounce, useWindowSize } from 'react-use'
|
||||
import { cx } from 'utils/cx'
|
||||
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
|
||||
|
|
@ -23,14 +23,14 @@ function getSafeDistance(y: number, height: number) {
|
|||
}
|
||||
|
||||
export function ToggleShowButton({ className, onClick, onHover }: Props) {
|
||||
const reload = React.useContext(ReloadContext)
|
||||
const reload = useContext(ReloadContext)
|
||||
const error = useLoadedContext(SideBarErrorContext).value
|
||||
|
||||
const ref = React.useRef<HTMLDivElement>(null)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const config = useConfigs()
|
||||
const [distance, setDistance] = React.useState(config.value.toggleButtonVerticalDistance)
|
||||
const [distance, setDistance] = useState(config.value.toggleButtonVerticalDistance)
|
||||
const { height } = useWindowSize()
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
// make sure it is inside viewport
|
||||
const safeDistance = getSafeDistance(distance, height)
|
||||
if (safeDistance !== distance) setDistance(safeDistance)
|
||||
|
|
@ -44,7 +44,7 @@ export function ToggleShowButton({ className, onClick, onHover }: Props) {
|
|||
)
|
||||
|
||||
// reposition on window height change, but ignores distance change
|
||||
React.useLayoutEffect(() => {
|
||||
useLayoutEffect(() => {
|
||||
if (ref.current) {
|
||||
ref.current.style.top = distance + 'px'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { cx } from 'utils/cx'
|
||||
import { hasUpperCase } from 'utils/general'
|
||||
import { ModeShape } from '.'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { cx } from 'utils/cx'
|
||||
import { searchKeyToRegexp } from 'utils/general'
|
||||
import { ModeShape } from '.'
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { SideBarStateContext } from 'containers/SideBarState'
|
|||
import { platform } from 'platforms'
|
||||
import { Gitea } from 'platforms/Gitea'
|
||||
import { Gitee } from 'platforms/Gitee'
|
||||
import * as React from 'react'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { IIFC } from 'react-iifc'
|
||||
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
|
||||
import { useStateIO } from 'utils/hooks/useStateIO'
|
||||
|
|
@ -17,19 +17,19 @@ export function AccessTokenSettings() {
|
|||
const configContext = useConfigs()
|
||||
const { accessToken } = configContext.value
|
||||
const hasAccessToken = Boolean(accessToken)
|
||||
const [accessTokenInputValue, setAccessTokenInputValue] = React.useState('')
|
||||
const [accessTokenInputValue, setAccessTokenInputValue] = useState('')
|
||||
const useAccessTokenHint = useStateIO<React.ReactNode>('')
|
||||
const focusInput = useStateIO(false)
|
||||
const sidebarState = useLoadedContext(SideBarStateContext).value
|
||||
|
||||
const { value: accessTokenHint } = useAccessTokenHint
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
// clear input when access token updates
|
||||
setAccessTokenInputValue('')
|
||||
}, [accessToken])
|
||||
|
||||
const saveToken = React.useCallback(
|
||||
const saveToken = useCallback(
|
||||
async (
|
||||
hint: typeof useAccessTokenHint.value = (
|
||||
<span>
|
||||
|
|
@ -73,16 +73,16 @@ export function AccessTokenSettings() {
|
|||
) : hasAccessToken ? (
|
||||
<IIFC>
|
||||
{() => {
|
||||
const [showConfirmButton, setShowConfirmButton] = React.useState(false)
|
||||
const [showConfirmButton, setShowConfirmButton] = useState(false)
|
||||
return (
|
||||
<Box>
|
||||
{showConfirmButton ? (
|
||||
<IIFC>
|
||||
{() => {
|
||||
const [allowClear, setAllowClear] = React.useState(false)
|
||||
const [allowClear, setAllowClear] = useState(false)
|
||||
const waitForSeconds = 3
|
||||
const timePast = useTimePast(1000, waitForSeconds * 1000)
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => setAllowClear(true), waitForSeconds * 1000)
|
||||
return () => clearTimeout(timeout)
|
||||
}, [])
|
||||
|
|
@ -168,8 +168,8 @@ export function AccessTokenSettings() {
|
|||
}
|
||||
|
||||
function useTimePast(unit = 1000, max?: number) {
|
||||
const [timePast, setTimePast] = React.useState(0)
|
||||
React.useEffect(() => {
|
||||
const [timePast, setTimePast] = useState(0)
|
||||
useEffect(() => {
|
||||
const checkInterval = (unit / 10) >> 0 // 10x check times for better accuracy
|
||||
const start = Date.now()
|
||||
let memoLastValue = 0
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { wikiLinks } from 'components/settings/SettingsBar'
|
||||
import { SimpleConfigFieldCheckbox } from 'components/settings/SimpleConfigField/Checkbox'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { Config } from 'utils/config/helper'
|
||||
import { Option } from '../Inputs/SelectInput'
|
||||
import { SettingsSection } from './SettingsSection'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Box, Button, FormControl, TextInput } from '@primer/react'
|
||||
import * as React from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useUpdateEffect } from 'react-use'
|
||||
import { cancelEvent } from 'utils/DOMHelper'
|
||||
import { friendlyFormatShortcut, noop } from 'utils/general'
|
||||
|
|
@ -15,7 +15,7 @@ export function KeyboardShortcutSetting({ label, value, onChange }: Props) {
|
|||
const $shortcut = useStateIO(value)
|
||||
useUpdateEffect(() => $shortcut.onChange(value), [value])
|
||||
|
||||
const id = React.useMemo(() => Math.random() + '', [])
|
||||
const id = useMemo(() => Math.random() + '', [])
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { RoundIconButton } from 'components/RoundIconButton'
|
|||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import { platform } from 'platforms'
|
||||
import { GitHub } from 'platforms/GitHub'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { useUpdateEffect } from 'react-use'
|
||||
import { useStateIO } from 'utils/hooks/useStateIO'
|
||||
import { AccessTokenSettings } from './AccessTokenSettings'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Box } from '@primer/react'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
|
||||
type Props = {
|
||||
title?: React.ReactNode
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { SimpleConfigFieldCheckbox } from 'components/settings/SimpleConfigField/Checkbox'
|
||||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { subIO } from 'utils/general'
|
||||
import { KeyboardShortcutSetting } from './KeyboardShortcutSetting'
|
||||
import { SettingsSection } from './SettingsSection'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { ConfigKeys } from 'utils/config/helper'
|
||||
import { SimpleConfigFieldProps, useSimpleConfigFieldIO } from '.'
|
||||
import { Checkbox } from '../../Inputs/Checkbox'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { InfoIcon, LinkExternalIcon } from '@primer/octicons-react'
|
||||
import { Box } from '@primer/react'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { ConfigKeys } from 'utils/config/helper'
|
||||
import { SimpleConfigField } from '.'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { SelectInput, SelectInputProps } from 'components/Inputs/SelectInput'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { Config, ConfigKeys } from 'utils/config/helper'
|
||||
import { SimpleConfigFieldProps, useSimpleConfigFieldIO } from '.'
|
||||
import { FieldLabel } from './FieldLabel'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import React from 'react'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { Config, ConfigKeys } from 'utils/config/helper'
|
||||
|
||||
export type SimpleConfigField<Key extends ConfigKeys> = {
|
||||
|
|
@ -26,8 +26,8 @@ export function useSimpleConfigFieldIO<Key extends ConfigKeys>(
|
|||
const value = configContext.value[field.key]
|
||||
|
||||
return {
|
||||
value: React.useMemo(() => (overwrite ? overwrite.value(value) : value), [overwrite, value]),
|
||||
onChange: React.useCallback(
|
||||
value: useMemo(() => (overwrite ? overwrite.value(value) : value), [overwrite, value]),
|
||||
onChange: useCallback(
|
||||
(newValue: Config[Key]) => {
|
||||
configContext.onChange({ [field.key]: overwrite ? overwrite.onChange(newValue) : newValue })
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { PropsWithChildren } from 'common'
|
||||
import * as React from 'react'
|
||||
import React, { useCallback, useContext, useEffect, useState } from 'react'
|
||||
import { Config, configHelper } from 'utils/config/helper'
|
||||
|
||||
type ContextShape = IO<Config, Partial<Config>>
|
||||
|
|
@ -8,11 +8,11 @@ export type ConfigsContextShape = ContextShape
|
|||
export const ConfigsContext = React.createContext<ContextShape | null>(null)
|
||||
|
||||
export function ConfigsContextWrapper(props: PropsWithChildren) {
|
||||
const [configs, setConfigs] = React.useState<Config | null>(null)
|
||||
React.useEffect(() => {
|
||||
const [configs, setConfigs] = useState<Config | null>(null)
|
||||
useEffect(() => {
|
||||
configHelper.get().then(setConfigs)
|
||||
}, [])
|
||||
const onChange = React.useCallback(
|
||||
const onChange = useCallback(
|
||||
(updatedConfigs: Partial<Config>) => {
|
||||
const mergedConfigs = { ...configs, ...updatedConfigs } as Config
|
||||
configHelper.set(mergedConfigs)
|
||||
|
|
@ -32,7 +32,7 @@ export const useConfigs = createUseNonNullContext(ConfigsContext)
|
|||
|
||||
function createUseNonNullContext<T>(theContext: React.Context<T | null>): () => T {
|
||||
return () => {
|
||||
const context = React.useContext(theContext)
|
||||
const context = useContext(theContext)
|
||||
if (context === null) throw new Error(`Empty context`)
|
||||
return context
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { raiseError } from 'analytics'
|
||||
import { PropsWithChildren } from 'common'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
|
||||
export class ErrorBoundary extends React.PureComponent<PropsWithChildren> {
|
||||
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { PropsWithChildren } from 'common'
|
||||
import { useInspector } from 'containers/Inspector'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { useStateIO } from 'utils/hooks/useStateIO'
|
||||
|
||||
export type SideBarErrorContextShape = IO<string | null>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { PropsWithChildren, ReactIO } from 'common'
|
||||
import { IN_PRODUCTION_MODE } from 'env'
|
||||
import * as React from 'react'
|
||||
import React, { useContext, useEffect } from 'react'
|
||||
import { Config } from 'utils/config/helper'
|
||||
import { noop } from 'utils/general'
|
||||
import { useStateIO } from 'utils/hooks/useStateIO'
|
||||
import { useConfigs } from './ConfigsContext'
|
||||
import { Config } from 'utils/config/helper'
|
||||
|
||||
export type InspectorContextShape = ReactIO<JSONObject>
|
||||
|
||||
|
|
@ -66,8 +66,8 @@ export const InspectorContextWrapper = IN_PRODUCTION_MODE
|
|||
export const useInspector = IN_PRODUCTION_MODE
|
||||
? noop
|
||||
: function useInspector(key: string, value: JSONValue) {
|
||||
const $ = React.useContext(InspectorContext)
|
||||
React.useEffect(() => {
|
||||
const $ = useContext(InspectorContext)
|
||||
useEffect(() => {
|
||||
$?.onChange(prev => ({ ...prev, [key]: value }))
|
||||
}, [key, value]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { PropsWithChildren } from 'common'
|
||||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import { platform } from 'platforms'
|
||||
import * as React from 'react'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { parseURLSearch, run } from 'utils/general'
|
||||
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
|
||||
import { useStateIO } from 'utils/hooks/useStateIO'
|
||||
|
|
@ -15,8 +15,8 @@ export function OAuthWrapper({ children }: PropsWithChildren) {
|
|||
const running = useGetAccessToken()
|
||||
const $state = useLoadedContext(SideBarStateContext)
|
||||
|
||||
const needGetAccessTokenRef = React.useRef(running)
|
||||
React.useEffect(() => {
|
||||
const needGetAccessTokenRef = useRef(running)
|
||||
useEffect(() => {
|
||||
if (needGetAccessTokenRef.current) {
|
||||
$state.onChange(running ? 'getting-access-token' : 'after-getting-access-token')
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ function useGetAccessToken() {
|
|||
const $block = useStateIO(() => Boolean(getCodeSearchParam()))
|
||||
const configContext = useConfigs()
|
||||
const { accessToken } = configContext.value
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
run(async function () {
|
||||
const code = getCodeSearchParam()
|
||||
if (code && !accessToken) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
|
||||
export type PortalContextShape = string | null
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { PropsWithChildren } from 'common'
|
||||
import * as React from 'react'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { noop } from 'utils/general'
|
||||
|
||||
export type ReloadContextShape = () => void
|
||||
|
|
@ -7,8 +7,8 @@ export type ReloadContextShape = () => void
|
|||
export const ReloadContext = React.createContext<ReloadContextShape>(noop)
|
||||
|
||||
export function ReloadContextWrapper({ children }: PropsWithChildren) {
|
||||
const [key, setKey] = React.useState(0)
|
||||
const reload = React.useCallback(() => setKey(key => key + 1), [])
|
||||
const [key, setKey] = useState(0)
|
||||
const reload = useCallback(() => setKey(key => key + 1), [])
|
||||
|
||||
return (
|
||||
<ReloadContext.Provider key={key} value={reload}>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { PropsWithChildren } from 'common'
|
||||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import { platform } from 'platforms'
|
||||
import * as React from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useAbortableEffect } from 'utils/hooks/useAbortableEffect'
|
||||
import { useEffectOnSerializableUpdates } from 'utils/hooks/useEffectOnSerializableUpdates'
|
||||
import { useAfterRedirect } from 'utils/hooks/useFastRedirect'
|
||||
import { useHandleNetworkError } from 'utils/hooks/useHandleNetworkError'
|
||||
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
|
||||
import { useStateIO } from 'utils/hooks/useStateIO'
|
||||
import { SideBarStateContext } from './SideBarState'
|
||||
import { useInspector } from './Inspector'
|
||||
import { SideBarStateContext } from './SideBarState'
|
||||
|
||||
export const RepoContext = React.createContext<MetaData | null>(null)
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ export function RepoContextWrapper({ children }: PropsWithChildren) {
|
|||
const metaData = useMetaData(partialMetaData)
|
||||
useInspector(
|
||||
'RepoContext',
|
||||
React.useMemo(
|
||||
useMemo(
|
||||
() => ({
|
||||
partialMetaData,
|
||||
metaData,
|
||||
|
|
@ -54,11 +54,11 @@ function usePartialMetaData(): PartialMetaData | null {
|
|||
// sync along URL and DOM
|
||||
const $partialMetaData = useStateIO(isGettingAccessToken ? null : resolvePartialMetaData)
|
||||
const $committedPartialMetaData = useStateIO($partialMetaData.value)
|
||||
const setPartialMetaData = React.useCallback(
|
||||
const setPartialMetaData = useCallback(
|
||||
() => $partialMetaData.onChange(resolvePartialMetaData()),
|
||||
[], // eslint-disable-line react-hooks/exhaustive-deps
|
||||
)
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!isGettingAccessToken) setPartialMetaData()
|
||||
}, [isGettingAccessToken, setPartialMetaData])
|
||||
useAfterRedirect(setPartialMetaData)
|
||||
|
|
@ -67,7 +67,7 @@ function usePartialMetaData(): PartialMetaData | null {
|
|||
JSON.stringify,
|
||||
$committedPartialMetaData.onChange,
|
||||
)
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!$partialMetaData.value && !isGettingAccessToken) {
|
||||
$state.onChange('disabled')
|
||||
}
|
||||
|
|
@ -76,12 +76,12 @@ function usePartialMetaData(): PartialMetaData | null {
|
|||
}
|
||||
|
||||
function useMetaData(partialMetaData: PartialMetaData | null) {
|
||||
const [metaData, changeMetaData] = React.useState<MetaData | null>(null)
|
||||
const [metaData, changeMetaData] = useState<MetaData | null>(null)
|
||||
const changeLoadedState = useLoadedContext(SideBarStateContext).onChange
|
||||
const handleNetworkError = useHandleNetworkError()
|
||||
|
||||
const { accessToken } = useConfigs().value
|
||||
const loadRepoMetaData = React.useCallback(
|
||||
const loadRepoMetaData = useCallback(
|
||||
async function* loadRepoMetaData() {
|
||||
if (!partialMetaData) return
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ function useMetaData(partialMetaData: PartialMetaData | null) {
|
|||
)
|
||||
|
||||
useAbortableEffect(
|
||||
React.useCallback(
|
||||
useCallback(
|
||||
() => ({
|
||||
getAsyncGenerator: loadRepoMetaData,
|
||||
cancel: () => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { PropsWithChildren } from 'common'
|
||||
import * as React from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
|
||||
import { useStateIO } from 'utils/hooks/useStateIO'
|
||||
import { SideBarErrorContext } from './ErrorContext'
|
||||
|
|
@ -26,7 +26,7 @@ export function StateBarStateContextWrapper({ children }: PropsWithChildren) {
|
|||
const $state = useStateIO<SideBarState>('disabled')
|
||||
useInspector('SideBarStateContext', $state.value)
|
||||
const error = useLoadedContext(SideBarErrorContext).value
|
||||
const $$state: IO<SideBarState> = React.useMemo(
|
||||
const $$state: IO<SideBarState> = useMemo(
|
||||
() =>
|
||||
error && $state.value !== 'error'
|
||||
? {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import primitives from '@primer/primitives'
|
|||
import { BaseStyles, ThemeProvider } from '@primer/react'
|
||||
import theme from '@primer/react/lib-esm/theme'
|
||||
import { PropsWithChildren } from 'common'
|
||||
import * as React from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
// Temporary color fix for out-of-date embedded @primer/primitives in @primer/react
|
||||
// The `*_tritanopia` themes are actually not bundled within @primer/react@35.2.2
|
||||
|
|
@ -17,7 +17,7 @@ const fixedTheme = {
|
|||
}
|
||||
|
||||
const validColorSchemes = Object.keys(fixedTheme.colorSchemes) as EnumString<
|
||||
keyof typeof primitives['colors']
|
||||
keyof (typeof primitives)['colors']
|
||||
>[]
|
||||
|
||||
const colorModeMap: Record<string, 'night' | 'day'> = {
|
||||
|
|
@ -39,8 +39,8 @@ const getPreferenceFromDOM = () => {
|
|||
}
|
||||
|
||||
function useThemePreference() {
|
||||
const [prefer, setPrefer] = React.useState(getPreferenceFromDOM)
|
||||
React.useEffect(() => {
|
||||
const [prefer, setPrefer] = useState(getPreferenceFromDOM)
|
||||
useEffect(() => {
|
||||
const match = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
const update = () => setPrefer(getPreferenceFromDOM)
|
||||
match.addEventListener('change', update)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Gitako } from 'components/Gitako'
|
||||
import * as React from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { insertMountPoint, insertSideBarMountPoint } from 'utils/DOMHelper'
|
||||
import { useAfterRedirect } from 'utils/hooks/useFastRedirect'
|
||||
|
|
@ -15,7 +15,7 @@ async function init() {
|
|||
await injectStyles(browser.runtime.getURL('content.css'))
|
||||
const mountPoint = insertSideBarMountPoint()
|
||||
const MountPointWatcher = () => {
|
||||
useAfterRedirect(React.useCallback(() => insertMountPoint(() => mountPoint), []))
|
||||
useAfterRedirect(useCallback(() => insertMountPoint(() => mountPoint), []))
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { cx } from 'utils/cx'
|
||||
import { copyElementContent } from 'utils/DOMHelper'
|
||||
import { getCodeElement } from './DOMHelper'
|
||||
|
|
@ -13,8 +13,8 @@ const contents = {
|
|||
}
|
||||
|
||||
export function CopyFileButton() {
|
||||
const [content, setContent] = React.useState(contents.normal)
|
||||
React.useEffect(() => {
|
||||
const [content, setContent] = useState(contents.normal)
|
||||
useEffect(() => {
|
||||
if (content !== contents.normal) {
|
||||
const timer = setTimeout(() => {
|
||||
setContent(contents.normal)
|
||||
|
|
@ -23,8 +23,8 @@ export function CopyFileButton() {
|
|||
}
|
||||
}, [content])
|
||||
|
||||
const elementRef = React.useRef<HTMLAnchorElement | null>(null)
|
||||
React.useEffect(() => {
|
||||
const elementRef = useRef<HTMLAnchorElement | null>(null)
|
||||
useEffect(() => {
|
||||
// Temporary fix:
|
||||
// React moved root node of event delegation since v17
|
||||
// onClick on <a /> won't work when rendered with `renderReact`
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { raiseError } from 'analytics'
|
||||
import { Clippy, ClippyClassName } from 'components/Clippy'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import * as s from 'superstruct'
|
||||
import { $ } from 'utils/$'
|
||||
import { formatClass, parseIntFromElement } from 'utils/DOMHelper'
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import { platform } from 'platforms'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { useAfterRedirect } from 'utils/hooks/useFastRedirect'
|
||||
import * as DOMHelper from '../DOMHelper'
|
||||
import { GitHub } from '../index'
|
||||
|
||||
export function useGitHubAttachCopySnippetButton(copySnippetButton: boolean) {
|
||||
const attachCopySnippetButton = React.useCallback(
|
||||
const attachCopySnippetButton = useCallback(
|
||||
function attachCopySnippetButton() {
|
||||
if (platform === GitHub && copySnippetButton) DOMHelper.attachCopySnippet()
|
||||
},
|
||||
[copySnippetButton],
|
||||
)
|
||||
React.useEffect(attachCopySnippetButton, [attachCopySnippetButton])
|
||||
useEffect(attachCopySnippetButton, [attachCopySnippetButton])
|
||||
useAfterRedirect(attachCopySnippetButton)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { raiseError } from 'analytics'
|
||||
import { Clippy, ClippyClassName } from 'components/Clippy'
|
||||
import * as React from 'react'
|
||||
import React from 'react'
|
||||
import { $ } from 'utils/$'
|
||||
import { formatClass } from 'utils/DOMHelper'
|
||||
import { renderReact } from 'utils/general'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { GITEE_OAUTH } from 'env'
|
||||
import { Base64 } from 'js-base64'
|
||||
import { errors, platform } from 'platforms'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { resolveGitModules } from 'utils/gitSubmodule'
|
||||
import { useAfterRedirect } from 'utils/hooks/useFastRedirect'
|
||||
import { useProgressBar } from 'utils/hooks/useProgressBar'
|
||||
|
|
@ -187,12 +187,12 @@ export const Gitee: Platform = {
|
|||
}
|
||||
|
||||
export function useGiteeAttachCopySnippetButton(copySnippetButton: boolean) {
|
||||
const attachCopySnippetButton = React.useCallback(
|
||||
const attachCopySnippetButton = useCallback(
|
||||
function attachCopySnippetButton() {
|
||||
if (platform === Gitee && copySnippetButton) DOMHelper.attachCopySnippet()
|
||||
},
|
||||
[copySnippetButton],
|
||||
)
|
||||
React.useEffect(attachCopySnippetButton, [attachCopySnippetButton])
|
||||
useEffect(attachCopySnippetButton, [attachCopySnippetButton])
|
||||
useAfterRedirect(attachCopySnippetButton)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { useState } from 'react'
|
||||
|
||||
export function useConditionalHook<T>(condition: () => boolean, hook: () => T) {
|
||||
const [use] = React.useState(condition)
|
||||
const [use] = useState(condition)
|
||||
if (use) return hook()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import * as React from 'react'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
|
||||
export function useEffectOnSerializableUpdates<T>(
|
||||
value: T,
|
||||
serialize: (value: T) => string,
|
||||
onChange: (value: T) => void,
|
||||
) {
|
||||
const serialized = React.useMemo(() => serialize(value), [value, serialize])
|
||||
React.useEffect(() => onChange(value), [onChange, serialized]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
const serialized = useMemo(() => serialize(value), [value, serialize])
|
||||
useEffect(() => onChange(value), [onChange, serialized]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import * as React from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import * as features from 'utils/features'
|
||||
import { Size2D } from '../../components/Size'
|
||||
|
||||
export function useElementSize<E extends HTMLElement>() {
|
||||
const ref = React.useRef<E | null>(null)
|
||||
const ref = useRef<E | null>(null)
|
||||
|
||||
const [size, setSize] = React.useState<Size2D>([0, 0])
|
||||
const [size, setSize] = useState<Size2D>([0, 0])
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
if (features.resize) {
|
||||
const observer = new window.ResizeObserver(entries => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import { platform } from 'platforms'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { useEvent, useInterval } from 'react-use'
|
||||
import { run } from 'utils/general'
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ const config: import('pjax-api').Config = {
|
|||
export function usePJAXAPI() {
|
||||
const { pjaxMode } = useConfigs().value
|
||||
// make history travel work
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (pjaxMode === 'pjax-api') {
|
||||
run(async () => {
|
||||
const { Pjax } = await import('pjax-api')
|
||||
|
|
@ -52,8 +52,8 @@ export const loadWithFastRedirect = (url: string, element: HTMLElement) => {
|
|||
}
|
||||
|
||||
export function useAfterRedirect(callback: () => void) {
|
||||
const latestHref = React.useRef(location.href)
|
||||
const raceCallback = React.useCallback(() => {
|
||||
const latestHref = useRef(location.href)
|
||||
const raceCallback = useCallback(() => {
|
||||
const { href } = location
|
||||
if (latestHref.current !== href) {
|
||||
latestHref.current = href
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import React, { useContext } from 'react'
|
||||
|
||||
export function useLoadedContext<T>(context: React.Context<T | null>): T {
|
||||
const ctx = React.useContext(context)
|
||||
const ctx = useContext(context)
|
||||
if (ctx === null) throw new Error(`Context not loaded`)
|
||||
return ctx
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useLocation } from 'react-use'
|
||||
|
||||
export function useOnLocationChange(
|
||||
|
|
@ -6,5 +6,5 @@ export function useOnLocationChange(
|
|||
extraDeps: React.DependencyList = [],
|
||||
) {
|
||||
const { href, pathname, search } = useLocation()
|
||||
React.useEffect(callback, [href, pathname, search, callback, ...extraDeps])
|
||||
useEffect(callback, [href, pathname, search, callback, ...extraDeps])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
|
||||
import * as keyHelper from 'utils/keyHelper'
|
||||
import { SideBarStateContext } from '../../containers/SideBarState'
|
||||
|
|
@ -9,7 +9,7 @@ export function useOnShortcutPressed(
|
|||
) {
|
||||
const state = useLoadedContext(SideBarStateContext).value
|
||||
const isDisabled = state === 'disabled' || !shortcut
|
||||
React.useEffect(
|
||||
useEffect(
|
||||
function attachKeyDown() {
|
||||
if (isDisabled) return
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import * as NProgress from 'nprogress'
|
||||
import * as React from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { useEvent } from 'react-use'
|
||||
|
||||
const progressBar = {
|
||||
|
|
@ -12,7 +12,7 @@ const progressBar = {
|
|||
}
|
||||
|
||||
export function useProgressBar() {
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
NProgress.configure({ showSpinner: false })
|
||||
}, [])
|
||||
useEvent('pjax:fetch', progressBar.mount, window)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import React, { useCallback, useEffect, useRef } from 'react'
|
||||
import { Size2D } from '../../components/Size'
|
||||
|
||||
export type ResizeState = 'idle' | 'resizing'
|
||||
|
|
@ -20,21 +20,21 @@ export function useResizeHandler(
|
|||
direction = 'right',
|
||||
}: ResizeHandlerOptions = {},
|
||||
) {
|
||||
const pointerDown = React.useRef(false)
|
||||
const pointerMoved = React.useRef(false)
|
||||
const initialSizeRef = React.useRef([0, 0])
|
||||
const baseSize = React.useRef(size)
|
||||
const latestPropSize = React.useRef(size)
|
||||
const pointerDown = useRef(false)
|
||||
const pointerMoved = useRef(false)
|
||||
const initialSizeRef = useRef([0, 0])
|
||||
const baseSize = useRef(size)
|
||||
const latestPropSize = useRef(size)
|
||||
const fix = {
|
||||
left: -1,
|
||||
right: 1,
|
||||
}[direction]
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
latestPropSize.current = size
|
||||
}, [size])
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const onPointerMove = ({ clientX, clientY }: PointerEvent) => {
|
||||
if (!pointerDown.current) return
|
||||
const [x0, y0] = initialSizeRef.current
|
||||
|
|
@ -48,7 +48,7 @@ export function useResizeHandler(
|
|||
return () => window.removeEventListener('pointermove', onPointerMove)
|
||||
}, [onResize, distanceTolerance, fix])
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const onPointerUp = (e: PointerEvent) => {
|
||||
if (pointerDown.current) {
|
||||
pointerDown.current = false
|
||||
|
|
@ -64,7 +64,7 @@ export function useResizeHandler(
|
|||
return () => window.removeEventListener('pointerup', onPointerUp)
|
||||
}, [onClick, onResizeStateChange])
|
||||
|
||||
const onPointerDown = React.useCallback(
|
||||
const onPointerDown = useCallback(
|
||||
(e: React.PointerEvent) => {
|
||||
e.preventDefault() // Prevent unexpected selection when dragging in Safari
|
||||
const { clientX, clientY } = e
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import * as React from 'react'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
|
||||
export function useStateIO<S>(initialState: S | (() => S)): {
|
||||
value: S
|
||||
onChange: React.Dispatch<React.SetStateAction<S>>
|
||||
} {
|
||||
const [value, onChange] = React.useState(initialState)
|
||||
return React.useMemo(() => ({ value, onChange }), [value])
|
||||
const [value, onChange] = useState(initialState)
|
||||
return useMemo(() => ({ value, onChange }), [value])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { IN_PRODUCTION_MODE } from 'env'
|
||||
import * as React from 'react'
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
export function useUpdateReason<P extends Record<string, unknown>>(props: P) {
|
||||
const lastPropsRef = React.useRef<P>(props)
|
||||
React.useEffect(() => {
|
||||
const lastPropsRef = useRef<P>(props)
|
||||
useEffect(() => {
|
||||
if (IN_PRODUCTION_MODE) return
|
||||
const output: ([string, keyof P, P[keyof P]] | [string, keyof P, P[keyof P], P[keyof P]])[] = []
|
||||
for (const key of Object.keys(props)) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue