mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f699d9675 | ||
|
|
a5aba6f7ae | ||
|
|
78c8711a79 | ||
|
|
8325236d0e | ||
|
|
437401e73f | ||
|
|
fa06d321d5 | ||
|
|
d1ddcb6ace | ||
|
|
6944d4dc0b | ||
|
|
c835d805b1 | ||
|
|
4a90e1f69d | ||
|
|
fcfeaa462e | ||
|
|
b16978d8fe | ||
|
|
68c62b4528 | ||
|
|
18f68aab31 | ||
|
|
8abb2770ec | ||
|
|
9156b8b6d0 | ||
|
|
2c32fa1e13 | ||
|
|
7e48afe36c | ||
|
|
cd94a3b56f | ||
|
|
22e0f1f382 | ||
|
|
e2eeba90ef | ||
|
|
662c0fc6b9 | ||
|
|
fafc0619ad | ||
|
|
3a5dc5d0ed | ||
|
|
23a696e644 | ||
|
|
72cb71a2fb | ||
|
|
9b757735b8 | ||
|
|
6b9f8f268f | ||
|
|
e5b0eb426c | ||
|
|
ea6c83ca33 | ||
|
|
763ce1e4fd | ||
|
|
e748499ed8 | ||
|
|
e430604528 | ||
|
|
5e08c81d12 | ||
|
|
84626e1ef2 | ||
|
|
191ece0bac | ||
|
|
24eaff61f2 | ||
|
|
aa5e9bfd83 | ||
|
|
a200147926 | ||
|
|
d6205b7da3 | ||
|
|
5ecf3e0fbf | ||
|
|
bb25e0ede6 | ||
|
|
f5c0e2d375 | ||
|
|
12ab5b1e7b | ||
|
|
3e6451289f | ||
|
|
09d21d88a4 | ||
|
|
2ec6d0a66a | ||
|
|
412fc52f1c | ||
|
|
b5e5989604 | ||
|
|
105ff46c01 | ||
|
|
f100f3f91a | ||
|
|
45eb436b8f | ||
|
|
bf3914e748 | ||
|
|
5df7aaf7cd | ||
|
|
f10cfd7ad0 |
41
CHANGELOG.md
41
CHANGELOG.md
@@ -1,12 +1,45 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [3.7.0]
|
||||||
|
|
||||||
|
- the sidebar is now resizable
|
||||||
|
- added the "f" keyboard shortcut to hide the sidebar
|
||||||
|
- added tooltips to relative dates with the exact date
|
||||||
|
- add a setting to hide commafeed from search engines (exposes a robots.txt file, enabled by default)
|
||||||
|
- the browser extension unread count now updates when articles are marked as read/unread in the app
|
||||||
|
- The "b" keyboard shortcut now works as expected on Chrome but requires the browser extension to be installed
|
||||||
|
- dark mode has been disabled on the api documentation page as it was unreadable
|
||||||
|
- improvement to the feed refresh queuing logic when "heavy load" mode is enabled
|
||||||
|
- fix a bug that could prevent feeds and categories from being edited
|
||||||
|
|
||||||
|
## [3.6.0]
|
||||||
|
|
||||||
|
- add a button to open CommaFeed in a new tab and a button to open options when using the browser extension
|
||||||
|
- clicking on the entry title in expanded mode now opens the link instead of doing nothing
|
||||||
|
- add tooltips to buttons when the mobile layout is used on desktop
|
||||||
|
- redirect the user to the welcome page if the user was deleted from the database
|
||||||
|
- add link to api documentation on welcome page
|
||||||
|
- the unread count is now correctly updated when using the "/next" bookmarklet while redis cache is enabled
|
||||||
|
|
||||||
|
## [3.5.0]
|
||||||
|
|
||||||
|
- add compatibility with the new version of the CommaFeed browser extension
|
||||||
|
- disable pull-to-refresh on mobile as it messes with vertical scrolling
|
||||||
|
- add css classes to feed entries to help with custom css rules
|
||||||
|
- api documentation page no longer requires users to be authenticated
|
||||||
|
- add a setting to limit the number of feeds a user can subscribe to
|
||||||
|
- add a setting to disable strict password policy
|
||||||
|
- add feed refresh engine metrics
|
||||||
|
- fix redis timeouts
|
||||||
|
|
||||||
## [3.4.0]
|
## [3.4.0]
|
||||||
|
|
||||||
- add support for arm64 docker images
|
- add support for arm64 docker images
|
||||||
- add divider to visually separate read-only information from form on the profile settings page
|
- add divider to visually separate read-only information from form on the profile settings page
|
||||||
- reduce javascript bundle size by 30% by loading only the necessary translations
|
- reduce javascript bundle size by 30% by loading only the necessary translations
|
||||||
- add a standalone donate page with all ways to support CommaFeed
|
- add a standalone donate page with all ways to support CommaFeed
|
||||||
- fix an issue introduced in 3.1.0 that could make CommaFeed not refresh feeds as fast as before on instances with lots of feeds
|
- fix an issue introduced in 3.1.0 that could make CommaFeed not refresh feeds as fast as before on instances with lots
|
||||||
|
of feeds
|
||||||
- fix alignment of icon with text for category tree nodes
|
- fix alignment of icon with text for category tree nodes
|
||||||
- fix alignment of burger button with the rest of the header on mobile
|
- fix alignment of burger button with the rest of the header on mobile
|
||||||
|
|
||||||
@@ -47,10 +80,10 @@
|
|||||||
## [3.0.1]
|
## [3.0.1]
|
||||||
|
|
||||||
- allow env variable substitution in config.yml
|
- allow env variable substitution in config.yml
|
||||||
- e.g. having a custom config.yml file with `app.session.path=${SOME_ENV_VAR}` will substitute `SOME_ENV_VAR` with
|
- e.g. having a custom config.yml file with `app.session.path=${SOME_ENV_VAR}` will substitute `SOME_ENV_VAR` with
|
||||||
its value
|
its value
|
||||||
- allow env variable prefixed with `CF_` to override config.yml properties
|
- allow env variable prefixed with `CF_` to override config.yml properties
|
||||||
- e.g. setting `CF_APP_ALLOWREGISTRATIONS=true` will set `app.allowRegistrations` to `true`
|
- e.g. setting `CF_APP_ALLOWREGISTRATIONS=true` will set `app.allowRegistrations` to `true`
|
||||||
|
|
||||||
## [3.0.0]
|
## [3.0.0]
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ EXPOSE 8082
|
|||||||
RUN mkdir -p /commafeed/data
|
RUN mkdir -p /commafeed/data
|
||||||
VOLUME /commafeed/data
|
VOLUME /commafeed/data
|
||||||
ENV CF_SESSION_PATH=/commafeed/data/sessions
|
ENV CF_SESSION_PATH=/commafeed/data/sessions
|
||||||
|
ENV CF_DATABASE_URL=jdbc:h2:/commafeed/data/db
|
||||||
|
|
||||||
COPY commafeed-server/config.yml.example config.yml
|
COPY commafeed-server/config.yml.example config.yml
|
||||||
COPY commafeed-server/target/commafeed.jar .
|
COPY commafeed-server/target/commafeed.jar .
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -15,15 +15,7 @@ Google Reader inspired self-hosted RSS reader, based on Dropwizard and React/Typ
|
|||||||
- Supports thousands of users and millions of feeds
|
- Supports thousands of users and millions of feeds
|
||||||
- OPML import/export
|
- OPML import/export
|
||||||
- REST API
|
- REST API
|
||||||
|
- [Browser extension](https://github.com/Athou/commafeed-browser-extension)
|
||||||
## Related open-source projects
|
|
||||||
|
|
||||||
Browser extensions:
|
|
||||||
|
|
||||||
- [Chrome](https://github.com/Athou/commafeed-chrome)
|
|
||||||
- [Firefox](https://github.com/Athou/commafeed-firefox)
|
|
||||||
- [Opera](https://github.com/Athou/commafeed-opera)
|
|
||||||
- [Safari](https://github.com/Athou/commafeed-safari)
|
|
||||||
|
|
||||||
## Deployment on your own server
|
## Deployment on your own server
|
||||||
|
|
||||||
|
|||||||
18
commafeed-client/package-lock.json
generated
18
commafeed-client/package-lock.json
generated
@@ -25,6 +25,7 @@
|
|||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"interweave": "^13.1.0",
|
"interweave": "^13.1.0",
|
||||||
"mousetrap": "^1.6.5",
|
"mousetrap": "^1.6.5",
|
||||||
|
"re-resizable": "^6.9.9",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-async-hook": "^4.0.0",
|
"react-async-hook": "^4.0.0",
|
||||||
"react-contexify": "^6.0.0",
|
"react-contexify": "^6.0.0",
|
||||||
@@ -66,7 +67,7 @@
|
|||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"rollup-plugin-visualizer": "^5.9.0",
|
"rollup-plugin-visualizer": "^5.9.0",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"vite": "^4.3.8",
|
"vite": "^4.3.9",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-tsconfig-paths": "^4.2.0",
|
"vite-tsconfig-paths": "^4.2.0",
|
||||||
"vitest": "^0.31.1",
|
"vitest": "^0.31.1",
|
||||||
@@ -10035,6 +10036,15 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/re-resizable": {
|
||||||
|
"version": "6.9.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.9.9.tgz",
|
||||||
|
"integrity": "sha512-l+MBlKZffv/SicxDySKEEh42hR6m5bAHfNu3Tvxks2c4Ah+ldnWjfnVRwxo/nxF27SsUsxDS0raAzFuJNKABXA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||||
@@ -11829,9 +11839,9 @@
|
|||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "4.3.8",
|
"version": "4.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",
|
||||||
"integrity": "sha512-uYB8PwN7hbMrf4j1xzGDk/lqjsZvCDbt/JC5dyfxc19Pg8kRm14LinK/uq+HSLNswZEoKmweGdtpbnxRtrAXiQ==",
|
"integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.17.5",
|
"esbuild": "^0.17.5",
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"interweave": "^13.1.0",
|
"interweave": "^13.1.0",
|
||||||
"mousetrap": "^1.6.5",
|
"mousetrap": "^1.6.5",
|
||||||
|
"re-resizable": "^6.9.9",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-async-hook": "^4.0.0",
|
"react-async-hook": "^4.0.0",
|
||||||
"react-contexify": "^6.0.0",
|
"react-contexify": "^6.0.0",
|
||||||
@@ -72,7 +73,7 @@
|
|||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"rollup-plugin-visualizer": "^5.9.0",
|
"rollup-plugin-visualizer": "^5.9.0",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"vite": "^4.3.8",
|
"vite": "^4.3.9",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-tsconfig-paths": "^4.2.0",
|
"vite-tsconfig-paths": "^4.2.0",
|
||||||
"vitest": "^0.31.1",
|
"vitest": "^0.31.1",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.commafeed</groupId>
|
<groupId>com.commafeed</groupId>
|
||||||
<artifactId>commafeed</artifactId>
|
<artifactId>commafeed</artifactId>
|
||||||
<version>3.4.0</version>
|
<version>3.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>commafeed-client</artifactId>
|
<artifactId>commafeed-client</artifactId>
|
||||||
<name>CommaFeed Client</name>
|
<name>CommaFeed Client</name>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { categoryUnreadCount } from "app/utils"
|
|||||||
import { ErrorBoundary } from "components/ErrorBoundary"
|
import { ErrorBoundary } from "components/ErrorBoundary"
|
||||||
import { Header } from "components/header/Header"
|
import { Header } from "components/header/Header"
|
||||||
import { Tree } from "components/sidebar/Tree"
|
import { Tree } from "components/sidebar/Tree"
|
||||||
|
import { useBrowserExtension } from "hooks/useBrowserExtension"
|
||||||
import { useI18n } from "i18n"
|
import { useI18n } from "i18n"
|
||||||
import { AdminUsersPage } from "pages/admin/AdminUsersPage"
|
import { AdminUsersPage } from "pages/admin/AdminUsersPage"
|
||||||
import { MetricsPage } from "pages/admin/MetricsPage"
|
import { MetricsPage } from "pages/admin/MetricsPage"
|
||||||
@@ -37,7 +38,7 @@ import useLocalStorage from "use-local-storage"
|
|||||||
function Providers(props: { children: React.ReactNode }) {
|
function Providers(props: { children: React.ReactNode }) {
|
||||||
const preferredColorScheme = useColorScheme()
|
const preferredColorScheme = useColorScheme()
|
||||||
const [colorScheme, setColorScheme] = useLocalStorage<ColorScheme>("color-scheme", preferredColorScheme)
|
const [colorScheme, setColorScheme] = useLocalStorage<ColorScheme>("color-scheme", preferredColorScheme)
|
||||||
const toggleColorScheme = (value?: ColorScheme) => setColorScheme(value || (colorScheme === "dark" ? "light" : "dark"))
|
const toggleColorScheme = (value?: ColorScheme) => setColorScheme(value ?? (colorScheme === "dark" ? "light" : "dark"))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<I18nProvider i18n={i18n}>
|
<I18nProvider i18n={i18n}>
|
||||||
@@ -65,6 +66,9 @@ function Providers(props: { children: React.ReactNode }) {
|
|||||||
const ApiDocumentationPage = React.lazy(() => import("pages/app/ApiDocumentationPage"))
|
const ApiDocumentationPage = React.lazy(() => import("pages/app/ApiDocumentationPage"))
|
||||||
|
|
||||||
function AppRoutes() {
|
function AppRoutes() {
|
||||||
|
const sidebarWidth = useAppSelector(state => state.tree.sidebarWidth)
|
||||||
|
const sidebarVisible = useAppSelector(state => state.tree.sidebarVisible)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Navigate to={`/app/category/${Constants.categories.all.id}`} replace />} />
|
<Route path="/" element={<Navigate to={`/app/category/${Constants.categories.all.id}`} replace />} />
|
||||||
@@ -72,7 +76,8 @@ function AppRoutes() {
|
|||||||
<Route path="login" element={<LoginPage />} />
|
<Route path="login" element={<LoginPage />} />
|
||||||
<Route path="register" element={<RegistrationPage />} />
|
<Route path="register" element={<RegistrationPage />} />
|
||||||
<Route path="passwordRecovery" element={<PasswordRecoveryPage />} />
|
<Route path="passwordRecovery" element={<PasswordRecoveryPage />} />
|
||||||
<Route path="app" element={<Layout header={<Header />} sidebar={<Tree />} />}>
|
<Route path="api" element={<ApiDocumentationPage />} />
|
||||||
|
<Route path="app" element={<Layout header={<Header />} sidebar={<Tree />} sidebarWidth={sidebarVisible ? sidebarWidth : 0} />}>
|
||||||
<Route path="category">
|
<Route path="category">
|
||||||
<Route path=":id" element={<FeedEntriesPage sourceType="category" />} />
|
<Route path=":id" element={<FeedEntriesPage sourceType="category" />} />
|
||||||
<Route path=":id/details" element={<CategoryDetailsPage />} />
|
<Route path=":id/details" element={<CategoryDetailsPage />} />
|
||||||
@@ -93,7 +98,6 @@ function AppRoutes() {
|
|||||||
</Route>
|
</Route>
|
||||||
<Route path="about" element={<AboutPage />} />
|
<Route path="about" element={<AboutPage />} />
|
||||||
<Route path="donate" element={<DonatePage />} />
|
<Route path="donate" element={<DonatePage />} />
|
||||||
<Route path="api" element={<ApiDocumentationPage />} />
|
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="*" element={<Navigate to="/" replace />} />
|
<Route path="*" element={<Navigate to="/" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
@@ -134,13 +138,28 @@ function FaviconHandler() {
|
|||||||
const root = useAppSelector(state => state.tree.rootCategory)
|
const root = useAppSelector(state => state.tree.rootCategory)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unreadCount = categoryUnreadCount(root)
|
const unreadCount = categoryUnreadCount(root)
|
||||||
if (unreadCount === 0) Tinycon.reset()
|
if (unreadCount === 0) {
|
||||||
else Tinycon.setBubble(unreadCount)
|
Tinycon.reset()
|
||||||
|
} else {
|
||||||
|
Tinycon.setBubble(unreadCount)
|
||||||
|
}
|
||||||
}, [root])
|
}, [root])
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function BrowserExtensionBadgeUnreadCountHandler() {
|
||||||
|
const root = useAppSelector(state => state.tree.rootCategory)
|
||||||
|
const { setBadgeUnreadCount } = useBrowserExtension()
|
||||||
|
useEffect(() => {
|
||||||
|
if (!root) return
|
||||||
|
const unreadCount = categoryUnreadCount(root)
|
||||||
|
setBadgeUnreadCount(unreadCount)
|
||||||
|
}, [root, setBadgeUnreadCount])
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
useI18n()
|
useI18n()
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
@@ -153,6 +172,7 @@ export function App() {
|
|||||||
<Providers>
|
<Providers>
|
||||||
<>
|
<>
|
||||||
<FaviconHandler />
|
<FaviconHandler />
|
||||||
|
<BrowserExtensionBadgeUnreadCountHandler />
|
||||||
<HashRouter>
|
<HashRouter>
|
||||||
<GoogleAnalyticsHandler />
|
<GoogleAnalyticsHandler />
|
||||||
<RedirectHandler />
|
<RedirectHandler />
|
||||||
|
|||||||
@@ -30,7 +30,10 @@ const axiosInstance = axios.create({ baseURL: "./rest", withCredentials: true })
|
|||||||
axiosInstance.interceptors.response.use(
|
axiosInstance.interceptors.response.use(
|
||||||
response => response,
|
response => response,
|
||||||
error => {
|
error => {
|
||||||
if (error.response.status === 401 && error.response.data === "Credentials are required to access this resource.") {
|
if (
|
||||||
|
(error.response.status === 401 && error.response.data === "Credentials are required to access this resource.") ||
|
||||||
|
(error.response.status === 403 && error.response.data === "You don't have the required role to access this resource.")
|
||||||
|
) {
|
||||||
window.location.hash = "/welcome"
|
window.location.hash = "/welcome"
|
||||||
}
|
}
|
||||||
throw error
|
throw error
|
||||||
|
|||||||
@@ -88,7 +88,6 @@ export const Constants = {
|
|||||||
layout: {
|
layout: {
|
||||||
mobileBreakpoint: DEFAULT_THEME.breakpoints.md,
|
mobileBreakpoint: DEFAULT_THEME.breakpoints.md,
|
||||||
headerHeight: 60,
|
headerHeight: 60,
|
||||||
sidebarWidth: 350,
|
|
||||||
entryMaxWidth: 650,
|
entryMaxWidth: 650,
|
||||||
isTopVisible: (div: HTMLElement) => div.getBoundingClientRect().top >= Constants.layout.headerHeight,
|
isTopVisible: (div: HTMLElement) => div.getBoundingClientRect().top >= Constants.layout.headerHeight,
|
||||||
isBottomVisible: (div: HTMLElement) => div.getBoundingClientRect().bottom <= window.innerHeight,
|
isBottomVisible: (div: HTMLElement) => div.getBoundingClientRect().bottom <= window.innerHeight,
|
||||||
@@ -97,5 +96,6 @@ export const Constants = {
|
|||||||
mainScrollAreaId: "main-scroll-area-id",
|
mainScrollAreaId: "main-scroll-area-id",
|
||||||
entryId: (entry: Entry) => `entry-id-${entry.id}`,
|
entryId: (entry: Entry) => `entry-id-${entry.id}`,
|
||||||
},
|
},
|
||||||
|
browserExtensionUrl: "https://github.com/Athou/commafeed-browser-extension",
|
||||||
bitcoinWalletAddress: "1dymfUxqCWpyD7a6rQSqNy4rLVDBsAr5e",
|
bitcoinWalletAddress: "1dymfUxqCWpyD7a6rQSqNy4rLVDBsAr5e",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ export const redirectToRegistration = createAsyncThunk("redirect/register", (_,
|
|||||||
export const redirectToPasswordRecovery = createAsyncThunk("redirect/passwordRecovery", (_, thunkApi) =>
|
export const redirectToPasswordRecovery = createAsyncThunk("redirect/passwordRecovery", (_, thunkApi) =>
|
||||||
thunkApi.dispatch(redirectTo("/passwordRecovery"))
|
thunkApi.dispatch(redirectTo("/passwordRecovery"))
|
||||||
)
|
)
|
||||||
|
export const redirectToApiDocumentation = createAsyncThunk("redirect/api", (_, thunkApi) => thunkApi.dispatch(redirectTo("/api")))
|
||||||
|
|
||||||
export const redirectToSelectedSource = createAsyncThunk<
|
export const redirectToSelectedSource = createAsyncThunk<
|
||||||
void,
|
void,
|
||||||
void,
|
void,
|
||||||
@@ -52,7 +54,6 @@ export const redirectToMetrics = createAsyncThunk("redirect/admin/metrics", (_,
|
|||||||
)
|
)
|
||||||
export const redirectToDonate = createAsyncThunk("redirect/donate", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/donate")))
|
export const redirectToDonate = createAsyncThunk("redirect/donate", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/donate")))
|
||||||
export const redirectToAbout = createAsyncThunk("redirect/about", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/about")))
|
export const redirectToAbout = createAsyncThunk("redirect/about", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/about")))
|
||||||
export const redirectToApiDocumentation = createAsyncThunk("redirect/api", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/api")))
|
|
||||||
|
|
||||||
export const redirectSlice = createSlice({
|
export const redirectSlice = createSlice({
|
||||||
name: "redirect",
|
name: "redirect",
|
||||||
|
|||||||
@@ -9,10 +9,14 @@ import { redirectTo } from "./redirect"
|
|||||||
interface TreeState {
|
interface TreeState {
|
||||||
rootCategory?: Category
|
rootCategory?: Category
|
||||||
mobileMenuOpen: boolean
|
mobileMenuOpen: boolean
|
||||||
|
sidebarWidth: number
|
||||||
|
sidebarVisible: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: TreeState = {
|
const initialState: TreeState = {
|
||||||
mobileMenuOpen: false,
|
mobileMenuOpen: false,
|
||||||
|
sidebarWidth: 350,
|
||||||
|
sidebarVisible: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const reloadTree = createAsyncThunk("tree/reload", () => client.category.getRoot().then(r => r.data))
|
export const reloadTree = createAsyncThunk("tree/reload", () => client.category.getRoot().then(r => r.data))
|
||||||
@@ -27,6 +31,12 @@ export const treeSlice = createSlice({
|
|||||||
setMobileMenuOpen: (state, action: PayloadAction<boolean>) => {
|
setMobileMenuOpen: (state, action: PayloadAction<boolean>) => {
|
||||||
state.mobileMenuOpen = action.payload
|
state.mobileMenuOpen = action.payload
|
||||||
},
|
},
|
||||||
|
setSidebarWidth: (state, action: PayloadAction<number>) => {
|
||||||
|
state.sidebarWidth = action.payload
|
||||||
|
},
|
||||||
|
toggleSidebar: state => {
|
||||||
|
state.sidebarVisible = !state.sidebarVisible
|
||||||
|
},
|
||||||
},
|
},
|
||||||
extraReducers: builder => {
|
extraReducers: builder => {
|
||||||
builder.addCase(reloadTree.fulfilled, (state, action) => {
|
builder.addCase(reloadTree.fulfilled, (state, action) => {
|
||||||
@@ -54,5 +64,5 @@ export const treeSlice = createSlice({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const { setMobileMenuOpen } = treeSlice.actions
|
export const { setMobileMenuOpen, setSidebarWidth, toggleSidebar } = treeSlice.actions
|
||||||
export default treeSlice.reducer
|
export default treeSlice.reducer
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ export interface Subscription {
|
|||||||
iconUrl: string
|
iconUrl: string
|
||||||
unread: number
|
unread: number
|
||||||
categoryId?: string
|
categoryId?: string
|
||||||
position?: number
|
position: number
|
||||||
newestItemTime?: number
|
newestItemTime?: number
|
||||||
filter?: string
|
filter?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,17 +52,4 @@ export const scrollToWithCallback = ({
|
|||||||
element.scrollTo(options)
|
element.scrollTo(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const openLinkInBackgroundTab = (url: string) => {
|
|
||||||
// simulate ctrl+click to open tab in background
|
|
||||||
const a = document.createElement("a")
|
|
||||||
a.href = url
|
|
||||||
a.rel = "noreferrer"
|
|
||||||
a.dispatchEvent(
|
|
||||||
new MouseEvent("click", {
|
|
||||||
ctrlKey: true,
|
|
||||||
metaKey: true,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const truncate = (str: string, n: number) => (str.length > n ? `${str.slice(0, n - 1)}\u2026` : str)
|
export const truncate = (str: string, n: number) => (str.length > n ? `${str.slice(0, n - 1)}\u2026` : str)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ActionIcon, Button, useMantineTheme } from "@mantine/core"
|
import { ActionIcon, Button, Tooltip, useMantineTheme } from "@mantine/core"
|
||||||
import { ActionIconProps } from "@mantine/core/lib/ActionIcon/ActionIcon"
|
import { ActionIconProps } from "@mantine/core/lib/ActionIcon/ActionIcon"
|
||||||
import { ButtonProps } from "@mantine/core/lib/Button/Button"
|
import { ButtonProps } from "@mantine/core/lib/Button/Button"
|
||||||
import { useMediaQuery } from "@mantine/hooks"
|
import { useMediaQuery } from "@mantine/hooks"
|
||||||
@@ -7,9 +7,10 @@ import { forwardRef, MouseEventHandler, ReactNode } from "react"
|
|||||||
interface ActionButtonProps {
|
interface ActionButtonProps {
|
||||||
className?: string
|
className?: string
|
||||||
icon?: ReactNode
|
icon?: ReactNode
|
||||||
label?: ReactNode
|
label: ReactNode
|
||||||
onClick?: MouseEventHandler
|
onClick?: MouseEventHandler
|
||||||
variant?: ActionIconProps["variant"] & ButtonProps["variant"]
|
variant?: ActionIconProps["variant"] & ButtonProps["variant"]
|
||||||
|
hideLabelOnDesktop?: boolean
|
||||||
showLabelOnMobile?: boolean
|
showLabelOnMobile?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,11 +21,13 @@ export const ActionButton = forwardRef<HTMLButtonElement, ActionButtonProps>((pr
|
|||||||
const theme = useMantineTheme()
|
const theme = useMantineTheme()
|
||||||
const variant = props.variant ?? "subtle"
|
const variant = props.variant ?? "subtle"
|
||||||
const mobile = !useMediaQuery(`(min-width: ${theme.breakpoints.lg})`)
|
const mobile = !useMediaQuery(`(min-width: ${theme.breakpoints.lg})`)
|
||||||
const iconOnly = !props.showLabelOnMobile && (mobile || !props.label)
|
const iconOnly = (mobile && !props.showLabelOnMobile) || (!mobile && props.hideLabelOnDesktop)
|
||||||
return iconOnly ? (
|
return iconOnly ? (
|
||||||
<ActionIcon ref={ref} color={theme.primaryColor} variant={variant} className={props.className} onClick={props.onClick}>
|
<Tooltip label={props.label} openDelay={500}>
|
||||||
{props.icon}
|
<ActionIcon ref={ref} color={theme.primaryColor} variant={variant} className={props.className} onClick={props.onClick}>
|
||||||
</ActionIcon>
|
{props.icon}
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<Button ref={ref} variant={variant} size="xs" className={props.className} leftIcon={props.icon} onClick={props.onClick}>
|
<Button ref={ref} variant={variant} size="xs" className={props.className} leftIcon={props.icon} onClick={props.onClick}>
|
||||||
{props.label}
|
{props.label}
|
||||||
|
|||||||
@@ -1,183 +1,200 @@
|
|||||||
import { Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { Kbd, Table } from "@mantine/core"
|
import { Anchor, Box, Kbd, Stack, Table } from "@mantine/core"
|
||||||
|
import { Constants } from "app/constants"
|
||||||
|
|
||||||
export function KeyboardShortcutsHelp() {
|
export function KeyboardShortcutsHelp() {
|
||||||
return (
|
return (
|
||||||
<Table striped highlightOnHover>
|
<Stack spacing="xs">
|
||||||
<tbody>
|
<Table striped highlightOnHover>
|
||||||
<tr>
|
<tbody>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Refresh</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Refresh</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>R</Kbd>
|
<td>
|
||||||
</td>
|
<Kbd>R</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Open next entry</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Open next entry</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>J</Kbd>
|
<td>
|
||||||
</td>
|
<Kbd>J</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Open previous entry</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Open previous entry</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>K</Kbd>
|
<td>
|
||||||
</td>
|
<Kbd>K</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Set focus on next entry without opening it</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Set focus on next entry without opening it</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>N</Kbd>
|
<td>
|
||||||
</td>
|
<Kbd>N</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Set focus on previous entry without opening it</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Set focus on previous entry without opening it</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>P</Kbd>
|
<td>
|
||||||
</td>
|
<Kbd>P</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Move the page down</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Move the page down</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>
|
<td>
|
||||||
<Trans>Space</Trans>
|
<Kbd>
|
||||||
</Kbd>
|
<Trans>Space</Trans>
|
||||||
</td>
|
</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Move the page up</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Move the page up</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>
|
<td>
|
||||||
<Trans>Shift</Trans>
|
<Kbd>
|
||||||
</Kbd>
|
<Trans>Shift</Trans>
|
||||||
<span> + </span>
|
</Kbd>
|
||||||
<Kbd>
|
<span> + </span>
|
||||||
<Trans>Space</Trans>
|
<Kbd>
|
||||||
</Kbd>
|
<Trans>Space</Trans>
|
||||||
</td>
|
</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Open/close current entry</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Open/close current entry</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>O</Kbd>
|
<td>
|
||||||
<span>, </span>
|
<Kbd>O</Kbd>
|
||||||
<Kbd>
|
<span>, </span>
|
||||||
<Trans>Enter</Trans>
|
<Kbd>
|
||||||
</Kbd>
|
<Trans>Enter</Trans>
|
||||||
</td>
|
</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Open current entry in a new tab</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Open current entry in a new tab</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>V</Kbd>
|
<td>
|
||||||
</td>
|
<Kbd>V</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Open current entry in a new tab in the background</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Open current entry in a new tab in the background</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>B</Kbd>
|
<td>
|
||||||
<span>, </span>
|
<Kbd>B</Kbd>
|
||||||
<Kbd>
|
<span>*, </span>
|
||||||
<Trans>Middle click</Trans>
|
<Kbd>
|
||||||
</Kbd>
|
<Trans>Middle click</Trans>
|
||||||
</td>
|
</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Toggle read status of current entry</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Toggle read status of current entry</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>M</Kbd>
|
<td>
|
||||||
<span>, </span>
|
<Kbd>M</Kbd>
|
||||||
<Trans>Swipe header to the right</Trans>
|
<span>, </span>
|
||||||
</td>
|
<Trans>Swipe header to the right</Trans>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Mark all entries as read</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Mark all entries as read</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>
|
<td>
|
||||||
<Trans>Shift</Trans>
|
<Kbd>
|
||||||
</Kbd>
|
<Trans>Shift</Trans>
|
||||||
<span> + </span>
|
</Kbd>
|
||||||
<Kbd>A</Kbd>
|
<span> + </span>
|
||||||
</td>
|
<Kbd>A</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Go to the All view</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Go to the All view</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>G</Kbd>
|
<td>
|
||||||
<span> </span>
|
<Kbd>G</Kbd>
|
||||||
<Kbd>A</Kbd>
|
<span> </span>
|
||||||
</td>
|
<Kbd>A</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Navigate to a subscription by entering its name</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Navigate to a subscription by entering its name</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>
|
<td>
|
||||||
<Trans>Ctrl</Trans>
|
<Kbd>
|
||||||
</Kbd>
|
<Trans>Ctrl</Trans>
|
||||||
<span> + </span>
|
</Kbd>
|
||||||
<Kbd>K</Kbd>
|
<span> + </span>
|
||||||
<span>, </span>
|
<Kbd>K</Kbd>
|
||||||
<Kbd>G</Kbd>
|
<span>, </span>
|
||||||
<span> </span>
|
<Kbd>G</Kbd>
|
||||||
<Kbd>U</Kbd>
|
<span> </span>
|
||||||
</td>
|
<Kbd>U</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Show entry menu (desktop)</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Show entry menu (desktop)</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>
|
<td>
|
||||||
<Trans>Right click</Trans>
|
<Kbd>
|
||||||
</Kbd>
|
<Trans>Right click</Trans>
|
||||||
</td>
|
</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Show entry menu (mobile)</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Show entry menu (mobile)</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>
|
<td>
|
||||||
<Trans>Long press</Trans>
|
<Kbd>
|
||||||
</Kbd>
|
<Trans>Long press</Trans>
|
||||||
</td>
|
</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<Trans>Show keyboard shortcut help</Trans>
|
<td>
|
||||||
</td>
|
<Trans>Toggle sidebar</Trans>
|
||||||
<td>
|
</td>
|
||||||
<Kbd>?</Kbd>
|
<td>
|
||||||
</td>
|
<Kbd>F</Kbd>
|
||||||
</tr>
|
</td>
|
||||||
</tbody>
|
</tr>
|
||||||
</Table>
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Trans>Show keyboard shortcut help</Trans>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Kbd>?</Kbd>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</Table>
|
||||||
|
<Box>
|
||||||
|
<span>* </span>
|
||||||
|
<Anchor href={Constants.browserExtensionUrl} target="_blank" rel="noreferrer">
|
||||||
|
<Trans>Browser extension required for Chrome</Trans>
|
||||||
|
</Anchor>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
|
import { Tooltip } from "@mantine/core"
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
@@ -10,5 +11,10 @@ export function RelativeDate(props: { date: Date | number | undefined }) {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
if (!props.date) return <Trans>N/A</Trans>
|
if (!props.date) return <Trans>N/A</Trans>
|
||||||
return <>{dayjs(props.date).from(dayjs(now))}</>
|
const date = dayjs(props.date)
|
||||||
|
return (
|
||||||
|
<Tooltip label={date.toString()} openDelay={500}>
|
||||||
|
<span>{date.from(dayjs(now))}</span>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,11 @@ import {
|
|||||||
selectPreviousEntry,
|
selectPreviousEntry,
|
||||||
} from "app/slices/entries"
|
} from "app/slices/entries"
|
||||||
import { redirectToRootCategory } from "app/slices/redirect"
|
import { redirectToRootCategory } from "app/slices/redirect"
|
||||||
|
import { toggleSidebar } from "app/slices/tree"
|
||||||
import { useAppDispatch, useAppSelector } from "app/store"
|
import { useAppDispatch, useAppSelector } from "app/store"
|
||||||
import { openLinkInBackgroundTab } from "app/utils"
|
|
||||||
import { KeyboardShortcutsHelp } from "components/KeyboardShortcutsHelp"
|
import { KeyboardShortcutsHelp } from "components/KeyboardShortcutsHelp"
|
||||||
import { Loader } from "components/Loader"
|
import { Loader } from "components/Loader"
|
||||||
|
import { useBrowserExtension } from "hooks/useBrowserExtension"
|
||||||
import { useMousetrap } from "hooks/useMousetrap"
|
import { useMousetrap } from "hooks/useMousetrap"
|
||||||
import { useViewMode } from "hooks/useViewMode"
|
import { useViewMode } from "hooks/useViewMode"
|
||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
@@ -29,16 +30,18 @@ export function FeedEntries() {
|
|||||||
const entriesTimestamp = useAppSelector(state => state.entries.timestamp)
|
const entriesTimestamp = useAppSelector(state => state.entries.timestamp)
|
||||||
const selectedEntryId = useAppSelector(state => state.entries.selectedEntryId)
|
const selectedEntryId = useAppSelector(state => state.entries.selectedEntryId)
|
||||||
const hasMore = useAppSelector(state => state.entries.hasMore)
|
const hasMore = useAppSelector(state => state.entries.hasMore)
|
||||||
const { viewMode } = useViewMode()
|
|
||||||
const scrollMarks = useAppSelector(state => state.user.settings?.scrollMarks)
|
const scrollMarks = useAppSelector(state => state.user.settings?.scrollMarks)
|
||||||
const scrollingToEntry = useAppSelector(state => state.entries.scrollingToEntry)
|
const scrollingToEntry = useAppSelector(state => state.entries.scrollingToEntry)
|
||||||
|
const sidebarVisible = useAppSelector(state => state.tree.sidebarVisible)
|
||||||
|
const { viewMode } = useViewMode()
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
const { openLinkInBackgroundTab } = useBrowserExtension()
|
||||||
|
|
||||||
const selectedEntry = entries.find(e => e.id === selectedEntryId)
|
const selectedEntry = entries.find(e => e.id === selectedEntryId)
|
||||||
|
|
||||||
const headerClicked = (entry: ExpendableEntry, event: React.MouseEvent) => {
|
const headerClicked = (entry: ExpendableEntry, event: React.MouseEvent) => {
|
||||||
if (event.button === 1 || event.ctrlKey || event.metaKey) {
|
const middleClick = event.button === 1 || event.ctrlKey || event.metaKey
|
||||||
// middle click
|
if (middleClick || viewMode === "expanded") {
|
||||||
dispatch(markEntry({ entry, read: true }))
|
dispatch(markEntry({ entry, read: true }))
|
||||||
} else if (event.button === 0) {
|
} else if (event.button === 0) {
|
||||||
// main click
|
// main click
|
||||||
@@ -211,7 +214,6 @@ export function FeedEntries() {
|
|||||||
window.open(selectedEntry.url, "_blank", "noreferrer")
|
window.open(selectedEntry.url, "_blank", "noreferrer")
|
||||||
})
|
})
|
||||||
useMousetrap("b", () => {
|
useMousetrap("b", () => {
|
||||||
// simulate ctrl+click to open tab in background
|
|
||||||
if (!selectedEntry) return
|
if (!selectedEntry) return
|
||||||
openLinkInBackgroundTab(selectedEntry.url)
|
openLinkInBackgroundTab(selectedEntry.url)
|
||||||
})
|
})
|
||||||
@@ -234,6 +236,7 @@ export function FeedEntries() {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
useMousetrap("g a", () => dispatch(redirectToRootCategory()))
|
useMousetrap("g a", () => dispatch(redirectToRootCategory()))
|
||||||
|
useMousetrap("f", () => dispatch(toggleSidebar()))
|
||||||
useMousetrap("?", () =>
|
useMousetrap("?", () =>
|
||||||
openModal({
|
openModal({
|
||||||
title: <Trans>Keyboard shortcuts</Trans>,
|
title: <Trans>Keyboard shortcuts</Trans>,
|
||||||
@@ -263,7 +266,9 @@ export function FeedEntries() {
|
|||||||
<FeedEntry
|
<FeedEntry
|
||||||
entry={entry}
|
entry={entry}
|
||||||
expanded={!!entry.expanded || viewMode === "expanded"}
|
expanded={!!entry.expanded || viewMode === "expanded"}
|
||||||
|
selected={entry.id === selectedEntryId}
|
||||||
showSelectionIndicator={entry.id === selectedEntryId && (!entry.expanded || viewMode === "expanded")}
|
showSelectionIndicator={entry.id === selectedEntryId && (!entry.expanded || viewMode === "expanded")}
|
||||||
|
maxWidth={sidebarVisible ? Constants.layout.entryMaxWidth : undefined}
|
||||||
onHeaderClick={event => headerClicked(entry, event)}
|
onHeaderClick={event => headerClicked(entry, event)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,22 +16,33 @@ import { FeedEntryHeader } from "./FeedEntryHeader"
|
|||||||
interface FeedEntryProps {
|
interface FeedEntryProps {
|
||||||
entry: Entry
|
entry: Entry
|
||||||
expanded: boolean
|
expanded: boolean
|
||||||
|
selected: boolean
|
||||||
showSelectionIndicator: boolean
|
showSelectionIndicator: boolean
|
||||||
|
maxWidth?: number
|
||||||
onHeaderClick: (e: React.MouseEvent) => void
|
onHeaderClick: (e: React.MouseEvent) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = createStyles((theme, props: FeedEntryProps & { viewMode?: ViewMode }) => {
|
const useStyles = createStyles((theme, props: FeedEntryProps & { viewMode?: ViewMode }) => {
|
||||||
let backgroundColor
|
let backgroundColor
|
||||||
if (theme.colorScheme === "dark") backgroundColor = props.entry.read ? "inherit" : theme.colors.dark[5]
|
if (theme.colorScheme === "dark") {
|
||||||
else backgroundColor = props.entry.read && !props.expanded ? theme.colors.gray[0] : "inherit"
|
backgroundColor = props.entry.read ? "inherit" : theme.colors.dark[5]
|
||||||
|
} else {
|
||||||
|
backgroundColor = props.entry.read && !props.expanded ? theme.colors.gray[0] : "inherit"
|
||||||
|
}
|
||||||
|
|
||||||
let marginY = 10
|
let marginY = 10
|
||||||
if (props.viewMode === "title") marginY = 2
|
if (props.viewMode === "title") {
|
||||||
else if (props.viewMode === "cozy") marginY = 6
|
marginY = 2
|
||||||
|
} else if (props.viewMode === "cozy") {
|
||||||
|
marginY = 6
|
||||||
|
}
|
||||||
|
|
||||||
let mobileMarginY = 6
|
let mobileMarginY = 6
|
||||||
if (props.viewMode === "title") mobileMarginY = 2
|
if (props.viewMode === "title") {
|
||||||
else if (props.viewMode === "cozy") mobileMarginY = 4
|
mobileMarginY = 2
|
||||||
|
} else if (props.viewMode === "cozy") {
|
||||||
|
mobileMarginY = 4
|
||||||
|
}
|
||||||
|
|
||||||
let backgroundHoverColor = backgroundColor
|
let backgroundHoverColor = backgroundColor
|
||||||
if (!props.expanded && !props.entry.read) {
|
if (!props.expanded && !props.entry.read) {
|
||||||
@@ -58,7 +69,7 @@ const useStyles = createStyles((theme, props: FeedEntryProps & { viewMode?: View
|
|||||||
textDecoration: "none",
|
textDecoration: "none",
|
||||||
},
|
},
|
||||||
body: {
|
body: {
|
||||||
maxWidth: Constants.layout.entryMaxWidth,
|
maxWidth: props.maxWidth ?? "100%",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +83,7 @@ const useStyles = createStyles((theme, props: FeedEntryProps & { viewMode?: View
|
|||||||
|
|
||||||
export function FeedEntry(props: FeedEntryProps) {
|
export function FeedEntry(props: FeedEntryProps) {
|
||||||
const { viewMode } = useViewMode()
|
const { viewMode } = useViewMode()
|
||||||
const { classes } = useStyles({ ...props, viewMode })
|
const { classes, cx } = useStyles({ ...props, viewMode })
|
||||||
|
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
@@ -86,16 +97,32 @@ export function FeedEntry(props: FeedEntryProps) {
|
|||||||
if (viewMode === "title" || viewMode === "cozy") paddingX = 6
|
if (viewMode === "title" || viewMode === "cozy") paddingX = 6
|
||||||
|
|
||||||
let paddingY: MantineNumberSize = "xs"
|
let paddingY: MantineNumberSize = "xs"
|
||||||
if (viewMode === "title") paddingY = 4
|
if (viewMode === "title") {
|
||||||
else if (viewMode === "cozy") paddingY = 8
|
paddingY = 4
|
||||||
|
} else if (viewMode === "cozy") {
|
||||||
|
paddingY = 8
|
||||||
|
}
|
||||||
|
|
||||||
let borderRadius: MantineNumberSize = "sm"
|
let borderRadius: MantineNumberSize = "sm"
|
||||||
if (viewMode === "title") borderRadius = 0
|
if (viewMode === "title") {
|
||||||
else if (viewMode === "cozy") borderRadius = "xs"
|
borderRadius = 0
|
||||||
|
} else if (viewMode === "cozy") {
|
||||||
|
borderRadius = "xs"
|
||||||
|
}
|
||||||
|
|
||||||
const compactHeader = !props.expanded && (viewMode === "title" || viewMode === "cozy")
|
const compactHeader = !props.expanded && (viewMode === "title" || viewMode === "cozy")
|
||||||
return (
|
return (
|
||||||
<Paper withBorder radius={borderRadius} className={classes.paper}>
|
<Paper
|
||||||
|
withBorder
|
||||||
|
radius={borderRadius}
|
||||||
|
className={cx(classes.paper, {
|
||||||
|
read: props.entry.read,
|
||||||
|
unread: !props.entry.read,
|
||||||
|
expanded: props.expanded,
|
||||||
|
selected: props.selected,
|
||||||
|
"show-selection-indicator": props.showSelectionIndicator,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
className={classes.headerLink}
|
className={classes.headerLink}
|
||||||
href={props.entry.url}
|
href={props.entry.url}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { markEntriesUpToEntry, markEntry, starEntry } from "app/slices/entries"
|
|||||||
import { redirectToFeed } from "app/slices/redirect"
|
import { redirectToFeed } from "app/slices/redirect"
|
||||||
import { useAppDispatch, useAppSelector } from "app/store"
|
import { useAppDispatch, useAppSelector } from "app/store"
|
||||||
import { Entry } from "app/types"
|
import { Entry } from "app/types"
|
||||||
import { openLinkInBackgroundTab, truncate } from "app/utils"
|
import { truncate } from "app/utils"
|
||||||
|
import { useBrowserExtension } from "hooks/useBrowserExtension"
|
||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
import { Item, Menu, Separator, useContextMenu } from "react-contexify"
|
import { Item, Menu, Separator, useContextMenu } from "react-contexify"
|
||||||
import { TbArrowBarToDown, TbExternalLink, TbEyeCheck, TbEyeOff, TbRss, TbStar, TbStarOff } from "react-icons/tb"
|
import { TbArrowBarToDown, TbExternalLink, TbEyeCheck, TbEyeOff, TbRss, TbStar, TbStarOff } from "react-icons/tb"
|
||||||
@@ -34,6 +35,7 @@ export function FeedEntryContextMenu(props: FeedEntryContextMenuProps) {
|
|||||||
const { classes, theme } = useStyles()
|
const { classes, theme } = useStyles()
|
||||||
const sourceType = useAppSelector(state => state.entries.source.type)
|
const sourceType = useAppSelector(state => state.entries.source.type)
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
const { openLinkInBackgroundTab } = useBrowserExtension()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu id={menuId(props.entry)} theme={theme.colorScheme} animation={false} className={classes.menu}>
|
<Menu id={menuId(props.entry)} theme={theme.colorScheme} animation={false} className={classes.menu}>
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import { useAppDispatch, useAppSelector } from "app/store"
|
|||||||
import { ActionButton } from "components/ActionButtton"
|
import { ActionButton } from "components/ActionButtton"
|
||||||
import { ButtonToolbar } from "components/ButtonToolbar"
|
import { ButtonToolbar } from "components/ButtonToolbar"
|
||||||
import { Loader } from "components/Loader"
|
import { Loader } from "components/Loader"
|
||||||
|
import { useBrowserExtension } from "hooks/useBrowserExtension"
|
||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
import { TbArrowDown, TbArrowUp, TbEye, TbEyeOff, TbRefresh, TbSearch, TbUser, TbX } from "react-icons/tb"
|
import { TbArrowDown, TbArrowUp, TbExternalLink, TbEye, TbEyeOff, TbRefresh, TbSearch, TbSettings, TbUser, TbX } from "react-icons/tb"
|
||||||
import { MarkAllAsReadButton } from "./MarkAllAsReadButton"
|
import { MarkAllAsReadButton } from "./MarkAllAsReadButton"
|
||||||
import { ProfileMenu } from "./ProfileMenu"
|
import { ProfileMenu } from "./ProfileMenu"
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ export function Header() {
|
|||||||
const settings = useAppSelector(state => state.user.settings)
|
const settings = useAppSelector(state => state.user.settings)
|
||||||
const profile = useAppSelector(state => state.user.profile)
|
const profile = useAppSelector(state => state.user.profile)
|
||||||
const searchFromStore = useAppSelector(state => state.entries.search)
|
const searchFromStore = useAppSelector(state => state.entries.search)
|
||||||
|
const { isBrowserExtensionPopup, openSettingsPage, openAppInNewTab } = useBrowserExtension()
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const searchForm = useForm<{ search: string }>({
|
const searchForm = useForm<{ search: string }>({
|
||||||
@@ -87,6 +89,23 @@ export function Header() {
|
|||||||
<HeaderDivider />
|
<HeaderDivider />
|
||||||
|
|
||||||
<ProfileMenu control={<ActionButton icon={<TbUser size={iconSize} />} label={profile?.name} />} />
|
<ProfileMenu control={<ActionButton icon={<TbUser size={iconSize} />} label={profile?.name} />} />
|
||||||
|
|
||||||
|
{isBrowserExtensionPopup && (
|
||||||
|
<>
|
||||||
|
<HeaderDivider />
|
||||||
|
|
||||||
|
<ActionButton
|
||||||
|
icon={<TbSettings size={iconSize} />}
|
||||||
|
label={<Trans>Extension options</Trans>}
|
||||||
|
onClick={() => openSettingsPage()}
|
||||||
|
/>
|
||||||
|
<ActionButton
|
||||||
|
icon={<TbExternalLink size={iconSize} />}
|
||||||
|
label={<Trans>Open CommaFeed</Trans>}
|
||||||
|
onClick={() => openAppInNewTab()}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</ButtonToolbar>
|
</ButtonToolbar>
|
||||||
</Center>
|
</Center>
|
||||||
)
|
)
|
||||||
|
|||||||
64
commafeed-client/src/hooks/useBrowserExtension.ts
Normal file
64
commafeed-client/src/hooks/useBrowserExtension.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
|
export const useBrowserExtension = () => {
|
||||||
|
// the extension will set the "browser-extension-installed" attribute on the root element
|
||||||
|
const [browserExtensionVersion, setBrowserExtensionVersion] = useState(
|
||||||
|
document.documentElement.getAttribute("browser-extension-installed")
|
||||||
|
)
|
||||||
|
|
||||||
|
// monitor the attribute on the root element as it may change after the page was loaded
|
||||||
|
useEffect(() => {
|
||||||
|
const observer = new MutationObserver(mutations => {
|
||||||
|
mutations.forEach(mutation => {
|
||||||
|
if (mutation.type === "attributes") {
|
||||||
|
const element = mutation.target as Element
|
||||||
|
const version = element.getAttribute("browser-extension-installed")
|
||||||
|
if (version) setBrowserExtensionVersion(version)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
attributes: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => observer.disconnect()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// when not in an iframe, window.parent is a reference to window
|
||||||
|
const isBrowserExtensionPopup = window.parent !== window
|
||||||
|
const isBrowserExtensionInstalled = isBrowserExtensionPopup || !!browserExtensionVersion
|
||||||
|
const isBrowserExtensionInstallable = !isBrowserExtensionPopup
|
||||||
|
|
||||||
|
const w = isBrowserExtensionPopup ? window.parent : window
|
||||||
|
const openSettingsPage = () => w.postMessage("open-settings-page", "*")
|
||||||
|
const openAppInNewTab = () => w.postMessage("open-app-in-new-tab", "*")
|
||||||
|
const openLinkInBackgroundTab = (url: string) => {
|
||||||
|
if (isBrowserExtensionInstalled) {
|
||||||
|
w.postMessage(`open-link-in-background-tab:${url}`, "*")
|
||||||
|
} else {
|
||||||
|
// fallback to ctrl+click simulation
|
||||||
|
const a = document.createElement("a")
|
||||||
|
a.href = url
|
||||||
|
a.rel = "noreferrer"
|
||||||
|
a.dispatchEvent(
|
||||||
|
new MouseEvent("click", {
|
||||||
|
ctrlKey: true,
|
||||||
|
metaKey: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const setBadgeUnreadCount = (count: number) => w.postMessage(`set-badge-unread-count:${count}`, "*")
|
||||||
|
|
||||||
|
return {
|
||||||
|
browserExtensionVersion,
|
||||||
|
isBrowserExtensionInstallable,
|
||||||
|
isBrowserExtensionInstalled,
|
||||||
|
isBrowserExtensionPopup,
|
||||||
|
openSettingsPage,
|
||||||
|
openAppInNewTab,
|
||||||
|
openLinkInBackgroundTab,
|
||||||
|
setBadgeUnreadCount,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "العودة"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "العودة لتسجيل الدخول"
|
msgstr "العودة لتسجيل الدخول"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "ملحقات المستعرض"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "تأكد من عمل الخلاصة"
|
msgstr "تأكد من عمل الخلاصة"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed هو مشروع مفتوح المصدر. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed التالي العنصر غير المقروء"
|
msgstr "CommaFeed التالي العنصر غير المقروء"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "إصدار CommaFeed {الإصدار} ({مراجعة})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "موسع"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "قم بتصدير اشتراكاتك وفئاتك كملف OPML يمكن استيراده في خدمات قراءة الأعلاف الأخرى"
|
msgstr "قم بتصدير اشتراكاتك وفئاتك كملف OPML يمكن استيراده في خدمات قراءة الأعلاف الأخرى"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "اسم الخلاصة"
|
msgstr "اسم الخلاصة"
|
||||||
@@ -540,6 +553,10 @@ msgstr "الأقدم أولا"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "اوووه!"
|
msgstr "اوووه!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "فتح الإدخال الحالي في علامة تبويب جديدة"
|
msgstr "فتح الإدخال الحالي في علامة تبويب جديدة"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "التبديل إلى النسق الداكن"
|
msgstr "التبديل إلى النسق الداكن"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "قم بالتبديل إلى النسق الفاتح"
|
msgstr "قم بالتبديل إلى النسق الفاتح"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "الموضوع"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "تبديل قراءة حالة الإدخال الحالي"
|
msgstr "تبديل قراءة حالة الإدخال الحالي"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "جرب CommaFeed باستخدام الحساب التجريبي: تجريبي / تجريبي"
|
msgstr "جرب CommaFeed باستخدام الحساب التجريبي: تجريبي / تجريبي"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Enrere"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Tornar a iniciar sessió"
|
msgstr "Tornar a iniciar sessió"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Extensions del navegador"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Comproveu que el canal funciona"
|
msgstr "Comproveu que el canal funciona"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed és un projecte de codi obert. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed següent element no llegit"
|
msgstr "CommaFeed següent element no llegit"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "Versió CommaFeed {versió} ({revisió})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Ampliat"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "exporteu les vostres subscripcions i categories com a fitxer OPML que es pot importar a altres serveis de lectura de feeds"
|
msgstr "exporteu les vostres subscripcions i categories com a fitxer OPML que es pot importar a altres serveis de lectura de feeds"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Nom del canal"
|
msgstr "Nom del canal"
|
||||||
@@ -540,6 +553,10 @@ msgstr "el més vell primer"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Vaja!"
|
msgstr "Vaja!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Obre l'entrada actual en una pestanya nova"
|
msgstr "Obre l'entrada actual en una pestanya nova"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Canvia al tema fosc"
|
msgstr "Canvia al tema fosc"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Canvia al tema clar"
|
msgstr "Canvia al tema clar"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Tema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Canvia l'estat de lectura de l'entrada actual"
|
msgstr "Canvia l'estat de lectura de l'entrada actual"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Proveu CommaFeed amb el compte de demostració: demo/demo"
|
msgstr "Proveu CommaFeed amb el compte de demostració: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Zpět"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Zpět k přihlášení"
|
msgstr "Zpět k přihlášení"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Rozšíření prohlížeče"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Zkontrolujte, zda zdroj funguje"
|
msgstr "Zkontrolujte, zda zdroj funguje"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed je projekt s otevřeným zdrojovým kódem. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed další nepřečtená položka"
|
msgstr "CommaFeed další nepřečtená položka"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed verze {version} ({revision})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Rozbaleno"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Exportujte svá předplatná a kategorie jako soubor OPML, který lze importovat do jiných služeb čtení kanálů"
|
msgstr "Exportujte svá předplatná a kategorie jako soubor OPML, který lze importovat do jiných služeb čtení kanálů"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Název zdroje"
|
msgstr "Název zdroje"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Nejdříve nejstarší"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Jejda!"
|
msgstr "Jejda!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Otevřete aktuální položku na nové kartě"
|
msgstr "Otevřete aktuální položku na nové kartě"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Přepněte na tmavý motiv"
|
msgstr "Přepněte na tmavý motiv"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Přepněte na světlé téma"
|
msgstr "Přepněte na světlé téma"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Téma"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Přepne stav čtení aktuálního záznamu"
|
msgstr "Přepne stav čtení aktuálního záznamu"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Vyzkoušejte CommaFeed s demo účtem: demo/demo"
|
msgstr "Vyzkoušejte CommaFeed s demo účtem: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Yn ôl"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Yn ôl i fewngofnodi"
|
msgstr "Yn ôl i fewngofnodi"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Estyniadau porwr"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Gwiriwch fod y porthiant yn gweithio"
|
msgstr "Gwiriwch fod y porthiant yn gweithio"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "Mae ComaFeed yn brosiect ffynhonnell agored. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed eitem nesaf heb ei darllen"
|
msgstr "CommaFeed eitem nesaf heb ei darllen"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "Fersiwn ComaFeed {fersiwn} ({ adolygu})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Ehangu"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Allforio eich tanysgrifiadau a'ch categorïau fel ffeil OPML y gellir ei mewnforio i wasanaethau darllen porthiant eraill"
|
msgstr "Allforio eich tanysgrifiadau a'ch categorïau fel ffeil OPML y gellir ei mewnforio i wasanaethau darllen porthiant eraill"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Enw porthiant"
|
msgstr "Enw porthiant"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Hynaf yn gyntaf"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Wps!"
|
msgstr "Wps!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Agorwch y cofnod cyfredol mewn tab newydd"
|
msgstr "Agorwch y cofnod cyfredol mewn tab newydd"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Newid i thema dywyll"
|
msgstr "Newid i thema dywyll"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Newid i thema golau"
|
msgstr "Newid i thema golau"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Thema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Toglo statws darllen y cofnod cyfredol"
|
msgstr "Toglo statws darllen y cofnod cyfredol"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Rhowch gynnig ar CommaFeed gyda'r cyfrif demo: demo / demo"
|
msgstr "Rhowch gynnig ar CommaFeed gyda'r cyfrif demo: demo / demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Tilbage"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Tilbage for at logge ind"
|
msgstr "Tilbage for at logge ind"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Browserudvidelser"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,15 +168,15 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Tjek, at foderet virker"
|
msgstr "Tjek, at foderet virker"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed er et open source-projekt. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed næste ulæste element"
|
msgstr "CommaFeed næste ulæste element"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
@@ -308,6 +316,11 @@ msgstr "Udvidet"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Eksporter dine abonnementer og kategorier som en OPML-fil, der kan importeres i andre feed-læsningstjenester"
|
msgstr "Eksporter dine abonnementer og kategorier som en OPML-fil, der kan importeres i andre feed-læsningstjenester"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Feednavn"
|
msgstr "Feednavn"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Ældst først"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Hovsa!"
|
msgstr "Hovsa!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Åbn den aktuelle post i en ny fane"
|
msgstr "Åbn den aktuelle post i en ny fane"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Skift til mørkt tema"
|
msgstr "Skift til mørkt tema"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Skift til lystema"
|
msgstr "Skift til lystema"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Tema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Skift læsestatus for den aktuelle post"
|
msgstr "Skift læsestatus for den aktuelle post"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Prøv CommaFeed med demokontoen: demo/demo"
|
msgstr "Prøv CommaFeed med demokontoen: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Zurück"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Zurück zum Anmelden"
|
msgstr "Zurück zum Anmelden"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Browsererweiterungen"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Überprüfen Sie, ob der Feed funktioniert"
|
msgstr "Überprüfen Sie, ob der Feed funktioniert"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed ist ein Open-Source-Projekt. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed nächstes ungelesenes Element"
|
msgstr "CommaFeed nächstes ungelesenes Element"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed-Version {Version} ({Revision})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Erweitert"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Exportieren Sie Ihre Abonnements und Kategorien als OPML-Datei, die in andere Feed-Lesedienste importiert werden kann"
|
msgstr "Exportieren Sie Ihre Abonnements und Kategorien als OPML-Datei, die in andere Feed-Lesedienste importiert werden kann"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Feedname"
|
msgstr "Feedname"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Älteste zuerst"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Ups!"
|
msgstr "Ups!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Aktuellen Eintrag in neuem Tab öffnen"
|
msgstr "Aktuellen Eintrag in neuem Tab öffnen"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Zum dunklen Design wechseln"
|
msgstr "Zum dunklen Design wechseln"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Wechseln Sie zum Lichtdesign"
|
msgstr "Wechseln Sie zum Lichtdesign"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Thema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Lesestatus des aktuellen Eintrags umschalten"
|
msgstr "Lesestatus des aktuellen Eintrags umschalten"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Testen Sie CommaFeed mit dem Demokonto: demo/demo"
|
msgstr "Testen Sie CommaFeed mit dem Demokonto: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr "{0} (in {1})"
|
msgstr "{0} (in {1})"
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr "<0>Complete syntax is available </0><1>here</1>."
|
msgstr "<0>Complete syntax is available </0><1>here</1>."
|
||||||
@@ -123,9 +127,13 @@ msgstr "Back"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Back to log in"
|
msgstr "Back to log in"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr "Browser extension required for Chrome"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Browser extentions"
|
msgstr "Browser extention"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Check that the feed is working"
|
msgstr "Check that the feed is working"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgstr "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed next unread item"
|
msgstr "CommaFeed next unread item"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed version {version} ({revision})"
|
msgstr "CommaFeed version {version} ({revision})."
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Expanded"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgstr "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr "Extension options"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Feed name"
|
msgstr "Feed name"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Oldest first"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Oops!"
|
msgstr "Oops!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr "Open CommaFeed"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Open current entry in a new tab"
|
msgstr "Open current entry in a new tab"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr "Swipe header to the right"
|
msgstr "Swipe header to the right"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Switch to dark theme"
|
msgstr "Switch to dark theme"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Switch to light theme"
|
msgstr "Switch to light theme"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Theme"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Toggle read status of current entry"
|
msgstr "Toggle read status of current entry"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr "Toggle sidebar"
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Try out CommaFeed with the demo account: demo/demo"
|
msgstr "Try out CommaFeed with the demo account: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Atrás"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Volver a iniciar sesión"
|
msgstr "Volver a iniciar sesión"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Extensiones del navegador"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Compruebe que el feed funciona"
|
msgstr "Compruebe que el feed funciona"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed es un proyecto de código abierto. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed siguiente elemento no leído"
|
msgstr "CommaFeed siguiente elemento no leído"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "versión de CommaFeed {versión} ({revisión})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Expandido"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Exporte sus suscripciones y categorías como un archivo OPML que se puede importar en otros servicios de lectura de feeds"
|
msgstr "Exporte sus suscripciones y categorías como un archivo OPML que se puede importar en otros servicios de lectura de feeds"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Nombre de alimentación"
|
msgstr "Nombre de alimentación"
|
||||||
@@ -540,6 +553,10 @@ msgstr "más antigua primero"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "¡Ups!"
|
msgstr "¡Ups!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Abrir la entrada actual en una nueva pestaña"
|
msgstr "Abrir la entrada actual en una nueva pestaña"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Cambiar a tema oscuro"
|
msgstr "Cambiar a tema oscuro"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Cambiar a tema claro"
|
msgstr "Cambiar a tema claro"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Tema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Alternar estado de lectura de la entrada actual"
|
msgstr "Alternar estado de lectura de la entrada actual"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Pruebe CommaFeed con la cuenta demo: demo/demo"
|
msgstr "Pruebe CommaFeed con la cuenta demo: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "برگشت"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "بازگشت برای ورود به سیستم"
|
msgstr "بازگشت برای ورود به سیستم"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "گسترش مرورگر"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "بررسی کنید که خوراک کار می کند"
|
msgstr "بررسی کنید که خوراک کار می کند"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed یک پروژه منبع باز است. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "مورد خوانده نشده بعدی CommaFeed"
|
msgstr "مورد خوانده نشده بعدی CommaFeed"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "نسخه {نسخه} CommaFeed ({نسخه})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "گسترش یافت"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "اشتراک ها و دسته های خود را به عنوان یک فایل OPML صادر کنید که می تواند در سایر خدمات خواندن فید وارد شود"
|
msgstr "اشتراک ها و دسته های خود را به عنوان یک فایل OPML صادر کنید که می تواند در سایر خدمات خواندن فید وارد شود"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "نام فید"
|
msgstr "نام فید"
|
||||||
@@ -540,6 +553,10 @@ msgstr "قدیمی ترین اول"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "اوه!"
|
msgstr "اوه!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "ورودی فعلی را در یک برگه جدید باز کنید"
|
msgstr "ورودی فعلی را در یک برگه جدید باز کنید"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "تغییر به تم تیره"
|
msgstr "تغییر به تم تیره"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "روی زمینه روشن"
|
msgstr "روی زمینه روشن"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "تم"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "وضعیت خواندن ورودی فعلی را تغییر دهید"
|
msgstr "وضعیت خواندن ورودی فعلی را تغییر دهید"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "CommaFeed را با حساب آزمایشی امتحان کنید: دمو/دمو"
|
msgstr "CommaFeed را با حساب آزمایشی امتحان کنید: دمو/دمو"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Takaisin"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Takaisin sisäänkirjautumiseen"
|
msgstr "Takaisin sisäänkirjautumiseen"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Selaimen laajennukset"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Tarkista, että syöttö toimii"
|
msgstr "Tarkista, että syöttö toimii"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed on avoimen lähdekoodin projekti. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed seuraava lukematon kohde"
|
msgstr "CommaFeed seuraava lukematon kohde"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed-versio {version} ({versio})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Laajennettu"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Vie tilauksesi ja luokat OPML-tiedostona, joka voidaan tuoda muihin syötteiden lukupalveluihin"
|
msgstr "Vie tilauksesi ja luokat OPML-tiedostona, joka voidaan tuoda muihin syötteiden lukupalveluihin"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Syötteen nimi"
|
msgstr "Syötteen nimi"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Vanhin ensin"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Hups!"
|
msgstr "Hups!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Avaa nykyinen merkintä uudessa välilehdessä"
|
msgstr "Avaa nykyinen merkintä uudessa välilehdessä"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Vaihda tummaan teemaan"
|
msgstr "Vaihda tummaan teemaan"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Vaihda vaaleaan teemaan"
|
msgstr "Vaihda vaaleaan teemaan"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Teema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Vaihda nykyisen merkinnän lukutila"
|
msgstr "Vaihda nykyisen merkinnän lukutila"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Kokeile CommaFeediä demotilillä: demo/demo"
|
msgstr "Kokeile CommaFeediä demotilillä: demo/demo"
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/content/add/CategorySelect.tsx
|
#: src/components/content/add/CategorySelect.tsx
|
||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr "{0} (sur {1})"
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr "<0>CommaFeed est un projet open-source. Les sources sont hébergées sur </0><1>GitHub</1>."
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
@@ -27,7 +31,7 @@ msgstr "<0>Déjà un compte ?</0><1>Connectez-vous !</1>"
|
|||||||
|
|
||||||
#: src/pages/app/DonatePage.tsx
|
#: src/pages/app/DonatePage.tsx
|
||||||
msgid "<0>Hey,</0><1>I'm Jérémie from Belgium and I've been working on CommaFeed in my free time for over 10 years now. Thanks for taking an interest in helping me continue supporting CommaFeed.</1>"
|
msgid "<0>Hey,</0><1>I'm Jérémie from Belgium and I've been working on CommaFeed in my free time for over 10 years now. Thanks for taking an interest in helping me continue supporting CommaFeed.</1>"
|
||||||
msgstr ""
|
msgstr "<0>Salut,</0><1>Je m'appelle Jérémie, je suis belge, et je développe CommaFeed sur mon temps libre depuis maintenant 10 ans. Merci de m'aider à continuer de maintenir CommaFeed.</1>"
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
msgid "<0>Need an account?</0><1>Sign up!</1>"
|
||||||
@@ -36,7 +40,7 @@ msgstr "<0>Besoin d'un compte ?</0><1>Enregistrez-vous !</1>"
|
|||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "About"
|
msgid "About"
|
||||||
msgstr "A propos"
|
msgstr "À propos"
|
||||||
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
msgid "Actions"
|
msgid "Actions"
|
||||||
@@ -85,11 +89,11 @@ msgstr "Clé API"
|
|||||||
|
|
||||||
#: src/pages/app/CategoryDetailsPage.tsx
|
#: src/pages/app/CategoryDetailsPage.tsx
|
||||||
msgid "Are you sure you want to delete category <0>{categoryName}</0>?"
|
msgid "Are you sure you want to delete category <0>{categoryName}</0>?"
|
||||||
msgstr "Etes-vous sûr de vouloir supprimer la catégorie <0>{categoryName}</0>?"
|
msgstr "Êtes-vous sûr de vouloir supprimer la catégorie <0>{categoryName}</0> ?"
|
||||||
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
msgid "Are you sure you want to delete user <0>{userName}</0> ?"
|
msgid "Are you sure you want to delete user <0>{userName}</0> ?"
|
||||||
msgstr "Etes-vous sûr de vouloir supprimer l'utilisateur <0>{userName}</0> ?"
|
msgstr "Êtes-vous sûr de vouloir supprimer l'utilisateur <0>{userName}</0> ?"
|
||||||
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
msgid "Are you sure you want to delete your account? There's no turning back!"
|
msgid "Are you sure you want to delete your account? There's no turning back!"
|
||||||
@@ -97,15 +101,15 @@ msgstr "Êtes-vous sûr de vouloir supprimer définitivement votre compte ?"
|
|||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/header/MarkAllAsReadButton.tsx
|
||||||
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark all entries of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Etes-vous sûr de vouloir marquer toutes les entrées de <0>{sourceLabel}</0> comme lues?"
|
msgstr "Êtes-vous sûr de vouloir marquer toutes les entrées de <0>{sourceLabel}</0> comme lues ?"
|
||||||
|
|
||||||
#: src/components/header/MarkAllAsReadButton.tsx
|
#: src/components/header/MarkAllAsReadButton.tsx
|
||||||
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel}</0> as read?"
|
||||||
msgstr "Etes-vous sûr de vouloir marquer les entrées de <0>{sourceLabel}</0> plus anciennes que {threshold} jours comme lues?"
|
msgstr "Êtes-vous sûr de vouloir marquer les entrées de <0>{sourceLabel}</0> plus anciennes que {threshold} jours comme lues ?"
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "Are you sure you want to unsubscribe from <0>{feedName}</0>?"
|
msgid "Are you sure you want to unsubscribe from <0>{feedName}</0>?"
|
||||||
msgstr "Etes-vous sûr de vouloir vous désabonner de <0>{feedName}</0>?"
|
msgstr "Êtes-vous sûr de vouloir vous désabonner de <0>{feedName}</0> ?"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/header/Header.tsx
|
||||||
msgid "Asc"
|
msgid "Asc"
|
||||||
@@ -123,9 +127,13 @@ msgstr "Retour"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Retour à la connexion"
|
msgstr "Retour à la connexion"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Extensions pour navigateurs"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Vérifie que le flux fonctionne"
|
msgstr "Vérifie que le flux fonctionne"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed est un projet open-source. Les sources sont hébergées sur <0>GitHub</0>."
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed prochain article non lu"
|
msgstr "CommaFeed prochain article non lu"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed version {version} ({revision})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -193,7 +201,7 @@ msgstr "Cozy"
|
|||||||
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
msgid "Create tag: {query}"
|
msgid "Create tag: {query}"
|
||||||
msgstr "Créer le tag: {query}"
|
msgstr "Créer le marqueur : {query}"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Ctrl"
|
msgid "Ctrl"
|
||||||
@@ -205,15 +213,15 @@ msgstr "Mot de passe actuel"
|
|||||||
|
|
||||||
#: src/pages/app/SettingsPage.tsx
|
#: src/pages/app/SettingsPage.tsx
|
||||||
msgid "Custom code"
|
msgid "Custom code"
|
||||||
msgstr ""
|
msgstr "Code personnalisé"
|
||||||
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
msgid "Custom CSS rules that will be applied"
|
msgid "Custom CSS rules that will be applied"
|
||||||
msgstr ""
|
msgstr "Code CSS personnalisé qui sera appliqué"
|
||||||
|
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
msgid "Custom JS code that will be executed on page load"
|
msgid "Custom JS code that will be executed on page load"
|
||||||
msgstr ""
|
msgstr "Code JS personnalisé qui sera appliqué au chargement des pages"
|
||||||
|
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
msgid "Date created"
|
msgid "Date created"
|
||||||
@@ -252,7 +260,7 @@ msgstr "Affichage"
|
|||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
#: src/pages/app/DonatePage.tsx
|
#: src/pages/app/DonatePage.tsx
|
||||||
msgid "Donate"
|
msgid "Donate"
|
||||||
msgstr ""
|
msgstr "Faites un don"
|
||||||
|
|
||||||
#: src/components/settings/ProfileSettings.tsx
|
#: src/components/settings/ProfileSettings.tsx
|
||||||
msgid "Download"
|
msgid "Download"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Vue étendue"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Exporter vos abonnements et catégories en tant que fichier OPML qui peut être importé dans d'autres services de lecture de flux"
|
msgstr "Exporter vos abonnements et catégories en tant que fichier OPML qui peut être importé dans d'autres services de lecture de flux"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Nom du flux"
|
msgstr "Nom du flux"
|
||||||
@@ -320,7 +333,7 @@ msgstr "URL du flux"
|
|||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Fetch all my feeds now"
|
msgid "Fetch all my feeds now"
|
||||||
msgstr ""
|
msgstr "Rafraîchir tous mes flux"
|
||||||
|
|
||||||
#: src/components/content/add/ImportOpml.tsx
|
#: src/components/content/add/ImportOpml.tsx
|
||||||
msgid "file is required"
|
msgid "file is required"
|
||||||
@@ -352,7 +365,7 @@ msgstr "URL du flux généré"
|
|||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Go to {0}"
|
msgid "Go to {0}"
|
||||||
msgstr ""
|
msgstr "Aller à {0}"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Go to the All view"
|
msgid "Go to the All view"
|
||||||
@@ -420,19 +433,19 @@ msgstr "Lien"
|
|||||||
|
|
||||||
#: src/hooks/useAppLoading.ts
|
#: src/hooks/useAppLoading.ts
|
||||||
msgid "Loading profile..."
|
msgid "Loading profile..."
|
||||||
msgstr "Chargement du profil ..."
|
msgstr "Chargement du profil..."
|
||||||
|
|
||||||
#: src/hooks/useAppLoading.ts
|
#: src/hooks/useAppLoading.ts
|
||||||
msgid "Loading settings..."
|
msgid "Loading settings..."
|
||||||
msgstr "Chargement des paramètres ..."
|
msgstr "Chargement des paramètres..."
|
||||||
|
|
||||||
#: src/hooks/useAppLoading.ts
|
#: src/hooks/useAppLoading.ts
|
||||||
msgid "Loading subscriptions..."
|
msgid "Loading subscriptions..."
|
||||||
msgstr "Chargement des abonnements ..."
|
msgstr "Chargement des abonnements..."
|
||||||
|
|
||||||
#: src/hooks/useAppLoading.ts
|
#: src/hooks/useAppLoading.ts
|
||||||
msgid "Loading tags..."
|
msgid "Loading tags..."
|
||||||
msgstr "Chargement des tags ..."
|
msgstr "Chargement des marqueurs..."
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
@@ -446,7 +459,7 @@ msgstr "Déconnexion"
|
|||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Long press"
|
msgid "Long press"
|
||||||
msgstr ""
|
msgstr "Appui long"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
#: src/pages/admin/AdminUsersPage.tsx
|
#: src/pages/admin/AdminUsersPage.tsx
|
||||||
@@ -478,7 +491,7 @@ msgstr "Métriques"
|
|||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Middle click"
|
msgid "Middle click"
|
||||||
msgstr ""
|
msgstr "Clic milieu"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Move the page down"
|
msgid "Move the page down"
|
||||||
@@ -526,7 +539,7 @@ msgstr "Bookmarklet vers le prochain article non lu"
|
|||||||
|
|
||||||
#: src/pages/app/FeedEntriesPage.tsx
|
#: src/pages/app/FeedEntriesPage.tsx
|
||||||
msgid "No more entries"
|
msgid "No more entries"
|
||||||
msgstr "Plus d'entrées"
|
msgstr "Fin de la liste"
|
||||||
|
|
||||||
#: src/components/sidebar/TreeSearch.tsx
|
#: src/components/sidebar/TreeSearch.tsx
|
||||||
msgid "Nothing found"
|
msgid "Nothing found"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Du plus ancien au plus récent"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Oups !"
|
msgstr "Oups !"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Ouvrir l'entrée actuelle dans un nouvel onglet"
|
msgstr "Ouvrir l'entrée actuelle dans un nouvel onglet"
|
||||||
@@ -554,11 +571,11 @@ msgstr "Ouvrir le lien"
|
|||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Open link in new background tab"
|
msgid "Open link in new background tab"
|
||||||
msgstr ""
|
msgstr "Ouvrir le lien dans un nouvel onglet en arrière-plan"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryContextMenu.tsx
|
#: src/components/content/FeedEntryContextMenu.tsx
|
||||||
msgid "Open link in new tab"
|
msgid "Open link in new tab"
|
||||||
msgstr ""
|
msgstr "Ouvrir le lien dans un nouvel onglet"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open next entry"
|
msgid "Open next entry"
|
||||||
@@ -641,7 +658,7 @@ msgstr "API REST"
|
|||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Right click"
|
msgid "Right click"
|
||||||
msgstr ""
|
msgstr "Clic droit"
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/settings/CustomCodeSettings.tsx
|
#: src/components/settings/CustomCodeSettings.tsx
|
||||||
@@ -697,11 +714,11 @@ msgstr "Maj"
|
|||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Show entry menu (desktop)"
|
msgid "Show entry menu (desktop)"
|
||||||
msgstr ""
|
msgstr "Afficher les options de l'entrée (ordinateur)"
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Show entry menu (mobile)"
|
msgid "Show entry menu (mobile)"
|
||||||
msgstr ""
|
msgstr "Afficher les options de l'entrée (mobile)"
|
||||||
|
|
||||||
#: src/components/settings/DisplaySettings.tsx
|
#: src/components/settings/DisplaySettings.tsx
|
||||||
msgid "Show feeds and categories with no unread entries"
|
msgid "Show feeds and categories with no unread entries"
|
||||||
@@ -759,16 +776,18 @@ msgid "Swipe header to the right"
|
|||||||
msgstr "Faire glisser le titre vers la droite"
|
msgstr "Faire glisser le titre vers la droite"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Activer le mode sombre"
|
msgstr "Activer le mode sombre"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Activer le mode clair"
|
msgstr "Activer le mode clair"
|
||||||
|
|
||||||
#: src/components/content/FeedEntryFooter.tsx
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Tags"
|
msgstr "Marqueurs"
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "The URL for the feed you want to subscribe to. You can also use the website's url directly and CommaFeed will try to find the feed in the page."
|
msgid "The URL for the feed you want to subscribe to. You can also use the website's url directly and CommaFeed will try to find the feed in the page."
|
||||||
@@ -782,13 +801,17 @@ msgstr "Thème"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Marquer l'entrée actuelle comme lue/non lue"
|
msgstr "Marquer l'entrée actuelle comme lue/non lue"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Essayez CommaFeed avec le compte de démonstration : demo/demo"
|
msgstr "Essayez CommaFeed avec le compte de démonstration : demo/demo"
|
||||||
|
|
||||||
#: src/pages/WelcomePage.tsx
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Try the demo!"
|
msgid "Try the demo!"
|
||||||
msgstr ""
|
msgstr "Essayez la version de démonstration !"
|
||||||
|
|
||||||
#: src/components/header/Header.tsx
|
#: src/components/header/Header.tsx
|
||||||
msgid "Unread"
|
msgid "Unread"
|
||||||
@@ -827,4 +850,4 @@ msgstr "Vous n'avez pas encore d'abonnements. Pourquoi ne pas essayer d'en ajout
|
|||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Your feeds have been queued for refresh."
|
msgid "Your feeds have been queued for refresh."
|
||||||
msgstr ""
|
msgstr "Vos flux sont en cours de rafraîchissement"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Atrás"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Volver para iniciar sesión"
|
msgstr "Volver para iniciar sesión"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Extensións do navegador"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Comproba que a fonte funciona"
|
msgstr "Comproba que a fonte funciona"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed é un proxecto de código aberto. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed seguinte elemento non lido"
|
msgstr "CommaFeed seguinte elemento non lido"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "Versión de CommaFeed {versión} ({revisión})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Ampliado"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Exporta as túas subscricións e categorías como ficheiro OPML que se pode importar noutros servizos de lectura de feeds"
|
msgstr "Exporta as túas subscricións e categorías como ficheiro OPML que se pode importar noutros servizos de lectura de feeds"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Nome do feed"
|
msgstr "Nome do feed"
|
||||||
@@ -540,6 +553,10 @@ msgstr "O máis vello primeiro"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Vaia!"
|
msgstr "Vaia!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Abrir a entrada actual nunha nova pestana"
|
msgstr "Abrir a entrada actual nunha nova pestana"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Cambiar ao tema escuro"
|
msgstr "Cambiar ao tema escuro"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Cambiar ao tema claro"
|
msgstr "Cambiar ao tema claro"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Tema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "alternar o estado de lectura da entrada actual"
|
msgstr "alternar o estado de lectura da entrada actual"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Proba CommaFeed coa conta de demostración: demo/demo"
|
msgstr "Proba CommaFeed coa conta de demostración: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Vissza"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Vissza a bejelentkezéshez"
|
msgstr "Vissza a bejelentkezéshez"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Böngészőbővítések"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,7 +168,7 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Ellenőrizze, hogy a feed működik-e"
|
msgstr "Ellenőrizze, hogy a feed működik-e"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
@@ -168,8 +176,8 @@ msgid "CommaFeed next unread item"
|
|||||||
msgstr "CommaFeed következő olvasatlan elem"
|
msgstr "CommaFeed következő olvasatlan elem"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed verzió {version} ({revision})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Kiterjesztve"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Exportálja előfizetéseit és kategóriáit OPML-fájlként, amely importálható más feedolvasó szolgáltatásokba"
|
msgstr "Exportálja előfizetéseit és kategóriáit OPML-fájlként, amely importálható más feedolvasó szolgáltatásokba"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Hírcsatorna neve"
|
msgstr "Hírcsatorna neve"
|
||||||
@@ -540,6 +553,10 @@ msgstr "A legidősebb első"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Hoppá!"
|
msgstr "Hoppá!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Az aktuális bejegyzés megnyitása új lapon"
|
msgstr "Az aktuális bejegyzés megnyitása új lapon"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Váltás sötét témára"
|
msgstr "Váltás sötét témára"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Váltás világos témára"
|
msgstr "Váltás világos témára"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Téma"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Az aktuális bejegyzés olvasási állapotának váltása"
|
msgstr "Az aktuális bejegyzés olvasási állapotának váltása"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Próbálja ki a CommaFeed-et a demo fiókkal: demo/demo"
|
msgstr "Próbálja ki a CommaFeed-et a demo fiókkal: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Kembali"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Kembali untuk masuk"
|
msgstr "Kembali untuk masuk"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Ekstensi peramban"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Periksa apakah umpannya berfungsi"
|
msgstr "Periksa apakah umpannya berfungsi"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed adalah proyek sumber terbuka. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed item yang belum dibaca berikutnya"
|
msgstr "CommaFeed item yang belum dibaca berikutnya"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed versi {versi} ({revisi})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Diperluas"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Ekspor langganan dan kategori Anda sebagai file OPML yang dapat diimpor ke layanan membaca feed lainnya"
|
msgstr "Ekspor langganan dan kategori Anda sebagai file OPML yang dapat diimpor ke layanan membaca feed lainnya"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Nama umpan"
|
msgstr "Nama umpan"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Tertua dulu"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Ups!"
|
msgstr "Ups!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Buka entri saat ini di tab baru"
|
msgstr "Buka entri saat ini di tab baru"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Beralih ke tema gelap"
|
msgstr "Beralih ke tema gelap"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Beralih ke tema terang"
|
msgstr "Beralih ke tema terang"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Tema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Beralih status baca entri saat ini"
|
msgstr "Beralih status baca entri saat ini"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Cobalah CommaFeed dengan akun demo: demo/demo"
|
msgstr "Cobalah CommaFeed dengan akun demo: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Indietro"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Torna per accedere"
|
msgstr "Torna per accedere"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Estensioni del browser"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Verifica che il feed funzioni"
|
msgstr "Verifica che il feed funzioni"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed è un progetto open source. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed successivo elemento non letto"
|
msgstr "CommaFeed successivo elemento non letto"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "Versione CommaFeed {versione} ({revisione})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Espanso"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Esporta le tue iscrizioni e categorie come file OPML che può essere importato in altri servizi di lettura feed"
|
msgstr "Esporta le tue iscrizioni e categorie come file OPML che può essere importato in altri servizi di lettura feed"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Nome del feed"
|
msgstr "Nome del feed"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Il più vecchio prima"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Ops!"
|
msgstr "Ops!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Apri la voce corrente in una nuova scheda"
|
msgstr "Apri la voce corrente in una nuova scheda"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Passa al tema scuro"
|
msgstr "Passa al tema scuro"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Passa al tema della luce"
|
msgstr "Passa al tema della luce"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Tema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Commuta lo stato di lettura della voce corrente"
|
msgstr "Commuta lo stato di lettura della voce corrente"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Prova CommaFeed con il conto demo: demo/demo"
|
msgstr "Prova CommaFeed con il conto demo: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "裏"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "ログインに戻る"
|
msgstr "ログインに戻る"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "ブラウザ拡張機能"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "フィードが動作していることを確認してください"
|
msgstr "フィードが動作していることを確認してください"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed はオープンソース プロジェクトです。"
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "次の未読アイテムをカンマフィード"
|
msgstr "次の未読アイテムをカンマフィード"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "コンマフィードのバージョン {version} ({revision})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "拡張"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "サブスクリプションとカテゴリを、他のフィード読み取りサービスにインポートできる OPML ファイルとしてエクスポートします"
|
msgstr "サブスクリプションとカテゴリを、他のフィード読み取りサービスにインポートできる OPML ファイルとしてエクスポートします"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "フィード名"
|
msgstr "フィード名"
|
||||||
@@ -540,6 +553,10 @@ msgstr "古い順"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "おっと!"
|
msgstr "おっと!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "現在のエントリを新しいタブで開く"
|
msgstr "現在のエントリを新しいタブで開く"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "ダークテーマに切り替え"
|
msgstr "ダークテーマに切り替え"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "ライトテーマに切り替え"
|
msgstr "ライトテーマに切り替え"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "テーマ"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "現在のエントリの読み取りステータスを切り替えます"
|
msgstr "現在のエントリの読み取りステータスを切り替えます"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "デモアカウントで CommaFeed を試す: demo/demo"
|
msgstr "デモアカウントで CommaFeed を試す: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "뒤로"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "로그인으로 돌아가기"
|
msgstr "로그인으로 돌아가기"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "브라우저 확장"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "피드가 작동하는지 확인"
|
msgstr "피드가 작동하는지 확인"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed는 오픈 소스 프로젝트입니다. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "다음 읽지 않은 항목을 쉼표로 피드"
|
msgstr "다음 읽지 않은 항목을 쉼표로 피드"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "쉼표 피드 버전 {버전}({개정})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "확장"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "구독 및 카테고리를 다른 피드 읽기 서비스에서 가져올 수 있는 OPML 파일로 내보내기"
|
msgstr "구독 및 카테고리를 다른 피드 읽기 서비스에서 가져올 수 있는 OPML 파일로 내보내기"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "피드 이름"
|
msgstr "피드 이름"
|
||||||
@@ -540,6 +553,10 @@ msgstr "가장 오래된 것부터"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "앗!"
|
msgstr "앗!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "새 탭에서 현재 항목 열기"
|
msgstr "새 탭에서 현재 항목 열기"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "어두운 테마로 전환"
|
msgstr "어두운 테마로 전환"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "밝은 테마로 전환"
|
msgstr "밝은 테마로 전환"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "테마"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "현재 항목의 읽기 상태 전환"
|
msgstr "현재 항목의 읽기 상태 전환"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "데모 계정으로 CommaFeed를 사용해 보세요: demo/demo"
|
msgstr "데모 계정으로 CommaFeed를 사용해 보세요: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Kembali"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Kembali untuk log masuk"
|
msgstr "Kembali untuk log masuk"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Peluasan penyemak imbas"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Semak sama ada suapan berfungsi"
|
msgstr "Semak sama ada suapan berfungsi"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed ialah projek sumber terbuka. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed item belum dibaca seterusnya"
|
msgstr "CommaFeed item belum dibaca seterusnya"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "Versi CommaFeed {versi} ({semakan})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Dikembangkan"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Eksport langganan dan kategori anda sebagai fail OPML yang boleh diimport dalam perkhidmatan membaca suapan lain"
|
msgstr "Eksport langganan dan kategori anda sebagai fail OPML yang boleh diimport dalam perkhidmatan membaca suapan lain"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Nama suapan"
|
msgstr "Nama suapan"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Tertua dahulu"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Aduh!"
|
msgstr "Aduh!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Buka entri semasa dalam tab baharu"
|
msgstr "Buka entri semasa dalam tab baharu"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Tukar kepada tema gelap"
|
msgstr "Tukar kepada tema gelap"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Tukar kepada tema cahaya"
|
msgstr "Tukar kepada tema cahaya"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Tema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Togol status bacaan entri semasa"
|
msgstr "Togol status bacaan entri semasa"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Cuba CommaFeed dengan akaun demo: demo/demo"
|
msgstr "Cuba CommaFeed dengan akaun demo: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Tilbake"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Tilbake for å logge inn"
|
msgstr "Tilbake for å logge inn"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Nettleserutvidelser"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Sjekk at feeden fungerer"
|
msgstr "Sjekk at feeden fungerer"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed er et åpen kildekode-prosjekt. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed neste uleste element"
|
msgstr "CommaFeed neste uleste element"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed versjon {versjon} ({revisjon})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Utvidet"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Eksporter abonnementene og kategoriene dine som en OPML-fil som kan importeres i andre feedlesetjenester"
|
msgstr "Eksporter abonnementene og kategoriene dine som en OPML-fil som kan importeres i andre feedlesetjenester"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Feednavn"
|
msgstr "Feednavn"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Eldste først"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Beklager!"
|
msgstr "Beklager!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Åpne gjeldende oppføring i en ny fane"
|
msgstr "Åpne gjeldende oppføring i en ny fane"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Bytt til mørkt tema"
|
msgstr "Bytt til mørkt tema"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Bytt til lystema"
|
msgstr "Bytt til lystema"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Tema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Veksle lesestatus for gjeldende oppføring"
|
msgstr "Veksle lesestatus for gjeldende oppføring"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Prøv CommaFeed med demokontoen: demo/demo"
|
msgstr "Prøv CommaFeed med demokontoen: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Terug"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Terug naar inloggen"
|
msgstr "Terug naar inloggen"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Browserextensies"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Controleer of de feed werkt"
|
msgstr "Controleer of de feed werkt"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed is een open-sourceproject. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed volgende ongelezen item"
|
msgstr "CommaFeed volgende ongelezen item"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed-versie {versie} ({revisie})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Uitgebreid"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Exporteer uw abonnementen en categorieën als een OPML-bestand dat kan worden geïmporteerd in andere feedleesservices"
|
msgstr "Exporteer uw abonnementen en categorieën als een OPML-bestand dat kan worden geïmporteerd in andere feedleesservices"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Feednaam"
|
msgstr "Feednaam"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Oudste eerst"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Oeps!"
|
msgstr "Oeps!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Huidige invoer openen in een nieuw tabblad"
|
msgstr "Huidige invoer openen in een nieuw tabblad"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Overschakelen naar donker thema"
|
msgstr "Overschakelen naar donker thema"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Overschakelen naar lichtthema"
|
msgstr "Overschakelen naar lichtthema"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Thema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Toggle leesstatus van huidige invoer"
|
msgstr "Toggle leesstatus van huidige invoer"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Probeer CommaFeed uit met het demo-account: demo/demo"
|
msgstr "Probeer CommaFeed uit met het demo-account: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Tilbake"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Tilbake for å logge inn"
|
msgstr "Tilbake for å logge inn"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Nettleserutvidelser"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Sjekk at feeden fungerer"
|
msgstr "Sjekk at feeden fungerer"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed er et åpen kildekode-prosjekt. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed neste uleste element"
|
msgstr "CommaFeed neste uleste element"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed versjon {versjon} ({revisjon})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Utvidet"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Eksporter abonnementene og kategoriene dine som en OPML-fil som kan importeres i andre feedlesetjenester"
|
msgstr "Eksporter abonnementene og kategoriene dine som en OPML-fil som kan importeres i andre feedlesetjenester"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Feednavn"
|
msgstr "Feednavn"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Eldste først"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Beklager!"
|
msgstr "Beklager!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Åpne gjeldende oppføring i en ny fane"
|
msgstr "Åpne gjeldende oppføring i en ny fane"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Bytt til mørkt tema"
|
msgstr "Bytt til mørkt tema"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Bytt til lystema"
|
msgstr "Bytt til lystema"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Tema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Veksle lesestatus for gjeldende oppføring"
|
msgstr "Veksle lesestatus for gjeldende oppføring"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Prøv CommaFeed med demokontoen: demo/demo"
|
msgstr "Prøv CommaFeed med demokontoen: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Powrót"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Powrót do logowania"
|
msgstr "Powrót do logowania"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Rozszerzenia przeglądarki"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Sprawdź, czy kanał działa"
|
msgstr "Sprawdź, czy kanał działa"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed to projekt typu open source. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "Przecinek następny nieprzeczytany element"
|
msgstr "Przecinek następny nieprzeczytany element"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "Wersja CommaFeed {wersja} ({wersja})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Rozszerzony"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Eksportuj swoje subskrypcje i kategorie jako plik OPML, który można zaimportować do innych usług odczytu kanałów"
|
msgstr "Eksportuj swoje subskrypcje i kategorie jako plik OPML, który można zaimportować do innych usług odczytu kanałów"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "nazwa kanału"
|
msgstr "nazwa kanału"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Najstarsze jako pierwsze"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Ups!"
|
msgstr "Ups!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Otwórz bieżący wpis w nowej karcie"
|
msgstr "Otwórz bieżący wpis w nowej karcie"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Przełącz na ciemny motyw"
|
msgstr "Przełącz na ciemny motyw"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Przełącz na jasny motyw"
|
msgstr "Przełącz na jasny motyw"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Motyw"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Przełącz stan odczytu bieżącego wpisu"
|
msgstr "Przełącz stan odczytu bieżącego wpisu"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Wypróbuj CommaFeed z kontem demo: demo/demo"
|
msgstr "Wypróbuj CommaFeed z kontem demo: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Voltar"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Voltar para logar"
|
msgstr "Voltar para logar"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Extensões do navegador"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Verifique se o feed está funcionando"
|
msgstr "Verifique se o feed está funcionando"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed é um projeto de código aberto. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed próximo item não lido"
|
msgstr "CommaFeed próximo item não lido"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "Versão do CommaFeed {versão} ({revisão})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Expandido"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Exporte suas inscrições e categorias como um arquivo OPML que pode ser importado em outros serviços de leitura de feed"
|
msgstr "Exporte suas inscrições e categorias como um arquivo OPML que pode ser importado em outros serviços de leitura de feed"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Nome do feed"
|
msgstr "Nome do feed"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Mais antigo primeiro"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Opa!"
|
msgstr "Opa!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Abrir a entrada atual em uma nova aba"
|
msgstr "Abrir a entrada atual em uma nova aba"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Mudar para tema escuro"
|
msgstr "Mudar para tema escuro"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Mudar para tema claro"
|
msgstr "Mudar para tema claro"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Tema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Alternar o status de leitura da entrada atual"
|
msgstr "Alternar o status de leitura da entrada atual"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Experimente o CommaFeed com a conta demo: demo/demo"
|
msgstr "Experimente o CommaFeed com a conta demo: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Назад"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Вернуться к входу"
|
msgstr "Вернуться к входу"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Расширения браузера"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Проверьте, работает ли лента."
|
msgstr "Проверьте, работает ли лента."
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed — это проект с открытым исходным кодом. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed следующий непрочитанный элемент"
|
msgstr "CommaFeed следующий непрочитанный элемент"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed версия {версия} ({редакция})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Расширенный"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Экспортируйте свои подписки и категории в виде файла OPML, который можно импортировать в другие службы чтения каналов."
|
msgstr "Экспортируйте свои подписки и категории в виде файла OPML, который можно импортировать в другие службы чтения каналов."
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Имя фида"
|
msgstr "Имя фида"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Сначала самые старые"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Ой!"
|
msgstr "Ой!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Открыть текущую запись в новой вкладке"
|
msgstr "Открыть текущую запись в новой вкладке"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Переключиться на темную тему"
|
msgstr "Переключиться на темную тему"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Переключиться на светлую тему"
|
msgstr "Переключиться на светлую тему"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Тема"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Переключить статус чтения текущей записи"
|
msgstr "Переключить статус чтения текущей записи"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Попробуйте CommaFeed на демо-счете: demo/demo"
|
msgstr "Попробуйте CommaFeed на демо-счете: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Späť"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Späť na prihlásenie"
|
msgstr "Späť na prihlásenie"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Rozšírenia prehliadača"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Skontrolujte, či feed funguje"
|
msgstr "Skontrolujte, či feed funguje"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed je projekt s otvoreným zdrojom. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed ďalšia neprečítaná položka"
|
msgstr "CommaFeed ďalšia neprečítaná položka"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed verzia {version} ({revision})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Rozšírené"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Exportujte svoje odbery a kategórie ako súbor OPML, ktorý je možné importovať do iných služieb na čítanie informačných kanálov"
|
msgstr "Exportujte svoje odbery a kategórie ako súbor OPML, ktorý je možné importovať do iných služieb na čítanie informačných kanálov"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Názov informačného kanála"
|
msgstr "Názov informačného kanála"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Najprv najstarší"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Ojoj!"
|
msgstr "Ojoj!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Otvorte aktuálny záznam na novej karte"
|
msgstr "Otvorte aktuálny záznam na novej karte"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Prepnúť na tmavú tému"
|
msgstr "Prepnúť na tmavú tému"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Prepnite na svetlú tému"
|
msgstr "Prepnite na svetlú tému"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Téma"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Prepne stav čítania aktuálneho záznamu"
|
msgstr "Prepne stav čítania aktuálneho záznamu"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Vyskúšajte CommaFeed s demo účtom: demo/demo"
|
msgstr "Vyskúšajte CommaFeed s demo účtom: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Tillbaka"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Tillbaka för att logga in"
|
msgstr "Tillbaka för att logga in"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Webbläsartillägg"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,15 +168,15 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Kontrollera att matningen fungerar"
|
msgstr "Kontrollera att matningen fungerar"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed är ett projekt med öppen källkod. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed nästa olästa objekt"
|
msgstr "CommaFeed nästa olästa objekt"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
@@ -308,6 +316,11 @@ msgstr "Utökad"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Exportera dina prenumerationer och kategorier som en OPML-fil som kan importeras i andra flödesläsningstjänster"
|
msgstr "Exportera dina prenumerationer och kategorier som en OPML-fil som kan importeras i andra flödesläsningstjänster"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Flödesnamn"
|
msgstr "Flödesnamn"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Äldst först"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Hoppsan!"
|
msgstr "Hoppsan!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Öppna aktuell post i en ny flik"
|
msgstr "Öppna aktuell post i en ny flik"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Byt till mörkt tema"
|
msgstr "Byt till mörkt tema"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Byt till ljustema"
|
msgstr "Byt till ljustema"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Tema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Växla lässtatus för aktuell post"
|
msgstr "Växla lässtatus för aktuell post"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "Prova CommaFeed med demokontot: demo/demo"
|
msgstr "Prova CommaFeed med demokontot: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "Geri"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "Giriş yapmak için geri dön"
|
msgstr "Giriş yapmak için geri dön"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "Tarayıcı uzantıları"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "Feed'in çalışıp çalışmadığını kontrol edin"
|
msgstr "Feed'in çalışıp çalışmadığını kontrol edin"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed açık kaynaklı bir projedir. "
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed sonraki okunmamış öğe"
|
msgstr "CommaFeed sonraki okunmamış öğe"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed sürümü {sürüm} ({revizyon})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "Genişletilmiş"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "Aboneliklerinizi ve kategorilerinizi diğer besleme okuma hizmetlerinde içe aktarılabilen bir OPML dosyası olarak dışa aktarın"
|
msgstr "Aboneliklerinizi ve kategorilerinizi diğer besleme okuma hizmetlerinde içe aktarılabilen bir OPML dosyası olarak dışa aktarın"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "Yayın adı"
|
msgstr "Yayın adı"
|
||||||
@@ -540,6 +553,10 @@ msgstr "Önce en eski"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "Hata!"
|
msgstr "Hata!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "Geçerli girişi yeni bir sekmede aç"
|
msgstr "Geçerli girişi yeni bir sekmede aç"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "Karanlık temaya geç"
|
msgstr "Karanlık temaya geç"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "Açık temaya geç"
|
msgstr "Açık temaya geç"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "Tema"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "Geçerli girişin okuma durumunu değiştir"
|
msgstr "Geçerli girişin okuma durumunu değiştir"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "CommaFeed'i demo hesabıyla deneyin: demo/demo"
|
msgstr "CommaFeed'i demo hesabıyla deneyin: demo/demo"
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ msgstr ""
|
|||||||
msgid "{0} (in {1})"
|
msgid "{0} (in {1})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/app/AboutPage.tsx
|
||||||
|
msgid "<0>CommaFeed is an open-source project. Sources are hosted on </0><1>GitHub</1>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/FeedDetailsPage.tsx
|
#: src/pages/app/FeedDetailsPage.tsx
|
||||||
msgid "<0>Complete syntax is available </0><1>here</1>."
|
msgid "<0>Complete syntax is available </0><1>here</1>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -123,9 +127,13 @@ msgstr "返回"
|
|||||||
msgid "Back to log in"
|
msgid "Back to log in"
|
||||||
msgstr "返回登录"
|
msgstr "返回登录"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Browser extension required for Chrome"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "Browser extentions"
|
msgid "Browser extention"
|
||||||
msgstr "浏览器扩展"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/admin/UserEdit.tsx
|
#: src/components/admin/UserEdit.tsx
|
||||||
#: src/components/content/add/AddCategory.tsx
|
#: src/components/content/add/AddCategory.tsx
|
||||||
@@ -160,16 +168,16 @@ msgid "Check that the feed is working"
|
|||||||
msgstr "检查提要是否正常工作"
|
msgstr "检查提要是否正常工作"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed is an open-source project. Sources are hosted on <0>GitHub</0>."
|
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||||
msgstr "CommaFeed 是一个开源项目。"
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed next unread item"
|
msgid "CommaFeed next unread item"
|
||||||
msgstr "CommaFeed 下一个未读项目"
|
msgstr "CommaFeed 下一个未读项目"
|
||||||
|
|
||||||
#: src/pages/app/AboutPage.tsx
|
#: src/pages/app/AboutPage.tsx
|
||||||
msgid "CommaFeed version {version} ({revision})"
|
msgid "CommaFeed version {version} ({revision})."
|
||||||
msgstr "CommaFeed 版本 {version} ({revision})"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Compact"
|
msgid "Compact"
|
||||||
@@ -308,6 +316,11 @@ msgstr "展开"
|
|||||||
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
msgid "Export your subscriptions and categories as an OPML file that can be imported in other feed reading services"
|
||||||
msgstr "将您的订阅和类别导出为 OPML 文件,可以在其他提要阅读服务中导入"
|
msgstr "将您的订阅和类别导出为 OPML 文件,可以在其他提要阅读服务中导入"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
|
msgid "Extension options"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/content/add/Subscribe.tsx
|
#: src/components/content/add/Subscribe.tsx
|
||||||
msgid "Feed name"
|
msgid "Feed name"
|
||||||
msgstr "提要名称"
|
msgstr "提要名称"
|
||||||
@@ -540,6 +553,10 @@ msgstr "最早的优先"
|
|||||||
msgid "Oops!"
|
msgid "Oops!"
|
||||||
msgstr "哎呀!"
|
msgstr "哎呀!"
|
||||||
|
|
||||||
|
#: src/components/header/Header.tsx
|
||||||
|
msgid "Open CommaFeed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/KeyboardShortcutsHelp.tsx
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
msgid "Open current entry in a new tab"
|
msgid "Open current entry in a new tab"
|
||||||
msgstr "在新选项卡中打开当前条目"
|
msgstr "在新选项卡中打开当前条目"
|
||||||
@@ -759,10 +776,12 @@ msgid "Swipe header to the right"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to dark theme"
|
msgid "Switch to dark theme"
|
||||||
msgstr "切换到深色主题"
|
msgstr "切换到深色主题"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
|
#: src/pages/WelcomePage.tsx
|
||||||
msgid "Switch to light theme"
|
msgid "Switch to light theme"
|
||||||
msgstr "切换到浅色主题"
|
msgstr "切换到浅色主题"
|
||||||
|
|
||||||
@@ -782,6 +801,10 @@ msgstr "主题"
|
|||||||
msgid "Toggle read status of current entry"
|
msgid "Toggle read status of current entry"
|
||||||
msgstr "切换当前条目的读取状态"
|
msgstr "切换当前条目的读取状态"
|
||||||
|
|
||||||
|
#: src/components/KeyboardShortcutsHelp.tsx
|
||||||
|
msgid "Toggle sidebar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/auth/LoginPage.tsx
|
#: src/pages/auth/LoginPage.tsx
|
||||||
msgid "Try out CommaFeed with the demo account: demo/demo"
|
msgid "Try out CommaFeed with the demo account: demo/demo"
|
||||||
msgstr "使用演示帐户试用 CommaFeed:demo/demo"
|
msgstr "使用演示帐户试用 CommaFeed:demo/demo"
|
||||||
|
|||||||
4
commafeed-client/src/main.css
Normal file
4
commafeed-client/src/main.css
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
html, body {
|
||||||
|
/* disable pull-to-refresh on mobile as it messes with vertical scrolling */
|
||||||
|
overscroll-behavior: none;
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import "@fontsource/open-sans"
|
import "@fontsource/open-sans"
|
||||||
|
import { App } from "App"
|
||||||
import { store } from "app/store"
|
import { store } from "app/store"
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import relativeTime from "dayjs/plugin/relativeTime"
|
import relativeTime from "dayjs/plugin/relativeTime"
|
||||||
|
import "main.css"
|
||||||
import "react-contexify/ReactContexify.css"
|
import "react-contexify/ReactContexify.css"
|
||||||
import ReactDOM from "react-dom/client"
|
import ReactDOM from "react-dom/client"
|
||||||
import { Provider } from "react-redux"
|
import { Provider } from "react-redux"
|
||||||
import { App } from "./App"
|
|
||||||
|
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ import { Anchor, Box, Center, Container, Divider, Group, Image, Title, useMantin
|
|||||||
import { useMediaQuery } from "@mantine/hooks"
|
import { useMediaQuery } from "@mantine/hooks"
|
||||||
import { client } from "app/client"
|
import { client } from "app/client"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "app/constants"
|
||||||
import { redirectToLogin, redirectToRegistration, redirectToRootCategory } from "app/slices/redirect"
|
import { redirectToApiDocumentation, redirectToLogin, redirectToRegistration, redirectToRootCategory } from "app/slices/redirect"
|
||||||
import { useAppDispatch, useAppSelector } from "app/store"
|
import { useAppDispatch, useAppSelector } from "app/store"
|
||||||
import welcome_page_dark from "assets/welcome_page_dark.png"
|
import welcome_page_dark from "assets/welcome_page_dark.png"
|
||||||
import welcome_page_light from "assets/welcome_page_light.png"
|
import welcome_page_light from "assets/welcome_page_light.png"
|
||||||
import { ActionButton } from "components/ActionButtton"
|
import { ActionButton } from "components/ActionButtton"
|
||||||
import { ButtonToolbar } from "components/ButtonToolbar"
|
import { ButtonToolbar } from "components/ButtonToolbar"
|
||||||
|
import { useBrowserExtension } from "hooks/useBrowserExtension"
|
||||||
import { useAsyncCallback } from "react-async-hook"
|
import { useAsyncCallback } from "react-async-hook"
|
||||||
import { SiGithub, TbKey, TbUserPlus } from "react-icons/all"
|
import { SiGithub, SiTwitter } from "react-icons/si"
|
||||||
import { SiTwitter } from "react-icons/si"
|
import { TbClock, TbKey, TbMoon, TbSettings, TbSun, TbUserPlus } from "react-icons/tb"
|
||||||
import { TbClock, TbMoon, TbSun } from "react-icons/tb"
|
|
||||||
import { PageTitle } from "./PageTitle"
|
import { PageTitle } from "./PageTitle"
|
||||||
|
|
||||||
export function WelcomePage() {
|
export function WelcomePage() {
|
||||||
@@ -63,8 +63,9 @@ function Buttons() {
|
|||||||
const iconSize = 18
|
const iconSize = 18
|
||||||
const serverInfos = useAppSelector(state => state.server.serverInfos)
|
const serverInfos = useAppSelector(state => state.server.serverInfos)
|
||||||
const { colorScheme, toggleColorScheme } = useMantineColorScheme()
|
const { colorScheme, toggleColorScheme } = useMantineColorScheme()
|
||||||
|
const { isBrowserExtensionPopup, openSettingsPage } = useBrowserExtension()
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
const dark = colorScheme === "dark"
|
||||||
|
|
||||||
const login = useAsyncCallback(client.user.login, {
|
const login = useAsyncCallback(client.user.login, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@@ -101,19 +102,30 @@ function Buttons() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
label={dark ? <Trans>Switch to light theme</Trans> : <Trans>Switch to dark theme</Trans>}
|
||||||
icon={colorScheme === "dark" ? <TbSun size={18} /> : <TbMoon size={iconSize} />}
|
icon={colorScheme === "dark" ? <TbSun size={18} /> : <TbMoon size={iconSize} />}
|
||||||
onClick={() => toggleColorScheme()}
|
onClick={() => toggleColorScheme()}
|
||||||
|
hideLabelOnDesktop
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{isBrowserExtensionPopup && (
|
||||||
|
<ActionButton
|
||||||
|
label={<Trans>Extension options</Trans>}
|
||||||
|
icon={<TbSettings size={iconSize} />}
|
||||||
|
onClick={() => openSettingsPage()}
|
||||||
|
hideLabelOnDesktop
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</ButtonToolbar>
|
</ButtonToolbar>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Footer() {
|
function Footer() {
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Group position="apart">
|
||||||
<Group>
|
<Group>
|
||||||
<span>© CommaFeed</span>
|
<span>© CommaFeed</span>
|
||||||
<span> - </span>
|
|
||||||
<Anchor variant="text" href="https://github.com/Athou/commafeed/" target="_blank" rel="noreferrer">
|
<Anchor variant="text" href="https://github.com/Athou/commafeed/" target="_blank" rel="noreferrer">
|
||||||
<SiGithub />
|
<SiGithub />
|
||||||
</Anchor>
|
</Anchor>
|
||||||
@@ -121,6 +133,11 @@ function Footer() {
|
|||||||
<SiTwitter />
|
<SiTwitter />
|
||||||
</Anchor>
|
</Anchor>
|
||||||
</Group>
|
</Group>
|
||||||
</Box>
|
<Box>
|
||||||
|
<Anchor variant="text" onClick={() => dispatch(redirectToApiDocumentation())}>
|
||||||
|
API documentation
|
||||||
|
</Anchor>
|
||||||
|
</Box>
|
||||||
|
</Group>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Accordion, Tabs } from "@mantine/core"
|
import { Accordion, Box, Tabs } from "@mantine/core"
|
||||||
import { client } from "app/client"
|
import { client } from "app/client"
|
||||||
import { Loader } from "components/Loader"
|
import { Loader } from "components/Loader"
|
||||||
|
import { Gauge } from "components/metrics/Gauge"
|
||||||
import { Meter } from "components/metrics/Meter"
|
import { Meter } from "components/metrics/Meter"
|
||||||
import { MetricAccordionItem } from "components/metrics/MetricAccordionItem"
|
import { MetricAccordionItem } from "components/metrics/MetricAccordionItem"
|
||||||
import { Timer } from "components/metrics/Timer"
|
import { Timer } from "components/metrics/Timer"
|
||||||
@@ -15,11 +16,17 @@ const shownMeters: { [key: string]: string } = {
|
|||||||
"com.commafeed.backend.feed.FeedRefreshUpdater.entryCacheMiss": "Entry cache miss rate",
|
"com.commafeed.backend.feed.FeedRefreshUpdater.entryCacheMiss": "Entry cache miss rate",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const shownGauges: { [key: string]: string } = {
|
||||||
|
"com.commafeed.backend.feed.FeedRefreshEngine.queue.size": "Queue size",
|
||||||
|
"com.commafeed.backend.feed.FeedRefreshEngine.worker.active": "Feed Worker active",
|
||||||
|
"com.commafeed.backend.feed.FeedRefreshEngine.updater.active": "Feed Updater active",
|
||||||
|
}
|
||||||
|
|
||||||
export function MetricsPage() {
|
export function MetricsPage() {
|
||||||
const query = useAsync(() => client.admin.getMetrics(), [])
|
const query = useAsync(() => client.admin.getMetrics(), [])
|
||||||
|
|
||||||
if (!query.result) return <Loader />
|
if (!query.result) return <Loader />
|
||||||
const { meters, timers } = query.result.data
|
const { meters, gauges, timers } = query.result.data
|
||||||
return (
|
return (
|
||||||
<Tabs defaultValue="stats">
|
<Tabs defaultValue="stats">
|
||||||
<Tabs.List>
|
<Tabs.List>
|
||||||
@@ -39,6 +46,15 @@ export function MetricsPage() {
|
|||||||
</MetricAccordionItem>
|
</MetricAccordionItem>
|
||||||
))}
|
))}
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
|
<Box pt="xs">
|
||||||
|
{Object.keys(shownGauges).map(g => (
|
||||||
|
<Box key={g}>
|
||||||
|
<span>{shownGauges[g]} </span>
|
||||||
|
<Gauge gauge={gauges[g]} />
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
|
|
||||||
<Tabs.Panel value="timers" pt="xs">
|
<Tabs.Panel value="timers" pt="xs">
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { redirectToApiDocumentation } from "app/slices/redirect"
|
|||||||
import { useAppDispatch, useAppSelector } from "app/store"
|
import { useAppDispatch, useAppSelector } from "app/store"
|
||||||
import { CategorySelect } from "components/content/add/CategorySelect"
|
import { CategorySelect } from "components/content/add/CategorySelect"
|
||||||
import { KeyboardShortcutsHelp } from "components/KeyboardShortcutsHelp"
|
import { KeyboardShortcutsHelp } from "components/KeyboardShortcutsHelp"
|
||||||
|
import { useBrowserExtension } from "hooks/useBrowserExtension"
|
||||||
import React, { useState } from "react"
|
import React, { useState } from "react"
|
||||||
import { TbHelp, TbKeyboard, TbPuzzle, TbRocket } from "react-icons/tb"
|
import { TbHelp, TbKeyboard, TbPuzzle, TbRocket } from "react-icons/tb"
|
||||||
|
|
||||||
@@ -60,19 +61,26 @@ function NextUnreadBookmarklet() {
|
|||||||
export function AboutPage() {
|
export function AboutPage() {
|
||||||
const version = useAppSelector(state => state.server.serverInfos?.version)
|
const version = useAppSelector(state => state.server.serverInfos?.version)
|
||||||
const revision = useAppSelector(state => state.server.serverInfos?.gitCommit)
|
const revision = useAppSelector(state => state.server.serverInfos?.gitCommit)
|
||||||
|
const { isBrowserExtensionInstalled, browserExtensionVersion, isBrowserExtensionInstallable } = useBrowserExtension()
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container size="xl">
|
<Container size="xl">
|
||||||
<SimpleGrid cols={2} breakpoints={[{ maxWidth: Constants.layout.mobileBreakpoint, cols: 1 }]}>
|
<SimpleGrid cols={2} breakpoints={[{ maxWidth: Constants.layout.mobileBreakpoint, cols: 1 }]}>
|
||||||
<Section title={<Trans>About</Trans>} icon={<TbHelp size={24} />}>
|
<Section title={<Trans>About</Trans>} icon={<TbHelp size={24} />}>
|
||||||
<Box>
|
<Box>
|
||||||
<Trans>
|
<Trans>
|
||||||
CommaFeed version {version} ({revision})
|
CommaFeed version {version} ({revision}).
|
||||||
</Trans>
|
</Trans>
|
||||||
</Box>
|
</Box>
|
||||||
|
{isBrowserExtensionInstallable && isBrowserExtensionInstalled && (
|
||||||
|
<Box>
|
||||||
|
<Trans>CommaFeed browser extension version {browserExtensionVersion}.</Trans>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
<Box mt="md">
|
<Box mt="md">
|
||||||
<Trans>
|
<Trans>
|
||||||
CommaFeed is an open-source project. Sources are hosted on
|
<span>CommaFeed is an open-source project. Sources are hosted on </span>
|
||||||
<Anchor href="https://github.com/Athou/commafeed" target="_blank" rel="noreferrer">
|
<Anchor href="https://github.com/Athou/commafeed" target="_blank" rel="noreferrer">
|
||||||
GitHub
|
GitHub
|
||||||
</Anchor>
|
</Anchor>
|
||||||
@@ -86,28 +94,9 @@ export function AboutPage() {
|
|||||||
<Section title={<Trans>Goodies</Trans>} icon={<TbPuzzle size={24} />}>
|
<Section title={<Trans>Goodies</Trans>} icon={<TbPuzzle size={24} />}>
|
||||||
<List>
|
<List>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<Trans>Browser extentions</Trans>
|
<Anchor href={Constants.browserExtensionUrl} target="_blank" rel="noreferrer">
|
||||||
<List withPadding>
|
<Trans>Browser extention</Trans>
|
||||||
<List.Item>
|
</Anchor>
|
||||||
<Anchor
|
|
||||||
href="https://addons.mozilla.org/en-US/firefox/addon/commafeed/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
Firefox
|
|
||||||
</Anchor>
|
|
||||||
</List.Item>
|
|
||||||
<List.Item>
|
|
||||||
<Anchor href="https://github.com/Athou/commafeed-chrome" target="_blank" rel="noreferrer">
|
|
||||||
Chrome
|
|
||||||
</Anchor>
|
|
||||||
</List.Item>
|
|
||||||
<List.Item>
|
|
||||||
<Anchor href="https://github.com/Athou/commafeed-opera" target="_blank" rel="noreferrer">
|
|
||||||
Opera
|
|
||||||
</Anchor>
|
|
||||||
</List.Item>
|
|
||||||
</List>
|
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<Trans>Subscribe URL</Trans>
|
<Trans>Subscribe URL</Trans>
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
|
import { Box } from "@mantine/core"
|
||||||
import SwaggerUI from "swagger-ui-react"
|
import SwaggerUI from "swagger-ui-react"
|
||||||
import "swagger-ui-react/swagger-ui.css"
|
import "swagger-ui-react/swagger-ui.css"
|
||||||
|
|
||||||
function ApiDocumentationPage() {
|
function ApiDocumentationPage() {
|
||||||
return <SwaggerUI url="swagger/swagger.json" />
|
return (
|
||||||
|
// force white background because swagger is unreadable with dark theme
|
||||||
|
<Box style={{ backgroundColor: "#fff" }}>
|
||||||
|
<SwaggerUI url="swagger/swagger.json" />
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ApiDocumentationPage
|
export default ApiDocumentationPage
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ import {
|
|||||||
Title,
|
Title,
|
||||||
useMantineTheme,
|
useMantineTheme,
|
||||||
} from "@mantine/core"
|
} from "@mantine/core"
|
||||||
import { useViewportSize } from "@mantine/hooks"
|
import { useMediaQuery, useViewportSize } from "@mantine/hooks"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "app/constants"
|
||||||
import { redirectToAdd, redirectToRootCategory } from "app/slices/redirect"
|
import { redirectToAdd, redirectToRootCategory } from "app/slices/redirect"
|
||||||
import { reloadTree, setMobileMenuOpen } from "app/slices/tree"
|
import { reloadTree, setMobileMenuOpen, setSidebarWidth } from "app/slices/tree"
|
||||||
import { reloadProfile, reloadSettings, reloadTags } from "app/slices/user"
|
import { reloadProfile, reloadSettings, reloadTags } from "app/slices/user"
|
||||||
import { useAppDispatch, useAppSelector } from "app/store"
|
import { useAppDispatch, useAppSelector } from "app/store"
|
||||||
import { Loader } from "components/Loader"
|
import { Loader } from "components/Loader"
|
||||||
@@ -26,28 +26,39 @@ import { OnMobile } from "components/responsive/OnMobile"
|
|||||||
import { useAppLoading } from "hooks/useAppLoading"
|
import { useAppLoading } from "hooks/useAppLoading"
|
||||||
import { useWebSocket } from "hooks/useWebSocket"
|
import { useWebSocket } from "hooks/useWebSocket"
|
||||||
import { LoadingPage } from "pages/LoadingPage"
|
import { LoadingPage } from "pages/LoadingPage"
|
||||||
|
import { Resizable } from "re-resizable"
|
||||||
import { ReactNode, Suspense, useEffect } from "react"
|
import { ReactNode, Suspense, useEffect } from "react"
|
||||||
import { TbPlus } from "react-icons/tb"
|
import { TbPlus } from "react-icons/tb"
|
||||||
import { Outlet } from "react-router-dom"
|
import { Outlet } from "react-router-dom"
|
||||||
|
|
||||||
interface LayoutProps {
|
interface LayoutProps {
|
||||||
sidebar: ReactNode
|
sidebar: ReactNode
|
||||||
|
sidebarWidth: number
|
||||||
header: ReactNode
|
header: ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
const sidebarPadding = DEFAULT_THEME.spacing.xs
|
const sidebarPadding = DEFAULT_THEME.spacing.xs
|
||||||
const sidebarRightBorderWidth = "1px"
|
const sidebarRightBorderWidth = "1px"
|
||||||
|
|
||||||
const useStyles = createStyles(theme => ({
|
const useStyles = createStyles((theme, props: LayoutProps) => ({
|
||||||
|
sidebar: {
|
||||||
|
"& .mantine-ScrollArea-scrollbar[data-orientation='horizontal']": {
|
||||||
|
display: "none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sidebarContentResizeWrapper: {
|
||||||
|
padding: sidebarPadding,
|
||||||
|
minHeight: `calc(100vh - ${Constants.layout.headerHeight}px)`,
|
||||||
|
},
|
||||||
sidebarContent: {
|
sidebarContent: {
|
||||||
maxWidth: `calc(${Constants.layout.sidebarWidth}px - ${sidebarPadding} * 2 - ${sidebarRightBorderWidth})`,
|
maxWidth: `calc(${props.sidebarWidth}px - ${sidebarPadding} * 2 - ${sidebarRightBorderWidth})`,
|
||||||
[theme.fn.smallerThan(Constants.layout.mobileBreakpoint)]: {
|
[theme.fn.smallerThan(Constants.layout.mobileBreakpoint)]: {
|
||||||
maxWidth: `calc(100vw - ${sidebarPadding} * 2 - ${sidebarRightBorderWidth})`,
|
maxWidth: `calc(100vw - ${sidebarPadding} * 2 - ${sidebarRightBorderWidth})`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mainContentWrapper: {
|
mainContentWrapper: {
|
||||||
paddingTop: Constants.layout.headerHeight,
|
paddingTop: Constants.layout.headerHeight,
|
||||||
paddingLeft: Constants.layout.sidebarWidth,
|
paddingLeft: props.sidebarWidth,
|
||||||
paddingRight: 0,
|
paddingRight: 0,
|
||||||
paddingBottom: 0,
|
paddingBottom: 0,
|
||||||
[theme.fn.smallerThan(Constants.layout.mobileBreakpoint)]: {
|
[theme.fn.smallerThan(Constants.layout.mobileBreakpoint)]: {
|
||||||
@@ -55,7 +66,7 @@ const useStyles = createStyles(theme => ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mainContent: {
|
mainContent: {
|
||||||
maxWidth: `calc(100vw - ${Constants.layout.sidebarWidth}px)`,
|
maxWidth: `calc(100vw - ${props.sidebarWidth}px)`,
|
||||||
padding: theme.spacing.md,
|
padding: theme.spacing.md,
|
||||||
[theme.fn.smallerThan(Constants.layout.mobileBreakpoint)]: {
|
[theme.fn.smallerThan(Constants.layout.mobileBreakpoint)]: {
|
||||||
maxWidth: "100vw",
|
maxWidth: "100vw",
|
||||||
@@ -76,15 +87,19 @@ function LogoAndTitle() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Layout({ sidebar, header }: LayoutProps) {
|
export default function Layout(props: LayoutProps) {
|
||||||
const { classes } = useStyles()
|
const { classes } = useStyles(props)
|
||||||
const theme = useMantineTheme()
|
const theme = useMantineTheme()
|
||||||
const viewport = useViewportSize()
|
const viewport = useViewportSize()
|
||||||
const { loading } = useAppLoading()
|
const { loading } = useAppLoading()
|
||||||
|
const mobile = !useMediaQuery(`(min-width: ${Constants.layout.mobileBreakpoint})`)
|
||||||
const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen)
|
const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen)
|
||||||
|
const sidebarHidden = props.sidebarWidth === 0
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
useWebSocket()
|
useWebSocket()
|
||||||
|
|
||||||
|
const handleResize = (element: HTMLElement) => dispatch(setSidebarWidth(element.offsetWidth))
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(reloadSettings())
|
dispatch(reloadSettings())
|
||||||
dispatch(reloadProfile())
|
dispatch(reloadProfile())
|
||||||
@@ -122,13 +137,29 @@ export default function Layout({ sidebar, header }: LayoutProps) {
|
|||||||
navbar={
|
navbar={
|
||||||
<Navbar
|
<Navbar
|
||||||
id="sidebar"
|
id="sidebar"
|
||||||
p={sidebarPadding}
|
hiddenBreakpoint={sidebarHidden ? 99999999 : Constants.layout.mobileBreakpoint}
|
||||||
hiddenBreakpoint={Constants.layout.mobileBreakpoint}
|
hidden={sidebarHidden || !mobileMenuOpen}
|
||||||
hidden={!mobileMenuOpen}
|
width={{ md: props.sidebarWidth }}
|
||||||
width={{ md: Constants.layout.sidebarWidth }}
|
className={classes.sidebar}
|
||||||
>
|
>
|
||||||
<Navbar.Section grow component={ScrollArea} mx="-xs" px="xs">
|
<Navbar.Section grow component={ScrollArea} mx={mobile ? 0 : "-sm"} px={mobile ? 0 : "sm"}>
|
||||||
<Box className={classes.sidebarContent}>{sidebar}</Box>
|
<Resizable
|
||||||
|
enable={{
|
||||||
|
top: false,
|
||||||
|
right: !mobile,
|
||||||
|
bottom: false,
|
||||||
|
left: false,
|
||||||
|
topRight: false,
|
||||||
|
bottomRight: false,
|
||||||
|
bottomLeft: false,
|
||||||
|
topLeft: false,
|
||||||
|
}}
|
||||||
|
onResize={(e, dir, el) => handleResize(el)}
|
||||||
|
minWidth={120}
|
||||||
|
className={classes.sidebarContentResizeWrapper}
|
||||||
|
>
|
||||||
|
<Box className={classes.sidebarContent}>{props.sidebar}</Box>
|
||||||
|
</Resizable>
|
||||||
</Navbar.Section>
|
</Navbar.Section>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
}
|
}
|
||||||
@@ -147,19 +178,19 @@ export default function Layout({ sidebar, header }: LayoutProps) {
|
|||||||
{!mobileMenuOpen && (
|
{!mobileMenuOpen && (
|
||||||
<Group>
|
<Group>
|
||||||
<Box mr="sm">{burger}</Box>
|
<Box mr="sm">{burger}</Box>
|
||||||
<Box sx={{ flexGrow: 1 }}>{header}</Box>
|
<Box sx={{ flexGrow: 1 }}>{props.header}</Box>
|
||||||
</Group>
|
</Group>
|
||||||
)}
|
)}
|
||||||
</OnMobile>
|
</OnMobile>
|
||||||
<OnDesktop>
|
<OnDesktop>
|
||||||
<Group>
|
<Group>
|
||||||
<Group position="apart" sx={{ width: Constants.layout.sidebarWidth - 16 }}>
|
<Group position="apart" sx={{ width: props.sidebarWidth - 16 }}>
|
||||||
<Box>
|
<Box>
|
||||||
<LogoAndTitle />
|
<LogoAndTitle />
|
||||||
</Box>
|
</Box>
|
||||||
<Box>{addButton}</Box>
|
<Box>{addButton}</Box>
|
||||||
</Group>
|
</Group>
|
||||||
<Box sx={{ flexGrow: 1 }}>{header}</Box>
|
<Box sx={{ flexGrow: 1 }}>{props.header}</Box>
|
||||||
</Group>
|
</Group>
|
||||||
</OnDesktop>
|
</OnDesktop>
|
||||||
</Header>
|
</Header>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export default defineConfig({
|
|||||||
port: 8082,
|
port: 8082,
|
||||||
proxy: {
|
proxy: {
|
||||||
"/rest": "http://localhost:8083",
|
"/rest": "http://localhost:8083",
|
||||||
|
"/next": "http://localhost:8083",
|
||||||
"/ws": "ws://localhost:8083",
|
"/ws": "ws://localhost:8083",
|
||||||
"/swagger": "http://localhost:8083",
|
"/swagger": "http://localhost:8083",
|
||||||
"/custom_css.css": "http://localhost:8083",
|
"/custom_css.css": "http://localhost:8083",
|
||||||
|
|||||||
@@ -3,9 +3,15 @@
|
|||||||
app:
|
app:
|
||||||
# url used to access commafeed
|
# url used to access commafeed
|
||||||
publicUrl: http://localhost:8082/
|
publicUrl: http://localhost:8082/
|
||||||
|
|
||||||
|
# whether to expose a robots.txt file that disallows web crawlers and search engine indexers
|
||||||
|
hideFromWebCrawlers: true
|
||||||
|
|
||||||
# wether to allow user registrations
|
# whether to allow user registrations
|
||||||
allowRegistrations: true
|
allowRegistrations: true
|
||||||
|
|
||||||
|
# whether to enable strict password validation (1 uppercase char, 1 lowercase char, 1 digit, 1 special char)
|
||||||
|
strictPasswordPolicy: true
|
||||||
|
|
||||||
# create a demo account the first time the app starts
|
# create a demo account the first time the app starts
|
||||||
createDemoAccount: true
|
createDemoAccount: true
|
||||||
@@ -37,14 +43,14 @@ app:
|
|||||||
graphitePort: 2003
|
graphitePort: 2003
|
||||||
graphiteInterval: 60
|
graphiteInterval: 60
|
||||||
|
|
||||||
# wether this commafeed instance has a lot of feeds to refresh
|
# whether this commafeed instance has a lot of feeds to refresh
|
||||||
# leave this to false in almost all cases
|
# leave this to false in almost all cases
|
||||||
heavyLoad: false
|
heavyLoad: false
|
||||||
|
|
||||||
# minimum amount of time commafeed will wait before refreshing the same feed
|
# minimum amount of time commafeed will wait before refreshing the same feed
|
||||||
refreshIntervalMinutes: 5
|
refreshIntervalMinutes: 5
|
||||||
|
|
||||||
# wether to enable pubsub
|
# whether to enable pubsub
|
||||||
# probably not needed if refreshIntervalMinutes is low
|
# probably not needed if refreshIntervalMinutes is low
|
||||||
pubsubhubbub: false
|
pubsubhubbub: false
|
||||||
|
|
||||||
@@ -60,7 +66,10 @@ app:
|
|||||||
|
|
||||||
# entries to keep per feed, old entries will be deleted, 0 to disable
|
# entries to keep per feed, old entries will be deleted, 0 to disable
|
||||||
maxFeedCapacity: 500
|
maxFeedCapacity: 500
|
||||||
|
|
||||||
|
# limit the number of feeds a user can subscribe to, 0 to disable
|
||||||
|
maxFeedsPerUser: 0
|
||||||
|
|
||||||
# cache service to use, possible values are 'noop' and 'redis'
|
# cache service to use, possible values are 'noop' and 'redis'
|
||||||
cache: noop
|
cache: noop
|
||||||
|
|
||||||
@@ -86,7 +95,7 @@ app:
|
|||||||
|
|
||||||
database:
|
database:
|
||||||
driverClass: org.h2.Driver
|
driverClass: org.h2.Driver
|
||||||
url: jdbc:h2:./target/example
|
url: jdbc:h2:./target/commafeed
|
||||||
user: sa
|
user: sa
|
||||||
password: sa
|
password: sa
|
||||||
properties:
|
properties:
|
||||||
|
|||||||
@@ -3,9 +3,15 @@
|
|||||||
app:
|
app:
|
||||||
# url used to access commafeed
|
# url used to access commafeed
|
||||||
publicUrl: http://localhost:8082/
|
publicUrl: http://localhost:8082/
|
||||||
|
|
||||||
|
# whether to expose a robots.txt file that disallows web crawlers and search engine indexers
|
||||||
|
hideFromWebCrawlers: true
|
||||||
|
|
||||||
# whether to allow user registrations
|
# whether to allow user registrations
|
||||||
allowRegistrations: false
|
allowRegistrations: false
|
||||||
|
|
||||||
|
# whether to enable strict password validation (1 uppercase char, 1 lowercase char, 1 digit, 1 special char)
|
||||||
|
strictPasswordPolicy: true
|
||||||
|
|
||||||
# create a demo account the first time the app starts
|
# create a demo account the first time the app starts
|
||||||
createDemoAccount: false
|
createDemoAccount: false
|
||||||
@@ -38,14 +44,14 @@ app:
|
|||||||
graphitePort: 2003
|
graphitePort: 2003
|
||||||
graphiteInterval: 60
|
graphiteInterval: 60
|
||||||
|
|
||||||
# wether this commafeed instance has a lot of feeds to refresh
|
# whether this commafeed instance has a lot of feeds to refresh
|
||||||
# leave this to false in almost all cases
|
# leave this to false in almost all cases
|
||||||
heavyLoad: false
|
heavyLoad: false
|
||||||
|
|
||||||
# minimum amount of time commafeed will wait before refreshing the same feed
|
# minimum amount of time commafeed will wait before refreshing the same feed
|
||||||
refreshIntervalMinutes: 5
|
refreshIntervalMinutes: 5
|
||||||
|
|
||||||
# wether to enable pubsub
|
# whether to enable pubsub
|
||||||
# probably not needed if refreshIntervalMinutes is low
|
# probably not needed if refreshIntervalMinutes is low
|
||||||
pubsubhubbub: false
|
pubsubhubbub: false
|
||||||
|
|
||||||
@@ -61,7 +67,10 @@ app:
|
|||||||
|
|
||||||
# entries to keep per feed, old entries will be deleted, 0 to disable
|
# entries to keep per feed, old entries will be deleted, 0 to disable
|
||||||
maxFeedCapacity: 500
|
maxFeedCapacity: 500
|
||||||
|
|
||||||
|
# limit the number of feeds a user can subscribe to, 0 to disable
|
||||||
|
maxFeedsPerUser: 0
|
||||||
|
|
||||||
# cache service to use, possible values are 'noop' and 'redis'
|
# cache service to use, possible values are 'noop' and 'redis'
|
||||||
cache: noop
|
cache: noop
|
||||||
|
|
||||||
@@ -87,7 +96,7 @@ app:
|
|||||||
|
|
||||||
database:
|
database:
|
||||||
driverClass: org.h2.Driver
|
driverClass: org.h2.Driver
|
||||||
url: jdbc:h2:/commafeed/data/db
|
url: jdbc:h2:./db/commafeed
|
||||||
user: sa
|
user: sa
|
||||||
password: sa
|
password: sa
|
||||||
properties:
|
properties:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.commafeed</groupId>
|
<groupId>com.commafeed</groupId>
|
||||||
<artifactId>commafeed</artifactId>
|
<artifactId>commafeed</artifactId>
|
||||||
<version>3.4.0</version>
|
<version>3.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>commafeed-server</artifactId>
|
<artifactId>commafeed-server</artifactId>
|
||||||
<name>CommaFeed Server</name>
|
<name>CommaFeed Server</name>
|
||||||
@@ -226,7 +226,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.commafeed</groupId>
|
<groupId>com.commafeed</groupId>
|
||||||
<artifactId>commafeed-client</artifactId>
|
<artifactId>commafeed-client</artifactId>
|
||||||
<version>3.4.0</version>
|
<version>3.7.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -278,11 +278,6 @@
|
|||||||
<groupId>io.dropwizard.metrics</groupId>
|
<groupId>io.dropwizard.metrics</groupId>
|
||||||
<artifactId>metrics-json</artifactId>
|
<artifactId>metrics-json</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>io.dropwizard.modules</groupId>
|
|
||||||
<artifactId>dropwizard-web</artifactId>
|
|
||||||
<version>1.5.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>be.tomcools</groupId>
|
<groupId>be.tomcools</groupId>
|
||||||
<artifactId>dropwizard-websocket-jee7-bundle</artifactId>
|
<artifactId>dropwizard-websocket-jee7-bundle</artifactId>
|
||||||
@@ -374,7 +369,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>redis.clients</groupId>
|
<groupId>redis.clients</groupId>
|
||||||
<artifactId>jedis</artifactId>
|
<artifactId>jedis</artifactId>
|
||||||
<version>4.3.2</version>
|
<version>4.4.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.sun.mail</groupId>
|
<groupId>com.sun.mail</groupId>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import com.commafeed.backend.model.UserSettings;
|
|||||||
import com.commafeed.backend.service.DatabaseStartupService;
|
import com.commafeed.backend.service.DatabaseStartupService;
|
||||||
import com.commafeed.backend.service.UserService;
|
import com.commafeed.backend.service.UserService;
|
||||||
import com.commafeed.backend.task.ScheduledTask;
|
import com.commafeed.backend.task.ScheduledTask;
|
||||||
|
import com.commafeed.frontend.auth.PasswordConstraintValidator;
|
||||||
import com.commafeed.frontend.auth.SecurityCheckFactoryProvider;
|
import com.commafeed.frontend.auth.SecurityCheckFactoryProvider;
|
||||||
import com.commafeed.frontend.resource.AdminREST;
|
import com.commafeed.frontend.resource.AdminREST;
|
||||||
import com.commafeed.frontend.resource.CategoryREST;
|
import com.commafeed.frontend.resource.CategoryREST;
|
||||||
@@ -46,6 +47,7 @@ import com.commafeed.frontend.servlet.CustomCssServlet;
|
|||||||
import com.commafeed.frontend.servlet.CustomJsServlet;
|
import com.commafeed.frontend.servlet.CustomJsServlet;
|
||||||
import com.commafeed.frontend.servlet.LogoutServlet;
|
import com.commafeed.frontend.servlet.LogoutServlet;
|
||||||
import com.commafeed.frontend.servlet.NextUnreadServlet;
|
import com.commafeed.frontend.servlet.NextUnreadServlet;
|
||||||
|
import com.commafeed.frontend.servlet.RobotsTxtDisallowAllServlet;
|
||||||
import com.commafeed.frontend.session.SessionHelperFactoryProvider;
|
import com.commafeed.frontend.session.SessionHelperFactoryProvider;
|
||||||
import com.commafeed.frontend.ws.WebSocketConfigurator;
|
import com.commafeed.frontend.ws.WebSocketConfigurator;
|
||||||
import com.commafeed.frontend.ws.WebSocketEndpoint;
|
import com.commafeed.frontend.ws.WebSocketEndpoint;
|
||||||
@@ -69,8 +71,6 @@ import io.dropwizard.migrations.MigrationsBundle;
|
|||||||
import io.dropwizard.servlets.CacheBustingFilter;
|
import io.dropwizard.servlets.CacheBustingFilter;
|
||||||
import io.dropwizard.setup.Bootstrap;
|
import io.dropwizard.setup.Bootstrap;
|
||||||
import io.dropwizard.setup.Environment;
|
import io.dropwizard.setup.Environment;
|
||||||
import io.dropwizard.web.WebBundle;
|
|
||||||
import io.dropwizard.web.conf.WebConfiguration;
|
|
||||||
import io.whitfin.dropwizard.configuration.EnvironmentSubstitutor;
|
import io.whitfin.dropwizard.configuration.EnvironmentSubstitutor;
|
||||||
|
|
||||||
public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
|
public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
|
||||||
@@ -118,24 +118,16 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
|
|||||||
public DataSourceFactory getDataSourceFactory(CommaFeedConfiguration configuration) {
|
public DataSourceFactory getDataSourceFactory(CommaFeedConfiguration configuration) {
|
||||||
DataSourceFactory factory = configuration.getDataSourceFactory();
|
DataSourceFactory factory = configuration.getDataSourceFactory();
|
||||||
|
|
||||||
// keep using old id generator for backward compatibility
|
factory.getProperties().put(AvailableSettings.PREFERRED_POOLED_OPTIMIZER, "pooled-lo");
|
||||||
factory.getProperties().put(AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "false");
|
|
||||||
|
|
||||||
factory.getProperties().put(AvailableSettings.STATEMENT_BATCH_SIZE, "50");
|
factory.getProperties().put(AvailableSettings.STATEMENT_BATCH_SIZE, "50");
|
||||||
factory.getProperties().put(AvailableSettings.BATCH_VERSIONED_DATA, "true");
|
factory.getProperties().put(AvailableSettings.BATCH_VERSIONED_DATA, "true");
|
||||||
|
factory.getProperties().put(AvailableSettings.ORDER_INSERTS, "true");
|
||||||
|
factory.getProperties().put(AvailableSettings.ORDER_UPDATES, "true");
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
bootstrap.addBundle(new WebBundle<CommaFeedConfiguration>() {
|
|
||||||
@Override
|
|
||||||
public WebConfiguration getWebConfiguration(CommaFeedConfiguration configuration) {
|
|
||||||
WebConfiguration config = new WebConfiguration();
|
|
||||||
config.getFrameOptionsHeaderFactory().setEnabled(true);
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
bootstrap.addBundle(new MigrationsBundle<CommaFeedConfiguration>() {
|
bootstrap.addBundle(new MigrationsBundle<CommaFeedConfiguration>() {
|
||||||
@Override
|
@Override
|
||||||
public DataSourceFactory getDataSourceFactory(CommaFeedConfiguration configuration) {
|
public DataSourceFactory getDataSourceFactory(CommaFeedConfiguration configuration) {
|
||||||
@@ -149,6 +141,8 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CommaFeedConfiguration config, Environment environment) throws Exception {
|
public void run(CommaFeedConfiguration config, Environment environment) throws Exception {
|
||||||
|
PasswordConstraintValidator.setStrict(config.getApplicationSettings().getStrictPasswordPolicy());
|
||||||
|
|
||||||
// guice init
|
// guice init
|
||||||
Injector injector = Guice.createInjector(new CommaFeedModule(hibernateBundle.getSessionFactory(), config, environment.metrics()));
|
Injector injector = Guice.createInjector(new CommaFeedModule(hibernateBundle.getSessionFactory(), config, environment.metrics()));
|
||||||
|
|
||||||
@@ -176,6 +170,11 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
|
|||||||
environment.servlets().addServlet("customCss", injector.getInstance(CustomCssServlet.class)).addMapping("/custom_css.css");
|
environment.servlets().addServlet("customCss", injector.getInstance(CustomCssServlet.class)).addMapping("/custom_css.css");
|
||||||
environment.servlets().addServlet("customJs", injector.getInstance(CustomJsServlet.class)).addMapping("/custom_js.js");
|
environment.servlets().addServlet("customJs", injector.getInstance(CustomJsServlet.class)).addMapping("/custom_js.js");
|
||||||
environment.servlets().addServlet("analytics.js", injector.getInstance(AnalyticsServlet.class)).addMapping("/analytics.js");
|
environment.servlets().addServlet("analytics.js", injector.getInstance(AnalyticsServlet.class)).addMapping("/analytics.js");
|
||||||
|
if (Boolean.TRUE.equals(config.getApplicationSettings().getHideFromWebCrawlers())) {
|
||||||
|
environment.servlets()
|
||||||
|
.addServlet("robots.txt", injector.getInstance(RobotsTxtDisallowAllServlet.class))
|
||||||
|
.addMapping("/robots.txt");
|
||||||
|
}
|
||||||
|
|
||||||
// WebSocket endpoint
|
// WebSocket endpoint
|
||||||
ServerEndpointConfig serverEndpointConfig = ServerEndpointConfig.Builder.create(WebSocketEndpoint.class, "/ws")
|
ServerEndpointConfig serverEndpointConfig = ServerEndpointConfig.Builder.create(WebSocketEndpoint.class, "/ws")
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||||||
import io.dropwizard.Configuration;
|
import io.dropwizard.Configuration;
|
||||||
import io.dropwizard.db.DataSourceFactory;
|
import io.dropwizard.db.DataSourceFactory;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@Setter
|
||||||
public class CommaFeedConfiguration extends Configuration {
|
public class CommaFeedConfiguration extends Configuration {
|
||||||
|
|
||||||
public enum CacheType {
|
public enum CacheType {
|
||||||
@@ -56,16 +58,25 @@ public class CommaFeedConfiguration extends Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@Setter
|
||||||
public static class ApplicationSettings {
|
public static class ApplicationSettings {
|
||||||
@NotNull
|
@NotNull
|
||||||
@NotBlank
|
@NotBlank
|
||||||
@Valid
|
@Valid
|
||||||
private String publicUrl;
|
private String publicUrl;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Valid
|
||||||
|
private Boolean hideFromWebCrawlers = true;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
private Boolean allowRegistrations;
|
private Boolean allowRegistrations;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Valid
|
||||||
|
private Boolean strictPasswordPolicy = true;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
private Boolean createDemoAccount;
|
private Boolean createDemoAccount;
|
||||||
@@ -124,6 +135,10 @@ public class CommaFeedConfiguration extends Configuration {
|
|||||||
@Valid
|
@Valid
|
||||||
private Integer maxFeedCapacity;
|
private Integer maxFeedCapacity;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Valid
|
||||||
|
private Integer maxFeedsPerUser = 0;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Min(0)
|
@Min(0)
|
||||||
@Valid
|
@Valid
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class FixedSizeSortedSet<E> {
|
public class FixedSizeSortedSet<E> {
|
||||||
|
|
||||||
private List<E> inner;
|
private final List<E> inner;
|
||||||
|
|
||||||
private final Comparator<? super E> comparator;
|
private final Comparator<? super E> comparator;
|
||||||
private final int capacity;
|
private final int capacity;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import com.querydsl.core.types.Predicate;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
|
public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
|
||||||
|
|
||||||
private QFeedCategory category = QFeedCategory.feedCategory;
|
private final QFeedCategory category = QFeedCategory.feedCategory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FeedCategoryDAO(SessionFactory sessionFactory) {
|
public FeedCategoryDAO(SessionFactory sessionFactory) {
|
||||||
|
|||||||
@@ -13,15 +13,15 @@ import org.hibernate.SessionFactory;
|
|||||||
import com.commafeed.backend.model.Feed;
|
import com.commafeed.backend.model.Feed;
|
||||||
import com.commafeed.backend.model.QFeed;
|
import com.commafeed.backend.model.QFeed;
|
||||||
import com.commafeed.backend.model.QFeedSubscription;
|
import com.commafeed.backend.model.QFeedSubscription;
|
||||||
import com.commafeed.backend.model.QUser;
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.querydsl.jpa.JPAExpressions;
|
import com.querydsl.jpa.JPAExpressions;
|
||||||
import com.querydsl.jpa.JPQLQuery;
|
import com.querydsl.jpa.impl.JPAQuery;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class FeedDAO extends GenericDAO<Feed> {
|
public class FeedDAO extends GenericDAO<Feed> {
|
||||||
|
|
||||||
private final QFeed feed = QFeed.feed;
|
private final QFeed feed = QFeed.feed;
|
||||||
|
private final QFeedSubscription subscription = QFeedSubscription.feedSubscription;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FeedDAO(SessionFactory sessionFactory) {
|
public FeedDAO(SessionFactory sessionFactory) {
|
||||||
@@ -29,14 +29,13 @@ public class FeedDAO extends GenericDAO<Feed> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<Feed> findNextUpdatable(int count, Date lastLoginThreshold) {
|
public List<Feed> findNextUpdatable(int count, Date lastLoginThreshold) {
|
||||||
JPQLQuery<Feed> query = query().selectFrom(feed);
|
JPAQuery<Feed> query = query().selectFrom(feed).where(feed.disabledUntil.isNull().or(feed.disabledUntil.lt(new Date())));
|
||||||
query.where(feed.disabledUntil.isNull().or(feed.disabledUntil.lt(new Date())));
|
|
||||||
|
|
||||||
if (lastLoginThreshold != null) {
|
if (lastLoginThreshold != null) {
|
||||||
QFeedSubscription subs = QFeedSubscription.feedSubscription;
|
query.where(JPAExpressions.selectOne()
|
||||||
QUser user = QUser.user;
|
.from(subscription)
|
||||||
|
.join(subscription.user)
|
||||||
query.join(subs).on(subs.feed.id.eq(feed.id)).join(subs.user, user).where(user.lastLogin.gt(lastLoginThreshold));
|
.where(subscription.feed.id.eq(feed.id), subscription.user.lastLogin.gt(lastLoginThreshold))
|
||||||
|
.exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
return query.orderBy(feed.disabledUntil.asc()).limit(count).fetch();
|
return query.orderBy(feed.disabledUntil.asc()).limit(count).fetch();
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import lombok.Getter;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
||||||
|
|
||||||
private QFeedEntry entry = QFeedEntry.feedEntry;
|
private final QFeedEntry entry = QFeedEntry.feedEntry;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FeedEntryDAO(SessionFactory sessionFactory) {
|
public FeedEntryDAO(SessionFactory sessionFactory) {
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
|
|||||||
private FeedEntryStatus handleStatus(User user, FeedEntryStatus status, FeedSubscription sub, FeedEntry entry) {
|
private FeedEntryStatus handleStatus(User user, FeedEntryStatus status, FeedSubscription sub, FeedEntry entry) {
|
||||||
if (status == null) {
|
if (status == null) {
|
||||||
Date unreadThreshold = config.getApplicationSettings().getUnreadThreshold();
|
Date unreadThreshold = config.getApplicationSettings().getUnreadThreshold();
|
||||||
boolean read = unreadThreshold == null ? false : entry.getUpdated().before(unreadThreshold);
|
boolean read = unreadThreshold != null && entry.getUpdated().before(unreadThreshold);
|
||||||
status = new FeedEntryStatus(user, sub, entry);
|
status = new FeedEntryStatus(user, sub, entry);
|
||||||
status.setRead(read);
|
status.setRead(read);
|
||||||
status.setMarkable(!read);
|
status.setMarkable(!read);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import com.commafeed.backend.model.User;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class FeedEntryTagDAO extends GenericDAO<FeedEntryTag> {
|
public class FeedEntryTagDAO extends GenericDAO<FeedEntryTag> {
|
||||||
|
|
||||||
private QFeedEntryTag tag = QFeedEntryTag.feedEntryTag;
|
private final QFeedEntryTag tag = QFeedEntryTag.feedEntryTag;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FeedEntryTagDAO(SessionFactory sessionFactory) {
|
public FeedEntryTagDAO(SessionFactory sessionFactory) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import com.querydsl.jpa.JPQLQuery;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
|
public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
|
||||||
|
|
||||||
private QFeedSubscription sub = QFeedSubscription.feedSubscription;
|
private final QFeedSubscription sub = QFeedSubscription.feedSubscription;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FeedSubscriptionDAO(SessionFactory sessionFactory) {
|
public FeedSubscriptionDAO(SessionFactory sessionFactory) {
|
||||||
@@ -59,6 +59,10 @@ public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
|
|||||||
return initRelations(subs);
|
return initRelations(subs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long count(User user) {
|
||||||
|
return query().select(sub.count()).from(sub).where(sub.user.eq(user)).fetchOne();
|
||||||
|
}
|
||||||
|
|
||||||
public List<FeedSubscription> findByCategory(User user, FeedCategory category) {
|
public List<FeedSubscription> findByCategory(User user, FeedCategory category) {
|
||||||
JPQLQuery<FeedSubscription> query = query().selectFrom(sub).where(sub.user.eq(user));
|
JPQLQuery<FeedSubscription> query = query().selectFrom(sub).where(sub.user.eq(user));
|
||||||
if (category == null) {
|
if (category == null) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import io.dropwizard.hibernate.AbstractDAO;
|
|||||||
|
|
||||||
public abstract class GenericDAO<T extends AbstractModel> extends AbstractDAO<T> {
|
public abstract class GenericDAO<T extends AbstractModel> extends AbstractDAO<T> {
|
||||||
|
|
||||||
private JPAQueryFactory factory;
|
private final JPAQueryFactory factory;
|
||||||
|
|
||||||
protected GenericDAO(SessionFactory sessionFactory) {
|
protected GenericDAO(SessionFactory sessionFactory) {
|
||||||
super(sessionFactory);
|
super(sessionFactory);
|
||||||
|
|||||||
@@ -1,53 +1,63 @@
|
|||||||
package com.commafeed.backend.dao;
|
package com.commafeed.backend.dao;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.SessionFactory;
|
import org.hibernate.SessionFactory;
|
||||||
import org.hibernate.Transaction;
|
import org.hibernate.Transaction;
|
||||||
import org.hibernate.context.internal.ManagedSessionContext;
|
import org.hibernate.context.internal.ManagedSessionContext;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor(onConstructor = @__({ @Inject }))
|
||||||
|
@Singleton
|
||||||
public class UnitOfWork {
|
public class UnitOfWork {
|
||||||
|
|
||||||
public static void run(SessionFactory sessionFactory, SessionRunner sessionRunner) {
|
private final SessionFactory sessionFactory;
|
||||||
call(sessionFactory, () -> {
|
|
||||||
|
public void run(SessionRunner sessionRunner) {
|
||||||
|
call(() -> {
|
||||||
sessionRunner.runInSession();
|
sessionRunner.runInSession();
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T call(SessionFactory sessionFactory, SessionRunnerReturningValue<T> sessionRunner) {
|
public <T> T call(SessionRunnerReturningValue<T> sessionRunner) {
|
||||||
final Session session = sessionFactory.openSession();
|
|
||||||
if (ManagedSessionContext.hasBind(sessionFactory)) {
|
|
||||||
throw new IllegalStateException("Already in a unit of work!");
|
|
||||||
}
|
|
||||||
T t = null;
|
T t = null;
|
||||||
try {
|
|
||||||
ManagedSessionContext.bind(session);
|
boolean sessionAlreadyBound = ManagedSessionContext.hasBind(sessionFactory);
|
||||||
session.beginTransaction();
|
try (Session session = sessionFactory.openSession()) {
|
||||||
|
if (!sessionAlreadyBound) {
|
||||||
|
ManagedSessionContext.bind(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transaction tx = session.beginTransaction();
|
||||||
try {
|
try {
|
||||||
t = sessionRunner.runInSession();
|
t = sessionRunner.runInSession();
|
||||||
commitTransaction(session);
|
commitTransaction(tx);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
rollbackTransaction(session);
|
rollbackTransaction(tx);
|
||||||
UnitOfWork.<RuntimeException> rethrow(e);
|
UnitOfWork.rethrow(e);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
session.close();
|
if (!sessionAlreadyBound) {
|
||||||
ManagedSessionContext.unbind(sessionFactory);
|
ManagedSessionContext.unbind(sessionFactory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void rollbackTransaction(Session session) {
|
private static void rollbackTransaction(Transaction tx) {
|
||||||
final Transaction txn = session.getTransaction();
|
if (tx != null && tx.isActive()) {
|
||||||
if (txn != null && txn.isActive()) {
|
tx.rollback();
|
||||||
txn.rollback();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void commitTransaction(Session session) {
|
private static void commitTransaction(Transaction tx) {
|
||||||
final Transaction txn = session.getTransaction();
|
if (tx != null && tx.isActive()) {
|
||||||
if (txn != null && txn.isActive()) {
|
tx.commit();
|
||||||
txn.commit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import com.commafeed.backend.model.User;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class UserDAO extends GenericDAO<User> {
|
public class UserDAO extends GenericDAO<User> {
|
||||||
|
|
||||||
private QUser user = QUser.user;
|
private final QUser user = QUser.user;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UserDAO(SessionFactory sessionFactory) {
|
public UserDAO(SessionFactory sessionFactory) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import com.commafeed.backend.model.UserRole.Role;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class UserRoleDAO extends GenericDAO<UserRole> {
|
public class UserRoleDAO extends GenericDAO<UserRole> {
|
||||||
|
|
||||||
private QUserRole role = QUserRole.userRole;
|
private final QUserRole role = QUserRole.userRole;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UserRoleDAO(SessionFactory sessionFactory) {
|
public UserRoleDAO(SessionFactory sessionFactory) {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import com.commafeed.backend.model.UserSettings;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class UserSettingsDAO extends GenericDAO<UserSettings> {
|
public class UserSettingsDAO extends GenericDAO<UserSettings> {
|
||||||
|
|
||||||
private QUserSettings settings = QUserSettings.userSettings;
|
private final QUserSettings settings = QUserSettings.userSettings;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UserSettingsDAO(SessionFactory sessionFactory) {
|
public UserSettingsDAO(SessionFactory sessionFactory) {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
public class FeedEntryKeyword {
|
public class FeedEntryKeyword {
|
||||||
|
|
||||||
public enum Mode {
|
public enum Mode {
|
||||||
INCLUDE, EXCLUDE;
|
INCLUDE, EXCLUDE
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String keyword;
|
private final String keyword;
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public class FeedFetcher {
|
|||||||
boolean etagHeaderValueChanged = !StringUtils.equals(eTag, result.getETag());
|
boolean etagHeaderValueChanged = !StringUtils.equals(eTag, result.getETag());
|
||||||
|
|
||||||
String hash = DigestUtils.sha1Hex(content);
|
String hash = DigestUtils.sha1Hex(content);
|
||||||
if (lastContentHash != null && hash != null && lastContentHash.equals(hash)) {
|
if (lastContentHash != null && lastContentHash.equals(hash)) {
|
||||||
log.debug("content hash not modified: {}", feedUrl);
|
log.debug("content hash not modified: {}", feedUrl);
|
||||||
throw new NotModifiedException("content hash not modified",
|
throw new NotModifiedException("content hash not modified",
|
||||||
lastModifiedHeaderValueChanged ? result.getLastModifiedSince() : null,
|
lastModifiedHeaderValueChanged ? result.getLastModifiedSince() : null,
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import javax.inject.Inject;
|
|||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
import org.hibernate.SessionFactory;
|
|
||||||
|
|
||||||
|
import com.codahale.metrics.Gauge;
|
||||||
import com.codahale.metrics.Meter;
|
import com.codahale.metrics.Meter;
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
import com.commafeed.CommaFeedConfiguration;
|
import com.commafeed.CommaFeedConfiguration;
|
||||||
@@ -33,7 +33,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class FeedRefreshEngine implements Managed {
|
public class FeedRefreshEngine implements Managed {
|
||||||
|
|
||||||
private final SessionFactory sessionFactory;
|
private final UnitOfWork unitOfWork;
|
||||||
private final FeedDAO feedDAO;
|
private final FeedDAO feedDAO;
|
||||||
private final FeedRefreshWorker worker;
|
private final FeedRefreshWorker worker;
|
||||||
private final FeedRefreshUpdater updater;
|
private final FeedRefreshUpdater updater;
|
||||||
@@ -45,13 +45,13 @@ public class FeedRefreshEngine implements Managed {
|
|||||||
private final ExecutorService feedProcessingLoopExecutor;
|
private final ExecutorService feedProcessingLoopExecutor;
|
||||||
private final ExecutorService refillLoopExecutor;
|
private final ExecutorService refillLoopExecutor;
|
||||||
private final ExecutorService refillExecutor;
|
private final ExecutorService refillExecutor;
|
||||||
private final ExecutorService workerExecutor;
|
private final ThreadPoolExecutor workerExecutor;
|
||||||
private final ExecutorService databaseUpdaterExecutor;
|
private final ThreadPoolExecutor databaseUpdaterExecutor;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FeedRefreshEngine(SessionFactory sessionFactory, FeedDAO feedDAO, FeedRefreshWorker worker, FeedRefreshUpdater updater,
|
public FeedRefreshEngine(UnitOfWork unitOfWork, FeedDAO feedDAO, FeedRefreshWorker worker, FeedRefreshUpdater updater,
|
||||||
CommaFeedConfiguration config, MetricRegistry metrics) {
|
CommaFeedConfiguration config, MetricRegistry metrics) {
|
||||||
this.sessionFactory = sessionFactory;
|
this.unitOfWork = unitOfWork;
|
||||||
this.feedDAO = feedDAO;
|
this.feedDAO = feedDAO;
|
||||||
this.worker = worker;
|
this.worker = worker;
|
||||||
this.updater = updater;
|
this.updater = updater;
|
||||||
@@ -65,6 +65,10 @@ public class FeedRefreshEngine implements Managed {
|
|||||||
this.refillExecutor = newDiscardingSingleThreadExecutorService();
|
this.refillExecutor = newDiscardingSingleThreadExecutorService();
|
||||||
this.workerExecutor = newBlockingExecutorService(config.getApplicationSettings().getBackgroundThreads());
|
this.workerExecutor = newBlockingExecutorService(config.getApplicationSettings().getBackgroundThreads());
|
||||||
this.databaseUpdaterExecutor = newBlockingExecutorService(config.getApplicationSettings().getDatabaseUpdateThreads());
|
this.databaseUpdaterExecutor = newBlockingExecutorService(config.getApplicationSettings().getDatabaseUpdateThreads());
|
||||||
|
|
||||||
|
metrics.register(MetricRegistry.name(getClass(), "queue", "size"), (Gauge<Integer>) queue::size);
|
||||||
|
metrics.register(MetricRegistry.name(getClass(), "worker", "active"), (Gauge<Integer>) workerExecutor::getActiveCount);
|
||||||
|
metrics.register(MetricRegistry.name(getClass(), "updater", "active"), (Gauge<Integer>) databaseUpdaterExecutor::getActiveCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -82,10 +86,12 @@ public class FeedRefreshEngine implements Managed {
|
|||||||
Feed feed = queue.take();
|
Feed feed = queue.take();
|
||||||
|
|
||||||
// send the feed to be processed
|
// send the feed to be processed
|
||||||
|
log.debug("got feed {} from the queue, send it for processing", feed.getId());
|
||||||
processFeedAsync(feed);
|
processFeedAsync(feed);
|
||||||
|
|
||||||
// we removed a feed from the queue, try to refill it as it may now be empty
|
// we removed a feed from the queue, try to refill it as it may now be empty
|
||||||
if (queue.isEmpty()) {
|
if (queue.isEmpty()) {
|
||||||
|
log.debug("took the last feed from the queue, try to refill");
|
||||||
refillQueueAsync();
|
refillQueueAsync();
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
@@ -104,9 +110,11 @@ public class FeedRefreshEngine implements Managed {
|
|||||||
while (!refillLoopExecutor.isShutdown()) {
|
while (!refillLoopExecutor.isShutdown()) {
|
||||||
try {
|
try {
|
||||||
if (queue.isEmpty()) {
|
if (queue.isEmpty()) {
|
||||||
|
log.debug("refilling queue");
|
||||||
refillQueueAsync();
|
refillQueueAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.debug("sleeping for 15s");
|
||||||
TimeUnit.SECONDS.sleep(15);
|
TimeUnit.SECONDS.sleep(15);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
log.debug("interrupted while sleeping");
|
log.debug("interrupted while sleeping");
|
||||||
@@ -119,6 +127,7 @@ public class FeedRefreshEngine implements Managed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void refreshImmediately(Feed feed) {
|
public void refreshImmediately(Feed feed) {
|
||||||
|
log.debug("add feed {} at the start of the queue", feed.getId());
|
||||||
// remove the feed from the queue if it was already queued to avoid refreshing it twice
|
// remove the feed from the queue if it was already queued to avoid refreshing it twice
|
||||||
queue.removeIf(f -> f.getId().equals(feed.getId()));
|
queue.removeIf(f -> f.getId().equals(feed.getId()));
|
||||||
queue.addFirst(feed);
|
queue.addFirst(feed);
|
||||||
@@ -132,7 +141,9 @@ public class FeedRefreshEngine implements Managed {
|
|||||||
|
|
||||||
refill.mark();
|
refill.mark();
|
||||||
|
|
||||||
for (Feed feed : getNextUpdatableFeeds(getBatchSize())) {
|
List<Feed> nextUpdatableFeeds = getNextUpdatableFeeds(getBatchSize());
|
||||||
|
log.debug("found {} feeds that are up for refresh", nextUpdatableFeeds.size());
|
||||||
|
for (Feed feed : nextUpdatableFeeds) {
|
||||||
// add the feed only if it was not already queued
|
// add the feed only if it was not already queued
|
||||||
if (queue.stream().noneMatch(f -> f.getId().equals(feed.getId()))) {
|
if (queue.stream().noneMatch(f -> f.getId().equals(feed.getId()))) {
|
||||||
queue.addLast(feed);
|
queue.addLast(feed);
|
||||||
@@ -156,8 +167,11 @@ public class FeedRefreshEngine implements Managed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<Feed> getNextUpdatableFeeds(int max) {
|
private List<Feed> getNextUpdatableFeeds(int max) {
|
||||||
return UnitOfWork.call(sessionFactory, () -> {
|
return unitOfWork.call(() -> {
|
||||||
List<Feed> feeds = feedDAO.findNextUpdatable(max, getLastLoginThreshold());
|
Date lastLoginThreshold = Boolean.TRUE.equals(config.getApplicationSettings().getHeavyLoad())
|
||||||
|
? DateUtils.addDays(new Date(), -30)
|
||||||
|
: null;
|
||||||
|
List<Feed> feeds = feedDAO.findNextUpdatable(max, lastLoginThreshold);
|
||||||
// update disabledUntil to prevent feeds from being returned again by feedDAO.findNextUpdatable()
|
// update disabledUntil to prevent feeds from being returned again by feedDAO.findNextUpdatable()
|
||||||
Date nextUpdateDate = DateUtils.addMinutes(new Date(), config.getApplicationSettings().getRefreshIntervalMinutes());
|
Date nextUpdateDate = DateUtils.addMinutes(new Date(), config.getApplicationSettings().getRefreshIntervalMinutes());
|
||||||
feedDAO.setDisabledUntil(feeds.stream().map(AbstractModel::getId).collect(Collectors.toList()), nextUpdateDate);
|
feedDAO.setDisabledUntil(feeds.stream().map(AbstractModel::getId).collect(Collectors.toList()), nextUpdateDate);
|
||||||
@@ -169,10 +183,6 @@ public class FeedRefreshEngine implements Managed {
|
|||||||
return Math.min(100, 3 * config.getApplicationSettings().getBackgroundThreads());
|
return Math.min(100, 3 * config.getApplicationSettings().getBackgroundThreads());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Date getLastLoginThreshold() {
|
|
||||||
return Boolean.TRUE.equals(config.getApplicationSettings().getHeavyLoad()) ? DateUtils.addDays(new Date(), -30) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
this.feedProcessingLoopExecutor.shutdownNow();
|
this.feedProcessingLoopExecutor.shutdownNow();
|
||||||
@@ -185,7 +195,7 @@ public class FeedRefreshEngine implements Managed {
|
|||||||
/**
|
/**
|
||||||
* returns an ExecutorService with a single thread that discards tasks if a task is already running
|
* returns an ExecutorService with a single thread that discards tasks if a task is already running
|
||||||
*/
|
*/
|
||||||
private ExecutorService newDiscardingSingleThreadExecutorService() {
|
private ThreadPoolExecutor newDiscardingSingleThreadExecutorService() {
|
||||||
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<>());
|
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<>());
|
||||||
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
|
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
|
||||||
return pool;
|
return pool;
|
||||||
@@ -194,7 +204,7 @@ public class FeedRefreshEngine implements Managed {
|
|||||||
/**
|
/**
|
||||||
* returns an ExecutorService that blocks submissions until a thread is available
|
* returns an ExecutorService that blocks submissions until a thread is available
|
||||||
*/
|
*/
|
||||||
private ExecutorService newBlockingExecutorService(int threads) {
|
private ThreadPoolExecutor newBlockingExecutorService(int threads) {
|
||||||
ThreadPoolExecutor pool = new ThreadPoolExecutor(threads, threads, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<>());
|
ThreadPoolExecutor pool = new ThreadPoolExecutor(threads, threads, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<>());
|
||||||
pool.setRejectedExecutionHandler((r, e) -> {
|
pool.setRejectedExecutionHandler((r, e) -> {
|
||||||
if (e.isShutdown()) {
|
if (e.isShutdown()) {
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import com.commafeed.backend.model.Feed;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class FeedRefreshIntervalCalculator {
|
public class FeedRefreshIntervalCalculator {
|
||||||
|
|
||||||
private boolean heavyLoad;
|
private final boolean heavyLoad;
|
||||||
private int refreshIntervalMinutes;
|
private final int refreshIntervalMinutes;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FeedRefreshIntervalCalculator(CommaFeedConfiguration config) {
|
public FeedRefreshIntervalCalculator(CommaFeedConfiguration config) {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import javax.inject.Singleton;
|
|||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
import org.hibernate.SessionFactory;
|
|
||||||
|
|
||||||
import com.codahale.metrics.Meter;
|
import com.codahale.metrics.Meter;
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
@@ -46,7 +45,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class FeedRefreshUpdater implements Managed {
|
public class FeedRefreshUpdater implements Managed {
|
||||||
|
|
||||||
private final SessionFactory sessionFactory;
|
private final UnitOfWork unitOfWork;
|
||||||
private final FeedService feedService;
|
private final FeedService feedService;
|
||||||
private final FeedEntryService feedEntryService;
|
private final FeedEntryService feedEntryService;
|
||||||
private final PubSubService pubSubService;
|
private final PubSubService pubSubService;
|
||||||
@@ -63,10 +62,10 @@ public class FeedRefreshUpdater implements Managed {
|
|||||||
private final Meter entryInserted;
|
private final Meter entryInserted;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FeedRefreshUpdater(SessionFactory sessionFactory, FeedService feedService, FeedEntryService feedEntryService,
|
public FeedRefreshUpdater(UnitOfWork unitOfWork, FeedService feedService, FeedEntryService feedEntryService,
|
||||||
PubSubService pubSubService, CommaFeedConfiguration config, MetricRegistry metrics, FeedSubscriptionDAO feedSubscriptionDAO,
|
PubSubService pubSubService, CommaFeedConfiguration config, MetricRegistry metrics, FeedSubscriptionDAO feedSubscriptionDAO,
|
||||||
CacheService cache, WebSocketSessions webSocketSessions) {
|
CacheService cache, WebSocketSessions webSocketSessions) {
|
||||||
this.sessionFactory = sessionFactory;
|
this.unitOfWork = unitOfWork;
|
||||||
this.feedService = feedService;
|
this.feedService = feedService;
|
||||||
this.feedEntryService = feedEntryService;
|
this.feedEntryService = feedEntryService;
|
||||||
this.pubSubService = pubSubService;
|
this.pubSubService = pubSubService;
|
||||||
@@ -89,7 +88,7 @@ public class FeedRefreshUpdater implements Managed {
|
|||||||
|
|
||||||
// lock on feed, make sure we are not updating the same feed twice at
|
// lock on feed, make sure we are not updating the same feed twice at
|
||||||
// the same time
|
// the same time
|
||||||
String key1 = StringUtils.trimToEmpty("" + feed.getId());
|
String key1 = StringUtils.trimToEmpty(String.valueOf(feed.getId()));
|
||||||
|
|
||||||
// lock on content, make sure we are not updating the same entry
|
// lock on content, make sure we are not updating the same entry
|
||||||
// twice at the same time
|
// twice at the same time
|
||||||
@@ -107,7 +106,7 @@ public class FeedRefreshUpdater implements Managed {
|
|||||||
locked2 = lock2.tryLock(1, TimeUnit.MINUTES);
|
locked2 = lock2.tryLock(1, TimeUnit.MINUTES);
|
||||||
if (locked1 && locked2) {
|
if (locked1 && locked2) {
|
||||||
processed = true;
|
processed = true;
|
||||||
inserted = UnitOfWork.call(sessionFactory, () -> feedEntryService.addEntry(feed, entry, subscriptions));
|
inserted = unitOfWork.call(() -> feedEntryService.addEntry(feed, entry, subscriptions));
|
||||||
if (inserted) {
|
if (inserted) {
|
||||||
entryInserted.mark();
|
entryInserted.mark();
|
||||||
}
|
}
|
||||||
@@ -164,7 +163,7 @@ public class FeedRefreshUpdater implements Managed {
|
|||||||
if (!lastEntries.contains(cacheKey)) {
|
if (!lastEntries.contains(cacheKey)) {
|
||||||
log.debug("cache miss for {}", entry.getUrl());
|
log.debug("cache miss for {}", entry.getUrl());
|
||||||
if (subscriptions == null) {
|
if (subscriptions == null) {
|
||||||
subscriptions = UnitOfWork.call(sessionFactory, () -> feedSubscriptionDAO.findByFeed(feed));
|
subscriptions = unitOfWork.call(() -> feedSubscriptionDAO.findByFeed(feed));
|
||||||
}
|
}
|
||||||
AddEntryResult addEntryResult = addEntry(feed, entry, subscriptions);
|
AddEntryResult addEntryResult = addEntry(feed, entry, subscriptions);
|
||||||
processed &= addEntryResult.processed;
|
processed &= addEntryResult.processed;
|
||||||
@@ -204,7 +203,7 @@ public class FeedRefreshUpdater implements Managed {
|
|||||||
feedUpdated.mark();
|
feedUpdated.mark();
|
||||||
}
|
}
|
||||||
|
|
||||||
UnitOfWork.run(sessionFactory, () -> feedService.save(feed));
|
unitOfWork.run(() -> feedService.save(feed));
|
||||||
|
|
||||||
return processed;
|
return processed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ public class FeedUtils {
|
|||||||
for (Emit emit : emits) {
|
for (Emit emit : emits) {
|
||||||
int matchIndex = emit.getStart();
|
int matchIndex = emit.getStart();
|
||||||
|
|
||||||
sb.append(source.substring(prevIndex, matchIndex));
|
sb.append(source, prevIndex, matchIndex);
|
||||||
sb.append(HtmlEntities.HTML_TO_NUMERIC_MAP.get(emit.getKeyword()));
|
sb.append(HtmlEntities.HTML_TO_NUMERIC_MAP.get(emit.getKeyword()));
|
||||||
prevIndex = emit.getEnd() + 1;
|
prevIndex = emit.getEnd() + 1;
|
||||||
}
|
}
|
||||||
@@ -228,7 +228,7 @@ public class FeedUtils {
|
|||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String encoding = pi.substring(index + 10, pi.length());
|
String encoding = pi.substring(index + 10);
|
||||||
encoding = encoding.substring(0, encoding.indexOf('"'));
|
encoding = encoding.substring(0, encoding.indexOf('"'));
|
||||||
return encoding;
|
return encoding;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,6 @@ public class FeedCategory extends AbstractModel {
|
|||||||
|
|
||||||
private boolean collapsed;
|
private boolean collapsed;
|
||||||
|
|
||||||
private Integer position;
|
private int position;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class FeedSubscription extends AbstractModel {
|
|||||||
@OneToMany(mappedBy = "subscription", cascade = CascadeType.REMOVE)
|
@OneToMany(mappedBy = "subscription", cascade = CascadeType.REMOVE)
|
||||||
private Set<FeedEntryStatus> statuses;
|
private Set<FeedEntryStatus> statuses;
|
||||||
|
|
||||||
private Integer position;
|
private int position;
|
||||||
|
|
||||||
@Column(name = "filtering_expression", length = 4096)
|
@Column(name = "filtering_expression", length = 4096)
|
||||||
private String filter;
|
private String filter;
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import javax.persistence.Table;
|
|||||||
import javax.persistence.Temporal;
|
import javax.persistence.Temporal;
|
||||||
import javax.persistence.TemporalType;
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
@@ -49,17 +47,4 @@ public class User extends AbstractModel {
|
|||||||
|
|
||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
private Date recoverPasswordTokenDate;
|
private Date recoverPasswordTokenDate;
|
||||||
|
|
||||||
@Column(name = "last_full_refresh")
|
|
||||||
@Temporal(TemporalType.TIMESTAMP)
|
|
||||||
private Date lastFullRefresh;
|
|
||||||
|
|
||||||
public boolean shouldRefreshFeedsAt(Date when) {
|
|
||||||
return lastFullRefresh == null || lastFullRefreshMoreThan30MinutesBefore(when);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean lastFullRefreshMoreThan30MinutesBefore(Date when) {
|
|
||||||
return lastFullRefresh.before(DateUtils.addMinutes(when, -30));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import com.commafeed.backend.feed.FeedUtils;
|
|||||||
import com.commafeed.backend.model.FeedCategory;
|
import com.commafeed.backend.model.FeedCategory;
|
||||||
import com.commafeed.backend.model.User;
|
import com.commafeed.backend.model.User;
|
||||||
import com.commafeed.backend.service.FeedSubscriptionService;
|
import com.commafeed.backend.service.FeedSubscriptionService;
|
||||||
import com.commafeed.backend.service.FeedSubscriptionService.FeedSubscriptionException;
|
|
||||||
import com.rometools.opml.feed.opml.Opml;
|
import com.rometools.opml.feed.opml.Opml;
|
||||||
import com.rometools.opml.feed.opml.Outline;
|
import com.rometools.opml.feed.opml.Outline;
|
||||||
import com.rometools.rome.io.FeedException;
|
import com.rometools.rome.io.FeedException;
|
||||||
@@ -78,8 +77,6 @@ public class OPMLImporter {
|
|||||||
// make sure we continue with the import process even if a feed failed
|
// make sure we continue with the import process even if a feed failed
|
||||||
try {
|
try {
|
||||||
feedSubscriptionService.subscribe(user, outline.getXmlUrl(), name, parent, position);
|
feedSubscriptionService.subscribe(user, outline.getXmlUrl(), name, parent, position);
|
||||||
} catch (FeedSubscriptionException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("error while importing {}: {}", outline.getXmlUrl(), e.getMessage());
|
log.error("error while importing {}: {}", outline.getXmlUrl(), e.getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,11 +23,7 @@ public class OPML11Parser extends OPML10Parser {
|
|||||||
public boolean isMyType(Document document) {
|
public boolean isMyType(Document document) {
|
||||||
Element e = document.getRootElement();
|
Element e = document.getRootElement();
|
||||||
|
|
||||||
if (e.getName().equals("opml")) {
|
return e.getName().equals("opml");
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import java.util.List;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.hibernate.SessionFactory;
|
|
||||||
|
|
||||||
import com.commafeed.backend.dao.FeedDAO;
|
import com.commafeed.backend.dao.FeedDAO;
|
||||||
import com.commafeed.backend.dao.FeedEntryContentDAO;
|
import com.commafeed.backend.dao.FeedEntryContentDAO;
|
||||||
import com.commafeed.backend.dao.FeedEntryDAO;
|
import com.commafeed.backend.dao.FeedEntryDAO;
|
||||||
@@ -30,7 +28,7 @@ public class DatabaseCleaningService {
|
|||||||
|
|
||||||
private static final int BATCH_SIZE = 100;
|
private static final int BATCH_SIZE = 100;
|
||||||
|
|
||||||
private final SessionFactory sessionFactory;
|
private final UnitOfWork unitOfWork;
|
||||||
private final FeedDAO feedDAO;
|
private final FeedDAO feedDAO;
|
||||||
private final FeedEntryDAO feedEntryDAO;
|
private final FeedEntryDAO feedEntryDAO;
|
||||||
private final FeedEntryContentDAO feedEntryContentDAO;
|
private final FeedEntryContentDAO feedEntryContentDAO;
|
||||||
@@ -42,16 +40,16 @@ public class DatabaseCleaningService {
|
|||||||
int deleted = 0;
|
int deleted = 0;
|
||||||
long entriesTotal = 0;
|
long entriesTotal = 0;
|
||||||
do {
|
do {
|
||||||
List<Feed> feeds = UnitOfWork.call(sessionFactory, () -> feedDAO.findWithoutSubscriptions(1));
|
List<Feed> feeds = unitOfWork.call(() -> feedDAO.findWithoutSubscriptions(1));
|
||||||
for (Feed feed : feeds) {
|
for (Feed feed : feeds) {
|
||||||
int entriesDeleted = 0;
|
int entriesDeleted = 0;
|
||||||
do {
|
do {
|
||||||
entriesDeleted = UnitOfWork.call(sessionFactory, () -> feedEntryDAO.delete(feed.getId(), BATCH_SIZE));
|
entriesDeleted = unitOfWork.call(() -> feedEntryDAO.delete(feed.getId(), BATCH_SIZE));
|
||||||
entriesTotal += entriesDeleted;
|
entriesTotal += entriesDeleted;
|
||||||
log.info("removed {} entries for feeds without subscriptions", entriesTotal);
|
log.info("removed {} entries for feeds without subscriptions", entriesTotal);
|
||||||
} while (entriesDeleted > 0);
|
} while (entriesDeleted > 0);
|
||||||
}
|
}
|
||||||
deleted = UnitOfWork.call(sessionFactory, () -> feedDAO.delete(feeds));
|
deleted = unitOfWork.call(() -> feedDAO.delete(feeds));
|
||||||
total += deleted;
|
total += deleted;
|
||||||
log.info("removed {} feeds without subscriptions", total);
|
log.info("removed {} feeds without subscriptions", total);
|
||||||
} while (deleted != 0);
|
} while (deleted != 0);
|
||||||
@@ -64,7 +62,7 @@ public class DatabaseCleaningService {
|
|||||||
long total = 0;
|
long total = 0;
|
||||||
int deleted = 0;
|
int deleted = 0;
|
||||||
do {
|
do {
|
||||||
deleted = UnitOfWork.call(sessionFactory, () -> feedEntryContentDAO.deleteWithoutEntries(BATCH_SIZE));
|
deleted = unitOfWork.call(() -> feedEntryContentDAO.deleteWithoutEntries(BATCH_SIZE));
|
||||||
total += deleted;
|
total += deleted;
|
||||||
log.info("removed {} contents without entries", total);
|
log.info("removed {} contents without entries", total);
|
||||||
} while (deleted != 0);
|
} while (deleted != 0);
|
||||||
@@ -75,8 +73,7 @@ public class DatabaseCleaningService {
|
|||||||
public long cleanEntriesForFeedsExceedingCapacity(final int maxFeedCapacity) {
|
public long cleanEntriesForFeedsExceedingCapacity(final int maxFeedCapacity) {
|
||||||
long total = 0;
|
long total = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
List<FeedCapacity> feeds = UnitOfWork.call(sessionFactory,
|
List<FeedCapacity> feeds = unitOfWork.call(() -> feedEntryDAO.findFeedsExceedingCapacity(maxFeedCapacity, BATCH_SIZE));
|
||||||
() -> feedEntryDAO.findFeedsExceedingCapacity(maxFeedCapacity, BATCH_SIZE));
|
|
||||||
if (feeds.isEmpty()) {
|
if (feeds.isEmpty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -85,8 +82,7 @@ public class DatabaseCleaningService {
|
|||||||
long remaining = feed.getCapacity() - maxFeedCapacity;
|
long remaining = feed.getCapacity() - maxFeedCapacity;
|
||||||
do {
|
do {
|
||||||
final long rem = remaining;
|
final long rem = remaining;
|
||||||
int deleted = UnitOfWork.call(sessionFactory,
|
int deleted = unitOfWork.call(() -> feedEntryDAO.deleteOldEntries(feed.getId(), Math.min(BATCH_SIZE, rem)));
|
||||||
() -> feedEntryDAO.deleteOldEntries(feed.getId(), Math.min(BATCH_SIZE, rem)));
|
|
||||||
total += deleted;
|
total += deleted;
|
||||||
remaining -= deleted;
|
remaining -= deleted;
|
||||||
log.info("removed {} entries for feeds exceeding capacity", total);
|
log.info("removed {} entries for feeds exceeding capacity", total);
|
||||||
@@ -102,8 +98,7 @@ public class DatabaseCleaningService {
|
|||||||
long total = 0;
|
long total = 0;
|
||||||
int deleted = 0;
|
int deleted = 0;
|
||||||
do {
|
do {
|
||||||
deleted = UnitOfWork.call(sessionFactory,
|
deleted = unitOfWork.call(() -> feedEntryStatusDAO.delete(feedEntryStatusDAO.getOldStatuses(olderThan, BATCH_SIZE)));
|
||||||
() -> feedEntryStatusDAO.delete(feedEntryStatusDAO.getOldStatuses(olderThan, BATCH_SIZE)));
|
|
||||||
total += deleted;
|
total += deleted;
|
||||||
log.info("removed {} old read statuses", total);
|
log.info("removed {} old read statuses", total);
|
||||||
} while (deleted != 0);
|
} while (deleted != 0);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class DatabaseStartupService implements Managed {
|
public class DatabaseStartupService implements Managed {
|
||||||
|
|
||||||
|
private final UnitOfWork unitOfWork;
|
||||||
private final SessionFactory sessionFactory;
|
private final SessionFactory sessionFactory;
|
||||||
private final UserDAO userDAO;
|
private final UserDAO userDAO;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
@@ -35,9 +36,9 @@ public class DatabaseStartupService implements Managed {
|
|||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void start() {
|
||||||
updateSchema();
|
updateSchema();
|
||||||
long count = UnitOfWork.call(sessionFactory, userDAO::count);
|
long count = unitOfWork.call(userDAO::count);
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
UnitOfWork.run(sessionFactory, this::initialData);
|
unitOfWork.run(this::initialData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class FeedService {
|
|||||||
private final FeedDAO feedDAO;
|
private final FeedDAO feedDAO;
|
||||||
private final Set<AbstractFaviconFetcher> faviconFetchers;
|
private final Set<AbstractFaviconFetcher> faviconFetchers;
|
||||||
|
|
||||||
private Favicon defaultFavicon;
|
private final Favicon defaultFavicon;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FeedService(FeedDAO feedDAO, Set<AbstractFaviconFetcher> faviconFetchers) {
|
public FeedService(FeedDAO feedDAO, Set<AbstractFaviconFetcher> faviconFetchers) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.commafeed.backend.service;
|
package com.commafeed.backend.service;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -57,6 +58,13 @@ public class FeedSubscriptionService {
|
|||||||
throw new FeedSubscriptionException("Could not subscribe to a feed from this CommaFeed instance");
|
throw new FeedSubscriptionException("Could not subscribe to a feed from this CommaFeed instance");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Integer maxFeedsPerUser = config.getApplicationSettings().getMaxFeedsPerUser();
|
||||||
|
if (maxFeedsPerUser > 0 && feedSubscriptionDAO.count(user) >= maxFeedsPerUser) {
|
||||||
|
String message = String.format("You cannot subscribe to more feeds on this CommaFeed instance (max %s feeds per user)",
|
||||||
|
maxFeedsPerUser);
|
||||||
|
throw new FeedSubscriptionException(message);
|
||||||
|
}
|
||||||
|
|
||||||
Feed feed = feedService.findOrCreate(url);
|
Feed feed = feedService.findOrCreate(url);
|
||||||
|
|
||||||
// upgrade feed to https if it was using http
|
// upgrade feed to https if it was using http
|
||||||
@@ -100,6 +108,17 @@ public class FeedSubscriptionService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void refreshAllUpForRefresh(User user) {
|
||||||
|
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
|
||||||
|
for (FeedSubscription sub : subs) {
|
||||||
|
Date disabledUntil = sub.getFeed().getDisabledUntil();
|
||||||
|
if (disabledUntil == null || disabledUntil.before(new Date())) {
|
||||||
|
Feed feed = sub.getFeed();
|
||||||
|
feedRefreshEngine.refreshImmediately(feed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Map<Long, UnreadCount> getUnreadCount(User user) {
|
public Map<Long, UnreadCount> getUnreadCount(User user) {
|
||||||
return feedSubscriptionDAO.findAll(user).stream().collect(Collectors.toMap(FeedSubscription::getId, s -> getUnreadCount(user, s)));
|
return feedSubscriptionDAO.findAll(user).stream().collect(Collectors.toMap(FeedSubscription::getId, s -> getUnreadCount(user, s)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ public class MailService {
|
|||||||
|
|
||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
props.put("mail.smtp.auth", "true");
|
props.put("mail.smtp.auth", "true");
|
||||||
props.put("mail.smtp.starttls.enable", "" + settings.isSmtpTls());
|
props.put("mail.smtp.starttls.enable", String.valueOf(settings.isSmtpTls()));
|
||||||
props.put("mail.smtp.host", settings.getSmtpHost());
|
props.put("mail.smtp.host", settings.getSmtpHost());
|
||||||
props.put("mail.smtp.port", "" + settings.getSmtpPort());
|
props.put("mail.smtp.port", String.valueOf(settings.getSmtpPort()));
|
||||||
|
|
||||||
Session session = Session.getInstance(props, new Authenticator() {
|
Session session = Session.getInstance(props, new Authenticator() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import org.apache.http.client.methods.HttpPost;
|
|||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
import org.apache.http.util.EntityUtils;
|
import org.apache.http.util.EntityUtils;
|
||||||
import org.hibernate.SessionFactory;
|
|
||||||
|
|
||||||
import com.commafeed.CommaFeedConfiguration;
|
import com.commafeed.CommaFeedConfiguration;
|
||||||
import com.commafeed.backend.HttpGetter;
|
import com.commafeed.backend.HttpGetter;
|
||||||
@@ -40,7 +39,7 @@ public class PubSubService {
|
|||||||
|
|
||||||
private final CommaFeedConfiguration config;
|
private final CommaFeedConfiguration config;
|
||||||
private final FeedService feedService;
|
private final FeedService feedService;
|
||||||
private final SessionFactory sessionFactory;
|
private final UnitOfWork unitOfWork;
|
||||||
|
|
||||||
public void subscribe(Feed feed) {
|
public void subscribe(Feed feed) {
|
||||||
String hub = feed.getPushHub();
|
String hub = feed.getPushHub();
|
||||||
@@ -75,7 +74,7 @@ public class PubSubService {
|
|||||||
if (code == 400 && StringUtils.contains(message, pushpressError)) {
|
if (code == 400 && StringUtils.contains(message, pushpressError)) {
|
||||||
String[] tokens = message.split(" ");
|
String[] tokens = message.split(" ");
|
||||||
feed.setPushTopic(tokens[tokens.length - 1]);
|
feed.setPushTopic(tokens[tokens.length - 1]);
|
||||||
UnitOfWork.run(sessionFactory, () -> feedService.save(feed));
|
unitOfWork.run(() -> feedService.save(feed));
|
||||||
log.debug("handled pushpress subfeed {} : {}", topic, feed.getPushTopic());
|
log.debug("handled pushpress subfeed {} : {}", topic, feed.getPushTopic());
|
||||||
} else {
|
} else {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.commafeed.backend.service;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -123,7 +124,7 @@ public class UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void createDemoUser() {
|
public void createDemoUser() {
|
||||||
register(CommaFeedApplication.USERNAME_DEMO, "demo", "demo@commafeed.com", Arrays.asList(Role.USER), true);
|
register(CommaFeedApplication.USERNAME_DEMO, "demo", "demo@commafeed.com", Collections.singletonList(Role.USER), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregister(User user) {
|
public void unregister(User user) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import javax.inject.Singleton;
|
|||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
|
||||||
import com.commafeed.CommaFeedConfiguration;
|
import com.commafeed.CommaFeedConfiguration;
|
||||||
|
import com.commafeed.backend.dao.UnitOfWork;
|
||||||
import com.commafeed.backend.dao.UserDAO;
|
import com.commafeed.backend.dao.UserDAO;
|
||||||
import com.commafeed.backend.model.User;
|
import com.commafeed.backend.model.User;
|
||||||
import com.commafeed.backend.service.FeedSubscriptionService;
|
import com.commafeed.backend.service.FeedSubscriptionService;
|
||||||
@@ -20,27 +21,29 @@ public class PostLoginActivities {
|
|||||||
|
|
||||||
private final UserDAO userDAO;
|
private final UserDAO userDAO;
|
||||||
private final FeedSubscriptionService feedSubscriptionService;
|
private final FeedSubscriptionService feedSubscriptionService;
|
||||||
|
private final UnitOfWork unitOfWork;
|
||||||
private final CommaFeedConfiguration config;
|
private final CommaFeedConfiguration config;
|
||||||
|
|
||||||
public void executeFor(User user) {
|
public void executeFor(User user) {
|
||||||
Date lastLogin = user.getLastLogin();
|
// only update lastLogin every once in a while in order to avoid invalidating the cache every time someone logs in
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
|
Date lastLogin = user.getLastLogin();
|
||||||
boolean saveUser = false;
|
if (lastLogin == null || lastLogin.before(DateUtils.addMinutes(now, -30))) {
|
||||||
// only update lastLogin field every hour in order to not
|
|
||||||
// invalidate the cache every time someone logs in
|
|
||||||
if (lastLogin == null || lastLogin.before(DateUtils.addHours(now, -1))) {
|
|
||||||
user.setLastLogin(now);
|
user.setLastLogin(now);
|
||||||
saveUser = true;
|
|
||||||
}
|
boolean heavyLoad = Boolean.TRUE.equals(config.getApplicationSettings().getHeavyLoad());
|
||||||
if (config.getApplicationSettings().getHeavyLoad() && user.shouldRefreshFeedsAt(now)) {
|
if (heavyLoad) {
|
||||||
feedSubscriptionService.refreshAll(user);
|
// the amount of feeds in the database that are up for refresh might be very large since we're in heavy load mode
|
||||||
user.setLastFullRefresh(now);
|
// the feed refresh engine might not be able to catch up quickly enough
|
||||||
saveUser = true;
|
// put feeds from online users that are up for refresh at the top of the queue
|
||||||
}
|
feedSubscriptionService.refreshAllUpForRefresh(user);
|
||||||
if (saveUser) {
|
}
|
||||||
userDAO.saveOrUpdate(user);
|
|
||||||
|
// Post login activites are susceptible to run for any webservice call.
|
||||||
|
// We update the user in a new transaction to update the user immediately.
|
||||||
|
// If we didn't and the webservice call takes time, subsequent webservice calls would have to wait for the first call to
|
||||||
|
// finish even if they didn't use the same database tables, because they updated the user too.
|
||||||
|
unitOfWork.run(() -> userDAO.saveOrUpdate(user));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.hibernate.SessionFactory;
|
|
||||||
|
|
||||||
import com.commafeed.CommaFeedApplication;
|
import com.commafeed.CommaFeedApplication;
|
||||||
import com.commafeed.CommaFeedConfiguration;
|
import com.commafeed.CommaFeedConfiguration;
|
||||||
import com.commafeed.backend.dao.UnitOfWork;
|
import com.commafeed.backend.dao.UnitOfWork;
|
||||||
@@ -23,7 +21,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
public class DemoAccountCleanupTask extends ScheduledTask {
|
public class DemoAccountCleanupTask extends ScheduledTask {
|
||||||
|
|
||||||
private final CommaFeedConfiguration config;
|
private final CommaFeedConfiguration config;
|
||||||
private final SessionFactory sessionFactory;
|
private final UnitOfWork unitOfWork;
|
||||||
private final UserDAO userDAO;
|
private final UserDAO userDAO;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
|
||||||
@@ -34,7 +32,7 @@ public class DemoAccountCleanupTask extends ScheduledTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.info("recreating demo user account");
|
log.info("recreating demo user account");
|
||||||
UnitOfWork.run(sessionFactory, () -> {
|
unitOfWork.run(() -> {
|
||||||
User demoUser = userDAO.findByName(CommaFeedApplication.USERNAME_DEMO);
|
User demoUser = userDAO.findByName(CommaFeedApplication.USERNAME_DEMO);
|
||||||
if (demoUser == null) {
|
if (demoUser == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -14,8 +14,13 @@ import org.passay.PasswordValidator;
|
|||||||
import org.passay.RuleResult;
|
import org.passay.RuleResult;
|
||||||
import org.passay.WhitespaceRule;
|
import org.passay.WhitespaceRule;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
public class PasswordConstraintValidator implements ConstraintValidator<ValidPassword, String> {
|
public class PasswordConstraintValidator implements ConstraintValidator<ValidPassword, String> {
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private static boolean strict = true;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(ValidPassword constraintAnnotation) {
|
public void initialize(ValidPassword constraintAnnotation) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
@@ -27,7 +32,7 @@ public class PasswordConstraintValidator implements ConstraintValidator<ValidPas
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PasswordValidator validator = buildPasswordValidator();
|
PasswordValidator validator = strict ? buildStrictPasswordValidator() : buildLoosePasswordValidator();
|
||||||
RuleResult result = validator.validate(new PasswordData(value));
|
RuleResult result = validator.validate(new PasswordData(value));
|
||||||
|
|
||||||
if (result.isValid()) {
|
if (result.isValid()) {
|
||||||
@@ -40,10 +45,10 @@ public class PasswordConstraintValidator implements ConstraintValidator<ValidPas
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PasswordValidator buildPasswordValidator() {
|
private PasswordValidator buildStrictPasswordValidator() {
|
||||||
return new PasswordValidator(
|
return new PasswordValidator(
|
||||||
// length
|
// length
|
||||||
new LengthRule(8, 128),
|
new LengthRule(8, 256),
|
||||||
// 1 uppercase char
|
// 1 uppercase char
|
||||||
new CharacterRule(EnglishCharacterData.UpperCase, 1),
|
new CharacterRule(EnglishCharacterData.UpperCase, 1),
|
||||||
// 1 lowercase char
|
// 1 lowercase char
|
||||||
@@ -56,4 +61,12 @@ public class PasswordConstraintValidator implements ConstraintValidator<ValidPas
|
|||||||
new WhitespaceRule());
|
new WhitespaceRule());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PasswordValidator buildLoosePasswordValidator() {
|
||||||
|
return new PasswordValidator(
|
||||||
|
// length
|
||||||
|
new LengthRule(6, 256),
|
||||||
|
// no whitespace
|
||||||
|
new WhitespaceRule());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import lombok.RequiredArgsConstructor;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class SecurityCheckFactoryProvider extends AbstractValueParamProvider {
|
public class SecurityCheckFactoryProvider extends AbstractValueParamProvider {
|
||||||
|
|
||||||
private UserService userService;
|
private final UserService userService;
|
||||||
private HttpServletRequest request;
|
private final HttpServletRequest request;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SecurityCheckFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider, UserService userService,
|
public SecurityCheckFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider, UserService userService,
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ public class Category implements Serializable {
|
|||||||
@ApiModelProperty(value = "category feeds", required = true)
|
@ApiModelProperty(value = "category feeds", required = true)
|
||||||
private List<Subscription> feeds = new ArrayList<>();
|
private List<Subscription> feeds = new ArrayList<>();
|
||||||
|
|
||||||
@ApiModelProperty(value = "wether the category is expanded or collapsed", required = true)
|
@ApiModelProperty(value = "whether the category is expanded or collapsed", required = true)
|
||||||
private boolean expanded;
|
private boolean expanded;
|
||||||
|
|
||||||
@ApiModelProperty(value = "position of the category in the list", required = true)
|
@ApiModelProperty(value = "position of the category in the list", required = true)
|
||||||
private Integer position;
|
private int position;
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.commafeed.frontend.model;
|
package com.commafeed.frontend.model;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Arrays;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -45,7 +45,7 @@ public class Entry implements Serializable {
|
|||||||
@ApiModelProperty(value = "comma-separated list of categories")
|
@ApiModelProperty(value = "comma-separated list of categories")
|
||||||
private String categories;
|
private String categories;
|
||||||
|
|
||||||
@ApiModelProperty(value = "wether entry content and title are rtl", required = true)
|
@ApiModelProperty(value = "whether entry content and title are rtl", required = true)
|
||||||
private boolean rtl;
|
private boolean rtl;
|
||||||
|
|
||||||
@ApiModelProperty(value = "entry author")
|
@ApiModelProperty(value = "entry author")
|
||||||
@@ -99,7 +99,7 @@ public class Entry implements Serializable {
|
|||||||
@ApiModelProperty(value = "starred status", required = true)
|
@ApiModelProperty(value = "starred status", required = true)
|
||||||
private boolean starred;
|
private boolean starred;
|
||||||
|
|
||||||
@ApiModelProperty(value = "wether the entry is still markable (old entry statuses are discarded)", required = true)
|
@ApiModelProperty(value = "whether the entry is still markable (old entry statuses are discarded)", required = true)
|
||||||
private boolean markable;
|
private boolean markable;
|
||||||
|
|
||||||
@ApiModelProperty(value = "tags", required = true)
|
@ApiModelProperty(value = "tags", required = true)
|
||||||
@@ -158,13 +158,13 @@ public class Entry implements Serializable {
|
|||||||
|
|
||||||
SyndContentImpl content = new SyndContentImpl();
|
SyndContentImpl content = new SyndContentImpl();
|
||||||
content.setValue(getContent());
|
content.setValue(getContent());
|
||||||
entry.setContents(Arrays.<SyndContent> asList(content));
|
entry.setContents(Collections.<SyndContent> singletonList(content));
|
||||||
|
|
||||||
if (getEnclosureUrl() != null) {
|
if (getEnclosureUrl() != null) {
|
||||||
SyndEnclosureImpl enclosure = new SyndEnclosureImpl();
|
SyndEnclosureImpl enclosure = new SyndEnclosureImpl();
|
||||||
enclosure.setType(getEnclosureType());
|
enclosure.setType(getEnclosureType());
|
||||||
enclosure.setUrl(getEnclosureUrl());
|
enclosure.setUrl(getEnclosureUrl());
|
||||||
entry.setEnclosures(Arrays.<SyndEnclosure> asList(enclosure));
|
entry.setEnclosures(Collections.<SyndEnclosure> singletonList(enclosure));
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.setLink(getUrl());
|
entry.setLink(getUrl());
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public class Subscription implements Serializable {
|
|||||||
private String categoryId;
|
private String categoryId;
|
||||||
|
|
||||||
@ApiModelProperty("position of the subscription's in the list")
|
@ApiModelProperty("position of the subscription's in the list")
|
||||||
private Integer position;
|
private int position;
|
||||||
|
|
||||||
@ApiModelProperty(value = "date of the newest item", dataType = "number")
|
@ApiModelProperty(value = "date of the newest item", dataType = "number")
|
||||||
private Date newestItemTime;
|
private Date newestItemTime;
|
||||||
|
|||||||
@@ -104,9 +104,7 @@ public class CategoryREST {
|
|||||||
@ApiParam(value = "only entries newer than this") @QueryParam("newerThan") Long newerThan,
|
@ApiParam(value = "only entries newer than this") @QueryParam("newerThan") Long newerThan,
|
||||||
@ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset,
|
@ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset,
|
||||||
@ApiParam(value = "limit for paging, default 20, maximum 1000") @DefaultValue("20") @QueryParam("limit") int limit,
|
@ApiParam(value = "limit for paging, default 20, maximum 1000") @DefaultValue("20") @QueryParam("limit") int limit,
|
||||||
@ApiParam(
|
@ApiParam(value = "ordering", allowableValues = "asc,desc") @QueryParam("order") @DefaultValue("desc") ReadingOrder order,
|
||||||
value = "ordering",
|
|
||||||
allowableValues = "asc,desc,abc,zyx") @QueryParam("order") @DefaultValue("desc") ReadingOrder order,
|
|
||||||
@ApiParam(
|
@ApiParam(
|
||||||
value = "search for keywords in either the title or the content of the entries, separated by spaces, 3 characters minimum") @QueryParam("keywords") String keywords,
|
value = "search for keywords in either the title or the content of the entries, separated by spaces, 3 characters minimum") @QueryParam("keywords") String keywords,
|
||||||
@ApiParam(value = "return only entry ids") @DefaultValue("false") @QueryParam("onlyIds") boolean onlyIds,
|
@ApiParam(value = "return only entry ids") @DefaultValue("false") @QueryParam("onlyIds") boolean onlyIds,
|
||||||
@@ -460,7 +458,7 @@ public class CategoryREST {
|
|||||||
category.getChildren().add(child);
|
category.getChildren().add(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.sort(category.getChildren(), (o1, o2) -> ObjectUtils.compare(o1.getPosition(), o2.getPosition()));
|
category.getChildren().sort(Comparator.comparing(Category::getPosition).thenComparing(Category::getName));
|
||||||
|
|
||||||
for (FeedSubscription subscription : subscriptions) {
|
for (FeedSubscription subscription : subscriptions) {
|
||||||
if (id == null && subscription.getCategory() == null
|
if (id == null && subscription.getCategory() == null
|
||||||
@@ -470,7 +468,7 @@ public class CategoryREST {
|
|||||||
category.getFeeds().add(sub);
|
category.getFeeds().add(sub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.sort(category.getFeeds(), (o1, o2) -> ObjectUtils.compare(o1.getPosition(), o2.getPosition()));
|
category.getFeeds().sort(Comparator.comparing(Subscription::getPosition).thenComparing(Subscription::getName));
|
||||||
|
|
||||||
return category;
|
return category;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user