mirror of
https://github.com/EnixCoda/Gitako.git
synced 2026-03-11 08:54:44 +00:00
fix: you can never be too cautious about types
This commit is contained in:
parent
9ea8b7614a
commit
764cddcd2f
9 changed files with 56 additions and 39 deletions
|
|
@ -10,9 +10,9 @@ export function raiseError(error: Error) {
|
|||
|
||||
export const withErrorLog: Middleware = function withErrorLog(method, args) {
|
||||
return [
|
||||
async function() {
|
||||
async function(...args: any[]) {
|
||||
try {
|
||||
await method.apply(this, arguments)
|
||||
await method.apply(null, args)
|
||||
} catch (error) {
|
||||
raiseError(error)
|
||||
}
|
||||
|
|
@ -21,9 +21,13 @@ export const withErrorLog: Middleware = function withErrorLog(method, args) {
|
|||
]
|
||||
}
|
||||
|
||||
function encodeParams(params: object) {
|
||||
function encodeParams<
|
||||
T extends {
|
||||
[key: string]: any
|
||||
}
|
||||
>(params: T) {
|
||||
return Object.keys(params)
|
||||
.map((key: keyof object) => `${key}=${encodeURIComponent(JSON.stringify(params[key]))}`)
|
||||
.map((key: keyof T) => `${key}=${encodeURIComponent(JSON.stringify(params[key]))}`)
|
||||
.join('&')
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,16 +14,13 @@ export type Props = {
|
|||
metaData: MetaData
|
||||
freeze: boolean
|
||||
compressSingletonFolder: boolean
|
||||
accessToken: string
|
||||
accessToken: string | undefined
|
||||
toggleShowSettings: React.MouseEventHandler
|
||||
}
|
||||
|
||||
class FileExplorer extends React.Component<Props & ConnectorState> {
|
||||
static defaultProps: Partial<Props & ConnectorState> = {
|
||||
treeData: null,
|
||||
metaData: null,
|
||||
freeze: false,
|
||||
visibleNodes: null,
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
|
|
@ -60,7 +57,7 @@ class FileExplorer extends React.Component<Props & ConnectorState> {
|
|||
<Node
|
||||
key={node.path}
|
||||
node={node}
|
||||
depth={depths.get(node)}
|
||||
depth={depths.get(node) || 0}
|
||||
focused={focusedNode === node}
|
||||
expanded={expandedNodes.has(node)}
|
||||
onClick={onNodeClick}
|
||||
|
|
@ -86,7 +83,7 @@ class FileExplorer extends React.Component<Props & ConnectorState> {
|
|||
className={cx(`file-explorer`, { freeze })}
|
||||
tabIndex={-1}
|
||||
onKeyDown={handleKeyDown}
|
||||
onClick={freeze ? toggleShowSettings : null}
|
||||
onClick={freeze ? toggleShowSettings : undefined}
|
||||
>
|
||||
{stateText ? (
|
||||
<LoadingIndicator text={stateText} />
|
||||
|
|
|
|||
|
|
@ -80,7 +80,8 @@ class Gitako extends React.PureComponent<Props & ConnectorState> {
|
|||
{metaData && <MetaBar metaData={metaData} />}
|
||||
{errorDueToAuth
|
||||
? this.renderAccessDeniedError()
|
||||
: metaData && (
|
||||
: metaData &&
|
||||
treeData && (
|
||||
<FileExplorer
|
||||
compressSingletonFolder={compressSingletonFolder}
|
||||
toggleShowSettings={toggleShowSettings}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,9 @@ export type Dispatch<Props, State> = {
|
|||
call: TriggerOtherMethod<Props, State>
|
||||
}
|
||||
|
||||
export type MethodCreator<Props, State, Args> = (dispatch: Dispatch<Props, State>) => Method<Args>
|
||||
export type MethodCreator<Props, State, Args = []> = (
|
||||
dispatch: Dispatch<Props, State>,
|
||||
) => Method<Args>
|
||||
|
||||
type Sources<P, S> = {
|
||||
[key: string]: MethodCreator<P, S, any>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,12 @@ function getVisibleParentNode(nodes: TreeNode[], focusedNode: TreeNode, depths:
|
|||
const focusedNodeIndex = nodes.indexOf(focusedNode)
|
||||
const focusedNodeDepth = depths.get(focusedNode)
|
||||
let indexOfParentNode = focusedNodeIndex - 1
|
||||
while (indexOfParentNode !== -1 && depths.get(nodes[indexOfParentNode]) >= focusedNodeDepth) {
|
||||
let depth: number | undefined
|
||||
while (indexOfParentNode !== -1) {
|
||||
depth = depths.get(nodes[indexOfParentNode])
|
||||
if (depth === undefined || focusedNodeDepth === undefined || !(depth >= focusedNodeDepth)) {
|
||||
break
|
||||
}
|
||||
--indexOfParentNode
|
||||
}
|
||||
const parentNode = nodes[indexOfParentNode]
|
||||
|
|
@ -44,13 +49,13 @@ const init: MethodCreator<Props, ConnectorState> = dispatch => () =>
|
|||
|
||||
function resolveGitModules(root: TreeNode, blobData: BlobData) {
|
||||
if (blobData) {
|
||||
if (blobData.encoding === 'base64') {
|
||||
if (blobData.encoding === 'base64' && blobData.content && Array.isArray(root.contents)) {
|
||||
const content = atob(blobData.content)
|
||||
const parsed = ini.parse(content)
|
||||
Object.values(parsed).map((value: { url: string; path: string }) => {
|
||||
const { url, path } = value
|
||||
// for now, handle modules at root only
|
||||
const node = root.contents.find(node => node.path === path)
|
||||
const node = Array.isArray(root.contents) && root.contents.find(node => node.path === path)
|
||||
if (node) {
|
||||
node.url = url
|
||||
}
|
||||
|
|
@ -66,6 +71,7 @@ const setUpTree: MethodCreator<Props, ConnectorState> = dispatch => () =>
|
|||
const { root, gitModules } = treeParser.parse(treeData, metaData)
|
||||
|
||||
if (gitModules) {
|
||||
if (metaData.userName && metaData.repoName && gitModules.sha) {
|
||||
const blobData = await GitHubHelper.getBlobData({
|
||||
userName: metaData.userName,
|
||||
repoName: metaData.repoName,
|
||||
|
|
@ -160,14 +166,16 @@ const handleKeyDown: MethodCreator<
|
|||
if (focusedNode.type === 'tree') {
|
||||
if (expandedNodes.has(focusedNode)) {
|
||||
const nextNode = nodes[focusedNodeIndex + 1]
|
||||
if (depths.get(nextNode) > depths.get(focusedNode)) {
|
||||
const d1 = depths.get(nextNode)
|
||||
const d2 = depths.get(focusedNode)
|
||||
if (d1 !== undefined && d2 !== undefined && d1 > d2) {
|
||||
dispatch.call(focusNode, nextNode, false)
|
||||
}
|
||||
} else {
|
||||
dispatch.call(setExpand, focusedNode, true)
|
||||
}
|
||||
} else if (focusedNode.type === 'blob') {
|
||||
DOMHelper.loadWithPJAX(focusedNode.url)
|
||||
if (focusedNode.url) DOMHelper.loadWithPJAX(focusedNode.url)
|
||||
} else if (focusedNode.type === 'commit') {
|
||||
window.open(focusedNode.url)
|
||||
}
|
||||
|
|
@ -177,7 +185,7 @@ const handleKeyDown: MethodCreator<
|
|||
if (focusedNode.type === 'tree') {
|
||||
dispatch.call(setExpand, focusedNode, true)
|
||||
} else if (focusedNode.type === 'blob') {
|
||||
DOMHelper.loadWithPJAX(focusedNode.url)
|
||||
if (focusedNode.url) DOMHelper.loadWithPJAX(focusedNode.url)
|
||||
} else if (focusedNode.type === 'commit') {
|
||||
window.open(focusedNode.url)
|
||||
}
|
||||
|
|
@ -231,13 +239,14 @@ const delayExpandThreshold = 400
|
|||
function shouldDelayExpand(node: TreeNode) {
|
||||
return (
|
||||
visibleNodesGenerator.visibleNodes.expandedNodes.has(node) &&
|
||||
Array.isArray(node.contents) &&
|
||||
node.contents.length > delayExpandThreshold
|
||||
)
|
||||
}
|
||||
|
||||
const setExpand: MethodCreator<Props, ConnectorState, [TreeNode, boolean]> = dispatch => (
|
||||
node,
|
||||
expand = false
|
||||
expand = false,
|
||||
) => {
|
||||
visibleNodesGenerator.setExpand(node, expand)
|
||||
const applyChanges = () => dispatch.call(focusNode, node, false)
|
||||
|
|
@ -251,7 +260,7 @@ const setExpand: MethodCreator<Props, ConnectorState, [TreeNode, boolean]> = dis
|
|||
|
||||
const toggleNodeExpansion: MethodCreator<Props, ConnectorState, [TreeNode, boolean]> = dispatch => (
|
||||
node,
|
||||
skipScrollToNode
|
||||
skipScrollToNode,
|
||||
) => {
|
||||
visibleNodesGenerator.toggleExpand(node)
|
||||
const applyChanges = () => {
|
||||
|
|
@ -285,7 +294,7 @@ const onNodeClick: MethodCreator<Props, ConnectorState, [TreeNode]> = dispatch =
|
|||
dispatch.call(toggleNodeExpansion, node, true)
|
||||
} else if (node.type === 'blob') {
|
||||
dispatch.call(focusNode, node, true)
|
||||
DOMHelper.loadWithPJAX(node.url)
|
||||
if (node.url) DOMHelper.loadWithPJAX(node.url)
|
||||
} else if (node.type === 'commit') {
|
||||
window.open(node.url, '_blank')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ const setShouldShow: MethodCreator<
|
|||
ConnectorState,
|
||||
[ConnectorState['shouldShow']]
|
||||
> = dispatch => shouldShow => {
|
||||
dispatch.set({ shouldShow }, shouldShow ? DOMHelper.focusFileExplorer : null)
|
||||
dispatch.set({ shouldShow }, shouldShow ? DOMHelper.focusFileExplorer : undefined)
|
||||
DOMHelper.setBodyIndent(shouldShow)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ function setBodyIndent(shouldShowGitako: boolean) {
|
|||
}
|
||||
}
|
||||
|
||||
function $<E extends (element: Element) => any, O extends () => any>(
|
||||
function $<EE extends Element, E extends (element: EE) => any, O extends () => any>(
|
||||
selector: string,
|
||||
existCallback?: E,
|
||||
otherwise?: O,
|
||||
|
|
@ -41,7 +41,7 @@ function $<E extends (element: Element) => any, O extends () => any>(
|
|||
: ReturnType<O> | ReturnType<E> {
|
||||
const element = document.querySelector(selector)
|
||||
if (element) {
|
||||
return existCallback ? existCallback(element) : element
|
||||
return existCallback ? existCallback(element as EE) : element
|
||||
}
|
||||
return otherwise ? otherwise() : null
|
||||
}
|
||||
|
|
@ -60,16 +60,15 @@ function getBranches() {
|
|||
function getCurrentBranch() {
|
||||
const selectedBranchButtonSelector =
|
||||
'.repository-content > .file-navigation > .branch-select-menu > button'
|
||||
const branchNameFromButtonElement = $(
|
||||
selectedBranchButtonSelector,
|
||||
(element: HTMLButtonElement) => element.title.trim(),
|
||||
const branchNameFromButtonElement = $(selectedBranchButtonSelector, element =>
|
||||
(element as HTMLButtonElement).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 ? element.textContent.trim() : '',
|
||||
)
|
||||
if (branchNameFromSelectElement) return branchNameFromSelectElement
|
||||
}
|
||||
|
|
@ -306,7 +305,7 @@ function attachCopySnippet() {
|
|||
* </div>
|
||||
* </article>
|
||||
*/
|
||||
target.parentNode.insertBefore(clippy, target)
|
||||
if (target.parentNode) target.parentNode.insertBefore(clippy, target)
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
|
@ -318,13 +317,18 @@ function attachCopySnippet() {
|
|||
*/
|
||||
function focusFileExplorer() {
|
||||
const sideBarContentSelector = '.gitako-side-bar .file-explorer'
|
||||
$(sideBarContentSelector, (sideBarElement: HTMLElement) => sideBarElement.focus())
|
||||
$(sideBarContentSelector, sideBarElement => {
|
||||
if (sideBarElement instanceof HTMLElement) sideBarElement.focus()
|
||||
})
|
||||
}
|
||||
|
||||
function focusSearchInput() {
|
||||
const searchInputSelector = '.search-input'
|
||||
$(searchInputSelector, (searchInputElement: HTMLElement) => {
|
||||
if (document.activeElement !== searchInputElement) {
|
||||
$(searchInputSelector, searchInputElement => {
|
||||
if (
|
||||
document.activeElement !== searchInputElement &&
|
||||
searchInputElement instanceof HTMLElement
|
||||
) {
|
||||
searchInputElement.focus()
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ type Options = {
|
|||
}
|
||||
|
||||
async function request(url: string, { accessToken }: Options = {}) {
|
||||
const headers = {} as {
|
||||
const headers = {} as HeadersInit & {
|
||||
Authorization?: string
|
||||
}
|
||||
if (accessToken) {
|
||||
|
|
@ -76,7 +76,7 @@ async function getTreeData({ userName, repoName, branchName, accessToken }: Meta
|
|||
export type ItemData = {
|
||||
userName: string
|
||||
repoName: string
|
||||
accessToken: string
|
||||
accessToken?: string
|
||||
}
|
||||
|
||||
export type BlobData = {
|
||||
|
|
@ -98,7 +98,7 @@ async function getBlobData({
|
|||
function getUrlForRedirect(
|
||||
{ userName, repoName, branchName }: MetaData,
|
||||
type = 'blob',
|
||||
path?: string
|
||||
path?: string,
|
||||
) {
|
||||
return `https://github.com/${userName}/${repoName}/${type}/${branchName}/${path}`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import storageHelper from 'utils/storageHelper'
|
|||
import { pick } from 'utils/general'
|
||||
|
||||
type Config = {
|
||||
shortcut: string | null
|
||||
access_token: string | null
|
||||
shortcut: string | undefined
|
||||
access_token: string | undefined
|
||||
compressSingletonFolder: boolean
|
||||
copyFileButton: boolean
|
||||
copySnippetButton: boolean
|
||||
|
|
@ -18,8 +18,8 @@ export enum configKeys {
|
|||
}
|
||||
|
||||
const defaultConfigs = {
|
||||
shortcut: null,
|
||||
access_token: null,
|
||||
shortcut: undefined,
|
||||
access_token: undefined,
|
||||
compressSingletonFolder: true,
|
||||
copyFileButton: true,
|
||||
copySnippetButton: true,
|
||||
|
|
|
|||
Loading…
Reference in a new issue