fetch progress indicator

This commit is contained in:
刘浩远 2020-06-05 20:30:42 +08:00
parent 9bdcf4ea34
commit 454cf48a45
7 changed files with 72 additions and 12 deletions

28
dist/styles.css vendored
View File

@ -53,6 +53,21 @@ i.ms-Nav-chevron {
user-select: none;
overflow: hidden;
}
nav .progress {
position: fixed;
top: 0;
left: 0;
z-index: 10;
width: 100%;
height: 2px;
overflow: hidden;
}
.ms-ProgressIndicator-itemProgress {
padding: 0;
}
.ms-ProgressIndicator-progressTrack {
background: none;
}
#root > nav span.title {
font-size: 12px;
line-height: 32px;
@ -240,7 +255,7 @@ img.favicon {
z-index: 1;
}
@media (min-width: 1721px) {
@media (min-width: 1441px) {
#root > nav.menu-on {
padding-left: 296px;
}
@ -256,6 +271,17 @@ img.favicon {
.menu-container .menu {
background-color: #edebe9;
}
.menu-container .menu::after {
content: "";
display: block;
pointer-events: none;
position: absolute;
top: -10%;
right: 0;
width: 120%;
height: 120%;
box-shadow: inset 5px 0 20px #0004;
}
.main.menu-on {
padding-left: 280px;
}

View File

@ -1,7 +1,8 @@
import * as React from "react"
import { ipcRenderer, remote } from "electron"
import { remote } from "electron"
import { Icon } from "@fluentui/react/lib/Icon"
import { AppState } from "../scripts/models/app"
import { ProgressIndicator } from "@fluentui/react"
type NavProps = {
state: AppState,
@ -56,6 +57,12 @@ class Nav extends React.Component<NavProps, NavState> {
if (this.canFetch()) this.props.fetch()
}
getProgress = () => {
return this.props.state.fetchingTotal > 0
? this.props.state.fetchingProgress / this.props.state.fetchingTotal
: null
}
render() {
return (
<nav className={this.hideButtons() + this.menuOn()}>
@ -78,6 +85,11 @@ class Nav extends React.Component<NavProps, NavState> {
</a>
<a className={"btn system close"+this.menuOn()} title="关闭" onClick={this.close}><Icon iconName="Cancel" /></a>
</div>
{!this.canFetch() &&
<ProgressIndicator
className="progress"
percentComplete={this.getProgress()} />
}
</nav>
)
}

View File

@ -28,7 +28,7 @@ class ProxyTab extends React.Component {
render = () => (
<div className="tab-body">
<Stack horizontal verticalAlign="center">
<Stack horizontal verticalAlign="baseline">
<Stack.Item grow>
<Label></Label>
</Stack.Item>

View File

@ -30,6 +30,8 @@ export class AppState {
sourceInit = false
feedInit = false
fetchingItems = false
fetchingProgress = 0
fetchingTotal = 0
menu = getWindowBreakpoint()
menuKey = ALL
title = "全部文章"
@ -182,7 +184,9 @@ export function appReducer(
switch (action.status) {
case ActionStatus.Request: return {
...state,
fetchingItems: true
fetchingItems: true,
fetchingProgress: 0,
fetchingTotal: action.fetchCount
}
case ActionStatus.Failure: return {
...state,
@ -199,6 +203,7 @@ export function appReducer(
case ActionStatus.Success: return {
...state,
fetchingItems: false,
fetchingTotal: 0,
logMenu: action.items.length == 0 ? state.logMenu : {
...state.logMenu,
logs: [...state.logMenu.logs, new AppLog(
@ -207,6 +212,11 @@ export function appReducer(
)]
}
}
case ActionStatus.Intermediate: return {
...state,
fetchingProgress: state.fetchingProgress + 1
}
default: return state
}
case SELECT_PAGE:
switch (action.pageType) {

View File

@ -251,6 +251,7 @@ export function feedReducer(
loading: false
}
}
default: return state
}
case SELECT_PAGE:
switch (action.pageType) {

View File

@ -55,6 +55,7 @@ export const MARK_UNREAD = "MARK_UNREAD"
interface FetchItemsAction {
type: typeof FETCH_ITEMS
status: ActionStatus
fetchCount?: number
items?: RSSItem[]
errSource?: RSSSource
err?
@ -72,10 +73,11 @@ interface MarkUnreadAction {
export type ItemActionTypes = FetchItemsAction | MarkReadAction | MarkUnreadAction
export function fetchItemsRequest(): ItemActionTypes {
export function fetchItemsRequest(fetchCount = 0): ItemActionTypes {
return {
type: FETCH_ITEMS,
status: ActionStatus.Request
status: ActionStatus.Request,
fetchCount: fetchCount
}
}
@ -96,6 +98,13 @@ export function fetchItemsFailure(source: RSSSource, err): ItemActionTypes {
}
}
export function fetchItemsIntermediate(): ItemActionTypes {
return {
type: FETCH_ITEMS,
status: ActionStatus.Intermediate
}
}
export function insertItems(items: RSSItem[]): Promise<RSSItem[]> {
return new Promise<RSSItem[]>((resolve, reject) => {
db.idb.find({}).projection({ id: 1 }).sort({ id: -1 }).limit(1).exec((err, docs) => {
@ -118,13 +127,15 @@ export function insertItems(items: RSSItem[]): Promise<RSSItem[]> {
export function fetchItems(): AppThunk<Promise<void>> {
return (dispatch, getState) => {
let p = new Array<Promise<RSSItem[]>>()
let promises = new Array<Promise<RSSItem[]>>()
if (!getState().app.fetchingItems) {
for (let source of <RSSSource[]>Object.values(getState().sources)) {
p.push(RSSSource.fetchItems(source, rssParser, db.idb))
let promise = RSSSource.fetchItems(source, rssParser, db.idb)
promise.finally(() => dispatch(fetchItemsIntermediate()))
promises.push(promise)
}
dispatch(fetchItemsRequest())
return Promise.allSettled(p).then(results => new Promise<void>((resolve, reject) => {
dispatch(fetchItemsRequest(promises.length))
return Promise.allSettled(promises).then(results => new Promise<void>((resolve, reject) => {
let items = new Array<RSSItem>()
results.map((r, i) => {
if (r.status === "fulfilled") items.push(...r.value)

View File

@ -4,7 +4,7 @@ import { AnyAction } from "redux"
import { RootState } from "./reducer"
export enum ActionStatus {
Request, Success, Failure
Request, Success, Failure, Intermediate
}
export type AppThunk<ReturnType = void> = ThunkAction<
@ -82,4 +82,4 @@ export function openExternal(url: string) {
export const urlTest = (s: string) =>
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi.test(s)
export const getWindowBreakpoint = () => remote.getCurrentWindow().getSize()[0] >= 1721
export const getWindowBreakpoint = () => remote.getCurrentWindow().getSize()[0] >= 1441