fix: you can never be too cautious about types

This commit is contained in:
EnixCoda 2019-02-20 17:25:01 +08:00
parent 9ea8b7614a
commit 764cddcd2f
No known key found for this signature in database
GPG key ID: 6825847C88AA329A
9 changed files with 56 additions and 39 deletions

View file

@ -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('&')
}

View file

@ -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} />

View file

@ -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}

View file

@ -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>

View file

@ -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')
}

View file

@ -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)
}

View file

@ -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()
}
})

View file

@ -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}`
}

View file

@ -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,