diff --git a/src/driver/connect.ts b/src/driver/connect.ts index abafee3..89ccde3 100644 --- a/src/driver/connect.ts +++ b/src/driver/connect.ts @@ -1,16 +1,9 @@ import * as React from 'react' -export type ParametersOfReturnedFunction = Func extends (( - ...args1: any[] // it's ok -) => (...args2: Args) => any) // it's ok - ? Args - : never - -export type Method = (...args: Args) => void | Promise -type Args = any[] // it's ok +export type Method = (...args: Args extends any[] ? Args : []) => void | Promise export type Middleware = ( method: M, - args: Parameters + args: Parameters, ) => [MM | M, Parameters] const middlewares: Middleware[] = [] @@ -40,11 +33,11 @@ function run([method, args]: [M, Parameters]) { export type DispatchState = React.Component['setState'] export type PreDispatch = ( dispatchCallback: (state: State, props: Props) => Props | Promise | void, - callback?: () => void + callback?: () => void, ) => void -export type TriggerOtherMethod = >( +export type TriggerOtherMethod = >( methodCreator: MC, - ...args: ParametersOfReturnedFunction + ...args: Parameters> ) => void export type Dispatch = { @@ -53,20 +46,25 @@ export type Dispatch = { call: TriggerOtherMethod } -export type MethodCreator = (dispatch: Dispatch) => Method +export type MethodCreator = ( + dispatch: Dispatch, +) => Method -type Sources = { - [key: string]: MethodCreator +type Sources = { + [key: string]: MethodCreator } type WrappedMethods = { [key: string]: Method } -function link(instance: React.Component, sources: Sources): WrappedMethods { +function link(instance: React.Component, sources: Sources): WrappedMethods { const wrappedMethods: WrappedMethods = { /* [keyof sources] -> wrappedMethods.method */ } - const map = new Map, Method>(/* sources.creator -> wrappedMethods.method */) + const map = new Map< + MethodCreator, + Method + >(/* sources.creator -> wrappedMethods.method */) const dispatchCall: TriggerOtherMethod = (createMethod, ...otherArgs) => { const isFromSource = sourcesValues.includes(createMethod) @@ -99,9 +97,9 @@ function link(instance: React.Component, sources: Sources): WrappedM return wrappedMethods } -export default function connect(mapping: Sources) { +export default function connect(mapping: Sources) { return function linkComponent( - ComponentClass: React.ComponentClass + ComponentClass: React.ComponentClass, ): React.ComponentClass { return class AwesomeApp extends React.PureComponent { static displayName = `Connected(${ComponentClass.displayName || ComponentClass.name})` diff --git a/src/driver/core/FileExplorer.ts b/src/driver/core/FileExplorer.ts index 461ae93..06a5adf 100644 --- a/src/driver/core/FileExplorer.ts +++ b/src/driver/core/FileExplorer.ts @@ -39,7 +39,8 @@ type Task = () => void const tasksAfterRender: (Task)[] = [] const visibleNodesGenerator = new VisibleNodesGenerator() -const init: MethodCreator = dispatch => () => dispatch.call(setStateText, 'Fetching File List...') +const init: MethodCreator = dispatch => () => + dispatch.call(setStateText, 'Fetching File List...') function resolveGitModules(root: TreeNode, blobData: BlobData) { if (blobData) { @@ -79,7 +80,7 @@ const setUpTree: MethodCreator = dispatch => () => await visibleNodesGenerator.plantTree(root as TreeNode) tasksAfterRender.push(DOMHelper.focusSearchInput) - dispatch.call(setStateText, null) + dispatch.call(setStateText, '') const currentPath = URLHelper.getCurrentPath(metaData.branchName) if (currentPath.length) { const nodeExpandedTo = visibleNodesGenerator.expandTo(currentPath.join('/')) @@ -99,12 +100,20 @@ const execAfterRender: MethodCreator = dispatch => () => tasksAfterRender.length = 0 } -const setStateText: MethodCreator = dispatch => (text: string) => +const setStateText: MethodCreator< + Props, + ConnectorState, + [ConnectorState['stateText']] +> = dispatch => (text: string) => dispatch.set({ stateText: text, }) -const handleKeyDown: MethodCreator = dispatch => event => +const handleKeyDown: MethodCreator< + Props, + ConnectorState, + [React.KeyboardEvent] +> = dispatch => event => dispatch.get(({ visibleNodes: { nodes, focusedNode, expandedNodes, depths } }) => { function handleVerticalMove(index: number) { if (0 <= index && index < nodes.length) { @@ -201,12 +210,17 @@ const handleKeyDown: MethodCreator = dispatch => event => } }) -const onFocusSearchBar: MethodCreator = dispatch => () => dispatch.call(focusNode, null) +const onFocusSearchBar: MethodCreator = dispatch => () => + dispatch.call(focusNode, null) -const handleSearchKeyChange: MethodCreator = dispatch => { +const handleSearchKeyChange: MethodCreator< + Props, + ConnectorState, + [React.FormEvent] +> = dispatch => { let i = 0 return async event => { - const searchKey = event.target.value + const searchKey = event.currentTarget.value const j = (i += 1) await visibleNodesGenerator.search(searchKey) if (i === j) dispatch.call(updateVisibleNodes) @@ -221,7 +235,10 @@ function shouldDelayExpand(node: TreeNode) { ) } -const setExpand: MethodCreator = dispatch => (node, expand) => { +const setExpand: MethodCreator = dispatch => ( + node, + expand = false +) => { visibleNodesGenerator.setExpand(node, expand) const applyChanges = () => dispatch.call(focusNode, node) if (shouldDelayExpand(node)) { @@ -232,7 +249,10 @@ const setExpand: MethodCreator = dispatch => (node, expan } } -const toggleNodeExpansion: MethodCreator = dispatch => (node, skipScrollToNode) => { +const toggleNodeExpansion: MethodCreator = dispatch => ( + node, + skipScrollToNode +) => { visibleNodesGenerator.toggleExpand(node) const applyChanges = () => { dispatch.call(focusNode, node, skipScrollToNode) @@ -246,7 +266,11 @@ const toggleNodeExpansion: MethodCreator = dispatch => (n } } -const focusNode: MethodCreator = dispatch => (node, skipScroll) => +const focusNode: MethodCreator< + Props, + ConnectorState, + [TreeNode] | [TreeNode, boolean] +> = dispatch => (node: TreeNode, skipScroll = false) => dispatch.get(({ visibleNodes: { nodes } }) => { visibleNodesGenerator.focusNode(node) if (node && !skipScroll) { @@ -257,7 +281,7 @@ const focusNode: MethodCreator = dispatch => (node, skipS dispatch.call(updateVisibleNodes) }) -const onNodeClick: MethodCreator = dispatch => node => { +const onNodeClick: MethodCreator = dispatch => node => { if (node.type === 'tree') { dispatch.call(toggleNodeExpansion, node, true) } else if (node.type === 'blob') { @@ -268,7 +292,11 @@ const onNodeClick: MethodCreator = dispatch => node => { } } -const mountExpandingIndicator: MethodCreator = dispatch => node => +const mountExpandingIndicator: MethodCreator< + Props, + ConnectorState, + [TreeNode] +> = dispatch => node => dispatch.get(({ visibleNodes }) => { const dummyVisibleNodes = { ...visibleNodes, diff --git a/src/driver/core/SideBar.ts b/src/driver/core/SideBar.ts index b9a06ea..02a601a 100644 --- a/src/driver/core/SideBar.ts +++ b/src/driver/core/SideBar.ts @@ -44,7 +44,7 @@ export type ConnectorState = { toggleShowSideBar: () => void toggleShowSettings: () => void onAccessTokenChange: SettingsBar['props']['onAccessTokenChange'] - onKeyDown: EventListener + onKeyDown: (e: KeyboardEvent) => string onShortcutChange: SettingsBar['props']['onShortcutChange'] setCopyFile: SettingsBar['props']['setCopyFile'] setCopySnippet: SettingsBar['props']['setCopySnippet'] @@ -134,7 +134,7 @@ const init: MethodCreator = dispatch => async () => { } } -const handleError: MethodCreator = dispatch => async err => { +const handleError: MethodCreator = dispatch => async err => { if (err.message === EMPTY_PROJECT) { dispatch.call(setError, 'This project seems to be empty.') } else if ( @@ -160,7 +160,7 @@ const onPJAXEnd: MethodCreator = dispatch => () => { }) } -const onKeyDown: MethodCreator = dispatch => e => { +const onKeyDown: MethodCreator = dispatch => e => { dispatch.get(({ toggleShowSideBarShortcut }) => { if (toggleShowSideBarShortcut) { const keys = keyHelper.parseEvent(e) @@ -172,16 +172,22 @@ const onKeyDown: MethodCreator = dispatch => e => { } const toggleShowSideBar: MethodCreator = dispatch => () => - dispatch.get(({ shouldShow }) => - dispatch.call(setShouldShow, !shouldShow) - ) + dispatch.get(({ shouldShow }) => dispatch.call(setShouldShow, !shouldShow)) -const setShouldShow: MethodCreator = dispatch => shouldShow => { +const setShouldShow: MethodCreator< + Props, + ConnectorState, + [ConnectorState['shouldShow']] +> = dispatch => shouldShow => { dispatch.set({ shouldShow }, shouldShow ? DOMHelper.focusFileExplorer : null) DOMHelper.setBodyIndent(shouldShow) } -const setError: MethodCreator = dispatch => error => { +const setError: MethodCreator< + Props, + ConnectorState, + [ConnectorState['error']] +> = dispatch => error => { dispatch.set({ error }) dispatch.call(setShouldShow, false) } @@ -191,22 +197,47 @@ const toggleShowSettings: MethodCreator = dispatch => () showSettings: !showSettings, })) -const setShowSettings: MethodCreator = dispatch => showSettings => dispatch.set({ showSettings }) +const setShowSettings: MethodCreator< + Props, + ConnectorState, + [ConnectorState['showSettings']] +> = dispatch => showSettings => dispatch.set({ showSettings }) -const onAccessTokenChange: MethodCreator = dispatch => accessToken => dispatch.set({ accessToken }) +const onAccessTokenChange: MethodCreator< + Props, + ConnectorState, + [ConnectorState['accessToken']] +> = dispatch => accessToken => dispatch.set({ accessToken }) -const onShortcutChange: MethodCreator = dispatch => shortcut => - dispatch.set({ toggleShowSideBarShortcut: shortcut }) +const onShortcutChange: MethodCreator< + Props, + ConnectorState, + [ConnectorState['toggleShowSideBarShortcut']] +> = dispatch => shortcut => dispatch.set({ toggleShowSideBarShortcut: shortcut }) -const setMetaData: MethodCreator = dispatch => metaData => dispatch.set({ metaData }) +const setMetaData: MethodCreator< + Props, + ConnectorState, + [ConnectorState['metaData']] +> = dispatch => metaData => dispatch.set({ metaData }) -const setCompressSingleton: MethodCreator = dispatch => compressSingletonFolder => - dispatch.set({ compressSingletonFolder }) +const setCompressSingleton: MethodCreator< + Props, + ConnectorState, + [ConnectorState['compressSingletonFolder']] +> = dispatch => compressSingletonFolder => dispatch.set({ compressSingletonFolder }) -const setCopyFile: MethodCreator = dispatch => copyFileButton => dispatch.set({ copyFileButton }) +const setCopyFile: MethodCreator< + Props, + ConnectorState, + [ConnectorState['copyFileButton']] +> = dispatch => copyFileButton => dispatch.set({ copyFileButton }) -const setCopySnippet: MethodCreator = dispatch => copySnippetButton => - dispatch.set({ copySnippetButton }) +const setCopySnippet: MethodCreator< + Props, + ConnectorState, + [ConnectorState['copySnippetButton']] +> = dispatch => copySnippetButton => dispatch.set({ copySnippetButton }) export default { init, diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 686d0d0..db08765 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -28,7 +28,11 @@ function setBodyIndent(shouldShowGitako: boolean) { } } -function $(selector: string, existCallback?: (element: Element) => any, otherwise?: () => any) { +function $( + selector: string, + existCallback?: (element: Element) => R1, + otherwise?: () => R2, +): R1 | R2 | Element { const element = document.querySelector(selector) if (element) { return existCallback ? existCallback(element) : element @@ -52,14 +56,14 @@ function getCurrentBranch() { '.repository-content > .file-navigation > .branch-select-menu > button' const branchNameFromButtonElement = $( selectedBranchButtonSelector, - (element: HTMLButtonElement) => element.title.trim() + (element: HTMLButtonElement) => element.title.trim(), ) if (branchNameFromButtonElement) return branchNameFromButtonElement const selectedBranchSelector = '.select-menu.branch-select-menu .select-menu-modal .select-menu-list .select-menu-item.selected svg.select-menu-item-icon + span' const branchNameFromSelectElement = $(selectedBranchSelector, element => - element.textContent.trim() + element.textContent.trim(), ) if (branchNameFromSelectElement) return branchNameFromSelectElement } @@ -299,7 +303,7 @@ function attachCopySnippet() { target.parentNode.insertBefore(clippy, target) } } - }) + }), ) } diff --git a/src/utils/keyHelper.ts b/src/utils/keyHelper.ts index e43b60d..b048c07 100644 --- a/src/utils/keyHelper.ts +++ b/src/utils/keyHelper.ts @@ -1,5 +1,3 @@ -import { KeyboardEvent } from 'react' - const keyCodeArray = [ ...'1234567890abcdefghijklmnopqrstuvwxyz'.split(''), ..."`[]\\;',./".split(''), @@ -38,7 +36,7 @@ function parseKeyCode(code: string) { return code.toLowerCase().replace(/^control$/, 'ctrl') } -function parseEvent(e: KeyboardEvent) { +function parseEvent(e: KeyboardEvent | React.KeyboardEvent) { const { altKey: alt, shiftKey: shift, metaKey: meta, ctrlKey: ctrl } = e const code = parseKeyCode(e.key) const keys = { meta, ctrl, shift, alt, [code]: true }