Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: compatible with miniflux #660

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
<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 * data:; 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>
<body>
<div id="app" style="height: 100%;"></div>
</body>
</html>
</html>
3 changes: 1 addition & 2 deletions src/scripts/models/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
DELETE_SOURCE,
initSources,
SourceOpenTarget,
updateFavicon,
} from "./source"
import { RSSItem, ItemActionTypes, FETCH_ITEMS, fetchItems } from "./item"
import {
Expand All @@ -34,7 +33,7 @@ import {
} from "./page"
import { getCurrentLocale, setThemeDefaultFont } from "../settings"
import locales from "../i18n/_locales"
import { SYNC_SERVICE, ServiceActionTypes } from "./service"
import { SYNC_SERVICE, ServiceActionTypes, updateFavicon } from "./service"

export const enum ContextMenuType {
Hidden,
Expand Down
39 changes: 38 additions & 1 deletion src/scripts/models/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
insertSource,
addSourceSuccess,
updateSource,
updateFavicon,
} from "./source"
import { createSourceGroup, addSourceToGroup } from "./group"
import { fetchFavicon } from "../utils"

import { feverServiceHooks } from "./services/fever"
import { feedbinServiceHooks } from "./services/feedbin"
Expand All @@ -27,6 +27,7 @@ export interface ServiceHooks {
updateSources?: () => AppThunk<Promise<[RSSSource[], Map<string, string>]>>
fetchItems?: () => AppThunk<Promise<[RSSItem[], ServiceConfigs]>>
syncItems?: () => AppThunk<Promise<[Set<string>, Set<string>]>>
fetchFavicon?: (item: RSSSource) => AppThunk<Promise<string>>
markRead?: (item: RSSItem) => AppThunk
markUnread?: (item: RSSItem) => AppThunk
markAllRead?: (
Expand Down Expand Up @@ -103,6 +104,42 @@ function reauthenticate(hooks: ServiceHooks): AppThunk<Promise<void>> {
}
}

export function updateFavicon(
sids?: number[],
force = false
): AppThunk<Promise<void>> {
return async (dispatch, getState) => {
const initSources = getState().sources
if (!sids) {
sids = Object.values(initSources)
.filter(s => s.iconurl === undefined)
.map(s => s.sid)
} else {
sids = sids.filter(sid => sid in initSources)
}
const promises = sids.map(async sid => {
const url = initSources[sid].url
const source = getState().sources[sid]
if (
source &&
source.url === url &&
(force || source.iconurl === undefined)
) {
const hooks = dispatch(getServiceHooks())
let favicon: string
if (hooks.fetchFavicon) {
favicon = await dispatch(hooks.fetchFavicon(source))
} else {
favicon = await fetchFavicon(url)
}
source.iconurl = favicon || ""
await dispatch(updateSource(source))
}
})
await Promise.all(promises)
}
}

function updateSources(
hook: ServiceHooks["updateSources"]
): AppThunk<Promise<void>> {
Expand Down
94 changes: 82 additions & 12 deletions src/scripts/models/services/miniflux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ interface Entries {
entries: Entry[]
}

interface IconEntity {
id: number
mime_type: string
data: string
}

const APIError = () => new Error(intl.get("service.failure"))

// base endpoint, authorization with dedicated token or http basic user/pass pair
Expand Down Expand Up @@ -82,6 +88,37 @@ async function fetchAPI(
}
}

async function fetchByPage(
configs: MinifluxConfigs,
params: string = ""
): Promise<Entry[]> {
const quantity = 125
let continueId: number
let entriesResponse: Entries

const items: Entry[] = new Array()
do {
try {
let endpoint = `entries?order=id&direction=desc&limit=${quantity}&${params}`
if (continueId) {
endpoint += `&before_entry_id=${continueId}`
}
entriesResponse = await fetchAPI(configs, endpoint).then(response =>
response.json()
)

items.push(...entriesResponse.entries)
continueId = items[items.length - 1].id
} catch {
break
}
} while (
entriesResponse.entries &&
entriesResponse.total > entriesResponse.entries.length
)
return items
}

export const minifluxServiceHooks: ServiceHooks = {
// poll service info endpoint to verify auth
authenticate: async (configs: MinifluxConfigs) => {
Expand Down Expand Up @@ -228,25 +265,58 @@ export const minifluxServiceHooks: ServiceHooks = {
syncItems: () => async (_, getState) => {
const configs = getState().service as MinifluxConfigs

const unreadPromise: Promise<Entries> = fetchAPI(
configs,
"entries?status=unread"
).then(response => response.json())
const starredPromise: Promise<Entries> = fetchAPI(
configs,
"entries?starred=true"
).then(response => response.json())
// const unreadPromise: Promise<Entries> = fetchAPI(
// configs,
// "entries?status=unread"
// ).then(response => response.json())
// const starredPromise: Promise<Entries> = fetchAPI(
// configs,
// "entries?starred=true"
// ).then(response => response.json())
// const [unread, starred] = await Promise.all([
// unreadPromise,
// starredPromise,
// ])

// return [
// new Set(unread.entries.map((entry: Entry) => String(entry.id))),
// new Set(starred.entries.map((entry: Entry) => String(entry.id))),
// ]

const [unread, starred] = await Promise.all([
unreadPromise,
starredPromise,
fetchByPage(configs, "status=unread"),
fetchByPage(configs, "starred=true"),
])

return [
new Set(unread.entries.map((entry: Entry) => String(entry.id))),
new Set(starred.entries.map((entry: Entry) => String(entry.id))),
new Set(unread.map((entry: Entry) => String(entry.id))),
new Set(starred.map((entry: Entry) => String(entry.id))),
]
},

fetchFavicon: (source: RSSSource) => async (_, getState) => {
const configs = getState().service as MinifluxConfigs
try {
let response = await fetchAPI(configs, `feeds/${source.serviceRef}/icon`)
if (response.ok) {
let iconEntity: IconEntity = await response.json()
if (iconEntity.data === undefined || iconEntity.data == null) {
return null
}
if (iconEntity.data.length <= 0) {
return null
}
if (!iconEntity.data.startsWith("image/")) {
return null
}
return "data:" + iconEntity.data
}
return null
} catch {
return null
}
},

markRead: (item: RSSItem) => async (_, getState) => {
if (!item.serviceRef) return

Expand Down
59 changes: 30 additions & 29 deletions src/scripts/models/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
import { saveSettings } from "./app"
import { SourceRule } from "./rule"
import { fixBrokenGroups } from "./group"
import { updateFavicon } from "./service"

export const enum SourceOpenTarget {
Local,
Expand Down Expand Up @@ -412,35 +413,35 @@ export function toggleSourceHidden(source: RSSSource): AppThunk<Promise<void>> {
}
}

export function updateFavicon(
sids?: number[],
force = false
): AppThunk<Promise<void>> {
return async (dispatch, getState) => {
const initSources = getState().sources
if (!sids) {
sids = Object.values(initSources)
.filter(s => s.iconurl === undefined)
.map(s => s.sid)
} else {
sids = sids.filter(sid => sid in initSources)
}
const promises = sids.map(async sid => {
const url = initSources[sid].url
let favicon = (await fetchFavicon(url)) || ""
const source = getState().sources[sid]
if (
source &&
source.url === url &&
(force || source.iconurl === undefined)
) {
source.iconurl = favicon
await dispatch(updateSource(source))
}
})
await Promise.all(promises)
}
}
// export function updateFavicon(
// sids?: number[],
// force = false
// ): AppThunk<Promise<void>> {
// return async (dispatch, getState) => {
// const initSources = getState().sources
// if (!sids) {
// sids = Object.values(initSources)
// .filter(s => s.iconurl === undefined)
// .map(s => s.sid)
// } else {
// sids = sids.filter(sid => sid in initSources)
// }
// const promises = sids.map(async sid => {
// const url = initSources[sid].url
// let favicon = (await fetchFavicon(url)) || ""
// const source = getState().sources[sid]
// if (
// source &&
// source.url === url &&
// (force || source.iconurl === undefined)
// ) {
// source.iconurl = favicon
// await dispatch(updateSource(source))
// }
// })
// await Promise.all(promises)
// }
// }

export function sourceReducer(
state: SourceState = {},
Expand Down