forked from Archives/Athou_commafeed
Compare commits
79 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a337b01bc7 | ||
|
|
689e5c0004 | ||
|
|
d5a3c81c85 | ||
|
|
8230fde5d2 | ||
|
|
b35513ea84 | ||
|
|
42a7785ca1 | ||
|
|
ea5ee4f04f | ||
|
|
3e14b12d4f | ||
|
|
78cc30f828 | ||
|
|
6091c84e60 | ||
|
|
6ea95ad254 | ||
|
|
7f888d926e | ||
|
|
5e4e02474f | ||
|
|
bff8611b42 | ||
|
|
f674048af3 | ||
|
|
0265c24cf9 | ||
|
|
f8c3a229ec | ||
|
|
c424f40420 | ||
|
|
b77666cfe5 | ||
|
|
193d1604d9 | ||
|
|
4efc6296b5 | ||
|
|
f753a4bdda | ||
|
|
afaaaf9657 | ||
|
|
4d46896bf0 | ||
|
|
2ad28c927f | ||
|
|
b9680a66ef | ||
|
|
4f687d5857 | ||
|
|
9cca026834 | ||
|
|
058a9cd192 | ||
|
|
57d2ede86e | ||
|
|
e3abea4ec5 | ||
|
|
b831f1f35c | ||
|
|
74bce1308c | ||
|
|
98cfa6d2c8 | ||
|
|
99a02a2186 | ||
|
|
3431a813b1 | ||
|
|
e9e0e8d32b | ||
|
|
2d14409d35 | ||
|
|
a8200e5c58 | ||
|
|
79a8df8b06 | ||
|
|
061a5f0262 | ||
|
|
821bdb3b0f | ||
|
|
606dfa9299 | ||
|
|
131357c616 | ||
|
|
f6d3493bad | ||
|
|
0c6104e25b | ||
|
|
d73735a35d | ||
|
|
e725d2d6b6 | ||
|
|
f0e1279d68 | ||
|
|
c74c74d2c4 | ||
|
|
aa70cf5dcd | ||
|
|
1055259627 | ||
|
|
302d37b6ef | ||
|
|
8532a73d94 | ||
|
|
ffafb272cb | ||
|
|
22e0171a34 | ||
|
|
2b410f040c | ||
|
|
259e8ad4e5 | ||
|
|
21244dd9f5 | ||
|
|
bc6206180d | ||
|
|
6e22d21358 | ||
|
|
95bdb4e700 | ||
|
|
9b7dbc68ab | ||
|
|
dc86c9b0db | ||
|
|
cb92ed753f | ||
|
|
10a085e24e | ||
|
|
3400a39edf | ||
|
|
21efffa345 | ||
|
|
e2e80ba7e5 | ||
|
|
d988dba66e | ||
|
|
403201fbff | ||
|
|
3cc93b51bb | ||
|
|
6a7d83bb45 | ||
|
|
19c8db8b31 | ||
|
|
0d75688ec8 | ||
|
|
e01dcb2f5b | ||
|
|
57757e2c14 | ||
|
|
779cd2fcfe | ||
|
|
94919f22e4 |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,5 +1,22 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [5.3.3]
|
||||||
|
|
||||||
|
- Removed image bottom margins (#1587)
|
||||||
|
|
||||||
|
## [5.3.2]
|
||||||
|
|
||||||
|
- Fixed an issue that could cause some images from not being rendered correctly (#1587)
|
||||||
|
|
||||||
|
## [5.3.1]
|
||||||
|
|
||||||
|
- Fixed an issue that could cause some HTTP feeds to return a 400 error (#1572)
|
||||||
|
|
||||||
|
## [5.3.0]
|
||||||
|
|
||||||
|
- Added a setting to set a cooldown on the "fetch all my feeds" action, disabled by default (#1556)
|
||||||
|
- Fixed an issue that could cause entries to not correctly load when using the "next" header button (#1557)
|
||||||
|
|
||||||
## [5.2.0]
|
## [5.2.0]
|
||||||
|
|
||||||
- Added an option to keep a number of entries above the selected entry when scrolling
|
- Added an option to keep a number of entries above the selected entry when scrolling
|
||||||
|
|||||||
@@ -114,18 +114,18 @@ The default user is `admin` and the default password is `admin`.
|
|||||||
|
|
||||||
When CommaFeed is up and running, you can subscribe to [this feed](https://github.com/Athou/commafeed/releases.atom) to be notified of new releases.
|
When CommaFeed is up and running, you can subscribe to [this feed](https://github.com/Athou/commafeed/releases.atom) to be notified of new releases.
|
||||||
|
|
||||||
### Memory management (`jvm` package only)
|
### Memory management
|
||||||
|
|
||||||
The Java Virtual Machine (JVM) is rather greedy by default and will not release unused memory to the
|
The Java Virtual Machine (JVM) is rather greedy by default and will not release unused memory to the
|
||||||
operating system. This is because acquiring memory from the operating system is a relatively expensive operation.
|
operating system. This is because acquiring memory from the operating system is a relatively expensive operation.
|
||||||
This can be problematic on systems with limited memory.
|
This can be problematic on systems with limited memory.
|
||||||
|
|
||||||
#### Hard limit
|
#### Hard limit (`native` and `jvm` packages)
|
||||||
|
|
||||||
The JVM can be configured to use a maximum amount of memory with the `-Xmx` parameter.
|
The JVM can be configured to use a maximum amount of memory with the `-Xmx` parameter.
|
||||||
For example, to limit the JVM to 256MB of memory, use `-Xmx256m`.
|
For example, to limit the JVM to 256MB of memory, use `-Xmx256m`.
|
||||||
|
|
||||||
#### Dynamic sizing
|
#### Dynamic sizing (`jvm` package)
|
||||||
|
|
||||||
In addition to the previous setting, the JVM can be configured to release unused memory to the operating system with the
|
In addition to the previous setting, the JVM can be configured to release unused memory to the operating system with the
|
||||||
following parameters:
|
following parameters:
|
||||||
@@ -137,7 +137,7 @@ and [here](https://docs.oracle.com/en/java/javase/17/gctuning/factors-affecting-
|
|||||||
more
|
more
|
||||||
information.
|
information.
|
||||||
|
|
||||||
#### OpenJ9
|
#### OpenJ9 (`jvm` package)
|
||||||
|
|
||||||
The [OpenJ9](https://eclipse.dev/openj9/) JVM is a more memory-efficient alternative to the HotSpot JVM, at the cost of
|
The [OpenJ9](https://eclipse.dev/openj9/) JVM is a more memory-efficient alternative to the HotSpot JVM, at the cost of
|
||||||
slightly slower throughput.
|
slightly slower throughput.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://biomejs.dev/schemas/1.9.1/schema.json",
|
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||||
"formatter": {
|
"formatter": {
|
||||||
"indentStyle": "space",
|
"indentStyle": "space",
|
||||||
"indentWidth": 4,
|
"indentWidth": 4,
|
||||||
|
|||||||
1082
commafeed-client/package-lock.json
generated
1082
commafeed-client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -17,22 +17,22 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.13.3",
|
"@emotion/react": "^11.13.3",
|
||||||
"@fontsource/open-sans": "^5.1.0",
|
"@fontsource/open-sans": "^5.1.0",
|
||||||
"@lingui/core": "^4.11.4",
|
"@lingui/core": "^4.13.0",
|
||||||
"@lingui/macro": "^4.11.4",
|
"@lingui/macro": "^4.13.0",
|
||||||
"@lingui/react": "^4.11.4",
|
"@lingui/react": "^4.13.0",
|
||||||
"@mantine/core": "^7.12.2",
|
"@mantine/core": "^7.13.3",
|
||||||
"@mantine/form": "^7.12.2",
|
"@mantine/form": "^7.13.3",
|
||||||
"@mantine/hooks": "^7.12.2",
|
"@mantine/hooks": "^7.13.3",
|
||||||
"@mantine/modals": "^7.12.2",
|
"@mantine/modals": "^7.13.3",
|
||||||
"@mantine/notifications": "^7.12.2",
|
"@mantine/notifications": "^7.13.3",
|
||||||
"@mantine/spotlight": "^7.12.2",
|
"@mantine/spotlight": "^7.13.3",
|
||||||
"@monaco-editor/react": "^4.6.0",
|
"@monaco-editor/react": "^4.6.0",
|
||||||
"@reduxjs/toolkit": "^2.2.7",
|
"@reduxjs/toolkit": "^2.3.0",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"escape-string-regexp": "^5.0.0",
|
"escape-string-regexp": "^5.0.0",
|
||||||
"interweave": "^13.1.0",
|
"interweave": "^13.1.0",
|
||||||
"monaco-editor": "^0.51.0",
|
"monaco-editor": "^0.52.0",
|
||||||
"mousetrap": "^1.6.5",
|
"mousetrap": "^1.6.5",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-async-hook": "^4.0.0",
|
"react-async-hook": "^4.0.0",
|
||||||
@@ -45,35 +45,36 @@
|
|||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
"react-infinite-scroller": "^1.2.6",
|
"react-infinite-scroller": "^1.2.6",
|
||||||
"react-redux": "^9.1.2",
|
"react-redux": "^9.1.2",
|
||||||
"react-router-dom": "^6.26.2",
|
"react-router-dom": "^6.27.0",
|
||||||
"react-swipeable": "^7.0.1",
|
"react-swipeable": "^7.0.1",
|
||||||
"redoc": "^2.1.5",
|
"redoc": "^2.2.0",
|
||||||
|
"style-to-object": "^1.0.8",
|
||||||
"throttle-debounce": "^5.0.2",
|
"throttle-debounce": "^5.0.2",
|
||||||
"tinycon": "^0.6.8",
|
"tinycon": "^0.6.8",
|
||||||
"tss-react": "^4.9.13",
|
"tss-react": "^4.9.13",
|
||||||
"websocket-heartbeat-js": "^1.1.3"
|
"websocket-heartbeat-js": "^1.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.9.1",
|
"@biomejs/biome": "^1.9.4",
|
||||||
"@lingui/cli": "^4.11.4",
|
"@lingui/cli": "^4.13.0",
|
||||||
"@lingui/vite-plugin": "^4.11.4",
|
"@lingui/vite-plugin": "^4.13.0",
|
||||||
"@types/mousetrap": "^1.6.15",
|
"@types/mousetrap": "^1.6.15",
|
||||||
"@types/react": "^18.3.7",
|
"@types/react": "^18.3.11",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.1",
|
||||||
"@types/react-helmet": "^6.1.11",
|
"@types/react-helmet": "^6.1.11",
|
||||||
"@types/react-infinite-scroller": "^1.2.5",
|
"@types/react-infinite-scroller": "^1.2.5",
|
||||||
"@types/swagger-ui-react": "^4.18.3",
|
"@types/swagger-ui-react": "^4.18.3",
|
||||||
"@types/throttle-debounce": "^5.0.2",
|
"@types/throttle-debounce": "^5.0.2",
|
||||||
"@types/tinycon": "^0.6.5",
|
"@types/tinycon": "^0.6.5",
|
||||||
"@vitejs/plugin-react": "^4.3.1",
|
"@vitejs/plugin-react": "^4.3.3",
|
||||||
"babel-plugin-macros": "^3.1.0",
|
"babel-plugin-macros": "^3.1.0",
|
||||||
"jsdom": "^25.0.0",
|
"jsdom": "^25.0.1",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"typescript": "^5.6.2",
|
"typescript": "^5.6.3",
|
||||||
"vite": "^5.4.6",
|
"vite": "^5.4.9",
|
||||||
"vite-plugin-checker": "^0.8.0",
|
"vite-plugin-checker": "^0.8.0",
|
||||||
"vite-tsconfig-paths": "^5.0.1",
|
"vite-tsconfig-paths": "^5.0.1",
|
||||||
"vitest": "^2.1.1",
|
"vitest": "^2.1.3",
|
||||||
"vitest-mock-extended": "^2.0.2"
|
"vitest-mock-extended": "^2.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,16 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.commafeed</groupId>
|
<groupId>com.commafeed</groupId>
|
||||||
<artifactId>commafeed</artifactId>
|
<artifactId>commafeed</artifactId>
|
||||||
<version>5.2.0</version>
|
<version>5.3.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>commafeed-client</artifactId>
|
<artifactId>commafeed-client</artifactId>
|
||||||
<name>CommaFeed Client</name>
|
<name>CommaFeed Client</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<!-- renovate: datasource=node-version depName=node -->
|
<!-- renovate: datasource=node-version depName=node -->
|
||||||
<node.version>v20.17.0</node.version>
|
<node.version>v20.18.0</node.version>
|
||||||
<!-- renovate: datasource=npm depName=npm -->
|
<!-- renovate: datasource=npm depName=npm -->
|
||||||
<npm.version>10.8.3</npm.version>
|
<npm.version>10.9.0</npm.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.github.eirslett</groupId>
|
<groupId>com.github.eirslett</groupId>
|
||||||
<artifactId>frontend-maven-plugin</artifactId>
|
<artifactId>frontend-maven-plugin</artifactId>
|
||||||
<version>1.15.0</version>
|
<version>1.15.1</version>
|
||||||
<?m2e ignore?>
|
<?m2e ignore?>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ export const selectPreviousEntry = createAppAsyncThunk(
|
|||||||
)
|
)
|
||||||
export const selectNextEntry = createAppAsyncThunk(
|
export const selectNextEntry = createAppAsyncThunk(
|
||||||
"entries/entry/selectNext",
|
"entries/entry/selectNext",
|
||||||
(
|
async (
|
||||||
arg: {
|
arg: {
|
||||||
expand: boolean
|
expand: boolean
|
||||||
markAsRead: boolean
|
markAsRead: boolean
|
||||||
@@ -239,12 +239,20 @@ export const selectNextEntry = createAppAsyncThunk(
|
|||||||
thunkApi
|
thunkApi
|
||||||
) => {
|
) => {
|
||||||
const state = thunkApi.getState()
|
const state = thunkApi.getState()
|
||||||
const { entries } = state.entries
|
const { entries, hasMore, loading } = state.entries
|
||||||
const nextIndex = entries.findIndex(e => e.id === state.entries.selectedEntryId) + 1
|
const nextIndex = entries.findIndex(e => e.id === state.entries.selectedEntryId) + 1
|
||||||
if (nextIndex < entries.length) {
|
|
||||||
|
// load more entries if needed
|
||||||
|
// this can happen if the last entry is too large to fit on the screen and the infinite loader doesn't trigger
|
||||||
|
if (nextIndex >= entries.length && hasMore && !loading) {
|
||||||
|
await thunkApi.dispatch(loadMoreEntries())
|
||||||
|
}
|
||||||
|
|
||||||
|
const entriesAfterLoading = thunkApi.getState().entries.entries
|
||||||
|
if (nextIndex < entriesAfterLoading.length) {
|
||||||
thunkApi.dispatch(
|
thunkApi.dispatch(
|
||||||
selectEntry({
|
selectEntry({
|
||||||
entry: entries[nextIndex],
|
entry: entriesAfterLoading[nextIndex],
|
||||||
expand: arg.expand,
|
expand: arg.expand,
|
||||||
markAsRead: arg.markAsRead,
|
markAsRead: arg.markAsRead,
|
||||||
scrollToEntry: arg.scrollToEntry,
|
scrollToEntry: arg.scrollToEntry,
|
||||||
|
|||||||
@@ -220,6 +220,7 @@ export interface ServerInfo {
|
|||||||
websocketEnabled: boolean
|
websocketEnabled: boolean
|
||||||
websocketPingInterval: number
|
websocketPingInterval: number
|
||||||
treeReloadInterval: number
|
treeReloadInterval: number
|
||||||
|
forceRefreshCooldownDuration: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SharingSettings {
|
export interface SharingSettings {
|
||||||
@@ -287,6 +288,7 @@ export interface UserModel {
|
|||||||
created: number
|
created: number
|
||||||
lastLogin?: number
|
lastLogin?: number
|
||||||
admin: boolean
|
admin: boolean
|
||||||
|
lastForceRefresh?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminSaveUserRequest {
|
export interface AdminSaveUserRequest {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ interface ImageWithPlaceholderWhileLoadingProps {
|
|||||||
title?: string
|
title?: string
|
||||||
width?: number
|
width?: number
|
||||||
height?: number | "auto"
|
height?: number | "auto"
|
||||||
|
style?: React.CSSProperties
|
||||||
placeholderWidth?: number
|
placeholderWidth?: number
|
||||||
placeholderHeight?: number
|
placeholderHeight?: number
|
||||||
placeholderBackgroundColor?: string
|
placeholderBackgroundColor?: string
|
||||||
@@ -42,6 +43,7 @@ export function ImageWithPlaceholderWhileLoading({
|
|||||||
src,
|
src,
|
||||||
title,
|
title,
|
||||||
width,
|
width,
|
||||||
|
style,
|
||||||
}: ImageWithPlaceholderWhileLoadingProps) {
|
}: ImageWithPlaceholderWhileLoadingProps) {
|
||||||
const { classes } = useStyles({
|
const { classes } = useStyles({
|
||||||
placeholderWidth,
|
placeholderWidth,
|
||||||
@@ -68,7 +70,7 @@ export function ImageWithPlaceholderWhileLoading({
|
|||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
onLoad={() => setLoading(false)}
|
onLoad={() => setLoading(false)}
|
||||||
style={{ display: loading ? "none" : "block" }}
|
style={{ ...style, display: loading ? "none" : (style?.display ?? "initial") }}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,14 +2,10 @@ import { Trans } from "@lingui/macro"
|
|||||||
import { Tooltip } from "@mantine/core"
|
import { Tooltip } from "@mantine/core"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "app/constants"
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import { useEffect, useState } from "react"
|
import { useNow } from "hooks/useNow"
|
||||||
|
|
||||||
export function RelativeDate(props: { date: Date | number | undefined }) {
|
export function RelativeDate(props: { date: Date | number | undefined }) {
|
||||||
const [now, setNow] = useState(new Date())
|
const now = useNow(60 * 1000)
|
||||||
useEffect(() => {
|
|
||||||
const interval = setInterval(() => setNow(new Date()), 60 * 1000)
|
|
||||||
return () => clearInterval(interval)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (!props.date) return <Trans>N/A</Trans>
|
if (!props.date) return <Trans>N/A</Trans>
|
||||||
const date = dayjs(props.date)
|
const date = dayjs(props.date)
|
||||||
|
|||||||
@@ -1,11 +1,24 @@
|
|||||||
import { TypographyStylesProvider } from "@mantine/core"
|
import { TypographyStylesProvider } from "@mantine/core"
|
||||||
import type { ReactNode } from "react"
|
import type { ReactNode } from "react"
|
||||||
|
import { tss } from "tss"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component is used to provide basic styles to html typography elements.
|
* This component is used to provide basic styles to html typography elements.
|
||||||
*
|
*
|
||||||
* see https://mantine.dev/core/typography-styles-provider/
|
* see https://mantine.dev/core/typography-styles-provider/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const useStyles = tss.create(() => ({
|
||||||
|
// override mantine default typography styles
|
||||||
|
content: {
|
||||||
|
paddingLeft: 0,
|
||||||
|
"& img": {
|
||||||
|
marginBottom: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
export const BasicHtmlStyles = (props: { children: ReactNode }) => {
|
export const BasicHtmlStyles = (props: { children: ReactNode }) => {
|
||||||
return <TypographyStylesProvider pl={0}>{props.children}</TypographyStylesProvider>
|
const { classes } = useStyles()
|
||||||
|
return <TypographyStylesProvider className={classes.content}>{props.children}</TypographyStylesProvider>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
|
|||||||
import escapeStringRegexp from "escape-string-regexp"
|
import escapeStringRegexp from "escape-string-regexp"
|
||||||
import { type ChildrenNode, Interweave, type MatchResponse, Matcher, type Node, type TransformCallback } from "interweave"
|
import { type ChildrenNode, Interweave, type MatchResponse, Matcher, type Node, type TransformCallback } from "interweave"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
|
import styleToObject from "style-to-object"
|
||||||
import { tss } from "tss"
|
import { tss } from "tss"
|
||||||
|
|
||||||
export interface ContentProps {
|
export interface ContentProps {
|
||||||
@@ -42,6 +43,7 @@ const transform: TransformCallback = node => {
|
|||||||
const nodeHeight = node.getAttribute("height")
|
const nodeHeight = node.getAttribute("height")
|
||||||
const width = nodeWidth ? Number.parseInt(nodeWidth, 10) : undefined
|
const width = nodeWidth ? Number.parseInt(nodeWidth, 10) : undefined
|
||||||
const height = nodeHeight ? Number.parseInt(nodeHeight, 10) : undefined
|
const height = nodeHeight ? Number.parseInt(nodeHeight, 10) : undefined
|
||||||
|
const style = styleToObject(node.getAttribute("style") ?? "") ?? undefined
|
||||||
const placeholderSize = calculatePlaceholderSize({
|
const placeholderSize = calculatePlaceholderSize({
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
@@ -55,6 +57,7 @@ const transform: TransformCallback = node => {
|
|||||||
title={title}
|
title={title}
|
||||||
width={width}
|
width={width}
|
||||||
height="auto"
|
height="auto"
|
||||||
|
style={style}
|
||||||
placeholderWidth={placeholderSize.width}
|
placeholderWidth={placeholderSize.width}
|
||||||
placeholderHeight={placeholderSize.height}
|
placeholderHeight={placeholderSize.height}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -128,36 +128,28 @@ export function FeedEntries() {
|
|||||||
}, [dispatch, entries, viewMode, scrollMarks, scrollingToEntry])
|
}, [dispatch, entries, viewMode, scrollMarks, scrollingToEntry])
|
||||||
|
|
||||||
useMousetrap("r", async () => await dispatch(reloadEntries()))
|
useMousetrap("r", async () => await dispatch(reloadEntries()))
|
||||||
useMousetrap("j", async () => {
|
useMousetrap(
|
||||||
// load more entries if needed
|
"j",
|
||||||
// this can happen if the last entry is too large to fit on the screen and the infinite loader doesn't trigger
|
async () =>
|
||||||
if (hasMore && !loading && selectedEntry === entries[entries.length - 1]) {
|
await dispatch(
|
||||||
await dispatch(loadMoreEntries())
|
selectNextEntry({
|
||||||
}
|
expand: true,
|
||||||
|
markAsRead: true,
|
||||||
await dispatch(
|
scrollToEntry: true,
|
||||||
selectNextEntry({
|
})
|
||||||
expand: true,
|
)
|
||||||
markAsRead: true,
|
)
|
||||||
scrollToEntry: true,
|
useMousetrap(
|
||||||
})
|
"n",
|
||||||
)
|
async () =>
|
||||||
})
|
await dispatch(
|
||||||
useMousetrap("n", async () => {
|
selectNextEntry({
|
||||||
// load more entries if needed
|
expand: false,
|
||||||
// this can happen if the last entry is too large to fit on the screen and the infinite loader doesn't trigger
|
markAsRead: false,
|
||||||
if (hasMore && !loading && selectedEntry === entries[entries.length - 1]) {
|
scrollToEntry: true,
|
||||||
await dispatch(loadMoreEntries())
|
})
|
||||||
}
|
)
|
||||||
|
)
|
||||||
await dispatch(
|
|
||||||
selectNextEntry({
|
|
||||||
expand: false,
|
|
||||||
markAsRead: false,
|
|
||||||
scrollToEntry: true,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
useMousetrap(
|
useMousetrap(
|
||||||
"k",
|
"k",
|
||||||
async () =>
|
async () =>
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ import { redirectToAbout, redirectToAdminUsers, redirectToDonate, redirectToMetr
|
|||||||
import { useAppDispatch, useAppSelector } from "app/store"
|
import { useAppDispatch, useAppSelector } from "app/store"
|
||||||
import type { ViewMode } from "app/types"
|
import type { ViewMode } from "app/types"
|
||||||
import { setViewMode } from "app/user/slice"
|
import { setViewMode } from "app/user/slice"
|
||||||
|
import { reloadProfile } from "app/user/thunks"
|
||||||
|
import dayjs from "dayjs"
|
||||||
|
import { useNow } from "hooks/useNow"
|
||||||
import { type ReactNode, useState } from "react"
|
import { type ReactNode, useState } from "react"
|
||||||
import {
|
import {
|
||||||
TbChartLine,
|
TbChartLine,
|
||||||
@@ -92,12 +95,19 @@ const viewModeData: ViewModeControlItem[] = [
|
|||||||
|
|
||||||
export function ProfileMenu(props: ProfileMenuProps) {
|
export function ProfileMenu(props: ProfileMenuProps) {
|
||||||
const [opened, setOpened] = useState(false)
|
const [opened, setOpened] = useState(false)
|
||||||
|
const now = useNow()
|
||||||
const profile = useAppSelector(state => state.user.profile)
|
const profile = useAppSelector(state => state.user.profile)
|
||||||
const admin = useAppSelector(state => state.user.profile?.admin)
|
const admin = useAppSelector(state => state.user.profile?.admin)
|
||||||
const viewMode = useAppSelector(state => state.user.localSettings.viewMode)
|
const viewMode = useAppSelector(state => state.user.localSettings.viewMode)
|
||||||
|
const forceRefreshCooldownDuration = useAppSelector(state => state.server.serverInfos?.forceRefreshCooldownDuration)
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const { colorScheme, setColorScheme } = useMantineColorScheme()
|
const { colorScheme, setColorScheme } = useMantineColorScheme()
|
||||||
|
|
||||||
|
const nextAvailableForceRefresh = profile?.lastForceRefresh
|
||||||
|
? profile.lastForceRefresh + (forceRefreshCooldownDuration ?? 0)
|
||||||
|
: now.getTime()
|
||||||
|
const forceRefreshEnabled = nextAvailableForceRefresh <= now.getTime()
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
window.location.href = "logout"
|
window.location.href = "logout"
|
||||||
}
|
}
|
||||||
@@ -118,18 +128,32 @@ export function ProfileMenu(props: ProfileMenuProps) {
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
leftSection={<TbWorldDownload size={iconSize} />}
|
leftSection={<TbWorldDownload size={iconSize} />}
|
||||||
onClick={async () =>
|
disabled={!forceRefreshEnabled}
|
||||||
await client.feed.refreshAll().then(() => {
|
onClick={async () => {
|
||||||
|
setOpened(false)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await client.feed.refreshAll()
|
||||||
|
|
||||||
|
// reload profile to update last force refresh timestamp
|
||||||
|
await dispatch(reloadProfile())
|
||||||
|
|
||||||
showNotification({
|
showNotification({
|
||||||
message: <Trans>Your feeds have been queued for refresh.</Trans>,
|
message: <Trans>Your feeds have been queued for refresh.</Trans>,
|
||||||
color: "green",
|
color: "green",
|
||||||
autoClose: 1000,
|
autoClose: 1000,
|
||||||
})
|
})
|
||||||
setOpened(false)
|
} catch (error) {
|
||||||
})
|
showNotification({
|
||||||
}
|
message: <Trans>Force fetching feeds is not yet available.</Trans>,
|
||||||
|
color: "red",
|
||||||
|
autoClose: 2000,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Trans>Fetch all my feeds now</Trans>
|
<Trans>Fetch all my feeds now</Trans>
|
||||||
|
{!forceRefreshEnabled && <span> ({dayjs.duration(nextAvailableForceRefresh - now.getTime()).format("HH:mm:ss")})</span>}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|||||||
10
commafeed-client/src/hooks/useNow.ts
Normal file
10
commafeed-client/src/hooks/useNow.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
|
export const useNow = (interval = 1000): Date => {
|
||||||
|
const [time, setTime] = useState(new Date())
|
||||||
|
useEffect(() => {
|
||||||
|
const t = setInterval(() => setTime(new Date()), interval)
|
||||||
|
return () => clearInterval(t)
|
||||||
|
}, [interval])
|
||||||
|
return time
|
||||||
|
}
|
||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "تصفية التعبير"
|
msgstr "تصفية التعبير"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "هل نسيت كلمة المرور؟"
|
msgstr "هل نسيت كلمة المرور؟"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Expressió de filtratge"
|
msgstr "Expressió de filtratge"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Heu oblidat la contrasenya?"
|
msgstr "Heu oblidat la contrasenya?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filtrování výrazu"
|
msgstr "Filtrování výrazu"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Zapomněli jste heslo?"
|
msgstr "Zapomněli jste heslo?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Hidlo mynegiant"
|
msgstr "Hidlo mynegiant"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Wedi anghofio cyfrinair?"
|
msgstr "Wedi anghofio cyfrinair?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filtrerende udtryk"
|
msgstr "Filtrerende udtryk"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Glemt adgangskode?"
|
msgstr "Glemt adgangskode?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filterausdruck"
|
msgstr "Filterausdruck"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Passwort vergessen?"
|
msgstr "Passwort vergessen?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr "Fever API URL"
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filtering expression"
|
msgstr "Filtering expression"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr "Force fetching feeds is not yet available."
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Forgot password?"
|
msgstr "Forgot password?"
|
||||||
|
|||||||
@@ -377,6 +377,10 @@ msgstr "URL de la API de Fever"
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Expresión de filtrado"
|
msgstr "Expresión de filtrado"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "¿Olvidaste la contraseña?"
|
msgstr "¿Olvidaste la contraseña?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "بیان فیلتر"
|
msgstr "بیان فیلتر"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "رمز عبور را فراموش کرده اید؟"
|
msgstr "رمز عبور را فراموش کرده اید؟"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Suodattava lauseke"
|
msgstr "Suodattava lauseke"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Unohditko salasanan?"
|
msgstr "Unohditko salasanan?"
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ msgstr "Entrez votre mot de passe actuel pour changer les paramètres du profil"
|
|||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Entries to keep above the selected entry when scrolling"
|
msgid "Entries to keep above the selected entry when scrolling"
|
||||||
msgstr ""
|
msgstr "Nombre d'entrées à conserver au-dessus de l'entrée sélectionnée lors d'un défilement"
|
||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Entry headers"
|
msgid "Entry headers"
|
||||||
@@ -376,6 +376,10 @@ msgstr "URL API Fever"
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Expression de filtrage"
|
msgstr "Expression de filtrage"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr "La récupération forcée des flux n'est pas encore disponible."
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Mot de passe oublié ?"
|
msgstr "Mot de passe oublié ?"
|
||||||
@@ -614,7 +618,7 @@ msgstr "Sur mobile, afficher les boutons d'action en bas de l'écran"
|
|||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Only applies to compact, cozy and detailed modes"
|
msgid "Only applies to compact, cozy and detailed modes"
|
||||||
msgstr ""
|
msgstr "Ne fonctionne que dans les modes Compact, Cozy, et Vue détaillée"
|
||||||
|
|
||||||
#: src/pages/ErrorPage.tsx
|
#: src/pages/ErrorPage.tsx
|
||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Expresión de filtrado"
|
msgstr "Expresión de filtrado"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Esqueceches o contrasinal?"
|
msgstr "Esqueceches o contrasinal?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Szűrő kifejezés"
|
msgstr "Szűrő kifejezés"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Elfelejtette a jelszavát?"
|
msgstr "Elfelejtette a jelszavát?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Memfilter ekspresi"
|
msgstr "Memfilter ekspresi"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Lupa kata sandi?"
|
msgstr "Lupa kata sandi?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Espressione filtrante"
|
msgstr "Espressione filtrante"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Password dimenticata?"
|
msgstr "Password dimenticata?"
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ msgstr "ユーザー追加"
|
|||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "管理人"
|
msgstr "管理者"
|
||||||
|
|
||||||
#: src/app/constants.ts
|
#: src/app/constants.ts
|
||||||
#: src/components/content/add/CategorySelect.tsx
|
#: src/components/content/add/CategorySelect.tsx
|
||||||
@@ -323,7 +323,7 @@ msgstr "プロファイル設定を変更するには、現在のパスワード
|
|||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Entries to keep above the selected entry when scrolling"
|
msgid "Entries to keep above the selected entry when scrolling"
|
||||||
msgstr ""
|
msgstr "エントリーを選択したとき、読みやすさに応じたスクロール調整を行います。"
|
||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Entry headers"
|
msgid "Entry headers"
|
||||||
@@ -376,6 +376,10 @@ msgstr "Fever API URL"
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "フィルタリング式"
|
msgstr "フィルタリング式"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr "フィードの強制フェッチはまだ利用できません。"
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "パスワードをお忘れですか?"
|
msgstr "パスワードをお忘れですか?"
|
||||||
@@ -398,7 +402,7 @@ msgstr "生成されたフィードURL"
|
|||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Go to {0}"
|
msgid "Go to {0}"
|
||||||
msgstr "Go to {0}"
|
msgstr "{0} に移動"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Go to the All view"
|
msgid "Go to the All view"
|
||||||
@@ -418,11 +422,11 @@ msgstr "ID"
|
|||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "If not empty, an expression evaluating to 'true' or 'false'. If false, new entries for this feed will be marked as read automatically."
|
msgid "If not empty, an expression evaluating to 'true' or 'false'. If false, new entries for this feed will be marked as read automatically."
|
||||||
msgstr "空でない場合は、'true' または 'false' に評価される式。 'false' の場合、このフィードの新しいエントリは自動的に既読としてマークされます。"
|
msgstr "空でない場合は、'true' または 'false' に評価される式。 'false' の場合、このフィードの新しいエントリーは自動的に既読としてマークされます。"
|
||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "If the entry doesn't entirely fit on the screen"
|
msgid "If the entry doesn't entirely fit on the screen"
|
||||||
msgstr "エントリが画面に完全に収まらない場合"
|
msgstr "エントリーが画面に完全に収まらない場合"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "If you encounter an issue, please report it on the issues page of the GitHub project."
|
msgid "If you encounter an issue, please report it on the issues page of the GitHub project."
|
||||||
@@ -614,7 +618,7 @@ msgstr "モバイルでは、画面の下部にアクションボタンを表示
|
|||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Only applies to compact, cozy and detailed modes"
|
msgid "Only applies to compact, cozy and detailed modes"
|
||||||
msgstr ""
|
msgstr "これはコンパクト/cozy/詳細モードでのみ適用されます"
|
||||||
|
|
||||||
#: src/pages/ErrorPage.tsx
|
#: src/pages/ErrorPage.tsx
|
||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
@@ -751,7 +755,7 @@ msgstr "保存"
|
|||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Scroll selected entry to the top of the page"
|
msgid "Scroll selected entry to the top of the page"
|
||||||
msgstr "選択されたエントリーをページの上部にスクロールする"
|
msgstr "エントリーを選択したときのスクロール調整"
|
||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Scroll smoothly when navigating between entries"
|
msgid "Scroll smoothly when navigating between entries"
|
||||||
@@ -774,11 +778,11 @@ msgstr "検索には少なくとも3文字が必要です"
|
|||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Set focus on next entry without opening it"
|
msgid "Set focus on next entry without opening it"
|
||||||
msgstr "次のエントリーを開かずにフォーカスを設定する"
|
msgstr "次のエントリーを開かずにフォーカスする"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Set focus on previous entry without opening it"
|
msgid "Set focus on previous entry without opening it"
|
||||||
msgstr "前のエントリーを開かずにフォーカスを設定する"
|
msgstr "前のエントリーを開かずにフォーカスする"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "필터링 표현식"
|
msgstr "필터링 표현식"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "비밀번호를 잊으셨나요?"
|
msgstr "비밀번호를 잊으셨나요?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Ungkapan penapisan"
|
msgstr "Ungkapan penapisan"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Lupa kata laluan?"
|
msgstr "Lupa kata laluan?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filtrerende uttrykk"
|
msgstr "Filtrerende uttrykk"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Glemt passord?"
|
msgstr "Glemt passord?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Uitdrukking filteren"
|
msgstr "Uitdrukking filteren"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Wachtwoord vergeten?"
|
msgstr "Wachtwoord vergeten?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filtrerende uttrykk"
|
msgstr "Filtrerende uttrykk"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Glemt passord?"
|
msgstr "Glemt passord?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Wyrażenie filtrujące"
|
msgstr "Wyrażenie filtrujące"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Zapomniałeś hasła?"
|
msgstr "Zapomniałeś hasła?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filtrando expressão"
|
msgstr "Filtrando expressão"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Esqueceu a senha?"
|
msgstr "Esqueceu a senha?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr "Ссылка Fever API"
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Выражение фильтрации"
|
msgstr "Выражение фильтрации"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Забыли пароль?"
|
msgstr "Забыли пароль?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filtrovanie výrazu"
|
msgstr "Filtrovanie výrazu"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Zabudli ste heslo?"
|
msgstr "Zabudli ste heslo?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filtrerande uttryck"
|
msgstr "Filtrerande uttryck"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Glömt lösenord?"
|
msgstr "Glömt lösenord?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr ""
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "Filtreleme ifadesi"
|
msgstr "Filtreleme ifadesi"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Parolanızı mı unuttunuz?"
|
msgstr "Parolanızı mı unuttunuz?"
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ msgstr "Fever API 网址"
|
|||||||
msgid "Filtering expression"
|
msgid "Filtering expression"
|
||||||
msgstr "过滤表达式"
|
msgstr "过滤表达式"
|
||||||
|
|
||||||
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
msgid "Force fetching feeds is not yet available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "忘记密码?"
|
msgstr "忘记密码?"
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import "react-contexify/ReactContexify.css"
|
|||||||
import { App } from "App"
|
import { App } from "App"
|
||||||
import { store } from "app/store"
|
import { store } from "app/store"
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
|
import duration from "dayjs/plugin/duration"
|
||||||
import relativeTime from "dayjs/plugin/relativeTime"
|
import relativeTime from "dayjs/plugin/relativeTime"
|
||||||
import ReactDOM from "react-dom/client"
|
import ReactDOM from "react-dom/client"
|
||||||
import { Provider } from "react-redux"
|
import { Provider } from "react-redux"
|
||||||
|
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
dayjs.extend(duration)
|
||||||
|
|
||||||
const root = document.getElementById("root")
|
const root = document.getElementById("root")
|
||||||
root &&
|
root &&
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ h|[.header-title]##Configuration property##
|
|||||||
h|Type
|
h|Type
|
||||||
h|Default
|
h|Default
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-hide-from-web-crawlers]] [.property-path]##`commafeed.hide-from-web-crawlers`##
|
a| [[commafeed-server_commafeed-hide-from-web-crawlers]] [.property-path]##link:#commafeed-server_commafeed-hide-from-web-crawlers[`commafeed.hide-from-web-crawlers`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -25,7 +25,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|boolean
|
|boolean
|
||||||
|`true`
|
|`true`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-image-proxy-enabled]] [.property-path]##`commafeed.image-proxy-enabled`##
|
a| [[commafeed-server_commafeed-image-proxy-enabled]] [.property-path]##link:#commafeed-server_commafeed-image-proxy-enabled[`commafeed.image-proxy-enabled`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -42,7 +42,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|boolean
|
|boolean
|
||||||
|`false`
|
|`false`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-password-recovery-enabled]] [.property-path]##`commafeed.password-recovery-enabled`##
|
a| [[commafeed-server_commafeed-password-recovery-enabled]] [.property-path]##link:#commafeed-server_commafeed-password-recovery-enabled[`commafeed.password-recovery-enabled`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -59,7 +59,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|boolean
|
|boolean
|
||||||
|`false`
|
|`false`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-announcement]] [.property-path]##`commafeed.announcement`##
|
a| [[commafeed-server_commafeed-announcement]] [.property-path]##link:#commafeed-server_commafeed-announcement[`commafeed.announcement`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -76,7 +76,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|string
|
|string
|
||||||
|
|
|
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-google-analytics-tracking-code]] [.property-path]##`commafeed.google-analytics-tracking-code`##
|
a| [[commafeed-server_commafeed-google-analytics-tracking-code]] [.property-path]##link:#commafeed-server_commafeed-google-analytics-tracking-code[`commafeed.google-analytics-tracking-code`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -93,7 +93,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|string
|
|string
|
||||||
|
|
|
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-google-auth-key]] [.property-path]##`commafeed.google-auth-key`##
|
a| [[commafeed-server_commafeed-google-auth-key]] [.property-path]##link:#commafeed-server_commafeed-google-auth-key[`commafeed.google-auth-key`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -110,11 +110,11 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|string
|
|string
|
||||||
|
|
|
|
||||||
|
|
||||||
h|[[commafeed-server_section_commafeed-http-client]] [.section-name.section-level0]##HTTP client configuration##
|
h|[[commafeed-server_section_commafeed-http-client]] [.section-name.section-level0]##link:#commafeed-server_section_commafeed-http-client[HTTP client configuration]##
|
||||||
h|Type
|
h|Type
|
||||||
h|Default
|
h|Default
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-http-client-user-agent]] [.property-path]##`commafeed.http-client.user-agent`##
|
a| [[commafeed-server_commafeed-http-client-user-agent]] [.property-path]##link:#commafeed-server_commafeed-http-client-user-agent[`commafeed.http-client.user-agent`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -131,7 +131,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|string
|
|string
|
||||||
|
|
|
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-http-client-connect-timeout]] [.property-path]##`commafeed.http-client.connect-timeout`##
|
a| [[commafeed-server_commafeed-http-client-connect-timeout]] [.property-path]##link:#commafeed-server_commafeed-http-client-connect-timeout[`commafeed.http-client.connect-timeout`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -148,7 +148,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|`5S`
|
|`5S`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-http-client-ssl-handshake-timeout]] [.property-path]##`commafeed.http-client.ssl-handshake-timeout`##
|
a| [[commafeed-server_commafeed-http-client-ssl-handshake-timeout]] [.property-path]##link:#commafeed-server_commafeed-http-client-ssl-handshake-timeout[`commafeed.http-client.ssl-handshake-timeout`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -165,7 +165,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|`5S`
|
|`5S`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-http-client-socket-timeout]] [.property-path]##`commafeed.http-client.socket-timeout`##
|
a| [[commafeed-server_commafeed-http-client-socket-timeout]] [.property-path]##link:#commafeed-server_commafeed-http-client-socket-timeout[`commafeed.http-client.socket-timeout`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -182,7 +182,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|`10S`
|
|`10S`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-http-client-response-timeout]] [.property-path]##`commafeed.http-client.response-timeout`##
|
a| [[commafeed-server_commafeed-http-client-response-timeout]] [.property-path]##link:#commafeed-server_commafeed-http-client-response-timeout[`commafeed.http-client.response-timeout`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -199,7 +199,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|`10S`
|
|`10S`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-http-client-connection-time-to-live]] [.property-path]##`commafeed.http-client.connection-time-to-live`##
|
a| [[commafeed-server_commafeed-http-client-connection-time-to-live]] [.property-path]##link:#commafeed-server_commafeed-http-client-connection-time-to-live[`commafeed.http-client.connection-time-to-live`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -216,7 +216,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|`30S`
|
|`30S`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-http-client-idle-connections-eviction-interval]] [.property-path]##`commafeed.http-client.idle-connections-eviction-interval`##
|
a| [[commafeed-server_commafeed-http-client-idle-connections-eviction-interval]] [.property-path]##link:#commafeed-server_commafeed-http-client-idle-connections-eviction-interval[`commafeed.http-client.idle-connections-eviction-interval`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -233,7 +233,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|`1M`
|
|`1M`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-http-client-max-response-size]] [.property-path]##`commafeed.http-client.max-response-size`##
|
a| [[commafeed-server_commafeed-http-client-max-response-size]] [.property-path]##link:#commafeed-server_commafeed-http-client-max-response-size[`commafeed.http-client.max-response-size`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -250,11 +250,11 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|MemorySize link:#memory-size-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the MemorySize format]]
|
|MemorySize link:#memory-size-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the MemorySize format]]
|
||||||
|`5M`
|
|`5M`
|
||||||
|
|
||||||
h|[[commafeed-server_section_commafeed-http-client-cache]] [.section-name.section-level1]##HTTP client cache configuration##
|
h|[[commafeed-server_section_commafeed-http-client-cache]] [.section-name.section-level1]##link:#commafeed-server_section_commafeed-http-client-cache[HTTP client cache configuration]##
|
||||||
h|Type
|
h|Type
|
||||||
h|Default
|
h|Default
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-http-client-cache-enabled]] [.property-path]##`commafeed.http-client.cache.enabled`##
|
a| [[commafeed-server_commafeed-http-client-cache-enabled]] [.property-path]##link:#commafeed-server_commafeed-http-client-cache-enabled[`commafeed.http-client.cache.enabled`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -271,7 +271,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|boolean
|
|boolean
|
||||||
|`true`
|
|`true`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-http-client-cache-maximum-memory-size]] [.property-path]##`commafeed.http-client.cache.maximum-memory-size`##
|
a| [[commafeed-server_commafeed-http-client-cache-maximum-memory-size]] [.property-path]##link:#commafeed-server_commafeed-http-client-cache-maximum-memory-size[`commafeed.http-client.cache.maximum-memory-size`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -288,7 +288,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|MemorySize link:#memory-size-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the MemorySize format]]
|
|MemorySize link:#memory-size-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the MemorySize format]]
|
||||||
|`10M`
|
|`10M`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-http-client-cache-expiration]] [.property-path]##`commafeed.http-client.cache.expiration`##
|
a| [[commafeed-server_commafeed-http-client-cache-expiration]] [.property-path]##link:#commafeed-server_commafeed-http-client-cache-expiration[`commafeed.http-client.cache.expiration`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -307,11 +307,11 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
h|[[commafeed-server_section_commafeed-feed-refresh]] [.section-name.section-level0]##Feed refresh engine settings##
|
h|[[commafeed-server_section_commafeed-feed-refresh]] [.section-name.section-level0]##link:#commafeed-server_section_commafeed-feed-refresh[Feed refresh engine settings]##
|
||||||
h|Type
|
h|Type
|
||||||
h|Default
|
h|Default
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-feed-refresh-interval]] [.property-path]##`commafeed.feed-refresh.interval`##
|
a| [[commafeed-server_commafeed-feed-refresh-interval]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-interval[`commafeed.feed-refresh.interval`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -328,7 +328,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|`5M`
|
|`5M`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-feed-refresh-interval-empirical]] [.property-path]##`commafeed.feed-refresh.interval-empirical`##
|
a| [[commafeed-server_commafeed-feed-refresh-interval-empirical]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-interval-empirical[`commafeed.feed-refresh.interval-empirical`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -345,7 +345,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|boolean
|
|boolean
|
||||||
|`false`
|
|`false`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-feed-refresh-http-threads]] [.property-path]##`commafeed.feed-refresh.http-threads`##
|
a| [[commafeed-server_commafeed-feed-refresh-http-threads]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-http-threads[`commafeed.feed-refresh.http-threads`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -362,7 +362,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|int
|
|int
|
||||||
|`3`
|
|`3`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-feed-refresh-database-threads]] [.property-path]##`commafeed.feed-refresh.database-threads`##
|
a| [[commafeed-server_commafeed-feed-refresh-database-threads]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-database-threads[`commafeed.feed-refresh.database-threads`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -379,7 +379,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|int
|
|int
|
||||||
|`1`
|
|`1`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-feed-refresh-user-inactivity-period]] [.property-path]##`commafeed.feed-refresh.user-inactivity-period`##
|
a| [[commafeed-server_commafeed-feed-refresh-user-inactivity-period]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-user-inactivity-period[`commafeed.feed-refresh.user-inactivity-period`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -396,7 +396,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|`0S`
|
|`0S`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-feed-refresh-filtering-expression-evaluation-timeout]] [.property-path]##`commafeed.feed-refresh.filtering-expression-evaluation-timeout`##
|
a| [[commafeed-server_commafeed-feed-refresh-filtering-expression-evaluation-timeout]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-filtering-expression-evaluation-timeout[`commafeed.feed-refresh.filtering-expression-evaluation-timeout`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -413,12 +413,29 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|`500MS`
|
|`500MS`
|
||||||
|
|
||||||
|
a| [[commafeed-server_commafeed-feed-refresh-force-refresh-cooldown-duration]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-force-refresh-cooldown-duration[`commafeed.feed-refresh.force-refresh-cooldown-duration`]##
|
||||||
|
|
||||||
h|[[commafeed-server_section_commafeed-database]] [.section-name.section-level0]##Database settings##
|
[.description]
|
||||||
|
--
|
||||||
|
Duration after which the "Fetch all my feeds now" action is available again after use to avoid spamming feeds.
|
||||||
|
|
||||||
|
|
||||||
|
ifdef::add-copy-button-to-env-var[]
|
||||||
|
Environment variable: env_var_with_copy_button:+++COMMAFEED_FEED_REFRESH_FORCE_REFRESH_COOLDOWN_DURATION+++[]
|
||||||
|
endif::add-copy-button-to-env-var[]
|
||||||
|
ifndef::add-copy-button-to-env-var[]
|
||||||
|
Environment variable: `+++COMMAFEED_FEED_REFRESH_FORCE_REFRESH_COOLDOWN_DURATION+++`
|
||||||
|
endif::add-copy-button-to-env-var[]
|
||||||
|
--
|
||||||
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|
|`0S`
|
||||||
|
|
||||||
|
|
||||||
|
h|[[commafeed-server_section_commafeed-database]] [.section-name.section-level0]##link:#commafeed-server_section_commafeed-database[Database settings]##
|
||||||
h|Type
|
h|Type
|
||||||
h|Default
|
h|Default
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-database-query-timeout]] [.property-path]##`commafeed.database.query-timeout`##
|
a| [[commafeed-server_commafeed-database-query-timeout]] [.property-path]##link:#commafeed-server_commafeed-database-query-timeout[`commafeed.database.query-timeout`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -435,11 +452,11 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|`0S`
|
|`0S`
|
||||||
|
|
||||||
h|[[commafeed-server_section_commafeed-database-cleanup]] [.section-name.section-level1]##Database cleanup settings##
|
h|[[commafeed-server_section_commafeed-database-cleanup]] [.section-name.section-level1]##link:#commafeed-server_section_commafeed-database-cleanup[Database cleanup settings]##
|
||||||
h|Type
|
h|Type
|
||||||
h|Default
|
h|Default
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-database-cleanup-entries-max-age]] [.property-path]##`commafeed.database.cleanup.entries-max-age`##
|
a| [[commafeed-server_commafeed-database-cleanup-entries-max-age]] [.property-path]##link:#commafeed-server_commafeed-database-cleanup-entries-max-age[`commafeed.database.cleanup.entries-max-age`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -456,7 +473,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|`365D`
|
|`365D`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-database-cleanup-statuses-max-age]] [.property-path]##`commafeed.database.cleanup.statuses-max-age`##
|
a| [[commafeed-server_commafeed-database-cleanup-statuses-max-age]] [.property-path]##link:#commafeed-server_commafeed-database-cleanup-statuses-max-age[`commafeed.database.cleanup.statuses-max-age`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -473,7 +490,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|`0S`
|
|`0S`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-database-cleanup-max-feed-capacity]] [.property-path]##`commafeed.database.cleanup.max-feed-capacity`##
|
a| [[commafeed-server_commafeed-database-cleanup-max-feed-capacity]] [.property-path]##link:#commafeed-server_commafeed-database-cleanup-max-feed-capacity[`commafeed.database.cleanup.max-feed-capacity`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -490,7 +507,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|int
|
|int
|
||||||
|`500`
|
|`500`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-database-cleanup-max-feeds-per-user]] [.property-path]##`commafeed.database.cleanup.max-feeds-per-user`##
|
a| [[commafeed-server_commafeed-database-cleanup-max-feeds-per-user]] [.property-path]##link:#commafeed-server_commafeed-database-cleanup-max-feeds-per-user[`commafeed.database.cleanup.max-feeds-per-user`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -507,7 +524,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|int
|
|int
|
||||||
|`0`
|
|`0`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-database-cleanup-batch-size]] [.property-path]##`commafeed.database.cleanup.batch-size`##
|
a| [[commafeed-server_commafeed-database-cleanup-batch-size]] [.property-path]##link:#commafeed-server_commafeed-database-cleanup-batch-size[`commafeed.database.cleanup.batch-size`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -526,11 +543,11 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
h|[[commafeed-server_section_commafeed-users]] [.section-name.section-level0]##Users settings##
|
h|[[commafeed-server_section_commafeed-users]] [.section-name.section-level0]##link:#commafeed-server_section_commafeed-users[Users settings]##
|
||||||
h|Type
|
h|Type
|
||||||
h|Default
|
h|Default
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-users-allow-registrations]] [.property-path]##`commafeed.users.allow-registrations`##
|
a| [[commafeed-server_commafeed-users-allow-registrations]] [.property-path]##link:#commafeed-server_commafeed-users-allow-registrations[`commafeed.users.allow-registrations`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -547,7 +564,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|boolean
|
|boolean
|
||||||
|`false`
|
|`false`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-users-strict-password-policy]] [.property-path]##`commafeed.users.strict-password-policy`##
|
a| [[commafeed-server_commafeed-users-strict-password-policy]] [.property-path]##link:#commafeed-server_commafeed-users-strict-password-policy[`commafeed.users.strict-password-policy`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -564,7 +581,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|boolean
|
|boolean
|
||||||
|`true`
|
|`true`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-users-create-demo-account]] [.property-path]##`commafeed.users.create-demo-account`##
|
a| [[commafeed-server_commafeed-users-create-demo-account]] [.property-path]##link:#commafeed-server_commafeed-users-create-demo-account[`commafeed.users.create-demo-account`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -582,11 +599,11 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|`false`
|
|`false`
|
||||||
|
|
||||||
|
|
||||||
h|[[commafeed-server_section_commafeed-websocket]] [.section-name.section-level0]##Websocket settings##
|
h|[[commafeed-server_section_commafeed-websocket]] [.section-name.section-level0]##link:#commafeed-server_section_commafeed-websocket[Websocket settings]##
|
||||||
h|Type
|
h|Type
|
||||||
h|Default
|
h|Default
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-websocket-enabled]] [.property-path]##`commafeed.websocket.enabled`##
|
a| [[commafeed-server_commafeed-websocket-enabled]] [.property-path]##link:#commafeed-server_commafeed-websocket-enabled[`commafeed.websocket.enabled`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -603,7 +620,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|boolean
|
|boolean
|
||||||
|`true`
|
|`true`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-websocket-ping-interval]] [.property-path]##`commafeed.websocket.ping-interval`##
|
a| [[commafeed-server_commafeed-websocket-ping-interval]] [.property-path]##link:#commafeed-server_commafeed-websocket-ping-interval[`commafeed.websocket.ping-interval`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
@@ -620,7 +637,7 @@ endif::add-copy-button-to-env-var[]
|
|||||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||||
|`15M`
|
|`15M`
|
||||||
|
|
||||||
a| [[commafeed-server_commafeed-websocket-tree-reload-interval]] [.property-path]##`commafeed.websocket.tree-reload-interval`##
|
a| [[commafeed-server_commafeed-websocket-tree-reload-interval]] [.property-path]##link:#commafeed-server_commafeed-websocket-tree-reload-interval[`commafeed.websocket.tree-reload-interval`]##
|
||||||
|
|
||||||
[.description]
|
[.description]
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -6,16 +6,16 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.commafeed</groupId>
|
<groupId>com.commafeed</groupId>
|
||||||
<artifactId>commafeed</artifactId>
|
<artifactId>commafeed</artifactId>
|
||||||
<version>5.2.0</version>
|
<version>5.3.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>commafeed-server</artifactId>
|
<artifactId>commafeed-server</artifactId>
|
||||||
<name>CommaFeed Server</name>
|
<name>CommaFeed Server</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<quarkus.version>3.14.4</quarkus.version>
|
<quarkus.version>3.15.1</quarkus.version>
|
||||||
<querydsl.version>6.7</querydsl.version>
|
<querydsl.version>6.8</querydsl.version>
|
||||||
<rome.version>2.1.0</rome.version>
|
<rome.version>2.1.0</rome.version>
|
||||||
<swagger.version>2.2.23</swagger.version>
|
<swagger.version>2.2.25</swagger.version>
|
||||||
|
|
||||||
<build.database>h2</build.database>
|
<build.database>h2</build.database>
|
||||||
</properties>
|
</properties>
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>3.5.0</version>
|
<version>3.5.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<systemPropertyVariables>
|
<systemPropertyVariables>
|
||||||
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-failsafe-plugin</artifactId>
|
<artifactId>maven-failsafe-plugin</artifactId>
|
||||||
<version>3.5.0</version>
|
<version>3.5.1</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<goals>
|
<goals>
|
||||||
@@ -238,7 +238,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.puppycrawl.tools</groupId>
|
<groupId>com.puppycrawl.tools</groupId>
|
||||||
<artifactId>checkstyle</artifactId>
|
<artifactId>checkstyle</artifactId>
|
||||||
<version>10.18.1</version>
|
<version>10.18.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<executions>
|
<executions>
|
||||||
@@ -294,7 +294,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.commafeed</groupId>
|
<groupId>com.commafeed</groupId>
|
||||||
<artifactId>commafeed-client</artifactId>
|
<artifactId>commafeed-client</artifactId>
|
||||||
<version>5.2.0</version>
|
<version>5.3.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- compile-time processors -->
|
<!-- compile-time processors -->
|
||||||
@@ -358,7 +358,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.dropwizard.metrics</groupId>
|
<groupId>io.dropwizard.metrics</groupId>
|
||||||
<artifactId>metrics-json</artifactId>
|
<artifactId>metrics-json</artifactId>
|
||||||
<version>4.2.27</version>
|
<version>4.2.28</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.swagger.core.v3</groupId>
|
<groupId>io.swagger.core.v3</groupId>
|
||||||
@@ -405,7 +405,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.passay</groupId>
|
<groupId>org.passay</groupId>
|
||||||
<artifactId>passay</artifactId>
|
<artifactId>passay</artifactId>
|
||||||
<version>1.6.5</version>
|
<version>1.6.6</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.rometools</groupId>
|
<groupId>com.rometools</groupId>
|
||||||
@@ -450,7 +450,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||||
<artifactId>httpclient5</artifactId>
|
<artifactId>httpclient5</artifactId>
|
||||||
<version>5.3.1</version>
|
<version>5.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- add brotli support for httpclient5 -->
|
<!-- add brotli support for httpclient5 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -461,7 +461,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.hakky54</groupId>
|
<groupId>io.github.hakky54</groupId>
|
||||||
<artifactId>sslcontext-kickstart-for-apache5</artifactId>
|
<artifactId>sslcontext-kickstart-for-apache5</artifactId>
|
||||||
<version>8.3.6</version>
|
<version>8.3.7</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- test dependencies -->
|
<!-- test dependencies -->
|
||||||
@@ -489,7 +489,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.microsoft.playwright</groupId>
|
<groupId>com.microsoft.playwright</groupId>
|
||||||
<artifactId>playwright</artifactId>
|
<artifactId>playwright</artifactId>
|
||||||
<version>1.47.0</version>
|
<version>1.48.0</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -209,6 +209,12 @@ public interface CommaFeedConfiguration {
|
|||||||
*/
|
*/
|
||||||
@WithDefault("500ms")
|
@WithDefault("500ms")
|
||||||
Duration filteringExpressionEvaluationTimeout();
|
Duration filteringExpressionEvaluationTimeout();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duration after which the "Fetch all my feeds now" action is available again after use to avoid spamming feeds.
|
||||||
|
*/
|
||||||
|
@WithDefault("0")
|
||||||
|
Duration forceRefreshCooldownDuration();
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Database {
|
interface Database {
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ public class JacksonCustomizer implements ObjectMapperCustomizer {
|
|||||||
objectMapper.registerModule(new JavaTimeModule());
|
objectMapper.registerModule(new JavaTimeModule());
|
||||||
|
|
||||||
// read and write instants as milliseconds instead of nanoseconds
|
// read and write instants as milliseconds instead of nanoseconds
|
||||||
objectMapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
|
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true)
|
||||||
|
.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
|
||||||
.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
|
.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
|
||||||
|
|
||||||
// add support for serializing metrics
|
// add support for serializing metrics
|
||||||
|
|||||||
@@ -126,7 +126,13 @@ public class HttpGetter {
|
|||||||
log.debug("fetching {}", request.getUrl());
|
log.debug("fetching {}", request.getUrl());
|
||||||
|
|
||||||
HttpClientContext context = HttpClientContext.create();
|
HttpClientContext context = HttpClientContext.create();
|
||||||
context.setRequestConfig(RequestConfig.custom().setResponseTimeout(Timeout.of(config.httpClient().responseTimeout())).build());
|
context.setRequestConfig(RequestConfig.custom()
|
||||||
|
.setResponseTimeout(Timeout.of(config.httpClient().responseTimeout()))
|
||||||
|
// causes issues with some feeds
|
||||||
|
// see https://github.com/Athou/commafeed/issues/1572
|
||||||
|
// and https://issues.apache.org/jira/browse/HTTPCLIENT-2344
|
||||||
|
.setProtocolUpgradeEnabled(false)
|
||||||
|
.build());
|
||||||
|
|
||||||
return client.execute(request.toClassicHttpRequest(), context, resp -> {
|
return client.execute(request.toClassicHttpRequest(), context, resp -> {
|
||||||
byte[] content = resp.getEntity() == null ? null
|
byte[] content = resp.getEntity() == null ? null
|
||||||
@@ -176,7 +182,7 @@ public class HttpGetter {
|
|||||||
|
|
||||||
int poolSize = config.feedRefresh().httpThreads();
|
int poolSize = config.feedRefresh().httpThreads();
|
||||||
return PoolingHttpClientConnectionManagerBuilder.create()
|
return PoolingHttpClientConnectionManagerBuilder.create()
|
||||||
.setSSLSocketFactory(Apache5SslUtils.toSocketFactory(sslFactory))
|
.setTlsSocketStrategy(Apache5SslUtils.toTlsSocketStrategy(sslFactory))
|
||||||
.setDefaultConnectionConfig(ConnectionConfig.custom()
|
.setDefaultConnectionConfig(ConnectionConfig.custom()
|
||||||
.setConnectTimeout(Timeout.of(config.httpClient().connectTimeout()))
|
.setConnectTimeout(Timeout.of(config.httpClient().connectTimeout()))
|
||||||
.setSocketTimeout(Timeout.of(config.httpClient().socketTimeout()))
|
.setSocketTimeout(Timeout.of(config.httpClient().socketTimeout()))
|
||||||
|
|||||||
@@ -52,4 +52,7 @@ public class User extends AbstractModel {
|
|||||||
|
|
||||||
@Column
|
@Column
|
||||||
private Instant recoverPasswordTokenDate;
|
private Instant recoverPasswordTokenDate;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private Instant lastForceRefresh;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,12 +98,19 @@ public class FeedSubscriptionService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshAll(User user) {
|
public void refreshAll(User user) throws ForceFeedRefreshTooSoonException {
|
||||||
|
Instant lastForceRefresh = user.getLastForceRefresh();
|
||||||
|
if (lastForceRefresh != null && lastForceRefresh.plus(config.feedRefresh().forceRefreshCooldownDuration()).isAfter(Instant.now())) {
|
||||||
|
throw new ForceFeedRefreshTooSoonException();
|
||||||
|
}
|
||||||
|
|
||||||
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
|
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
|
||||||
for (FeedSubscription sub : subs) {
|
for (FeedSubscription sub : subs) {
|
||||||
Feed feed = sub.getFeed();
|
Feed feed = sub.getFeed();
|
||||||
feedRefreshEngine.refreshImmediately(feed);
|
feedRefreshEngine.refreshImmediately(feed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user.setLastForceRefresh(Instant.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshAllUpForRefresh(User user) {
|
public void refreshAllUpForRefresh(User user) {
|
||||||
@@ -130,4 +137,11 @@ public class FeedSubscriptionService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public static class ForceFeedRefreshTooSoonException extends Exception {
|
||||||
|
private ForceFeedRefreshTooSoonException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,4 +43,7 @@ public class ServerInfo implements Serializable {
|
|||||||
@Schema(requiredMode = RequiredMode.REQUIRED)
|
@Schema(requiredMode = RequiredMode.REQUIRED)
|
||||||
private long treeReloadInterval;
|
private long treeReloadInterval;
|
||||||
|
|
||||||
|
@Schema(requiredMode = RequiredMode.REQUIRED)
|
||||||
|
private long forceRefreshCooldownDuration;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,4 +41,7 @@ public class UserModel implements Serializable {
|
|||||||
@Schema(description = "user is admin", requiredMode = RequiredMode.REQUIRED)
|
@Schema(description = "user is admin", requiredMode = RequiredMode.REQUIRED)
|
||||||
private boolean admin;
|
private boolean admin;
|
||||||
|
|
||||||
|
@Schema(description = "user last force refresh", type = "number")
|
||||||
|
private Instant lastForceRefresh;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import java.util.Objects;
|
|||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.apache.hc.core5.http.HttpStatus;
|
||||||
import org.jboss.resteasy.reactive.Cache;
|
import org.jboss.resteasy.reactive.Cache;
|
||||||
import org.jboss.resteasy.reactive.RestForm;
|
import org.jboss.resteasy.reactive.RestForm;
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ import com.commafeed.backend.service.FeedEntryFilteringService.FeedEntryFilterEx
|
|||||||
import com.commafeed.backend.service.FeedEntryService;
|
import com.commafeed.backend.service.FeedEntryService;
|
||||||
import com.commafeed.backend.service.FeedService;
|
import com.commafeed.backend.service.FeedService;
|
||||||
import com.commafeed.backend.service.FeedSubscriptionService;
|
import com.commafeed.backend.service.FeedSubscriptionService;
|
||||||
|
import com.commafeed.backend.service.FeedSubscriptionService.ForceFeedRefreshTooSoonException;
|
||||||
import com.commafeed.frontend.model.Entries;
|
import com.commafeed.frontend.model.Entries;
|
||||||
import com.commafeed.frontend.model.Entry;
|
import com.commafeed.frontend.model.Entry;
|
||||||
import com.commafeed.frontend.model.FeedInfo;
|
import com.commafeed.frontend.model.FeedInfo;
|
||||||
@@ -276,8 +278,13 @@ public class FeedREST {
|
|||||||
@Operation(summary = "Queue all feeds of the user for refresh", description = "Manually add all feeds of the user to the refresh queue")
|
@Operation(summary = "Queue all feeds of the user for refresh", description = "Manually add all feeds of the user to the refresh queue")
|
||||||
public Response queueAllForRefresh() {
|
public Response queueAllForRefresh() {
|
||||||
User user = authenticationContext.getCurrentUser();
|
User user = authenticationContext.getCurrentUser();
|
||||||
feedSubscriptionService.refreshAll(user);
|
try {
|
||||||
return Response.ok().build();
|
feedSubscriptionService.refreshAll(user);
|
||||||
|
return Response.ok().build();
|
||||||
|
} catch (ForceFeedRefreshTooSoonException e) {
|
||||||
|
return Response.status(HttpStatus.SC_TOO_MANY_REQUESTS).build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("/refresh")
|
@Path("/refresh")
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ public class ServerREST {
|
|||||||
infos.setWebsocketEnabled(config.websocket().enabled());
|
infos.setWebsocketEnabled(config.websocket().enabled());
|
||||||
infos.setWebsocketPingInterval(config.websocket().pingInterval().toMillis());
|
infos.setWebsocketPingInterval(config.websocket().pingInterval().toMillis());
|
||||||
infos.setTreeReloadInterval(config.websocket().treeReloadInterval().toMillis());
|
infos.setTreeReloadInterval(config.websocket().treeReloadInterval().toMillis());
|
||||||
|
infos.setForceRefreshCooldownDuration(config.feedRefresh().forceRefreshCooldownDuration().toMillis());
|
||||||
return Response.ok(infos).build();
|
return Response.ok(infos).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ public class UserREST {
|
|||||||
userModel.setEmail(user.getEmail());
|
userModel.setEmail(user.getEmail());
|
||||||
userModel.setEnabled(!user.isDisabled());
|
userModel.setEnabled(!user.isDisabled());
|
||||||
userModel.setApiKey(user.getApiKey());
|
userModel.setApiKey(user.getApiKey());
|
||||||
|
userModel.setLastForceRefresh(user.getLastForceRefresh());
|
||||||
for (UserRole role : userRoleDAO.findAll(user)) {
|
for (UserRole role : userRoleDAO.findAll(user)) {
|
||||||
if (role.getRole() == Role.ADMIN) {
|
if (role.getRole() == Role.ADMIN) {
|
||||||
userModel.setAdmin(true);
|
userModel.setAdmin(true);
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ quarkus.native.add-all-charsets=true
|
|||||||
%test.commafeed.users.allow-registrations=true
|
%test.commafeed.users.allow-registrations=true
|
||||||
%test.commafeed.password-recovery-enabled=true
|
%test.commafeed.password-recovery-enabled=true
|
||||||
%test.commafeed.http-client.cache.enabled=false
|
%test.commafeed.http-client.cache.enabled=false
|
||||||
|
%test.commafeed.feed-refresh.force-refresh-cooldown-duration=1m
|
||||||
|
|
||||||
|
|
||||||
# prod profile overrides
|
# prod profile overrides
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
||||||
|
|
||||||
|
<changeSet id="lastForceRefresh" author="athou">
|
||||||
|
<addColumn tableName="USERS">
|
||||||
|
<column name="lastForceRefresh" type="${timestamp_type}" />
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -33,5 +33,6 @@
|
|||||||
<include file="changelogs/db.changelog-4.4.xml" />
|
<include file="changelogs/db.changelog-4.4.xml" />
|
||||||
<include file="changelogs/db.changelog-5.1.xml" />
|
<include file="changelogs/db.changelog-5.1.xml" />
|
||||||
<include file="changelogs/db.changelog-5.2.xml" />
|
<include file="changelogs/db.changelog-5.2.xml" />
|
||||||
|
<include file="changelogs/db.changelog-5.3.xml" />
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
@@ -239,6 +239,24 @@ class HttpGetterTest {
|
|||||||
Assertions.assertEquals("ok", new String(result.getContent()));
|
Assertions.assertEquals("ok", new String(result.getContent()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void doesNotUseUpgradeProtocolHeader() {
|
||||||
|
AtomicInteger calls = new AtomicInteger();
|
||||||
|
|
||||||
|
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(req -> {
|
||||||
|
calls.incrementAndGet();
|
||||||
|
|
||||||
|
if (req.containsHeader(HttpHeaders.UPGRADE)) {
|
||||||
|
throw new Exception("upgrade header should not be sent by the client");
|
||||||
|
}
|
||||||
|
|
||||||
|
return HttpResponse.response().withBody("ok");
|
||||||
|
});
|
||||||
|
|
||||||
|
Assertions.assertDoesNotThrow(() -> getter.get(this.feedUrl));
|
||||||
|
Assertions.assertEquals(1, calls.get());
|
||||||
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class Compression {
|
class Compression {
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ public abstract class BaseIT {
|
|||||||
.as(Entries.class);
|
.as(Entries.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void forceRefreshAllFeeds() {
|
protected int forceRefreshAllFeeds() {
|
||||||
RestAssured.given().get("rest/feed/refreshAll").then().statusCode(HttpStatus.SC_OK);
|
return RestAssured.given().get("rest/feed/refreshAll").then().extract().statusCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,11 +183,13 @@ class FeedIT extends BaseIT {
|
|||||||
|
|
||||||
// mariadb/mysql timestamp precision is 1 second
|
// mariadb/mysql timestamp precision is 1 second
|
||||||
Instant threshold = Instant.now().minus(Duration.ofSeconds(1));
|
Instant threshold = Instant.now().minus(Duration.ofSeconds(1));
|
||||||
forceRefreshAllFeeds();
|
Assertions.assertEquals(HttpStatus.SC_OK, forceRefreshAllFeeds());
|
||||||
|
|
||||||
Awaitility.await()
|
Awaitility.await()
|
||||||
.atMost(Duration.ofSeconds(15))
|
.atMost(Duration.ofSeconds(15))
|
||||||
.until(() -> getSubscription(subscriptionId), f -> f.getLastRefresh().isAfter(threshold));
|
.until(() -> getSubscription(subscriptionId), f -> f.getLastRefresh().isAfter(threshold));
|
||||||
|
|
||||||
|
Assertions.assertEquals(HttpStatus.SC_TOO_MANY_REQUESTS, forceRefreshAllFeeds());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class ServerIT extends BaseIT {
|
|||||||
Assertions.assertTrue(serverInfos.isWebsocketEnabled());
|
Assertions.assertTrue(serverInfos.isWebsocketEnabled());
|
||||||
Assertions.assertEquals(900000, serverInfos.getWebsocketPingInterval());
|
Assertions.assertEquals(900000, serverInfos.getWebsocketPingInterval());
|
||||||
Assertions.assertEquals(30000, serverInfos.getTreeReloadInterval());
|
Assertions.assertEquals(30000, serverInfos.getTreeReloadInterval());
|
||||||
|
Assertions.assertEquals(60000, serverInfos.getForceRefreshCooldownDuration());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
pom.xml
2
pom.xml
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<groupId>com.commafeed</groupId>
|
<groupId>com.commafeed</groupId>
|
||||||
<artifactId>commafeed</artifactId>
|
<artifactId>commafeed</artifactId>
|
||||||
<version>5.2.0</version>
|
<version>5.3.3</version>
|
||||||
<name>CommaFeed</name>
|
<name>CommaFeed</name>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user