import groups from service & fix freshrss
This commit is contained in:
parent
38646b227c
commit
8b27f9cb99
|
@ -1 +1,2 @@
|
|||
dist/article/article.js text eol=lf
|
||||
dist/article/mercury.web.js text eol=lf
|
|
@ -96,6 +96,9 @@ div[role="tabpanel"] {
|
|||
.settings .loading .ms-Spinner {
|
||||
margin-top: 180px;
|
||||
}
|
||||
.settings .loading .ms-Spinner:focus {
|
||||
outline: none;
|
||||
}
|
||||
.tab-body .ms-StackItem {
|
||||
margin-right: 6px;
|
||||
margin-bottom: 12px;
|
||||
|
|
|
@ -3,7 +3,7 @@ import intl from "react-intl-universal"
|
|||
import { Icon } from "@fluentui/react/lib/Icon"
|
||||
import { AnimationClassNames } from "@fluentui/react/lib/Styling"
|
||||
import AboutTab from "./settings/about"
|
||||
import { Pivot, PivotItem, Spinner } from "@fluentui/react"
|
||||
import { Pivot, PivotItem, Spinner, FocusTrapZone } from "@fluentui/react"
|
||||
import SourcesTabContainer from "../containers/settings/sources-container"
|
||||
import GroupsTabContainer from "../containers/settings/groups-container"
|
||||
import AppTabContainer from "../containers/settings/app-container"
|
||||
|
@ -30,9 +30,11 @@ class Settings extends React.Component<SettingsProps> {
|
|||
</a>
|
||||
</div>
|
||||
<div className={"settings " + AnimationClassNames.slideUpIn20}>
|
||||
{this.props.blocked && <div className="loading">
|
||||
<Spinner label={intl.get("settings.fetching")} />
|
||||
</div>}
|
||||
{this.props.blocked && (
|
||||
<FocusTrapZone isClickableOutsideFocusTrap={true} className="loading">
|
||||
<Spinner label={intl.get("settings.fetching")} tabIndex={0} />
|
||||
</FocusTrapZone>
|
||||
)}
|
||||
<Pivot>
|
||||
<PivotItem headerText={intl.get("settings.sources")} itemIcon="Source">
|
||||
<SourcesTabContainer />
|
||||
|
|
|
@ -3,19 +3,20 @@ 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, MessageBar, MessageBarType } from "@fluentui/react"
|
||||
TextField, PrimaryButton, DefaultButton, Dropdown, IDropdownOption, CommandBarButton, MarqueeSelection, IDragDropEvents, MessageBar, MessageBarType, MessageBarButton } 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,
|
||||
deleteGroup: (groupIndex: number) => void,
|
||||
removeFromGroup: (groupIndex: number, sids: number[]) => void,
|
||||
sources: SourceState
|
||||
groups: SourceGroup[]
|
||||
serviceOn: boolean
|
||||
createGroup: (name: string) => void
|
||||
updateGroup: (group: SourceGroup) => void
|
||||
addToGroup: (groupIndex: number, sid: number) => void
|
||||
deleteGroup: (groupIndex: number) => void
|
||||
removeFromGroup: (groupIndex: number, sids: number[]) => void
|
||||
reorderGroups: (groups: SourceGroup[]) => void
|
||||
importGroups: () => Promise<void>
|
||||
}
|
||||
|
||||
type GroupsTabState = {
|
||||
|
@ -264,9 +265,6 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||
|
||||
render = () => (
|
||||
<div className="tab-body">
|
||||
{this.props.serviceOn && (
|
||||
<MessageBar messageBarType={MessageBarType.info}>{intl.get("service.groupsWarning")}</MessageBar>
|
||||
)}
|
||||
{this.state.manageGroup && this.state.selectedGroup &&
|
||||
<>
|
||||
<Stack horizontal horizontalAlign="space-between" style={{height: 40}}>
|
||||
|
@ -295,6 +293,14 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||
</>}
|
||||
{(!this.state.manageGroup || !this.state.selectedGroup)
|
||||
?<>
|
||||
{this.props.serviceOn && (
|
||||
<MessageBar
|
||||
messageBarType={MessageBarType.info}
|
||||
isMultiline={false}
|
||||
actions={<MessageBarButton text={intl.get("service.importGroups")} onClick={this.props.importGroups} />}>
|
||||
{intl.get("service.groupsWarning")}
|
||||
</MessageBar>
|
||||
)}
|
||||
<form onSubmit={this.createGroup}>
|
||||
<Label htmlFor="newGroupName">{intl.get("groups.create")}</Label>
|
||||
<Stack horizontal>
|
||||
|
|
|
@ -98,9 +98,6 @@ class FeverConfigsTab extends React.Component<ServiceConfigsTabProps, FeverConfi
|
|||
{!this.state.existing && (
|
||||
<MessageBar messageBarType={MessageBarType.warning}>{intl.get("service.overwriteWarning")}</MessageBar>
|
||||
)}
|
||||
{!this.state.existing && this.state.importGroups && (
|
||||
<MessageBar messageBarType={MessageBarType.info}>{intl.get("service.groupsWarning")}</MessageBar>
|
||||
)}
|
||||
<Stack horizontalAlign="center" style={{marginTop: 48}}>
|
||||
<Icon iconName="Calories" style={{color: "var(--black)", fontSize: 32, userSelect: "none"}} />
|
||||
<Label style={{margin: "8px 0 36px"}}>Fever API</Label>
|
||||
|
|
|
@ -5,6 +5,8 @@ import GroupsTab from "../../components/settings/groups"
|
|||
import { createSourceGroup, updateSourceGroup, addSourceToGroup,
|
||||
deleteSourceGroup, removeSourceFromGroup, reorderSourceGroups } from "../../scripts/models/group"
|
||||
import { SourceGroup, SyncService } from "../../schema-types"
|
||||
import { importGroups } from "../../scripts/models/service"
|
||||
import { AppDispatch } from "../../scripts/utils"
|
||||
|
||||
const getSources = (state: RootState) => state.sources
|
||||
const getGroups = (state: RootState) => state.groups
|
||||
|
@ -20,13 +22,14 @@ const mapStateToProps = createSelector(
|
|||
})
|
||||
)
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
const mapDispatchToProps = (dispatch: AppDispatch) => ({
|
||||
createGroup: (name: string) => dispatch(createSourceGroup(name)),
|
||||
updateGroup: (group: SourceGroup) => dispatch(updateSourceGroup(group)),
|
||||
addToGroup: (groupIndex: number, sid: number) => dispatch(addSourceToGroup(groupIndex, sid)),
|
||||
deleteGroup: (groupIndex: number) => dispatch(deleteSourceGroup(groupIndex)),
|
||||
removeFromGroup: (groupIndex: number, sids: number[]) => dispatch(removeSourceFromGroup(groupIndex, sids)),
|
||||
reorderGroups: (groups: SourceGroup[]) => dispatch(reorderSourceGroups(groups))
|
||||
reorderGroups: (groups: SourceGroup[]) => dispatch(reorderSourceGroups(groups)),
|
||||
importGroups: () => dispatch(importGroups()),
|
||||
})
|
||||
|
||||
const GroupsTabContainer = connect(mapStateToProps, mapDispatchToProps)(GroupsTab)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src-elem 'self'; img-src *; style-src 'self' 'unsafe-inline'; font-src 'self' https://static2.sharepointonline.com; connect-src https://* http://*">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src-elem 'self'; img-src *; style-src 'self' 'unsafe-inline'; font-src 'self' https://static2.sharepointonline.com; connect-src https: http:">
|
||||
<title>Fluent Reader</title>
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
|
|
|
@ -41,6 +41,7 @@ export const enum SyncService {
|
|||
}
|
||||
export interface ServiceConfigs {
|
||||
type: SyncService
|
||||
importGroups?: boolean
|
||||
}
|
||||
|
||||
export type SchemaTypes = {
|
||||
|
|
|
@ -185,7 +185,7 @@
|
|||
"select": "Select a service",
|
||||
"suggest": "Suggest a new service",
|
||||
"overwriteWarning": "Local sources will be deleted if they exist in the service.",
|
||||
"groupsWarning": "Groups are only imported on the first sync and will not stay synced.",
|
||||
"groupsWarning": "Groups aren't automatically synced with the service.",
|
||||
"endpoint": "Endpoint",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
|
|
|
@ -183,7 +183,7 @@
|
|||
"select": "选择服务",
|
||||
"suggest": "建议一项新服务",
|
||||
"overwriteWarning": "若本地与服务端存在URL相同的订阅源,则本地订阅源将被删除",
|
||||
"groupsWarning": "分组仅在第一次同步时导入而不会与服务端保持同步",
|
||||
"groupsWarning": "分组不会自动与服务端保持同步",
|
||||
"endpoint": "端点",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
|
|
|
@ -61,6 +61,18 @@ export function syncWithService(background = false): AppThunk<Promise<void>> {
|
|||
}
|
||||
}
|
||||
|
||||
export function importGroups(): AppThunk<Promise<void>> {
|
||||
return async (dispatch, getState) => {
|
||||
const configs = getState().service
|
||||
if (configs.type !== SyncService.None) {
|
||||
dispatch(saveSettings())
|
||||
configs.importGroups = true
|
||||
dispatch(saveServiceConfigs(configs))
|
||||
await dispatch(syncWithService())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function removeService(): AppThunk<Promise<void>> {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch(saveSettings())
|
||||
|
|
|
@ -17,7 +17,7 @@ export interface FeverConfigs extends ServiceConfigs {
|
|||
apiKey: string
|
||||
fetchLimit: number
|
||||
lastId?: number
|
||||
importGroups?: boolean
|
||||
useInt32?: boolean
|
||||
}
|
||||
|
||||
async function fetchAPI(configs: FeverConfigs, params="", postparams="") {
|
||||
|
@ -150,14 +150,24 @@ export const feverServiceHooks: ServiceHooks = {
|
|||
const configs = state.service as FeverConfigs
|
||||
const items = new Array()
|
||||
configs.lastId = configs.lastId || 0
|
||||
let min = 2147483647
|
||||
let min = configs.useInt32 ? 2147483647 : Number.MAX_SAFE_INTEGER
|
||||
let response
|
||||
do {
|
||||
response = await fetchAPI(configs, `&items&max_id=${min}`)
|
||||
if (response.items === undefined) throw APIError()
|
||||
items.push(...response.items.filter(i => i.id > configs.lastId))
|
||||
min = response.items.reduce((m, n) => Math.min(m, n.id), min)
|
||||
} while (min > configs.lastId && response.items.length >= 50 && items.length < configs.fetchLimit)
|
||||
if (response.items.length === 0 && min === Number.MAX_SAFE_INTEGER) {
|
||||
configs.useInt32 = true
|
||||
min = 2147483647
|
||||
response = undefined
|
||||
} else {
|
||||
min = response.items.reduce((m, n) => Math.min(m, n.id), min)
|
||||
}
|
||||
} while (
|
||||
min > configs.lastId &&
|
||||
(response === undefined || response.items.length >= 50) &&
|
||||
items.length < configs.fetchLimit
|
||||
)
|
||||
configs.lastId = items.reduce((m, n) => Math.max(m, n.id), configs.lastId)
|
||||
if (items.length > 0) {
|
||||
const fidMap = new Map<number, RSSSource>()
|
||||
|
@ -178,7 +188,7 @@ export const feverServiceHooks: ServiceHooks = {
|
|||
snippet: htmlDecode(i.html).trim(),
|
||||
creator: i.author,
|
||||
hasRead: Boolean(i.is_read),
|
||||
serviceRef: i.id
|
||||
serviceRef: typeof i.id === "string" ? parseInt(i.id) : i.id,
|
||||
} as RSSItem
|
||||
if (i.is_saved) item.starred = true
|
||||
// Try to get the thumbnail of the item
|
||||
|
|
Loading…
Reference in New Issue