diff --git a/dist/styles/global.css b/dist/styles/global.css index ffa68be..ff0a129 100644 --- a/dist/styles/global.css +++ b/dist/styles/global.css @@ -109,6 +109,10 @@ i.ms-Nav-chevron { .ms-ActivityItem-timeStamp { color: var(--neutralSecondaryAlt); } +.ms-MessageBar { + user-select: none; + margin-bottom: 8px; +} #root > nav { height: var(--navHeight); diff --git a/src/components/settings/groups.tsx b/src/components/settings/groups.tsx index 31d2a01..135be72 100644 --- a/src/components/settings/groups.tsx +++ b/src/components/settings/groups.tsx @@ -3,12 +3,13 @@ import intl from "react-intl-universal" import { SourceGroup } from "../../schema-types" import { SourceState, RSSSource } from "../../scripts/models/source" 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" type GroupsTabProps = { sources: SourceState, groups: SourceGroup[], + serviceOn: boolean, createGroup: (name: string) => void, updateGroup: (group: SourceGroup) => void, addToGroup: (groupIndex: number, sid: number) => void, @@ -263,6 +264,9 @@ class GroupsTab extends React.Component { render = () => (
+ {this.props.serviceOn && ( + {intl.get("service.groupsWarning")} + )} {this.state.manageGroup && this.state.selectedGroup && <> diff --git a/src/components/settings/services/fever.tsx b/src/components/settings/services/fever.tsx index 036802c..cfc0c6f 100644 --- a/src/components/settings/services/fever.tsx +++ b/src/components/settings/services/fever.tsx @@ -95,14 +95,12 @@ class FeverConfigsTab extends React.Component - - {!this.state.existing && ( - {intl.get("service.overwriteWarning")} - )} - {!this.state.existing && this.state.importGroups && ( - {intl.get("service.groupsWarning")} - )} - + {!this.state.existing && ( + {intl.get("service.overwriteWarning")} + )} + {!this.state.existing && this.state.importGroups && ( + {intl.get("service.groupsWarning")} + )} diff --git a/src/components/settings/sources.tsx b/src/components/settings/sources.tsx index 0931785..549b5b9 100644 --- a/src/components/settings/sources.tsx +++ b/src/components/settings/sources.tsx @@ -1,13 +1,15 @@ import * as React from "react" import intl from "react-intl-universal" 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 { urlTest } from "../../scripts/utils" import DangerButton from "../utils/danger-button" type SourcesTabProps = { sources: SourceState + serviceOn: boolean addSource: (url: string) => void updateSourceName: (source: RSSSource, name: string) => void updateSourceIcon: (source: RSSSource, iconUrl: string) => Promise @@ -154,6 +156,9 @@ class SourcesTab extends React.Component { render = () => (
+ {this.props.serviceOn && ( + {intl.get("sources.serviceWarning")} + )} @@ -196,6 +201,9 @@ class SourcesTab extends React.Component { selectionMode={SelectionMode.multiple} /> {this.state.selectedSource && <> + {this.state.selectedSource.serviceRef && ( + {intl.get("sources.serviceManaged")} + )} @@ -251,34 +259,39 @@ class SourcesTab extends React.Component { } - - - - - - + {!this.state.selectedSource.serviceRef && <> + + + + + + + } - - - this.props.deleteSource(this.state.selectedSource)} - key={this.state.selectedSource.sid} - text={intl.get("sources.delete")} /> - - - {intl.get("sources.deleteWarning")} - - + {!this.state.selectedSource.serviceRef && ( + + + this.props.deleteSource(this.state.selectedSource)} + key={this.state.selectedSource.sid} + text={intl.get("sources.delete")} /> + + + {intl.get("sources.deleteWarning")} + + + )} } - {this.state.selectedSources && <> + {this.state.selectedSources && (this.state.selectedSources.filter(s => s.serviceRef).length === 0 + ? <> @@ -290,7 +303,10 @@ class SourcesTab extends React.Component { {intl.get("sources.deleteWarning")} - } + + : ( + {intl.get("sources.serviceManaged")} + ))}
) } diff --git a/src/containers/settings/groups-container.tsx b/src/containers/settings/groups-container.tsx index 3cef872..174518e 100644 --- a/src/containers/settings/groups-container.tsx +++ b/src/containers/settings/groups-container.tsx @@ -4,16 +4,18 @@ import { RootState } from "../../scripts/reducer" import GroupsTab from "../../components/settings/groups" import { createSourceGroup, updateSourceGroup, addSourceToGroup, 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 getGroups = (state: RootState) => state.groups +const getServiceOn = (state: RootState) => state.service.type !== SyncService.None const mapStateToProps = createSelector( - [getSources, getGroups], - (sources, groups) => ({ + [getSources, getGroups, getServiceOn], + (sources, groups, serviceOn) => ({ sources: sources, groups: groups.map((g, i) => ({ ...g, index: i })), + serviceOn: serviceOn, key: groups.length }) ) diff --git a/src/containers/settings/sources-container.tsx b/src/containers/settings/sources-container.tsx index 72e7b50..b13984b 100644 --- a/src/containers/settings/sources-container.tsx +++ b/src/containers/settings/sources-container.tsx @@ -7,13 +7,16 @@ import { addSource, RSSSource, updateSource, deleteSource, SourceOpenTarget, del import { importOPML, exportOPML } from "../../scripts/models/group" import { AppDispatch, validateFavicon } from "../../scripts/utils" import { saveSettings } from "../../scripts/models/app" +import { SyncService } from "../../schema-types" const getSources = (state: RootState) => state.sources +const getServiceOn = (state: RootState) => state.service.type !== SyncService.None const mapStateToProps = createSelector( - [getSources], - (sources) => ({ - sources: sources + [getSources, getServiceOn], + (sources, serviceOn) => ({ + sources: sources, + serviceOn: serviceOn }) ) diff --git a/src/scripts/i18n/en-US.json b/src/scripts/i18n/en-US.json index cf10048..fa1baec 100644 --- a/src/scripts/i18n/en-US.json +++ b/src/scripts/i18n/en-US.json @@ -32,7 +32,8 @@ "fetchFailure": "Failed to load source \"{name}\".", "fetchSuccess": "Successfully fetched {count, plural, =1 {# article} other {# articles}}.", "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": { "menu": "Menu", @@ -112,6 +113,8 @@ "feedback": "Feedback" }, "sources": { + "serviceWarning": "Sources imported or added here will not be synced with your service.", + "serviceManaged": "This source is managed by your service.", "untitled": "Source", "errorAdd": "An error has occured when adding the source.", "errorParse": "An error has occurred when parsing the OPML file.", diff --git a/src/scripts/i18n/zh-CN.json b/src/scripts/i18n/zh-CN.json index 263e268..32a49fb 100644 --- a/src/scripts/i18n/zh-CN.json +++ b/src/scripts/i18n/zh-CN.json @@ -32,7 +32,8 @@ "fetchFailure": "无法加载订阅源“{name}”", "fetchSuccess": "成功加载 {count} 篇文章", "networkError": "连接订阅源时出错", - "parseError": "解析XML信息流时出错" + "parseError": "解析XML信息流时出错", + "syncFailure": "无法与服务同步" }, "nav": { "menu": "菜单", @@ -110,6 +111,8 @@ "feedback": "反馈" }, "sources": { + "serviceWarning": "此处导入或添加的订阅源将不会与服务端同步", + "serviceManaged": "该订阅源由服务端管理", "untitled": "订阅源", "errorAdd": "添加订阅源时出错", "errorParse": "解析OPML文件时出错", @@ -187,7 +190,7 @@ "fetchLimit": "同步数量", "fetchLimitNum": "最近 {count} 篇文章", "importGroups": "导入分组", - "failure": "无法连接到服务", + "failure": "连接到服务时出错", "failureHint": "请检查服务配置或网络连接" }, "app": { diff --git a/src/scripts/models/app.ts b/src/scripts/models/app.ts index 7932042..21c5eb1 100644 --- a/src/scripts/models/app.ts +++ b/src/scripts/models/app.ts @@ -356,6 +356,19 @@ export function appReducer( ...state, 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 { ...state, syncing: false @@ -467,6 +480,7 @@ export function appReducer( settings: { ...state.settings, display: true, + changed: true, saving: !state.settings.saving } }