mirror of
https://github.com/yang991178/fluent-reader.git
synced 2025-04-14 18:32:20 +02:00
add service warnings
This commit is contained in:
parent
fc0183a80d
commit
c35efa04ba
4
dist/styles/global.css
vendored
4
dist/styles/global.css
vendored
@ -109,6 +109,10 @@ i.ms-Nav-chevron {
|
|||||||
.ms-ActivityItem-timeStamp {
|
.ms-ActivityItem-timeStamp {
|
||||||
color: var(--neutralSecondaryAlt);
|
color: var(--neutralSecondaryAlt);
|
||||||
}
|
}
|
||||||
|
.ms-MessageBar {
|
||||||
|
user-select: none;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
#root > nav {
|
#root > nav {
|
||||||
height: var(--navHeight);
|
height: var(--navHeight);
|
||||||
|
@ -3,12 +3,13 @@ import intl from "react-intl-universal"
|
|||||||
import { SourceGroup } from "../../schema-types"
|
import { SourceGroup } from "../../schema-types"
|
||||||
import { SourceState, RSSSource } from "../../scripts/models/source"
|
import { SourceState, RSSSource } from "../../scripts/models/source"
|
||||||
import { IColumn, Selection, SelectionMode, DetailsList, Label, Stack,
|
import { IColumn, Selection, SelectionMode, DetailsList, Label, Stack,
|
||||||
TextField, PrimaryButton, DefaultButton, Dropdown, IDropdownOption, CommandBarButton, MarqueeSelection, IDragDropEvents } from "@fluentui/react"
|
TextField, PrimaryButton, DefaultButton, Dropdown, IDropdownOption, CommandBarButton, MarqueeSelection, IDragDropEvents, MessageBar, MessageBarType } from "@fluentui/react"
|
||||||
import DangerButton from "../utils/danger-button"
|
import DangerButton from "../utils/danger-button"
|
||||||
|
|
||||||
type GroupsTabProps = {
|
type GroupsTabProps = {
|
||||||
sources: SourceState,
|
sources: SourceState,
|
||||||
groups: SourceGroup[],
|
groups: SourceGroup[],
|
||||||
|
serviceOn: boolean,
|
||||||
createGroup: (name: string) => void,
|
createGroup: (name: string) => void,
|
||||||
updateGroup: (group: SourceGroup) => void,
|
updateGroup: (group: SourceGroup) => void,
|
||||||
addToGroup: (groupIndex: number, sid: number) => void,
|
addToGroup: (groupIndex: number, sid: number) => void,
|
||||||
@ -263,6 +264,9 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||||||
|
|
||||||
render = () => (
|
render = () => (
|
||||||
<div className="tab-body">
|
<div className="tab-body">
|
||||||
|
{this.props.serviceOn && (
|
||||||
|
<MessageBar messageBarType={MessageBarType.info}>{intl.get("service.groupsWarning")}</MessageBar>
|
||||||
|
)}
|
||||||
{this.state.manageGroup && this.state.selectedGroup &&
|
{this.state.manageGroup && this.state.selectedGroup &&
|
||||||
<>
|
<>
|
||||||
<Stack horizontal horizontalAlign="space-between" style={{height: 40}}>
|
<Stack horizontal horizontalAlign="space-between" style={{height: 40}}>
|
||||||
|
@ -95,14 +95,12 @@ class FeverConfigsTab extends React.Component<ServiceConfigsTabProps, FeverConfi
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <>
|
return <>
|
||||||
<Stack tokens={{childrenGap: 8}}>
|
{!this.state.existing && (
|
||||||
{!this.state.existing && (
|
<MessageBar messageBarType={MessageBarType.warning}>{intl.get("service.overwriteWarning")}</MessageBar>
|
||||||
<MessageBar messageBarType={MessageBarType.warning}>{intl.get("service.overwriteWarning")}</MessageBar>
|
)}
|
||||||
)}
|
{!this.state.existing && this.state.importGroups && (
|
||||||
{!this.state.existing && this.state.importGroups && (
|
<MessageBar messageBarType={MessageBarType.info}>{intl.get("service.groupsWarning")}</MessageBar>
|
||||||
<MessageBar messageBarType={MessageBarType.info}>{intl.get("service.groupsWarning")}</MessageBar>
|
)}
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
<Stack horizontalAlign="center" style={{marginTop: 48}}>
|
<Stack horizontalAlign="center" style={{marginTop: 48}}>
|
||||||
<Icon iconName="Calories" style={{fontSize: 32, userSelect: "none"}} />
|
<Icon iconName="Calories" style={{fontSize: 32, userSelect: "none"}} />
|
||||||
<Label style={{margin: "8px 0 36px"}}>Fever API</Label>
|
<Label style={{margin: "8px 0 36px"}}>Fever API</Label>
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import intl from "react-intl-universal"
|
import intl from "react-intl-universal"
|
||||||
import { Label, DefaultButton, TextField, Stack, PrimaryButton, DetailsList,
|
import { Label, DefaultButton, TextField, Stack, PrimaryButton, DetailsList,
|
||||||
IColumn, SelectionMode, Selection, IChoiceGroupOption, ChoiceGroup, IDropdownOption, Dropdown } from "@fluentui/react"
|
IColumn, SelectionMode, Selection, IChoiceGroupOption, ChoiceGroup, IDropdownOption,
|
||||||
|
Dropdown, MessageBar, MessageBarType } from "@fluentui/react"
|
||||||
import { SourceState, RSSSource, SourceOpenTarget } from "../../scripts/models/source"
|
import { SourceState, RSSSource, SourceOpenTarget } from "../../scripts/models/source"
|
||||||
import { urlTest } from "../../scripts/utils"
|
import { urlTest } from "../../scripts/utils"
|
||||||
import DangerButton from "../utils/danger-button"
|
import DangerButton from "../utils/danger-button"
|
||||||
|
|
||||||
type SourcesTabProps = {
|
type SourcesTabProps = {
|
||||||
sources: SourceState
|
sources: SourceState
|
||||||
|
serviceOn: boolean
|
||||||
addSource: (url: string) => void
|
addSource: (url: string) => void
|
||||||
updateSourceName: (source: RSSSource, name: string) => void
|
updateSourceName: (source: RSSSource, name: string) => void
|
||||||
updateSourceIcon: (source: RSSSource, iconUrl: string) => Promise<void>
|
updateSourceIcon: (source: RSSSource, iconUrl: string) => Promise<void>
|
||||||
@ -154,6 +156,9 @@ class SourcesTab extends React.Component<SourcesTabProps, SourcesTabState> {
|
|||||||
|
|
||||||
render = () => (
|
render = () => (
|
||||||
<div className="tab-body">
|
<div className="tab-body">
|
||||||
|
{this.props.serviceOn && (
|
||||||
|
<MessageBar messageBarType={MessageBarType.info}>{intl.get("sources.serviceWarning")}</MessageBar>
|
||||||
|
)}
|
||||||
<Label>{intl.get("sources.opmlFile")}</Label>
|
<Label>{intl.get("sources.opmlFile")}</Label>
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<Stack.Item>
|
<Stack.Item>
|
||||||
@ -196,6 +201,9 @@ class SourcesTab extends React.Component<SourcesTabProps, SourcesTabState> {
|
|||||||
selectionMode={SelectionMode.multiple} />
|
selectionMode={SelectionMode.multiple} />
|
||||||
|
|
||||||
{this.state.selectedSource && <>
|
{this.state.selectedSource && <>
|
||||||
|
{this.state.selectedSource.serviceRef && (
|
||||||
|
<MessageBar messageBarType={MessageBarType.info}>{intl.get("sources.serviceManaged")}</MessageBar>
|
||||||
|
)}
|
||||||
<Label>{intl.get("sources.selected")}</Label>
|
<Label>{intl.get("sources.selected")}</Label>
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<Stack.Item>
|
<Stack.Item>
|
||||||
@ -251,34 +259,39 @@ class SourcesTab extends React.Component<SourcesTabProps, SourcesTabState> {
|
|||||||
</>}
|
</>}
|
||||||
|
|
||||||
</Stack>
|
</Stack>
|
||||||
<Label>{intl.get("sources.fetchFrequency")}</Label>
|
{!this.state.selectedSource.serviceRef && <>
|
||||||
<Stack>
|
<Label>{intl.get("sources.fetchFrequency")}</Label>
|
||||||
<Stack.Item>
|
<Stack>
|
||||||
<Dropdown
|
<Stack.Item>
|
||||||
options={this.fetchFrequencyOptions()}
|
<Dropdown
|
||||||
selectedKey={this.state.selectedSource.fetchFrequency ? String(this.state.selectedSource.fetchFrequency) : "0"}
|
options={this.fetchFrequencyOptions()}
|
||||||
onChange={this.onFetchFrequencyChange}
|
selectedKey={this.state.selectedSource.fetchFrequency ? String(this.state.selectedSource.fetchFrequency) : "0"}
|
||||||
style={{width: 200}} />
|
onChange={this.onFetchFrequencyChange}
|
||||||
</Stack.Item>
|
style={{width: 200}} />
|
||||||
</Stack>
|
</Stack.Item>
|
||||||
|
</Stack>
|
||||||
|
</>}
|
||||||
<ChoiceGroup
|
<ChoiceGroup
|
||||||
label={intl.get("sources.openTarget")}
|
label={intl.get("sources.openTarget")}
|
||||||
options={this.sourceOpenTargetChoices()}
|
options={this.sourceOpenTargetChoices()}
|
||||||
selectedKey={String(this.state.selectedSource.openTarget)}
|
selectedKey={String(this.state.selectedSource.openTarget)}
|
||||||
onChange={this.onOpenTargetChange} />
|
onChange={this.onOpenTargetChange} />
|
||||||
<Stack horizontal>
|
{!this.state.selectedSource.serviceRef && (
|
||||||
<Stack.Item>
|
<Stack horizontal>
|
||||||
<DangerButton
|
<Stack.Item>
|
||||||
onClick={() => this.props.deleteSource(this.state.selectedSource)}
|
<DangerButton
|
||||||
key={this.state.selectedSource.sid}
|
onClick={() => this.props.deleteSource(this.state.selectedSource)}
|
||||||
text={intl.get("sources.delete")} />
|
key={this.state.selectedSource.sid}
|
||||||
</Stack.Item>
|
text={intl.get("sources.delete")} />
|
||||||
<Stack.Item>
|
</Stack.Item>
|
||||||
<span className="settings-hint">{intl.get("sources.deleteWarning")}</span>
|
<Stack.Item>
|
||||||
</Stack.Item>
|
<span className="settings-hint">{intl.get("sources.deleteWarning")}</span>
|
||||||
</Stack>
|
</Stack.Item>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
</>}
|
</>}
|
||||||
{this.state.selectedSources && <>
|
{this.state.selectedSources && (this.state.selectedSources.filter(s => s.serviceRef).length === 0
|
||||||
|
? <>
|
||||||
<Label>{intl.get("sources.selectedMulti")}</Label>
|
<Label>{intl.get("sources.selectedMulti")}</Label>
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<Stack.Item>
|
<Stack.Item>
|
||||||
@ -290,7 +303,10 @@ class SourcesTab extends React.Component<SourcesTabProps, SourcesTabState> {
|
|||||||
<span className="settings-hint">{intl.get("sources.deleteWarning")}</span>
|
<span className="settings-hint">{intl.get("sources.deleteWarning")}</span>
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
</Stack>
|
</Stack>
|
||||||
</>}
|
</>
|
||||||
|
: (
|
||||||
|
<MessageBar messageBarType={MessageBarType.info}>{intl.get("sources.serviceManaged")}</MessageBar>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -4,16 +4,18 @@ import { RootState } from "../../scripts/reducer"
|
|||||||
import GroupsTab from "../../components/settings/groups"
|
import GroupsTab from "../../components/settings/groups"
|
||||||
import { createSourceGroup, updateSourceGroup, addSourceToGroup,
|
import { createSourceGroup, updateSourceGroup, addSourceToGroup,
|
||||||
deleteSourceGroup, removeSourceFromGroup, reorderSourceGroups } from "../../scripts/models/group"
|
deleteSourceGroup, removeSourceFromGroup, reorderSourceGroups } from "../../scripts/models/group"
|
||||||
import { SourceGroup } from "../../schema-types"
|
import { SourceGroup, SyncService } from "../../schema-types"
|
||||||
|
|
||||||
const getSources = (state: RootState) => state.sources
|
const getSources = (state: RootState) => state.sources
|
||||||
const getGroups = (state: RootState) => state.groups
|
const getGroups = (state: RootState) => state.groups
|
||||||
|
const getServiceOn = (state: RootState) => state.service.type !== SyncService.None
|
||||||
|
|
||||||
const mapStateToProps = createSelector(
|
const mapStateToProps = createSelector(
|
||||||
[getSources, getGroups],
|
[getSources, getGroups, getServiceOn],
|
||||||
(sources, groups) => ({
|
(sources, groups, serviceOn) => ({
|
||||||
sources: sources,
|
sources: sources,
|
||||||
groups: groups.map((g, i) => ({ ...g, index: i })),
|
groups: groups.map((g, i) => ({ ...g, index: i })),
|
||||||
|
serviceOn: serviceOn,
|
||||||
key: groups.length
|
key: groups.length
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -7,13 +7,16 @@ import { addSource, RSSSource, updateSource, deleteSource, SourceOpenTarget, del
|
|||||||
import { importOPML, exportOPML } from "../../scripts/models/group"
|
import { importOPML, exportOPML } from "../../scripts/models/group"
|
||||||
import { AppDispatch, validateFavicon } from "../../scripts/utils"
|
import { AppDispatch, validateFavicon } from "../../scripts/utils"
|
||||||
import { saveSettings } from "../../scripts/models/app"
|
import { saveSettings } from "../../scripts/models/app"
|
||||||
|
import { SyncService } from "../../schema-types"
|
||||||
|
|
||||||
const getSources = (state: RootState) => state.sources
|
const getSources = (state: RootState) => state.sources
|
||||||
|
const getServiceOn = (state: RootState) => state.service.type !== SyncService.None
|
||||||
|
|
||||||
const mapStateToProps = createSelector(
|
const mapStateToProps = createSelector(
|
||||||
[getSources],
|
[getSources, getServiceOn],
|
||||||
(sources) => ({
|
(sources, serviceOn) => ({
|
||||||
sources: sources
|
sources: sources,
|
||||||
|
serviceOn: serviceOn
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,7 +32,8 @@
|
|||||||
"fetchFailure": "Failed to load source \"{name}\".",
|
"fetchFailure": "Failed to load source \"{name}\".",
|
||||||
"fetchSuccess": "Successfully fetched {count, plural, =1 {# article} other {# articles}}.",
|
"fetchSuccess": "Successfully fetched {count, plural, =1 {# article} other {# articles}}.",
|
||||||
"networkError": "A network error has occurred.",
|
"networkError": "A network error has occurred.",
|
||||||
"parseError": "An error has occurred when parsing the XML feed."
|
"parseError": "An error has occurred when parsing the XML feed.",
|
||||||
|
"syncFailure": "Failed to sync with service"
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"menu": "Menu",
|
"menu": "Menu",
|
||||||
@ -112,6 +113,8 @@
|
|||||||
"feedback": "Feedback"
|
"feedback": "Feedback"
|
||||||
},
|
},
|
||||||
"sources": {
|
"sources": {
|
||||||
|
"serviceWarning": "Sources imported or added here will not be synced with your service.",
|
||||||
|
"serviceManaged": "This source is managed by your service.",
|
||||||
"untitled": "Source",
|
"untitled": "Source",
|
||||||
"errorAdd": "An error has occured when adding the source.",
|
"errorAdd": "An error has occured when adding the source.",
|
||||||
"errorParse": "An error has occurred when parsing the OPML file.",
|
"errorParse": "An error has occurred when parsing the OPML file.",
|
||||||
|
@ -32,7 +32,8 @@
|
|||||||
"fetchFailure": "无法加载订阅源“{name}”",
|
"fetchFailure": "无法加载订阅源“{name}”",
|
||||||
"fetchSuccess": "成功加载 {count} 篇文章",
|
"fetchSuccess": "成功加载 {count} 篇文章",
|
||||||
"networkError": "连接订阅源时出错",
|
"networkError": "连接订阅源时出错",
|
||||||
"parseError": "解析XML信息流时出错"
|
"parseError": "解析XML信息流时出错",
|
||||||
|
"syncFailure": "无法与服务同步"
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"menu": "菜单",
|
"menu": "菜单",
|
||||||
@ -110,6 +111,8 @@
|
|||||||
"feedback": "反馈"
|
"feedback": "反馈"
|
||||||
},
|
},
|
||||||
"sources": {
|
"sources": {
|
||||||
|
"serviceWarning": "此处导入或添加的订阅源将不会与服务端同步",
|
||||||
|
"serviceManaged": "该订阅源由服务端管理",
|
||||||
"untitled": "订阅源",
|
"untitled": "订阅源",
|
||||||
"errorAdd": "添加订阅源时出错",
|
"errorAdd": "添加订阅源时出错",
|
||||||
"errorParse": "解析OPML文件时出错",
|
"errorParse": "解析OPML文件时出错",
|
||||||
@ -187,7 +190,7 @@
|
|||||||
"fetchLimit": "同步数量",
|
"fetchLimit": "同步数量",
|
||||||
"fetchLimitNum": "最近 {count} 篇文章",
|
"fetchLimitNum": "最近 {count} 篇文章",
|
||||||
"importGroups": "导入分组",
|
"importGroups": "导入分组",
|
||||||
"failure": "无法连接到服务",
|
"failure": "连接到服务时出错",
|
||||||
"failureHint": "请检查服务配置或网络连接"
|
"failureHint": "请检查服务配置或网络连接"
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
|
@ -356,6 +356,19 @@ export function appReducer(
|
|||||||
...state,
|
...state,
|
||||||
syncing: true
|
syncing: true
|
||||||
}
|
}
|
||||||
|
case ActionStatus.Failure: return {
|
||||||
|
...state,
|
||||||
|
syncing: false,
|
||||||
|
logMenu: {
|
||||||
|
...state.logMenu,
|
||||||
|
notify: true,
|
||||||
|
logs: [...state.logMenu.logs, new AppLog(
|
||||||
|
AppLogType.Failure,
|
||||||
|
intl.get("log.syncFailure"),
|
||||||
|
String(action.err)
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
}
|
||||||
default: return {
|
default: return {
|
||||||
...state,
|
...state,
|
||||||
syncing: false
|
syncing: false
|
||||||
@ -467,6 +480,7 @@ export function appReducer(
|
|||||||
settings: {
|
settings: {
|
||||||
...state.settings,
|
...state.settings,
|
||||||
display: true,
|
display: true,
|
||||||
|
changed: true,
|
||||||
saving: !state.settings.saving
|
saving: !state.settings.saving
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user