feat: loading indicator

This commit is contained in:
EnixCoda 2018-08-05 22:59:53 +08:00
parent 71e815bdca
commit 83815ea051
6 changed files with 92 additions and 38 deletions

View file

@ -6,6 +6,7 @@ import { FileExplorer as FileExplorerCore } from '../driver/core'
import SearchBar from './SearchBar'
import Node from './Node'
import LoadingIndicator from './LoadingIndicator'
import cx from '../utils/cx'
@ -45,44 +46,53 @@ export default class FileExplorer extends React.Component {
execAfterRender()
}
componentWillReceiveProps(nextProps) {
if (nextProps.treeData !== this.props.treeData) {
const { init } = nextProps
init()
}
}
componentDidUpdate() {
const { execAfterRender } = this.props
execAfterRender()
}
renderFiles(visibleNodes, onNodeClick) {
const { nodes, depths, focusedNode, expandedNodes } = visibleNodes
if (nodes.length === 0) {
return (
<label className={'no-results'}>
No results found.
</label>
)
}
return (
<div className={'files'}>
{nodes.map(node => (
<Node
key={node.path}
node={node}
depth={depths.get(node)}
focused={focusedNode === node}
expanded={expandedNodes.has(node)}
onClick={onNodeClick}
/>
))}
</div>
)
}
render() {
const {
visibleNodes,
freeze,
handleKeyDown,
handleSearchKeyChange,
onNodeClick
} = this.props
const {
nodes,
depths,
focusedNode,
expandedNodes,
} = visibleNodes || {}
const { stateText, visibleNodes, freeze, handleKeyDown, handleSearchKeyChange, onNodeClick } = this.props
return (
<div className={cx(`file-explorer`, { freeze })} tabIndex={-1} onKeyDown={handleKeyDown}>
<SearchBar onSearchKeyChange={handleSearchKeyChange} />
{!visibleNodes || !nodes || nodes.length === 0 ? (
<label className={'no-results'}>No results found.</label>
) : (
<div className={'files'}>
{nodes.map(node => (
<Node
key={node.path}
node={node}
depth={depths.get(node)}
focused={focusedNode === node}
expanded={expandedNodes.has(node)}
onClick={onNodeClick}
/>
))}
</div>
)}
{
!visibleNodes || stateText
? <LoadingIndicator text={stateText} />
: this.renderFiles(visibleNodes, onNodeClick)
}
</div>
)
}

View file

@ -0,0 +1,13 @@
import React from 'react'
import Icon from './Icon';
export default function LoadingIndicator({ text }) {
return (
<div className={'loading-indicator-container'}>
<div className={'loading-indicator'}>
<Icon className={'loading-indicator-icon'} type={'gear'} />
{text}
</div>
</div>
)
}

View file

@ -94,9 +94,8 @@ export default class Gitako extends React.PureComponent {
{metaData && <MetaBar metaData={metaData} />}
{errorDueToAuth && this.renderAccessDeniedError()}
{metaData &&
treeData && (
<FileExplorer metaData={metaData} treeData={treeData} freeze={showSettings} accessToken={accessToken} compressSingletonFolder={compressSingletonFolder} />
)}
<FileExplorer metaData={metaData} treeData={treeData} freeze={showSettings} accessToken={accessToken} compressSingletonFolder={compressSingletonFolder} />
}
</div>
)
}

View file

@ -215,6 +215,25 @@
padding: 4px 10px;
}
.loading-indicator-container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
.loading-indicator {
display: flex;
align-items: center;
}
.loading-indicator-icon {
width: 20px;
height: 20px;
animation: rotate 2s infinite linear;
}
}
.file-explorer {
width: 100%;
height: 100%;
@ -373,3 +392,11 @@
}
}
}
@keyframes rotate {
from {
transform: rotateZ(0);
} to {
transform: rotateZ(360deg);
}
}

View file

@ -21,9 +21,15 @@ const tasksAfterRender = []
const visibleNodesGenerator = new VisibleNodesGenerator()
const init = dispatch => () => dispatch(async (state, { treeData, metaData, compressSingletonFolder }) => {
if (!treeData) {
dispatch(setStateText, 'Fetching Tree Data...')
return
}
dispatch(setStateText, 'Rendering File List...')
const { root } = treeParser.parse(treeData, metaData)
visibleNodesGenerator.setCompress(compressSingletonFolder)
await visibleNodesGenerator.plantTree(root)
dispatch(setStateText, null)
const currentPath = URLHelper.getCurrentPath(true)
if (currentPath.length) {
const nodeExpandedTo = visibleNodesGenerator.expandTo(currentPath.join('/'))
@ -44,6 +50,10 @@ const execAfterRender = dispatch => () => {
tasksAfterRender.length = 0
}
const setStateText = dispatch => text => dispatch({
stateText: text,
})
const handleKeyDown = dispatch => ({ key }) => dispatch(({ visibleNodes: { nodes, focusedNode, expandedNodes, depths } }) => {
if (focusedNode) {
const focusedNodeIndex = nodes.indexOf(focusedNode)
@ -174,6 +184,7 @@ const updateVisibleNodes = dispatch => () => {
export default {
init,
execAfterRender,
setStateText,
handleKeyDown,
handleSearchKeyChange,
setExpand,

View file

@ -16,15 +16,9 @@ const init = dispatch => async () => {
dispatch(setMetaData, metaData)
const shouldShow = URLHelper.isInCodePage(metaData)
dispatch(setShouldShow, shouldShow)
if (shouldShow) {
DOMHelper.mountTopProgressBar()
}
const treeData = await GitHubHelper.getTreeData({ ...metaData, accessToken })
dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() })
dispatch({ treeData })
if (shouldShow) {
DOMHelper.unmountTopProgressBar()
}
} catch (err) {
// TODO: detect request time exceeds limit
if (err.message === NOT_FOUND || err.message === BAD_CREDENTIALS) {