Compare commits

..

123 Commits
4.4.0 ... 4.4.1

Author SHA1 Message Date
Athou
cd06055246 release 4.4.1 2024-06-15 14:52:26 +02:00
Jérémie Panzer
62c1f25ffc Merge pull request #1448 from Athou/renovate/vite-5.x
Update dependency vite to ^5.3.1
2024-06-15 14:42:42 +02:00
Jérémie Panzer
415dc15d6c Merge pull request #1449 from Athou/renovate/mantine-monorepo
Update mantine monorepo to ^7.10.2
2024-06-15 14:41:52 +02:00
renovate[bot]
1d0c87c679 Update mantine monorepo to ^7.10.2 2024-06-15 12:32:58 +00:00
renovate[bot]
e51c486a04 Update dependency vite to ^5.3.1 2024-06-15 12:32:47 +00:00
Athou
73808c1a70 make renovate also bump versions in package.json 2024-06-15 14:32:05 +02:00
Athou
fbcc2ecd0f add a little delay to simulate a network operation 2024-06-15 14:00:15 +02:00
Athou
3997606774 looks like sometimes the websocket connection is not established on github actions, refresh the tree with an interval smaller than the timeout of playwright 2024-06-15 08:46:55 +02:00
Jérémie Panzer
b988b599d5 Merge pull request #1446 from Athou/renovate/org.apache.maven.plugins-maven-failsafe-plugin-3.x
Update dependency org.apache.maven.plugins:maven-failsafe-plugin to v3.3.0
2024-06-14 22:31:42 +02:00
Jérémie Panzer
3e2ff2959d Merge pull request #1447 from Athou/renovate/org.apache.maven.plugins-maven-surefire-plugin-3.x
Update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.3.0
2024-06-14 22:31:31 +02:00
renovate[bot]
5714a63d27 Update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.3.0 2024-06-14 19:55:02 +00:00
renovate[bot]
12b18d1e04 Update dependency org.apache.maven.plugins:maven-failsafe-plugin to v3.3.0 2024-06-14 19:54:59 +00:00
Athou
232141cb56 fix UnsupportedTemporalTypeException when tests fail 2024-06-14 21:50:50 +02:00
Jérémie Panzer
c4334e5e6e Merge pull request #1445 from Athou/renovate/vite-5.x-lockfile
Update dependency vite to v5.3.1
2024-06-14 13:00:43 +02:00
renovate[bot]
ddf78f880b Update dependency vite to v5.3.1 2024-06-14 09:59:39 +00:00
Athou
b3651f3fba upload playwright artifacts on test failure to help debug what went wrong 2024-06-14 07:59:26 +02:00
Jérémie Panzer
24943b868c Merge pull request #1442 from Athou/renovate/mantine-monorepo
Update mantine monorepo to v7.10.2
2024-06-13 23:51:58 +02:00
renovate[bot]
ef71a691ef Update mantine monorepo to v7.10.2 2024-06-13 21:47:30 +00:00
Jérémie Panzer
01593d94eb Merge pull request #1443 from Athou/renovate/vite-5.x-lockfile
Update dependency vite to v5.3.0
2024-06-13 23:45:28 +02:00
renovate[bot]
b793cc66d1 Update dependency vite to v5.3.0 2024-06-13 21:31:42 +00:00
Athou
3810dedf47 replace complex eslint config with biome 2024-06-13 23:28:45 +02:00
Athou
9115797dee try to fix renovatebot warning about not being able to update commafeed-client 2024-06-13 06:53:23 +02:00
Athou
232658b934 remove commons-io since we already have guava 2024-06-12 17:17:54 +02:00
Athou
f99fe57695 remove 32bit arm7 because its support was dropped from temurin 21 2024-06-12 16:40:08 +02:00
Athou
ec89d41112 update mvn wrapper 2024-06-12 16:36:57 +02:00
Jérémie Panzer
f6d26a77cc Merge pull request #1439 from Athou/renovate/eclipse-temurin-21.x
Update eclipse-temurin Docker tag to v21
2024-06-12 16:33:07 +02:00
renovate[bot]
860852cc12 Update eclipse-temurin Docker tag to v21 2024-06-12 14:28:49 +00:00
Athou
d06d76401c download maven-wrapper binaries if needed 2024-06-12 16:24:26 +02:00
Athou
f5b04a783e remove commons-codec since we already have guava 2024-06-12 16:18:52 +02:00
Athou
964e470951 manually bump dependencies left behind by dependabot 2024-06-12 15:45:58 +02:00
Jérémie Panzer
612f8722dd Merge pull request #1433 from Athou/renovate/prettier-3.x-lockfile
Update dependency prettier to v3.3.2
2024-06-12 15:33:22 +02:00
Jérémie Panzer
e118dc9b7f Merge pull request #1434 from Athou/renovate/eclipse-temurin-17.x
Update eclipse-temurin Docker tag to v17.0.11_9-jre
2024-06-12 15:32:49 +02:00
renovate[bot]
6e42cdaf2d Update eclipse-temurin Docker tag to v17.0.11_9-jre 2024-06-12 13:28:00 +00:00
renovate[bot]
5198792ca5 Update dependency prettier to v3.3.2 2024-06-12 13:27:56 +00:00
Athou
10a71213f3 use renovate instead of dependabot 2024-06-12 15:27:48 +02:00
Jérémie Panzer
a5d0979d9f Merge pull request #1432 from Athou/renovate/configure
Configure Renovate
2024-06-12 15:27:13 +02:00
renovate[bot]
d84225ab1c Add renovate.json 2024-06-12 13:23:16 +00:00
Athou
cd86947e64 keep pull to refresh for safari (#1168) 2024-06-12 13:12:33 +02:00
Athou
f6b3114a91 use react helmet to manipulate scripts and styles programatically 2024-06-12 13:03:19 +02:00
Athou
cd50b6b058 use new playwright locators 2024-06-12 11:39:51 +02:00
Athou
b0c7ef18db fix test race condition 2024-06-12 10:30:47 +02:00
Athou
24171faf86 fetchFeedInternal follows redirects, we don't need to call it twice (#1431) 2024-06-12 08:21:11 +02:00
Jérémie Panzer
941f14dd41 Merge pull request #1374 from Athou/dependabot/npm_and_yarn/commafeed-client/eslint-plugin-react-hooks-4.6.2
Bump eslint-plugin-react-hooks from 4.6.0 to 4.6.2 in /commafeed-client
2024-06-11 08:37:23 +02:00
Jérémie Panzer
d46ef787db Merge pull request #1404 from Athou/dependabot/npm_and_yarn/commafeed-client/vitest-1.6.0
Bump vitest from 1.5.0 to 1.6.0 in /commafeed-client
2024-06-11 08:37:10 +02:00
Jérémie Panzer
ec7447a38c Merge pull request #1411 from Athou/dependabot/npm_and_yarn/commafeed-client/react-router-dom-6.23.1
Bump react-router-dom from 6.22.3 to 6.23.1 in /commafeed-client
2024-06-11 08:33:22 +02:00
dependabot[bot]
2a3fc3ae15 Bump eslint-plugin-react-hooks from 4.6.0 to 4.6.2 in /commafeed-client
Bumps [eslint-plugin-react-hooks](https://github.com/facebook/react/tree/HEAD/packages/eslint-plugin-react-hooks) from 4.6.0 to 4.6.2.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/HEAD/packages/eslint-plugin-react-hooks)

---
updated-dependencies:
- dependency-name: eslint-plugin-react-hooks
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 06:31:59 +00:00
dependabot[bot]
ef25582bcb Bump react-router-dom from 6.22.3 to 6.23.1 in /commafeed-client
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.22.3 to 6.23.1.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.23.1/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 06:29:29 +00:00
Jérémie Panzer
55bbb2542d Merge pull request #1402 from Athou/dependabot/npm_and_yarn/commafeed-client/axios-1.7.2
Bump axios from 1.6.8 to 1.7.2 in /commafeed-client
2024-06-11 08:28:51 +02:00
Jérémie Panzer
8e94ac74a8 Merge pull request #1401 from Athou/dependabot/npm_and_yarn/commafeed-client/react-redux-9.1.2
Bump react-redux from 9.1.0 to 9.1.2 in /commafeed-client
2024-06-11 08:28:42 +02:00
Jérémie Panzer
90ecb9253c Merge pull request #1416 from Athou/dependabot/npm_and_yarn/commafeed-client/eslint-plugin-react-7.34.2
Bump eslint-plugin-react from 7.34.1 to 7.34.2 in /commafeed-client
2024-06-11 08:28:26 +02:00
Jérémie Panzer
6721842d98 Merge pull request #1414 from Athou/dependabot/npm_and_yarn/commafeed-client/lingui-240f3c17ef
Bump the lingui group across 1 directory with 5 updates
2024-06-11 08:28:15 +02:00
Jérémie Panzer
8b487ec414 Merge pull request #1428 from Athou/dependabot/npm_and_yarn/commafeed-client/typescript-eslint/eslint-plugin-7.13.0
Bump @typescript-eslint/eslint-plugin from 7.6.0 to 7.13.0 in /commafeed-client
2024-06-11 08:28:03 +02:00
dependabot[bot]
d6382861c3 Bump the lingui group across 1 directory with 5 updates
Bumps the lingui group with 5 updates in the /commafeed-client directory:

| Package | From | To |
| --- | --- | --- |
| [@lingui/core](https://github.com/lingui/js-lingui) | `4.10.0` | `4.11.1` |
| [@lingui/macro](https://github.com/lingui/js-lingui) | `4.10.0` | `4.11.1` |
| [@lingui/react](https://github.com/lingui/js-lingui) | `4.10.0` | `4.11.1` |
| [@lingui/cli](https://github.com/lingui/js-lingui) | `4.10.0` | `4.11.1` |
| [@lingui/vite-plugin](https://github.com/lingui/js-lingui) | `4.10.0` | `4.11.1` |



Updates `@lingui/core` from 4.10.0 to 4.11.1
- [Release notes](https://github.com/lingui/js-lingui/releases)
- [Changelog](https://github.com/lingui/js-lingui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lingui/js-lingui/compare/v4.10.0...v4.11.1)

Updates `@lingui/macro` from 4.10.0 to 4.11.1
- [Release notes](https://github.com/lingui/js-lingui/releases)
- [Changelog](https://github.com/lingui/js-lingui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lingui/js-lingui/compare/v4.10.0...v4.11.1)

Updates `@lingui/react` from 4.10.0 to 4.11.1
- [Release notes](https://github.com/lingui/js-lingui/releases)
- [Changelog](https://github.com/lingui/js-lingui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lingui/js-lingui/compare/v4.10.0...v4.11.1)

Updates `@lingui/cli` from 4.10.0 to 4.11.1
- [Release notes](https://github.com/lingui/js-lingui/releases)
- [Changelog](https://github.com/lingui/js-lingui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lingui/js-lingui/compare/v4.10.0...v4.11.1)

Updates `@lingui/vite-plugin` from 4.10.0 to 4.11.1
- [Release notes](https://github.com/lingui/js-lingui/releases)
- [Changelog](https://github.com/lingui/js-lingui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lingui/js-lingui/compare/v4.10.0...v4.11.1)

---
updated-dependencies:
- dependency-name: "@lingui/core"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: lingui
- dependency-name: "@lingui/macro"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: lingui
- dependency-name: "@lingui/react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: lingui
- dependency-name: "@lingui/cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: lingui
- dependency-name: "@lingui/vite-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: lingui
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:27:42 +00:00
dependabot[bot]
2cdea99a69 Bump axios from 1.6.8 to 1.7.2 in /commafeed-client
Bumps [axios](https://github.com/axios/axios) from 1.6.8 to 1.7.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.6.8...v1.7.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:26:35 +00:00
dependabot[bot]
b1ae1c8afd Bump react-redux from 9.1.0 to 9.1.2 in /commafeed-client
Bumps [react-redux](https://github.com/reduxjs/react-redux) from 9.1.0 to 9.1.2.
- [Release notes](https://github.com/reduxjs/react-redux/releases)
- [Changelog](https://github.com/reduxjs/react-redux/blob/master/CHANGELOG.md)
- [Commits](https://github.com/reduxjs/react-redux/compare/v9.1.0...v9.1.2)

---
updated-dependencies:
- dependency-name: react-redux
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:26:07 +00:00
Jérémie Panzer
c09cd0c717 Merge pull request #1379 from Athou/dependabot/npm_and_yarn/commafeed-client/dayjs-1.11.11
Bump dayjs from 1.11.10 to 1.11.11 in /commafeed-client
2024-06-11 07:26:00 +02:00
Jérémie Panzer
f50e0ae272 Merge pull request #1375 from Athou/dependabot/npm_and_yarn/commafeed-client/multi-ec30208de6
Bump react-dom and @types/react-dom in /commafeed-client
2024-06-11 07:25:51 +02:00
Jérémie Panzer
b99b91a2a8 Merge pull request #1384 from Athou/dependabot/npm_and_yarn/commafeed-client/tss-react-4.9.10
Bump tss-react from 4.9.6 to 4.9.10 in /commafeed-client
2024-06-11 07:25:28 +02:00
Jérémie Panzer
d9759de6f1 Merge pull request #1403 from Athou/dependabot/npm_and_yarn/commafeed-client/monaco-editor-0.49.0
Bump monaco-editor from 0.47.0 to 0.49.0 in /commafeed-client
2024-06-11 07:25:19 +02:00
dependabot[bot]
cf2b7f9e4f Bump eslint-plugin-react from 7.34.1 to 7.34.2 in /commafeed-client
Bumps [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) from 7.34.1 to 7.34.2.
- [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases)
- [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.34.1...v7.34.2)

---
updated-dependencies:
- dependency-name: eslint-plugin-react
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:25:14 +00:00
Jérémie Panzer
ee880c06ed Merge pull request #1407 from Athou/dependabot/npm_and_yarn/commafeed-client/react-icons-5.2.1
Bump react-icons from 5.0.1 to 5.2.1 in /commafeed-client
2024-06-11 07:25:10 +02:00
Jérémie Panzer
bc2e13ef22 Merge pull request #1410 from Athou/dependabot/npm_and_yarn/commafeed-client/reduxjs/toolkit-2.2.5
Bump @reduxjs/toolkit from 2.2.3 to 2.2.5 in /commafeed-client
2024-06-11 07:25:01 +02:00
dependabot[bot]
39ecfe2782 Bump @typescript-eslint/eslint-plugin in /commafeed-client
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 7.6.0 to 7.13.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.13.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:24:54 +00:00
Jérémie Panzer
3295d82f69 Merge pull request #1371 from Athou/dependabot/npm_and_yarn/commafeed-client/fontsource/open-sans-5.0.28
Bump @fontsource/open-sans from 5.0.27 to 5.0.28 in /commafeed-client
2024-06-11 07:24:42 +02:00
dependabot[bot]
1cd27a59e2 Bump vitest from 1.5.0 to 1.6.0 in /commafeed-client
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v1.6.0/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:24:35 +00:00
Jérémie Panzer
e1602edff1 Merge pull request #1426 from Athou/dependabot/npm_and_yarn/commafeed-client/redoc-2.1.5
Bump redoc from 2.1.3 to 2.1.5 in /commafeed-client
2024-06-11 07:24:03 +02:00
Jérémie Panzer
ef8e61d6fc Merge pull request #1427 from Athou/dependabot/npm_and_yarn/commafeed-client/prettier-3.3.1
Bump prettier from 3.2.5 to 3.3.1 in /commafeed-client
2024-06-11 07:23:54 +02:00
Jérémie Panzer
0057030442 Merge pull request #1429 from Athou/dependabot/npm_and_yarn/commafeed-client/vite-5.2.13
Bump vite from 5.2.8 to 5.2.13 in /commafeed-client
2024-06-11 07:23:33 +02:00
Jérémie Panzer
6fabe46d6e Merge pull request #1430 from Athou/dependabot/npm_and_yarn/commafeed-client/vitejs/plugin-react-4.3.1
Bump @vitejs/plugin-react from 4.2.1 to 4.3.1 in /commafeed-client
2024-06-11 07:23:21 +02:00
dependabot[bot]
37c58f2755 Bump monaco-editor from 0.47.0 to 0.49.0 in /commafeed-client
Bumps [monaco-editor](https://github.com/microsoft/monaco-editor) from 0.47.0 to 0.49.0.
- [Release notes](https://github.com/microsoft/monaco-editor/releases)
- [Changelog](https://github.com/microsoft/monaco-editor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/microsoft/monaco-editor/compare/v0.47.0...v0.49.0)

---
updated-dependencies:
- dependency-name: monaco-editor
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:18:41 +00:00
dependabot[bot]
bb982c3caf Bump @reduxjs/toolkit from 2.2.3 to 2.2.5 in /commafeed-client
Bumps [@reduxjs/toolkit](https://github.com/reduxjs/redux-toolkit) from 2.2.3 to 2.2.5.
- [Release notes](https://github.com/reduxjs/redux-toolkit/releases)
- [Commits](https://github.com/reduxjs/redux-toolkit/compare/v2.2.3...v2.2.5)

---
updated-dependencies:
- dependency-name: "@reduxjs/toolkit"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:18:39 +00:00
Jérémie Panzer
7e4c3737a8 Merge pull request #1415 from Athou/dependabot/npm_and_yarn/commafeed-client/mantine-1f5c67be2f
Bump the mantine group across 1 directory with 6 updates
2024-06-11 07:17:44 +02:00
dependabot[bot]
23596b5ac6 Bump @vitejs/plugin-react from 4.2.1 to 4.3.1 in /commafeed-client
Bumps [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) from 4.2.1 to 4.3.1.
- [Release notes](https://github.com/vitejs/vite-plugin-react/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-react/commits/v4.3.1/packages/plugin-react)

---
updated-dependencies:
- dependency-name: "@vitejs/plugin-react"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:12:52 +00:00
dependabot[bot]
2fdeb7acd8 Bump vite from 5.2.8 to 5.2.13 in /commafeed-client
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.2.8 to 5.2.13.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.2.13/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.2.13/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:12:29 +00:00
dependabot[bot]
c62cac478c Bump the mantine group across 1 directory with 6 updates
Bumps the mantine group with 6 updates in the /commafeed-client directory:

| Package | From | To |
| --- | --- | --- |
| [@mantine/core](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/core) | `7.8.0` | `7.10.1` |
| [@mantine/form](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/form) | `7.8.0` | `7.10.1` |
| [@mantine/hooks](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/hooks) | `7.8.0` | `7.10.1` |
| [@mantine/modals](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/modals) | `7.8.0` | `7.10.1` |
| [@mantine/notifications](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/notifications) | `7.8.0` | `7.10.1` |
| [@mantine/spotlight](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/spotlight) | `7.8.0` | `7.10.1` |



Updates `@mantine/core` from 7.8.0 to 7.10.1
- [Release notes](https://github.com/mantinedev/mantine/releases)
- [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mantinedev/mantine/commits/7.10.1/packages/@mantine/core)

Updates `@mantine/form` from 7.8.0 to 7.10.1
- [Release notes](https://github.com/mantinedev/mantine/releases)
- [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mantinedev/mantine/commits/7.10.1/packages/@mantine/form)

Updates `@mantine/hooks` from 7.8.0 to 7.10.1
- [Release notes](https://github.com/mantinedev/mantine/releases)
- [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mantinedev/mantine/commits/7.10.1/packages/@mantine/hooks)

Updates `@mantine/modals` from 7.8.0 to 7.10.1
- [Release notes](https://github.com/mantinedev/mantine/releases)
- [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mantinedev/mantine/commits/7.10.1/packages/@mantine/modals)

Updates `@mantine/notifications` from 7.8.0 to 7.10.1
- [Release notes](https://github.com/mantinedev/mantine/releases)
- [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mantinedev/mantine/commits/7.10.1/packages/@mantine/notifications)

Updates `@mantine/spotlight` from 7.8.0 to 7.10.1
- [Release notes](https://github.com/mantinedev/mantine/releases)
- [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mantinedev/mantine/commits/7.10.1/packages/@mantine/spotlight)

---
updated-dependencies:
- dependency-name: "@mantine/core"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: mantine
- dependency-name: "@mantine/form"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: mantine
- dependency-name: "@mantine/hooks"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: mantine
- dependency-name: "@mantine/modals"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: mantine
- dependency-name: "@mantine/notifications"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: mantine
- dependency-name: "@mantine/spotlight"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: mantine
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:12:05 +00:00
dependabot[bot]
e9026e0371 Bump prettier from 3.2.5 to 3.3.1 in /commafeed-client
Bumps [prettier](https://github.com/prettier/prettier) from 3.2.5 to 3.3.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.2.5...3.3.1)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:11:50 +00:00
dependabot[bot]
7446d906ae Bump redoc from 2.1.3 to 2.1.5 in /commafeed-client
Bumps [redoc](https://github.com/Redocly/redoc) from 2.1.3 to 2.1.5.
- [Release notes](https://github.com/Redocly/redoc/releases)
- [Changelog](https://github.com/Redocly/redoc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Redocly/redoc/compare/v2.1.3...v2.1.5)

---
updated-dependencies:
- dependency-name: redoc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:11:42 +00:00
Jérémie Panzer
62ad09ac93 Merge pull request #1358 from Athou/dependabot/maven/com.ibm.icu-icu4j-75.1
Bump com.ibm.icu:icu4j from 74.2 to 75.1
2024-06-11 07:10:33 +02:00
Jérémie Panzer
01d1f920a8 Merge pull request #1391 from Athou/dependabot/maven/com.microsoft.playwright-playwright-1.44.0
Bump com.microsoft.playwright:playwright from 1.43.0 to 1.44.0
2024-06-11 07:10:25 +02:00
Jérémie Panzer
057810470c Merge pull request #1392 from Athou/dependabot/maven/io.swagger.core.v3-swagger-annotations-2.2.22
Bump io.swagger.core.v3:swagger-annotations from 2.2.21 to 2.2.22
2024-06-11 07:10:18 +02:00
Jérémie Panzer
5a6d6be8e5 Merge pull request #1394 from Athou/dependabot/maven/com.google.apis-google-api-services-youtube-v3-rev20240514-2.0.0
Bump com.google.apis:google-api-services-youtube from v3-rev20240310-2.0.0 to v3-rev20240514-2.0.0
2024-06-11 07:10:09 +02:00
Jérémie Panzer
c6c813a4ee Merge pull request #1395 from Athou/dependabot/maven/io.swagger.core.v3-swagger-maven-plugin-jakarta-2.2.22
Bump io.swagger.core.v3:swagger-maven-plugin-jakarta from 2.2.21 to 2.2.22
2024-06-11 07:10:01 +02:00
Jérémie Panzer
ad5787a38b Merge pull request #1421 from Athou/dependabot/maven/io.github.hakky54-sslcontext-kickstart-for-apache5-8.3.6
Bump io.github.hakky54:sslcontext-kickstart-for-apache5 from 8.3.4 to 8.3.6
2024-06-11 07:09:53 +02:00
Jérémie Panzer
387ceabf30 Merge pull request #1418 from Athou/dependabot/maven/org.apache.maven.plugins-maven-shade-plugin-3.6.0
Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.2 to 3.6.0
2024-06-11 07:09:32 +02:00
Jérémie Panzer
ffe6962c36 Merge pull request #1423 from Athou/dependabot/maven/io.github.git-commit-id-git-commit-id-maven-plugin-9.0.0
Bump io.github.git-commit-id:git-commit-id-maven-plugin from 8.0.2 to 9.0.0
2024-06-11 07:09:24 +02:00
Jérémie Panzer
6d599fc77d Merge pull request #1422 from Athou/dependabot/maven/org.apache.maven.plugins-maven-checkstyle-plugin-3.4.0
Bump org.apache.maven.plugins:maven-checkstyle-plugin from 3.3.1 to 3.4.0
2024-06-11 07:09:01 +02:00
Jérémie Panzer
9fcff1342c Merge pull request #1425 from Athou/dependabot/npm_and_yarn/commafeed-client/braces-3.0.3
Bump braces from 3.0.2 to 3.0.3 in /commafeed-client
2024-06-11 07:08:01 +02:00
dependabot[bot]
f7dbc2e9aa Bump braces from 3.0.2 to 3.0.3 in /commafeed-client
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 05:03:52 +00:00
Athou
468f2e4c76 remove warnings 2024-06-11 07:02:38 +02:00
Athou
883c9c79aa replace source and target with the new release setting 2024-06-10 12:20:26 +02:00
Athou
f171d05088 querydsl is no longer maintained, use an active fork 2024-06-10 12:17:36 +02:00
dependabot[bot]
f85745fe40 Bump io.github.git-commit-id:git-commit-id-maven-plugin
Bumps [io.github.git-commit-id:git-commit-id-maven-plugin](https://github.com/git-commit-id/git-commit-id-maven-plugin) from 8.0.2 to 9.0.0.
- [Release notes](https://github.com/git-commit-id/git-commit-id-maven-plugin/releases)
- [Commits](https://github.com/git-commit-id/git-commit-id-maven-plugin/compare/v8.0.2...v9.0.0)

---
updated-dependencies:
- dependency-name: io.github.git-commit-id:git-commit-id-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-10 09:17:15 +00:00
Athou
5ad93bb3ba add more checkstyle rules 2024-06-10 07:40:35 +02:00
dependabot[bot]
d80ed9d4dd Bump org.apache.maven.plugins:maven-checkstyle-plugin
Bumps [org.apache.maven.plugins:maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.3.1 to 3.4.0.
- [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.3.1...maven-checkstyle-plugin-3.4.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-08 20:35:05 +00:00
dependabot[bot]
69b5f5418a Bump io.github.hakky54:sslcontext-kickstart-for-apache5
Bumps [io.github.hakky54:sslcontext-kickstart-for-apache5](https://github.com/Hakky54/sslcontext-kickstart) from 8.3.4 to 8.3.6.
- [Changelog](https://github.com/Hakky54/sslcontext-kickstart/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Hakky54/sslcontext-kickstart/compare/v8.3.4...v8.3.6)

---
updated-dependencies:
- dependency-name: io.github.hakky54:sslcontext-kickstart-for-apache5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-08 20:35:01 +00:00
dependabot[bot]
06aa37659c Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.2 to 3.6.0
Bumps [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.5.2 to 3.6.0.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.5.2...maven-shade-plugin-3.6.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-03 09:45:39 +00:00
Jérémie Panzer
d5c98de839 Merge pull request #1360 from Athou/dependabot/maven/org.apache.maven.plugins-maven-jar-plugin-3.4.1
Bump org.apache.maven.plugins:maven-jar-plugin from 3.4.0 to 3.4.1
2024-06-02 10:19:14 +02:00
Jérémie Panzer
920975059c Merge pull request #1393 from Athou/dependabot/maven/org.mariadb.jdbc-mariadb-java-client-3.4.0
Bump org.mariadb.jdbc:mariadb-java-client from 3.3.3 to 3.4.0
2024-06-02 10:18:07 +02:00
Jérémie Panzer
7c6e4c3356 Merge pull request #1397 from Athou/dependabot/maven/redis.clients-jedis-5.1.3
Bump redis.clients:jedis from 5.1.2 to 5.1.3
2024-06-02 10:17:46 +02:00
Jérémie Panzer
143971da5e Merge pull request #1385 from Athou/dependabot/maven/com.mysql-mysql-connector-j-8.4.0
Bump com.mysql:mysql-connector-j from 8.3.0 to 8.4.0
2024-06-02 10:17:32 +02:00
dependabot[bot]
8976e9c01a Bump react-icons from 5.0.1 to 5.2.1 in /commafeed-client
Bumps [react-icons](https://github.com/react-icons/react-icons) from 5.0.1 to 5.2.1.
- [Release notes](https://github.com/react-icons/react-icons/releases)
- [Commits](https://github.com/react-icons/react-icons/compare/v5.0.1...v5.2.1)

---
updated-dependencies:
- dependency-name: react-icons
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-27 07:10:59 +00:00
dependabot[bot]
20c6355efd Bump redis.clients:jedis from 5.1.2 to 5.1.3
Bumps [redis.clients:jedis](https://github.com/redis/jedis) from 5.1.2 to 5.1.3.
- [Release notes](https://github.com/redis/jedis/releases)
- [Commits](https://github.com/redis/jedis/compare/v5.1.2...v5.1.3)

---
updated-dependencies:
- dependency-name: redis.clients:jedis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-27 07:06:21 +00:00
Jérémie Panzer
f86f38ef7a Merge pull request #1396 from SConaway/patch-1
login page: don't autocapitalize the username input
2024-05-26 10:14:36 +02:00
Jérémie Panzer
24311df551 Merge pull request #1389 from luckrnx09/luckrnx09-cmd-k-for-macos-users
Maint: Show `Cmd + K` for macOS users
2024-05-26 10:14:10 +02:00
luckrnx09
d02aa78def fix: determine os type from useOs hook 2024-05-25 21:35:10 +08:00
Steven Conaway
b131020f46 login page: don't autocapitalize the username input 2024-05-22 10:37:32 -06:00
dependabot[bot]
4ab82782b0 Bump io.swagger.core.v3:swagger-maven-plugin-jakarta
Bumps io.swagger.core.v3:swagger-maven-plugin-jakarta from 2.2.21 to 2.2.22.

---
updated-dependencies:
- dependency-name: io.swagger.core.v3:swagger-maven-plugin-jakarta
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-20 09:56:18 +00:00
dependabot[bot]
6f9ebd5d78 Bump com.google.apis:google-api-services-youtube
Bumps com.google.apis:google-api-services-youtube from v3-rev20240310-2.0.0 to v3-rev20240514-2.0.0.

---
updated-dependencies:
- dependency-name: com.google.apis:google-api-services-youtube
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-20 09:56:15 +00:00
dependabot[bot]
7ebbf26369 Bump org.mariadb.jdbc:mariadb-java-client from 3.3.3 to 3.4.0
Bumps [org.mariadb.jdbc:mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.3.3 to 3.4.0.
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.3.3...3.4.0)

---
updated-dependencies:
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-20 09:55:10 +00:00
dependabot[bot]
dbc93f9928 Bump io.swagger.core.v3:swagger-annotations from 2.2.21 to 2.2.22
Bumps io.swagger.core.v3:swagger-annotations from 2.2.21 to 2.2.22.

---
updated-dependencies:
- dependency-name: io.swagger.core.v3:swagger-annotations
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-20 09:55:06 +00:00
dependabot[bot]
ad6ebd7e4d Bump com.microsoft.playwright:playwright from 1.43.0 to 1.44.0
Bumps [com.microsoft.playwright:playwright](https://github.com/microsoft/playwright-java) from 1.43.0 to 1.44.0.
- [Release notes](https://github.com/microsoft/playwright-java/releases)
- [Commits](https://github.com/microsoft/playwright-java/compare/v1.43.0...v1.44.0)

---
updated-dependencies:
- dependency-name: com.microsoft.playwright:playwright
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-20 09:55:04 +00:00
luckrnx09
ab86247c8c update the hot key to mod+k for call spotlight 2024-05-15 21:47:34 +08:00
luckrnx09
884516be28 Maint: Show Cmd + K for macOS users 2024-05-15 21:42:32 +08:00
dependabot[bot]
c236b1adda Bump com.mysql:mysql-connector-j from 8.3.0 to 8.4.0
Bumps [com.mysql:mysql-connector-j](https://github.com/mysql/mysql-connector-j) from 8.3.0 to 8.4.0.
- [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.x/CHANGES)
- [Commits](https://github.com/mysql/mysql-connector-j/compare/8.3.0...8.4.0)

---
updated-dependencies:
- dependency-name: com.mysql:mysql-connector-j
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 09:42:14 +00:00
dependabot[bot]
222117dafe Bump tss-react from 4.9.6 to 4.9.10 in /commafeed-client
Bumps [tss-react](https://github.com/garronej/tss-react) from 4.9.6 to 4.9.10.
- [Release notes](https://github.com/garronej/tss-react/releases)
- [Commits](https://github.com/garronej/tss-react/compare/v4.9.6...v4.9.10)

---
updated-dependencies:
- dependency-name: tss-react
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-01 09:22:50 +00:00
dependabot[bot]
38cd27df57 Bump dayjs from 1.11.10 to 1.11.11 in /commafeed-client
Bumps [dayjs](https://github.com/iamkun/dayjs) from 1.11.10 to 1.11.11.
- [Release notes](https://github.com/iamkun/dayjs/releases)
- [Changelog](https://github.com/iamkun/dayjs/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/iamkun/dayjs/compare/v1.11.10...v1.11.11)

---
updated-dependencies:
- dependency-name: dayjs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 15:58:08 +00:00
dependabot[bot]
5e07e74bb2 Bump react-dom and @types/react-dom in /commafeed-client
Bumps [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) and [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom). These dependencies needed to be updated together.

Updates `react-dom` from 18.2.0 to 18.3.1
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/HEAD/packages/react-dom)

Updates `@types/react-dom` from 18.2.25 to 18.3.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

---
updated-dependencies:
- dependency-name: react-dom
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: "@types/react-dom"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-26 19:04:55 +00:00
dependabot[bot]
fe779e361f Bump @fontsource/open-sans from 5.0.27 to 5.0.28 in /commafeed-client
Bumps [@fontsource/open-sans](https://github.com/fontsource/font-files/tree/HEAD/fonts/google/open-sans) from 5.0.27 to 5.0.28.
- [Changelog](https://github.com/fontsource/font-files/blob/main/fonts/google/open-sans/CHANGELOG.md)
- [Commits](https://github.com/fontsource/font-files/commits/HEAD/fonts/google/open-sans)

---
updated-dependencies:
- dependency-name: "@fontsource/open-sans"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-26 19:03:47 +00:00
dependabot[bot]
1a51799497 Bump org.apache.maven.plugins:maven-jar-plugin from 3.4.0 to 3.4.1
Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/apache/maven-jar-plugin/releases)
- [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.4.0...maven-jar-plugin-3.4.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-jar-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-22 09:47:58 +00:00
dependabot[bot]
6ea926cdb0 Bump com.ibm.icu:icu4j from 74.2 to 75.1
Bumps [com.ibm.icu:icu4j](https://github.com/unicode-org/icu) from 74.2 to 75.1.
- [Release notes](https://github.com/unicode-org/icu/releases)
- [Commits](https://github.com/unicode-org/icu/commits)

---
updated-dependencies:
- dependency-name: com.ibm.icu:icu4j
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-22 09:46:50 +00:00
Jérémie Panzer
439d61946a Merge pull request #1351 from WangLei1993/master
add Chinese translation for new entry
2024-04-16 10:20:52 +02:00
WangLei1993
426c8d7dfb add Chinese translation for new entry 2024-04-16 15:01:23 +08:00
Athou
f1b51e8342 set the default value for new users to the same value as the default value for existing users 2024-04-15 21:59:54 +02:00
142 changed files with 15597 additions and 17503 deletions

View File

@@ -1,24 +0,0 @@
version: 2
updates:
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 50
- package-ecosystem: "npm"
directory: "/commafeed-client"
schedule:
interval: "monthly"
open-pull-requests-limit: 50
groups:
mantine:
patterns:
- "@mantine/*"
lingui:
patterns:
- "@lingui/*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10

View File

@@ -40,6 +40,14 @@ jobs:
name: commafeed.jar name: commafeed.jar
path: commafeed-server/target/commafeed.jar path: commafeed-server/target/commafeed.jar
- name: Upload Playwright artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-artifacts
path: |
**/target/playwright-artifacts/
# Docker # Docker
- name: Login to Container Registry - name: Login to Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v3
@@ -54,7 +62,7 @@ jobs:
with: with:
context: . context: .
push: true push: true
platforms: linux/amd64,linux/arm/v7,linux/arm64/v8 platforms: linux/amd64,linux/arm64/v8
tags: | tags: |
athou/commafeed:latest athou/commafeed:latest
athou/commafeed:${{ github.ref_name }} athou/commafeed:${{ github.ref_name }}
@@ -65,7 +73,7 @@ jobs:
with: with:
context: . context: .
push: true push: true
platforms: linux/amd64,linux/arm/v7,linux/arm64/v8 platforms: linux/amd64,linux/arm64/v8
tags: athou/commafeed:master tags: athou/commafeed:master
# Create GitHub release after Docker image has been published # Create GitHub release after Docker image has been published

Binary file not shown.

View File

@@ -6,7 +6,7 @@
# "License"); you may not use this file except in compliance # "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at # with the License. You may obtain a copy of the License at
# #
# https://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, # Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an # software distributed under the License is distributed on an
@@ -14,5 +14,5 @@
# KIND, either express or implied. See the License for the # KIND, either express or implied. See the License for the
# specific language governing permissions and limitations # specific language governing permissions and limitations
# under the License. # under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip distributionType=only-script
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip

View File

@@ -1,5 +1,14 @@
# Changelog # Changelog
## [4.4.1]
- fix vertical scrolling issues with Safari (#1168)
- the default value for new users for the "star entry" button and the "open in new tab" button in the entry headers is
now "on desktop" instead of "always"
- the "keyboard shortcuts" help page now shows "Cmd" instead of "Ctrl" on macOS (#1389)
- remove a superfluous feed fetch when subscribing to a feed (#1431)
- the Docker image now uses Java 21
## [4.4.0] ## [4.4.0]
- add support for sharing using the browser native capabilities if available (#1255) - add support for sharing using the browser native capabilities if available (#1255)

View File

@@ -1,4 +1,4 @@
FROM eclipse-temurin:17-jre FROM eclipse-temurin:21.0.3_9-jre
EXPOSE 8082 EXPOSE 8082

View File

@@ -1,8 +0,0 @@
dist
node_modules
vite.config.ts
# compiled linguijs locales
# they no longer exist but we keep this to avoid issues with people still having those files on disk
src/locales/**/*.ts

View File

@@ -1,52 +0,0 @@
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
"eslint:recommended",
"standard",
"love",
"plugin:@typescript-eslint/strict-type-checked",
"plugin:@typescript-eslint/stylistic-type-checked",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:prettier/recommended",
],
settings: {
react: {
version: "detect",
},
},
overrides: [
{
env: {
node: true,
},
files: [".eslintrc.{js,cjs}"],
parserOptions: {
sourceType: "script",
},
},
],
parserOptions: {
project: true,
ecmaVersion: "latest",
sourceType: "module",
},
plugins: ["react"],
rules: {
"@typescript-eslint/consistent-type-assertions": ["error", { assertionStyle: "as" }],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-confusing-void-expression": ["error", { ignoreArrowShorthand: true }],
"@typescript-eslint/no-floating-promises": "off",
"@typescript-eslint/no-misused-promises": "off",
"@typescript-eslint/prefer-nullish-coalescing": ["error", { ignoreConditionalTests: true }],
"@typescript-eslint/restrict-template-expressions": ["error", { allowNumber: true }],
"@typescript-eslint/strict-boolean-expressions": "off",
"react/jsx-curly-brace-presence": ["error", "never"],
"react/no-unescaped-entities": "off",
"react/react-in-jsx-scope": "off",
"react-hooks/exhaustive-deps": "error",
},
}

View File

@@ -1,8 +0,0 @@
{
"printWidth": 140,
"semi": false,
"tabWidth": 4,
"arrowParens": "avoid",
"endOfLine": "auto",
"trailingComma": "es5"
}

View File

@@ -0,0 +1,19 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.1/schema.json",
"formatter": {
"indentStyle": "space",
"indentWidth": 4,
"lineEnding": "lf",
"lineWidth": 140
},
"javascript": {
"formatter": {
"trailingCommas": "es5",
"semicolons": "asNeeded",
"arrowParentheses": "asNeeded"
}
},
"files": {
"ignore": ["dist", "node_modules", "target", "target-ide"]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,83 +1,79 @@
{ {
"name": "commafeed-client", "name": "commafeed-client",
"private": true, "private": true,
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite --host", "dev": "vite --host",
"dev:typescript": "tsc --watch", "dev:typescript": "tsc --watch",
"build": "tsc && vite build", "build": "tsc && vite build",
"preview": "vite preview", "preview": "vite preview",
"test": "vitest", "test": "vitest",
"test:ci": "vitest run", "test:ci": "vitest run",
"eslint": "eslint --ext=.js,.jsx,.ts,.tsx src", "lint": "biome check ./src",
"i18n:extract": "lingui extract --clean" "lint:fix": "biome check --write ./src",
}, "i18n:extract": "lingui extract --clean"
"dependencies": { },
"@emotion/react": "^11.11.4", "dependencies": {
"@fontsource/open-sans": "^5.0.27", "@emotion/react": "^11.11.4",
"@lingui/core": "^4.10.0", "@fontsource/open-sans": "^5.0.28",
"@lingui/macro": "^4.10.0", "@lingui/core": "^4.11.1",
"@lingui/react": "^4.10.0", "@lingui/macro": "^4.11.1",
"@mantine/core": "^7.8.0", "@lingui/react": "^4.11.1",
"@mantine/form": "^7.8.0", "@mantine/core": "^7.10.2",
"@mantine/hooks": "^7.8.0", "@mantine/form": "^7.10.2",
"@mantine/modals": "^7.8.0", "@mantine/hooks": "^7.10.2",
"@mantine/notifications": "^7.8.0", "@mantine/modals": "^7.10.2",
"@mantine/spotlight": "^7.8.0", "@mantine/notifications": "^7.10.2",
"@monaco-editor/react": "^4.6.0", "@mantine/spotlight": "^7.10.2",
"@reduxjs/toolkit": "^2.2.3", "@monaco-editor/react": "^4.6.0",
"axios": "^1.6.8", "@reduxjs/toolkit": "^2.2.5",
"dayjs": "^1.11.10", "axios": "^1.7.2",
"escape-string-regexp": "^5.0.0", "dayjs": "^1.11.11",
"interweave": "^13.1.0", "escape-string-regexp": "^5.0.0",
"monaco-editor": "^0.47.0", "interweave": "^13.1.0",
"mousetrap": "^1.6.5", "monaco-editor": "^0.49.0",
"react": "^18.2.0", "mousetrap": "^1.6.5",
"react-async-hook": "^4.0.0", "react": "^18.3.1",
"react-contexify": "^6.0.0", "react-async-hook": "^4.0.0",
"react-dom": "^18.2.0", "react-contexify": "^6.0.0",
"react-draggable": "^4.4.6", "react-device-detect": "^2.2.3",
"react-ga4": "^2.1.0", "react-dom": "^18.3.1",
"react-icons": "^5.0.1", "react-draggable": "^4.4.6",
"react-infinite-scroller": "^1.2.6", "react-ga4": "^2.1.0",
"react-redux": "^9.1.0", "react-helmet": "^6.1.0",
"react-router-dom": "^6.22.3", "react-icons": "^5.2.1",
"react-swipeable": "^7.0.1", "react-infinite-scroller": "^1.2.6",
"redoc": "^2.1.3", "react-redux": "^9.1.2",
"throttle-debounce": "^5.0.0", "react-router-dom": "^6.23.1",
"tinycon": "^0.6.8", "react-swipeable": "^7.0.1",
"tss-react": "^4.9.6", "redoc": "^2.1.5",
"use-local-storage": "^3.0.0", "throttle-debounce": "^5.0.0",
"websocket-heartbeat-js": "^1.1.3" "tinycon": "^0.6.8",
}, "tss-react": "^4.9.10",
"devDependencies": { "use-local-storage": "^3.0.0",
"@lingui/cli": "^4.10.0", "vite-plugin-biome": "^1.0.10",
"@lingui/vite-plugin": "^4.10.0", "websocket-heartbeat-js": "^1.1.3"
"@types/mousetrap": "^1.6.15", },
"@types/react": "^18.2.78", "devDependencies": {
"@types/react-dom": "^18.2.25", "@biomejs/biome": "^1.8.1",
"@types/react-infinite-scroller": "^1.2.5", "@lingui/cli": "^4.11.1",
"@types/swagger-ui-react": "^4.18.3", "@lingui/vite-plugin": "^4.11.1",
"@types/throttle-debounce": "^5.0.2", "@types/mousetrap": "^1.6.15",
"@types/tinycon": "^0.6.5", "@types/react": "^18.3.3",
"@typescript-eslint/eslint-plugin": "^7.6.0", "@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.2.1", "@types/react-helmet": "^6.1.11",
"babel-plugin-macros": "^3.1.0", "@types/react-infinite-scroller": "^1.2.5",
"eslint": "^8.57.0", "@types/swagger-ui-react": "^4.18.3",
"eslint-config-love": "^47.0.0", "@types/throttle-debounce": "^5.0.2",
"eslint-config-prettier": "^9.1.0", "@types/tinycon": "^0.6.5",
"eslint-config-standard": "^17.1.0", "@vitejs/plugin-react": "^4.3.1",
"eslint-plugin-prettier": "^5.1.3", "babel-plugin-macros": "^3.1.0",
"eslint-plugin-react": "^7.34.1", "rollup-plugin-visualizer": "^5.12.0",
"eslint-plugin-react-hooks": "^4.6.0", "typescript": "^5.4.5",
"prettier": "^3.2.5", "vite": "^5.3.1",
"rollup-plugin-visualizer": "^5.12.0", "vite-tsconfig-paths": "^4.3.2",
"typescript": "^5.4.5", "vitest": "^1.6.0",
"vite": "^5.2.8", "vitest-mock-extended": "^1.3.1"
"vite-plugin-eslint": "^1.8.1", }
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.5.0",
"vitest-mock-extended": "^1.3.1"
}
} }

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.commafeed</groupId> <groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId> <artifactId>commafeed</artifactId>
<version>4.4.0</version> <version>4.4.1</version>
</parent> </parent>
<artifactId>commafeed-client</artifactId> <artifactId>commafeed-client</artifactId>
<name>CommaFeed Client</name> <name>CommaFeed Client</name>

View File

@@ -1,7 +1,6 @@
import { i18n } from "@lingui/core" import { i18n } from "@lingui/core"
import { I18nProvider } from "@lingui/react" import { I18nProvider } from "@lingui/react"
import { MantineProvider } from "@mantine/core" import { MantineProvider } from "@mantine/core"
import { useDidUpdate } from "@mantine/hooks"
import { ModalsProvider } from "@mantine/modals" import { ModalsProvider } from "@mantine/modals"
import { Notifications } from "@mantine/notifications" import { Notifications } from "@mantine/notifications"
import { Constants } from "app/constants" import { Constants } from "app/constants"
@@ -9,11 +8,13 @@ import { redirectTo } from "app/redirect/slice"
import { reloadServerInfos } from "app/server/thunks" import { reloadServerInfos } from "app/server/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { categoryUnreadCount } from "app/utils" import { categoryUnreadCount } from "app/utils"
import { DisablePullToRefresh } from "components/DisablePullToRefresh"
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 { useBrowserExtension } from "hooks/useBrowserExtension"
import { useI18n } from "i18n" import { useI18n } from "i18n"
import { WelcomePage } from "pages/WelcomePage"
import { AdminUsersPage } from "pages/admin/AdminUsersPage" import { AdminUsersPage } from "pages/admin/AdminUsersPage"
import { MetricsPage } from "pages/admin/MetricsPage" import { MetricsPage } from "pages/admin/MetricsPage"
import { AboutPage } from "pages/app/AboutPage" import { AboutPage } from "pages/app/AboutPage"
@@ -28,9 +29,10 @@ import { TagDetailsPage } from "pages/app/TagDetailsPage"
import { LoginPage } from "pages/auth/LoginPage" import { LoginPage } from "pages/auth/LoginPage"
import { PasswordRecoveryPage } from "pages/auth/PasswordRecoveryPage" import { PasswordRecoveryPage } from "pages/auth/PasswordRecoveryPage"
import { RegistrationPage } from "pages/auth/RegistrationPage" import { RegistrationPage } from "pages/auth/RegistrationPage"
import { WelcomePage } from "pages/WelcomePage" import React, { useEffect } from "react"
import React, { useEffect, useRef } from "react" import { isSafari } from "react-device-detect"
import ReactGA from "react-ga4" import ReactGA from "react-ga4"
import { Helmet } from "react-helmet"
import { HashRouter, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom" import { HashRouter, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom"
import Tinycon from "tinycon" import Tinycon from "tinycon"
@@ -166,38 +168,13 @@ function BrowserExtensionBadgeUnreadCountHandler() {
return null return null
} }
function CustomJs() { function CustomCode() {
const scriptLoaded = useRef(false) return (
<Helmet>
// useDidUpdate is used instead of useEffect because we want to skip the first render <link rel="stylesheet" type="text/css" href="custom_css.css" />
// the first render is the render of react-router, the routes are actually loaded in a second render <script type="text/javascript" src="custom_js.js" />
// we want the script to be executed when the first route is done loading </Helmet>
useDidUpdate(() => { )
if (scriptLoaded.current) {
return
}
const script = document.createElement("script")
script.src = "custom_js.js"
script.async = true
document.body.appendChild(script)
scriptLoaded.current = true
})
return null
}
function CustomCss() {
useEffect(() => {
const link = document.createElement("link")
link.rel = "stylesheet"
link.type = "text/css"
link.href = "custom_css.css"
document.head.appendChild(link)
}, [])
return null
} }
export function App() { export function App() {
@@ -217,8 +194,12 @@ export function App() {
<GoogleAnalyticsHandler /> <GoogleAnalyticsHandler />
<RedirectHandler /> <RedirectHandler />
<AppRoutes /> <AppRoutes />
<CustomJs /> <CustomCode />
<CustomCss /> {/* disable pull-to-refresh as it messes with vertical scrolling
safari behaves weirdly when overscroll-behavior is set to none so we disable it only for other browsers
https://github.com/Athou/commafeed/issues/1168
*/}
{!isSafari && <DisablePullToRefresh />}
</HashRouter> </HashRouter>
</> </>
</Providers> </Providers>

View File

@@ -1,5 +1,5 @@
import { createAsyncThunk } from "@reduxjs/toolkit" import { createAsyncThunk } from "@reduxjs/toolkit"
import { type AppDispatch, type RootState } from "app/store" import type { AppDispatch, RootState } from "app/store"
export const createAppAsyncThunk = createAsyncThunk.withTypes<{ export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: RootState state: RootState

View File

@@ -1,31 +1,31 @@
import axios, { type AxiosError } from "axios" import axios, { type AxiosError } from "axios"
import { import type {
type AddCategoryRequest, AddCategoryRequest,
type AdminSaveUserRequest, AdminSaveUserRequest,
type AuthenticationError, AuthenticationError,
type Category, Category,
type CategoryModificationRequest, CategoryModificationRequest,
type CollapseRequest, CollapseRequest,
type Entries, Entries,
type FeedInfo, FeedInfo,
type FeedInfoRequest, FeedInfoRequest,
type FeedModificationRequest, FeedModificationRequest,
type GetEntriesPaginatedRequest, GetEntriesPaginatedRequest,
type IDRequest, IDRequest,
type LoginRequest, LoginRequest,
type MarkRequest, MarkRequest,
type Metrics, Metrics,
type MultipleMarkRequest, MultipleMarkRequest,
type PasswordResetRequest, PasswordResetRequest,
type ProfileModificationRequest, ProfileModificationRequest,
type RegistrationRequest, RegistrationRequest,
type ServerInfo, ServerInfo,
type Settings, Settings,
type StarRequest, StarRequest,
type SubscribeRequest, SubscribeRequest,
type Subscription, Subscription,
type TagRequest, TagRequest,
type UserModel, UserModel,
} from "./types" } from "./types"
const axiosInstance = axios.create({ baseURL: "./rest", withCredentials: true }) const axiosInstance = axios.create({ baseURL: "./rest", withCredentials: true })

View File

@@ -1,8 +1,8 @@
import { t } from "@lingui/macro" import { t } from "@lingui/macro"
import { type IconType } from "react-icons" import type { IconType } from "react-icons"
import { FaAt } from "react-icons/fa" import { FaAt } from "react-icons/fa"
import { SiBuffer, SiFacebook, SiGmail, SiInstapaper, SiPocket, SiTumblr, SiTwitter } from "react-icons/si" import { SiBuffer, SiFacebook, SiGmail, SiInstapaper, SiPocket, SiTumblr, SiTwitter } from "react-icons/si"
import { type Category, type Entry, type SharingSettings } from "./types" import type { Category, Entry, SharingSettings } from "./types"
const categories: Record<string, Category> = { const categories: Record<string, Category> = {
all: { all: {

View File

@@ -1,9 +1,9 @@
import { configureStore } from "@reduxjs/toolkit" import { configureStore } from "@reduxjs/toolkit"
import { type client } from "app/client" import type { client } from "app/client"
import { loadEntries, loadMoreEntries, markAllEntries, markEntry } from "app/entries/thunks" import { loadEntries, loadMoreEntries, markAllEntries, markEntry } from "app/entries/thunks"
import { reducers, type RootState } from "app/store" import { type RootState, reducers } from "app/store"
import { type Entries, type Entry } from "app/types" import type { Entries, Entry } from "app/types"
import { type AxiosResponse } from "axios" import type { AxiosResponse } from "axios"
import { beforeEach, describe, expect, it, vi } from "vitest" import { beforeEach, describe, expect, it, vi } from "vitest"
import { mockReset } from "vitest-mock-extended" import { mockReset } from "vitest-mock-extended"

View File

@@ -1,7 +1,7 @@
import { createSlice, type PayloadAction } from "@reduxjs/toolkit" import { type PayloadAction, createSlice } from "@reduxjs/toolkit"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { loadEntries, loadMoreEntries, markAllEntries, markEntry, markMultipleEntries, starEntry, tagEntry } from "app/entries/thunks" import { loadEntries, loadMoreEntries, markAllEntries, markEntry, markMultipleEntries, starEntry, tagEntry } from "app/entries/thunks"
import { type Entry } from "app/types" import type { Entry } from "app/types"
export type EntrySourceType = "category" | "feed" | "tag" export type EntrySourceType = "category" | "feed" | "tag"
@@ -51,11 +51,9 @@ export const entriesSlice = createSlice({
state.selectedEntryId = action.payload.id state.selectedEntryId = action.payload.id
}, },
setEntryExpanded: (state, action: PayloadAction<{ entry: Entry; expanded: boolean }>) => { setEntryExpanded: (state, action: PayloadAction<{ entry: Entry; expanded: boolean }>) => {
state.entries for (const e of state.entries.filter(e => e.id === action.payload.entry.id)) {
.filter(e => e.id === action.payload.entry.id) e.expanded = action.payload.expanded
.forEach(e => { }
e.expanded = action.payload.expanded
})
}, },
setScrollingToEntry: (state, action: PayloadAction<boolean>) => { setScrollingToEntry: (state, action: PayloadAction<boolean>) => {
state.scrollingToEntry = action.payload state.scrollingToEntry = action.payload
@@ -66,32 +64,24 @@ export const entriesSlice = createSlice({
}, },
extraReducers: builder => { extraReducers: builder => {
builder.addCase(markEntry.pending, (state, action) => { builder.addCase(markEntry.pending, (state, action) => {
state.entries for (const e of state.entries.filter(e => e.id === action.meta.arg.entry.id)) {
.filter(e => e.id === action.meta.arg.entry.id) e.read = action.meta.arg.read
.forEach(e => { }
e.read = action.meta.arg.read
})
}) })
builder.addCase(markMultipleEntries.pending, (state, action) => { builder.addCase(markMultipleEntries.pending, (state, action) => {
state.entries for (const e of state.entries.filter(e => action.meta.arg.entries.some(e2 => e2.id === e.id))) {
.filter(e => action.meta.arg.entries.some(e2 => e2.id === e.id)) e.read = action.meta.arg.read
.forEach(e => { }
e.read = action.meta.arg.read
})
}) })
builder.addCase(markAllEntries.pending, (state, action) => { builder.addCase(markAllEntries.pending, (state, action) => {
state.entries for (const e of state.entries.filter(e => (action.meta.arg.req.olderThan ? e.date < action.meta.arg.req.olderThan : true))) {
.filter(e => (action.meta.arg.req.olderThan ? e.date < action.meta.arg.req.olderThan : true)) e.read = true
.forEach(e => { }
e.read = true
})
}) })
builder.addCase(starEntry.pending, (state, action) => { builder.addCase(starEntry.pending, (state, action) => {
state.entries for (const e of state.entries.filter(e => action.meta.arg.entry.id === e.id && action.meta.arg.entry.feedId === e.feedId)) {
.filter(e => action.meta.arg.entry.id === e.id && action.meta.arg.entry.feedId === e.feedId) e.starred = action.meta.arg.starred
.forEach(e => { }
e.starred = action.meta.arg.starred
})
}) })
builder.addCase(loadEntries.pending, (state, action) => { builder.addCase(loadEntries.pending, (state, action) => {
state.source = action.meta.arg.source state.source = action.meta.arg.source
@@ -122,11 +112,9 @@ export const entriesSlice = createSlice({
state.loading = false state.loading = false
}) })
builder.addCase(tagEntry.pending, (state, action) => { builder.addCase(tagEntry.pending, (state, action) => {
state.entries for (const e of state.entries.filter(e => +e.id === action.meta.arg.entryId)) {
.filter(e => +e.id === action.meta.arg.entryId) e.tags = action.meta.arg.tags
.forEach(e => { }
e.tags = action.meta.arg.tags
})
}) })
}, },
}) })

View File

@@ -1,7 +1,7 @@
import { createAppAsyncThunk } from "app/async-thunk" import { createAppAsyncThunk } from "app/async-thunk"
import { client } from "app/client" import { client } from "app/client"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { entriesSlice, type EntrySource, type EntrySourceType, setSearch } from "app/entries/slice" import { type EntrySource, type EntrySourceType, entriesSlice, setSearch } from "app/entries/slice"
import type { RootState } from "app/store" import type { RootState } from "app/store"
import { reloadTree } from "app/tree/thunks" import { reloadTree } from "app/tree/thunks"
import type { Entry, MarkRequest, TagRequest } from "app/types" import type { Entry, MarkRequest, TagRequest } from "app/types"

View File

@@ -1,4 +1,4 @@
import { createSlice, type PayloadAction } from "@reduxjs/toolkit" import { type PayloadAction, createSlice } from "@reduxjs/toolkit"
interface RedirectState { interface RedirectState {
to?: string to?: string

View File

@@ -1,6 +1,6 @@
import { createSlice, type PayloadAction } from "@reduxjs/toolkit" import { type PayloadAction, createSlice } from "@reduxjs/toolkit"
import { reloadServerInfos } from "app/server/thunks" import { reloadServerInfos } from "app/server/thunks"
import { type ServerInfo } from "app/types" import type { ServerInfo } from "app/types"
interface ServerState { interface ServerState {
serverInfos?: ServerInfo serverInfos?: ServerInfo

View File

@@ -1,8 +1,8 @@
import { createSlice, type PayloadAction } from "@reduxjs/toolkit" import { type PayloadAction, createSlice } from "@reduxjs/toolkit"
import { markEntry } from "app/entries/thunks" import { markEntry } from "app/entries/thunks"
import { redirectTo } from "app/redirect/slice" import { redirectTo } from "app/redirect/slice"
import { collapseTreeCategory, reloadTree } from "app/tree/thunks" import { collapseTreeCategory, reloadTree } from "app/tree/thunks"
import { type Category } from "app/types" import type { Category } from "app/types"
import { visitCategoryTree } from "app/utils" import { visitCategoryTree } from "app/utils"
interface TreeState { interface TreeState {
@@ -34,13 +34,11 @@ export const treeSlice = createSlice({
}> }>
) => { ) => {
if (!state.rootCategory) return if (!state.rootCategory) return
visitCategoryTree(state.rootCategory, c => visitCategoryTree(state.rootCategory, c => {
c.feeds for (const f of c.feeds.filter(f => f.id === action.payload.feedId)) {
.filter(f => f.id === action.payload.feedId) f.unread += action.payload.amount
.forEach(f => { }
f.unread += action.payload.amount })
})
)
}, },
}, },
extraReducers: builder => { extraReducers: builder => {
@@ -55,13 +53,11 @@ export const treeSlice = createSlice({
}) })
builder.addCase(markEntry.pending, (state, action) => { builder.addCase(markEntry.pending, (state, action) => {
if (!state.rootCategory) return if (!state.rootCategory) return
visitCategoryTree(state.rootCategory, c => visitCategoryTree(state.rootCategory, c => {
c.feeds for (const f of c.feeds.filter(f => f.id === +action.meta.arg.entry.feedId)) {
.filter(f => f.id === +action.meta.arg.entry.feedId) f.unread = action.meta.arg.read ? f.unread - 1 : f.unread + 1
.forEach(f => { }
f.unread = action.meta.arg.read ? f.unread - 1 : f.unread + 1 })
})
)
}) })
builder.addCase(redirectTo, state => { builder.addCase(redirectTo, state => {
state.mobileMenuOpen = false state.mobileMenuOpen = false

View File

@@ -1,7 +1,7 @@
import { t } from "@lingui/macro" import { t } from "@lingui/macro"
import { showNotification } from "@mantine/notifications" import { showNotification } from "@mantine/notifications"
import { createSlice, isAnyOf } from "@reduxjs/toolkit" import { createSlice, isAnyOf } from "@reduxjs/toolkit"
import { type Settings, type UserModel } from "app/types" import type { Settings, UserModel } from "app/types"
import { import {
changeCustomContextMenu, changeCustomContextMenu,
changeExternalLinkIconDisplayMode, changeExternalLinkIconDisplayMode,

View File

@@ -1,9 +1,11 @@
import { throttle } from "throttle-debounce" import { throttle } from "throttle-debounce"
import { type Category } from "./types" import type { Category } from "./types"
export function visitCategoryTree(category: Category, visitor: (category: Category) => void): void { export function visitCategoryTree(category: Category, visitor: (category: Category) => void): void {
visitor(category) visitor(category)
category.children.forEach(child => visitCategoryTree(child, visitor)) for (const child of category.children) {
visitCategoryTree(child, visitor)
}
} }
export function flattenCategoryTree(category: Category): Category[] { export function flattenCategoryTree(category: Category): Category[] {

View File

@@ -1,8 +1,8 @@
import { ActionIcon, Button, type ButtonVariant, Tooltip, useMantineTheme } from "@mantine/core" import { ActionIcon, Button, type ButtonVariant, Tooltip, useMantineTheme } from "@mantine/core"
import { type ActionIconVariant } from "@mantine/core/lib/components/ActionIcon/ActionIcon" import type { ActionIconVariant } from "@mantine/core/lib/components/ActionIcon/ActionIcon"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { useActionButton } from "hooks/useActionButton" import { useActionButton } from "hooks/useActionButton"
import { forwardRef, type MouseEventHandler, type ReactNode } from "react" import { type MouseEventHandler, type ReactNode, forwardRef } from "react"
interface ActionButtonProps { interface ActionButtonProps {
className?: string className?: string

View File

@@ -1,5 +1,5 @@
import { Trans } from "@lingui/macro" import { Trans } from "@lingui/macro"
import { Alert as MantineAlert, Box } from "@mantine/core" import { Box, Alert as MantineAlert } from "@mantine/core"
import { Fragment } from "react" import { Fragment } from "react"
import { TbAlertCircle, TbAlertTriangle, TbCircleCheck } from "react-icons/tb" import { TbAlertCircle, TbAlertTriangle, TbCircleCheck } from "react-icons/tb"

View File

@@ -0,0 +1,15 @@
import { Helmet } from "react-helmet"
export const DisablePullToRefresh = () => {
return (
<Helmet>
<style type="text/css">
{`
html, body {
overscroll-behavior: none;
}
`}
</style>
</Helmet>
)
}

View File

@@ -1,8 +1,10 @@
import { Trans } from "@lingui/macro" import { Trans } from "@lingui/macro"
import { Anchor, Box, Kbd, Stack, Table } from "@mantine/core" import { Anchor, Box, Kbd, Stack, Table } from "@mantine/core"
import { useOs } from "@mantine/hooks"
import { Constants } from "app/constants" import { Constants } from "app/constants"
export function KeyboardShortcutsHelp() { export function KeyboardShortcutsHelp() {
const isMacOS = useOs() === "macos"
return ( return (
<Stack gap="xs"> <Stack gap="xs">
<Table striped highlightOnHover> <Table striped highlightOnHover>
@@ -149,7 +151,7 @@ export function KeyboardShortcutsHelp() {
</Table.Td> </Table.Td>
<Table.Td> <Table.Td>
<Kbd> <Kbd>
<Trans>Ctrl</Trans> <Trans>{isMacOS ? "Cmd" : "Ctrl"}</Trans>
</Kbd> </Kbd>
<span> + </span> <span> + </span>
<Kbd>K</Kbd> <Kbd>K</Kbd>

View File

@@ -2,7 +2,7 @@ import { Trans } from "@lingui/macro"
import { Box, Button, Checkbox, Group, PasswordInput, Stack, TextInput } from "@mantine/core" import { Box, Button, Checkbox, Group, PasswordInput, Stack, TextInput } from "@mantine/core"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client" import { client, errorToStrings } from "app/client"
import { type AdminSaveUserRequest, type UserModel } from "app/types" import type { AdminSaveUserRequest, UserModel } from "app/types"
import { Alert } from "components/Alert" import { Alert } from "components/Alert"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"
import { TbDeviceFloppy } from "react-icons/tb" import { TbDeviceFloppy } from "react-icons/tb"

View File

@@ -1,7 +1,7 @@
import { Input, Textarea } from "@mantine/core" import { Input, Textarea } from "@mantine/core"
import RichCodeEditor from "components/code/RichCodeEditor" import RichCodeEditor from "components/code/RichCodeEditor"
import { useMobile } from "hooks/useMobile" import { useMobile } from "hooks/useMobile"
import { type ReactNode } from "react" import type { ReactNode } from "react"
interface CodeEditorProps { interface CodeEditorProps {
description?: ReactNode description?: ReactNode

View File

@@ -5,7 +5,7 @@ import { useAsync } from "react-async-hook"
const init = async () => { const init = async () => {
window.MonacoEnvironment = { window.MonacoEnvironment = {
async getWorker(_, label) { async getWorker(_, label) {
let worker let worker: typeof import("*?worker")
if (label === "css") { if (label === "css") {
worker = await import("monaco-editor/esm/vs/language/css/css.worker?worker") worker = await import("monaco-editor/esm/vs/language/css/css.worker?worker")
} else if (label === "javascript") { } else if (label === "javascript") {
@@ -13,7 +13,6 @@ const init = async () => {
} else { } else {
worker = await import("monaco-editor/esm/vs/editor/editor.worker?worker") worker = await import("monaco-editor/esm/vs/editor/editor.worker?worker")
} }
// eslint-disable-next-line new-cap
return new worker.default() return new worker.default()
}, },
} }

View File

@@ -1,5 +1,5 @@
import { TypographyStylesProvider } from "@mantine/core" import { TypographyStylesProvider } from "@mantine/core"
import { type ReactNode } from "react" import type { ReactNode } from "react"
/** /**
* This component is used to provide basic styles to html typography elements. * This component is used to provide basic styles to html typography elements.

View File

@@ -1,10 +1,10 @@
import { Box, Mark } from "@mantine/core" import { Box, Mark } from "@mantine/core"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { calculatePlaceholderSize } from "app/utils" import { calculatePlaceholderSize } from "app/utils"
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading" import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
import escapeStringRegexp from "escape-string-regexp" import escapeStringRegexp from "escape-string-regexp"
import { type ChildrenNode, Interweave, Matcher, type MatchResponse, type Node, type TransformCallback } from "interweave" import { type ChildrenNode, Interweave, type MatchResponse, Matcher, type Node, type TransformCallback } from "interweave"
import React from "react" import React from "react"
import { tss } from "tss" import { tss } from "tss"
@@ -40,8 +40,8 @@ const transform: TransformCallback = node => {
const title = node.getAttribute("title") ?? undefined const title = node.getAttribute("title") ?? undefined
const nodeWidth = node.getAttribute("width") const nodeWidth = node.getAttribute("width")
const nodeHeight = node.getAttribute("height") const nodeHeight = node.getAttribute("height")
const width = nodeWidth ? parseInt(nodeWidth, 10) : undefined const width = nodeWidth ? Number.parseInt(nodeWidth, 10) : undefined
const height = nodeHeight ? parseInt(nodeHeight, 10) : undefined const height = nodeHeight ? Number.parseInt(nodeHeight, 10) : undefined
const placeholderSize = calculatePlaceholderSize({ const placeholderSize = calculatePlaceholderSize({
width, width,
height, height,

View File

@@ -1,7 +1,10 @@
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading" import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
export function Enclosure(props: { enclosureType: string; enclosureUrl: string }) { export function Enclosure(props: {
enclosureType: string
enclosureUrl: string
}) {
const hasVideo = props.enclosureType.startsWith("video") const hasVideo = props.enclosureType.startsWith("video")
const hasAudio = props.enclosureType.startsWith("audio") const hasAudio = props.enclosureType.startsWith("audio")
const hasImage = props.enclosureType.startsWith("image") const hasImage = props.enclosureType.startsWith("image")
@@ -9,11 +12,13 @@ export function Enclosure(props: { enclosureType: string; enclosureUrl: string }
return ( return (
<BasicHtmlStyles> <BasicHtmlStyles>
{hasVideo && ( {hasVideo && (
// biome-ignore lint/a11y/useMediaCaption: we don't have any captions for videos
<video controls width="100%"> <video controls width="100%">
<source src={props.enclosureUrl} type={props.enclosureType} /> <source src={props.enclosureUrl} type={props.enclosureType} />
</video> </video>
)} )}
{hasAudio && ( {hasAudio && (
// biome-ignore lint/a11y/useMediaCaption: we don't have any captions for audio
<audio controls> <audio controls>
<source src={props.enclosureUrl} type={props.enclosureType} /> <source src={props.enclosureUrl} type={props.enclosureType} />
</audio> </audio>

View File

@@ -2,7 +2,7 @@ import { Trans } from "@lingui/macro"
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import { openModal } from "@mantine/modals" import { openModal } from "@mantine/modals"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { type ExpendableEntry } from "app/entries/slice" import type { ExpendableEntry } from "app/entries/slice"
import { import {
loadMoreEntries, loadMoreEntries,
markAllEntries, markAllEntries,
@@ -126,7 +126,7 @@ export function FeedEntries() {
}) })
window.addEventListener("scroll", listener) window.addEventListener("scroll", listener)
return () => window.removeEventListener("scroll", listener) return () => window.removeEventListener("scroll", listener)
}, [dispatch, contextMenu, entries, viewMode, scrollMarks, scrollingToEntry]) }, [dispatch, entries, viewMode, scrollMarks, scrollingToEntry])
useMousetrap("r", async () => await dispatch(reloadEntries())) useMousetrap("r", async () => await dispatch(reloadEntries()))
useMousetrap( useMousetrap(

View File

@@ -1,12 +1,12 @@
import { Box, Divider, type MantineRadius, type MantineSpacing, Paper } from "@mantine/core" import { Box, Divider, type MantineRadius, type MantineSpacing, Paper } from "@mantine/core"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { useAppSelector } from "app/store" import { useAppSelector } from "app/store"
import { type Entry, type ViewMode } from "app/types" import type { Entry, ViewMode } from "app/types"
import { FeedEntryCompactHeader } from "components/content/header/FeedEntryCompactHeader" import { FeedEntryCompactHeader } from "components/content/header/FeedEntryCompactHeader"
import { FeedEntryHeader } from "components/content/header/FeedEntryHeader" import { FeedEntryHeader } from "components/content/header/FeedEntryHeader"
import { useMobile } from "hooks/useMobile" import { useMobile } from "hooks/useMobile"
import { useViewMode } from "hooks/useViewMode" import { useViewMode } from "hooks/useViewMode"
import React from "react" import type React from "react"
import { useSwipeable } from "react-swipeable" import { useSwipeable } from "react-swipeable"
import { tss } from "tss" import { tss } from "tss"
import { FeedEntryBody } from "./FeedEntryBody" import { FeedEntryBody } from "./FeedEntryBody"
@@ -35,7 +35,7 @@ const useStyles = tss
maxWidth?: number maxWidth?: number
}>() }>()
.create(({ theme, colorScheme, read, expanded, viewMode, rtl, showSelectionIndicator, maxWidth }) => { .create(({ theme, colorScheme, read, expanded, viewMode, rtl, showSelectionIndicator, maxWidth }) => {
let backgroundColor let backgroundColor: string
if (colorScheme === "dark") { if (colorScheme === "dark") {
backgroundColor = read ? "inherit" : theme.colors.dark[5] backgroundColor = read ? "inherit" : theme.colors.dark[5]
} else { } else {
@@ -61,7 +61,7 @@ const useStyles = tss
backgroundHoverColor = colorScheme === "dark" ? theme.colors.dark[6] : theme.colors.gray[1] backgroundHoverColor = colorScheme === "dark" ? theme.colors.dark[6] : theme.colors.gray[1]
} }
let paperBorderLeftColor let paperBorderLeftColor = ""
if (showSelectionIndicator) { if (showSelectionIndicator) {
const borderLeftColor = colorScheme === "dark" ? theme.colors[theme.primaryColor][4] : theme.colors[theme.primaryColor][6] const borderLeftColor = colorScheme === "dark" ? theme.colors[theme.primaryColor][4] : theme.colors[theme.primaryColor][6]
paperBorderLeftColor = `${borderLeftColor} !important` paperBorderLeftColor = `${borderLeftColor} !important`

View File

@@ -1,6 +1,6 @@
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import { useAppSelector } from "app/store" import { useAppSelector } from "app/store"
import { type Entry } from "app/types" import type { Entry } from "app/types"
import { Content } from "./Content" import { Content } from "./Content"
import { Enclosure } from "./Enclosure" import { Enclosure } from "./Enclosure"
import { Media } from "./Media" import { Media } from "./Media"

View File

@@ -4,7 +4,7 @@ import { Constants } from "app/constants"
import { markEntriesUpToEntry, markEntry, starEntry } from "app/entries/thunks" import { markEntriesUpToEntry, markEntry, starEntry } from "app/entries/thunks"
import { redirectToFeed } from "app/redirect/thunks" import { redirectToFeed } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { type Entry } from "app/types" import type { Entry } from "app/types"
import { truncate } from "app/utils" import { truncate } from "app/utils"
import { useBrowserExtension } from "hooks/useBrowserExtension" import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useColorScheme } from "hooks/useColorScheme" import { useColorScheme } from "hooks/useColorScheme"

View File

@@ -1,8 +1,8 @@
import { t, Trans } from "@lingui/macro" import { Trans, t } from "@lingui/macro"
import { Group, Indicator, Popover, TagsInput } from "@mantine/core" import { Group, Indicator, Popover, TagsInput } from "@mantine/core"
import { markEntriesUpToEntry, markEntry, starEntry, tagEntry } from "app/entries/thunks" import { markEntriesUpToEntry, markEntry, starEntry, tagEntry } from "app/entries/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { type Entry } from "app/types" import type { Entry } from "app/types"
import { ActionButton } from "components/ActionButton" import { ActionButton } from "components/ActionButton"
import { useActionButton } from "hooks/useActionButton" import { useActionButton } from "hooks/useActionButton"
import { useMobile } from "hooks/useMobile" import { useMobile } from "hooks/useMobile"

View File

@@ -1,8 +1,8 @@
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { calculatePlaceholderSize } from "app/utils" import { calculatePlaceholderSize } from "app/utils"
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading" import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
import { Content } from "./Content" import { Content } from "./Content"
export interface MediaProps { export interface MediaProps {

View File

@@ -2,10 +2,10 @@ import { Trans } from "@lingui/macro"
import { ActionIcon, Box, CopyButton, Divider, SimpleGrid } from "@mantine/core" import { ActionIcon, Box, CopyButton, Divider, SimpleGrid } from "@mantine/core"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { useAppSelector } from "app/store" import { useAppSelector } from "app/store"
import { type SharingSettings } from "app/types" import type { SharingSettings } from "app/types"
import { useBrowserExtension } from "hooks/useBrowserExtension" import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useMobile } from "hooks/useMobile" import { useMobile } from "hooks/useMobile"
import { type IconType } from "react-icons" import type { IconType } from "react-icons"
import { TbCheck, TbCopy, TbDeviceDesktopShare, TbDeviceMobileShare } from "react-icons/tb" import { TbCheck, TbCopy, TbDeviceDesktopShare, TbDeviceMobileShare } from "react-icons/tb"
import { tss } from "tss" import { tss } from "tss"

View File

@@ -1,11 +1,11 @@
import { t, Trans } from "@lingui/macro" import { Trans, t } from "@lingui/macro"
import { Box, Button, Group, Stack, TextInput } from "@mantine/core" import { Box, Button, Group, Stack, TextInput } from "@mantine/core"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client" import { client, errorToStrings } from "app/client"
import { redirectToSelectedSource } from "app/redirect/thunks" import { redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch } from "app/store" import { useAppDispatch } from "app/store"
import { reloadTree } from "app/tree/thunks" import { reloadTree } from "app/tree/thunks"
import { type AddCategoryRequest } from "app/types" import type { AddCategoryRequest } from "app/types"
import { Alert } from "components/Alert" import { Alert } from "components/Alert"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"
import { TbFolderPlus } from "react-icons/tb" import { TbFolderPlus } from "react-icons/tb"

View File

@@ -1,9 +1,9 @@
import { t } from "@lingui/macro" import { t } from "@lingui/macro"
import { Select, type SelectProps } from "@mantine/core" import { Select, type SelectProps } from "@mantine/core"
import { type ComboboxItem } from "@mantine/core/lib/components/Combobox/Combobox.types" import type { ComboboxItem } from "@mantine/core/lib/components/Combobox/Combobox.types"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { useAppSelector } from "app/store" import { useAppSelector } from "app/store"
import { type Category } from "app/types" import type { Category } from "app/types"
import { flattenCategoryTree } from "app/utils" import { flattenCategoryTree } from "app/utils"
type CategorySelectProps = Partial<SelectProps> & { type CategorySelectProps = Partial<SelectProps> & {
@@ -18,7 +18,8 @@ export function CategorySelect(props: CategorySelectProps) {
map.set(c.id, c) map.set(c.id, c)
return map return map
}, new Map<string, Category>()) }, new Map<string, Category>())
const categoryLabel = (cat: Category) => { const categoryLabel = (category: Category) => {
let cat = category
let label = cat.name let label = cat.name
while (cat.parentId) { while (cat.parentId) {

View File

@@ -1,4 +1,4 @@
import { t, Trans } from "@lingui/macro" import { Trans, t } from "@lingui/macro"
import { Box, Button, FileInput, Group, Stack } from "@mantine/core" import { Box, Button, FileInput, Group, Stack } from "@mantine/core"
import { isNotEmpty, useForm } from "@mantine/form" import { isNotEmpty, useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client" import { client, errorToStrings } from "app/client"

View File

@@ -6,7 +6,7 @@ import { Constants } from "app/constants"
import { redirectToFeed, redirectToSelectedSource } from "app/redirect/thunks" import { redirectToFeed, redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch } from "app/store" import { useAppDispatch } from "app/store"
import { reloadTree } from "app/tree/thunks" import { reloadTree } from "app/tree/thunks"
import { type FeedInfoRequest, type SubscribeRequest } from "app/types" import type { FeedInfoRequest, SubscribeRequest } from "app/types"
import { Alert } from "components/Alert" import { Alert } from "components/Alert"
import { useState } from "react" import { useState } from "react"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"

View File

@@ -1,9 +1,9 @@
import { Box, Text } from "@mantine/core" import { Box, Text } from "@mantine/core"
import { type Entry } from "app/types" import type { Entry } from "app/types"
import { RelativeDate } from "components/RelativeDate"
import { FeedFavicon } from "components/content/FeedFavicon" import { FeedFavicon } from "components/content/FeedFavicon"
import { OpenExternalLink } from "components/content/header/OpenExternalLink" import { OpenExternalLink } from "components/content/header/OpenExternalLink"
import { Star } from "components/content/header/Star" import { Star } from "components/content/header/Star"
import { RelativeDate } from "components/RelativeDate"
import { OnDesktop } from "components/responsive/OnDesktop" import { OnDesktop } from "components/responsive/OnDesktop"
import { tss } from "tss" import { tss } from "tss"
import { FeedEntryTitle } from "./FeedEntryTitle" import { FeedEntryTitle } from "./FeedEntryTitle"

View File

@@ -1,9 +1,9 @@
import { Box, Flex, Space, Text } from "@mantine/core" import { Box, Flex, Space, Text } from "@mantine/core"
import { type Entry } from "app/types" import type { Entry } from "app/types"
import { RelativeDate } from "components/RelativeDate"
import { FeedFavicon } from "components/content/FeedFavicon" import { FeedFavicon } from "components/content/FeedFavicon"
import { OpenExternalLink } from "components/content/header/OpenExternalLink" import { OpenExternalLink } from "components/content/header/OpenExternalLink"
import { Star } from "components/content/header/Star" import { Star } from "components/content/header/Star"
import { RelativeDate } from "components/RelativeDate"
import { tss } from "tss" import { tss } from "tss"
import { FeedEntryTitle } from "./FeedEntryTitle" import { FeedEntryTitle } from "./FeedEntryTitle"

View File

@@ -1,6 +1,6 @@
import { Highlight } from "@mantine/core" import { Highlight } from "@mantine/core"
import { useAppSelector } from "app/store" import { useAppSelector } from "app/store"
import { type Entry } from "app/types" import type { Entry } from "app/types"
export interface FeedEntryTitleProps { export interface FeedEntryTitleProps {
entry: Entry entry: Entry

View File

@@ -3,7 +3,7 @@ import { ActionIcon, Anchor, Tooltip } from "@mantine/core"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { markEntry } from "app/entries/thunks" import { markEntry } from "app/entries/thunks"
import { useAppDispatch } from "app/store" import { useAppDispatch } from "app/store"
import { type Entry } from "app/types" import type { Entry } from "app/types"
import { TbExternalLink } from "react-icons/tb" import { TbExternalLink } from "react-icons/tb"
export function OpenExternalLink(props: { entry: Entry }) { export function OpenExternalLink(props: { entry: Entry }) {

View File

@@ -1,4 +1,4 @@
import { t, Trans } from "@lingui/macro" import { Trans, t } from "@lingui/macro"
import { Box, Center, CloseButton, Divider, Group, Indicator, Popover, TextInput } from "@mantine/core" import { Box, Center, CloseButton, Divider, Group, Indicator, Popover, TextInput } from "@mantine/core"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { reloadEntries, search, selectNextEntry, selectPreviousEntry } from "app/entries/thunks" import { reloadEntries, search, selectNextEntry, selectPreviousEntry } from "app/entries/thunks"

View File

@@ -13,7 +13,7 @@ import { showNotification } from "@mantine/notifications"
import { client } from "app/client" import { client } from "app/client"
import { redirectToAbout, redirectToAdminUsers, redirectToDonate, redirectToMetrics, redirectToSettings } from "app/redirect/thunks" import { redirectToAbout, redirectToAdminUsers, redirectToDonate, redirectToMetrics, redirectToSettings } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { type ViewMode } from "app/types" import type { ViewMode } from "app/types"
import { useViewMode } from "hooks/useViewMode" import { useViewMode } from "hooks/useViewMode"
import { type ReactNode, useState } from "react" import { type ReactNode, useState } from "react"
import { import {

View File

@@ -1,4 +1,4 @@
import { type MetricGauge } from "app/types" import type { MetricGauge } from "app/types"
interface MeterProps { interface MeterProps {
gauge: MetricGauge gauge: MetricGauge

View File

@@ -1,5 +1,5 @@
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import { type MetricMeter } from "app/types" import type { MetricMeter } from "app/types"
interface MeterProps { interface MeterProps {
meter: MetricMeter meter: MetricMeter

View File

@@ -1,5 +1,5 @@
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import { type MetricTimer } from "app/types" import type { MetricTimer } from "app/types"
interface MetricTimerProps { interface MetricTimerProps {
timer: MetricTimer timer: MetricTimer

View File

@@ -1,6 +1,6 @@
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import { useMobile } from "hooks/useMobile" import { useMobile } from "hooks/useMobile"
import React from "react" import type React from "react"
export function OnDesktop(props: { children: React.ReactNode }) { export function OnDesktop(props: { children: React.ReactNode }) {
const mobile = useMobile() const mobile = useMobile()

View File

@@ -1,6 +1,6 @@
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import { useMobile } from "hooks/useMobile" import { useMobile } from "hooks/useMobile"
import React from "react" import type React from "react"
export function OnMobile(props: { children: React.ReactNode }) { export function OnMobile(props: { children: React.ReactNode }) {
const mobile = useMobile() const mobile = useMobile()

View File

@@ -1,9 +1,9 @@
import { t, Trans } from "@lingui/macro" import { Trans, t } from "@lingui/macro"
import { Divider, Group, Radio, Select, SimpleGrid, Stack, Switch } from "@mantine/core" import { Divider, Group, Radio, Select, SimpleGrid, Stack, Switch } from "@mantine/core"
import { type ComboboxData } from "@mantine/core/lib/components/Combobox/Combobox.types" import type { ComboboxData } from "@mantine/core/lib/components/Combobox/Combobox.types"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { type IconDisplayMode, type ScrollMode, type SharingSettings } from "app/types" import type { IconDisplayMode, ScrollMode, SharingSettings } from "app/types"
import { import {
changeCustomContextMenu, changeCustomContextMenu,
changeExternalLinkIconDisplayMode, changeExternalLinkIconDisplayMode,
@@ -18,7 +18,7 @@ import {
changeStarIconDisplayMode, changeStarIconDisplayMode,
} from "app/user/thunks" } from "app/user/thunks"
import { locales } from "i18n" import { locales } from "i18n"
import { type ReactNode } from "react" import type { ReactNode } from "react"
export function DisplaySettings() { export function DisplaySettings() {
const language = useAppSelector(state => state.user.settings?.language) const language = useAppSelector(state => state.user.settings?.language)

View File

@@ -1,11 +1,11 @@
import { t, Trans } from "@lingui/macro" import { Trans, t } from "@lingui/macro"
import { Anchor, Box, Button, Checkbox, Divider, Group, Input, PasswordInput, Stack, Text, TextInput } from "@mantine/core" import { Anchor, Box, Button, Checkbox, Divider, Group, Input, PasswordInput, Stack, Text, TextInput } from "@mantine/core"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { openConfirmModal } from "@mantine/modals" import { openConfirmModal } from "@mantine/modals"
import { client, errorToStrings } from "app/client" import { client, errorToStrings } from "app/client"
import { redirectToLogin, redirectToSelectedSource } from "app/redirect/thunks" import { redirectToLogin, redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { type ProfileModificationRequest } from "app/types" import type { ProfileModificationRequest } from "app/types"
import { reloadProfile } from "app/user/thunks" import { reloadProfile } from "app/user/thunks"
import { Alert } from "components/Alert" import { Alert } from "components/Alert"
import { useEffect } from "react" import { useEffect } from "react"

View File

@@ -11,7 +11,7 @@ import {
} from "app/redirect/thunks" } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { collapseTreeCategory } from "app/tree/thunks" import { collapseTreeCategory } from "app/tree/thunks"
import { type Category, type Subscription } from "app/types" import type { Category, Subscription } from "app/types"
import { categoryUnreadCount, flattenCategoryTree } from "app/utils" import { categoryUnreadCount, flattenCategoryTree } from "app/utils"
import { Loader } from "components/Loader" import { Loader } from "components/Loader"
import { OnDesktop } from "components/responsive/OnDesktop" import { OnDesktop } from "components/responsive/OnDesktop"

View File

@@ -1,13 +1,13 @@
import { Box, Center } from "@mantine/core" import { Box, Center } from "@mantine/core"
import { FeedFavicon } from "components/content/FeedFavicon" import { FeedFavicon } from "components/content/FeedFavicon"
import React, { type ReactNode } from "react" import type React from "react"
import { tss } from "tss" import { tss } from "tss"
import { UnreadCount } from "./UnreadCount" import { UnreadCount } from "./UnreadCount"
interface TreeNodeProps { interface TreeNodeProps {
id: string id: string
name: ReactNode name: React.ReactNode
icon: ReactNode icon: React.ReactNode
unread: number unread: number
selected: boolean selected: boolean
expanded?: boolean expanded?: boolean
@@ -27,7 +27,7 @@ const useStyles = tss
let backgroundColor = "inherit" let backgroundColor = "inherit"
if (selected) backgroundColor = colorScheme === "dark" ? theme.colors.dark[4] : theme.colors.gray[1] if (selected) backgroundColor = colorScheme === "dark" ? theme.colors.dark[4] : theme.colors.gray[1]
let color let color: string
if (hasError) { if (hasError) {
color = theme.colors.red[6] color = theme.colors.red[6]
} else if (colorScheme === "dark") { } else if (colorScheme === "dark") {

View File

@@ -1,9 +1,10 @@
import { t, Trans } from "@lingui/macro" import { Trans, t } from "@lingui/macro"
import { Box, Center, Kbd, TextInput } from "@mantine/core" import { Box, Center, Kbd, TextInput } from "@mantine/core"
import { Spotlight, spotlight, type SpotlightActionData } from "@mantine/spotlight" import { useOs } from "@mantine/hooks"
import { Spotlight, type SpotlightActionData, spotlight } from "@mantine/spotlight"
import { redirectToFeed } from "app/redirect/thunks" import { redirectToFeed } from "app/redirect/thunks"
import { useAppDispatch } from "app/store" import { useAppDispatch } from "app/store"
import { type Subscription } from "app/types" import type { Subscription } from "app/types"
import { FeedFavicon } from "components/content/FeedFavicon" import { FeedFavicon } from "components/content/FeedFavicon"
import { useMousetrap } from "hooks/useMousetrap" import { useMousetrap } from "hooks/useMousetrap"
import { TbSearch } from "react-icons/tb" import { TbSearch } from "react-icons/tb"
@@ -14,7 +15,7 @@ export interface TreeSearchProps {
export function TreeSearch(props: TreeSearchProps) { export function TreeSearch(props: TreeSearchProps) {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const isMacOS = useOs() === "macos"
const actions: SpotlightActionData[] = props.feeds const actions: SpotlightActionData[] = props.feeds
.map(f => ({ .map(f => ({
id: `${f.id}`, id: `${f.id}`,
@@ -27,7 +28,7 @@ export function TreeSearch(props: TreeSearchProps) {
const searchIcon = <TbSearch size={18} /> const searchIcon = <TbSearch size={18} />
const rightSection = ( const rightSection = (
<Center style={{ cursor: "pointer" }} onClick={() => spotlight.open()}> <Center style={{ cursor: "pointer" }} onClick={() => spotlight.open()}>
<Kbd>Ctrl</Kbd> <Kbd>{isMacOS ? "Cmd" : "Ctrl"}</Kbd>
<Box mx={5}>+</Box> <Box mx={5}>+</Box>
<Kbd>K</Kbd> <Kbd>K</Kbd>
</Center> </Center>
@@ -56,13 +57,13 @@ export function TreeSearch(props: TreeSearchProps) {
<Spotlight <Spotlight
actions={actions} actions={actions}
limit={10} limit={10}
shortcut="ctrl+k" shortcut="mod+k"
searchProps={{ searchProps={{
leftSection: searchIcon, leftSection: searchIcon,
placeholder: t`Search`, placeholder: t`Search`,
}} }}
nothingFound={<Trans>Nothing found</Trans>} nothingFound={<Trans>Nothing found</Trans>}
></Spotlight> />
</> </>
) )
} }

View File

@@ -9,13 +9,13 @@ export const useBrowserExtension = () => {
// monitor the attribute on the root element as it may change after the page was loaded // monitor the attribute on the root element as it may change after the page was loaded
useEffect(() => { useEffect(() => {
const observer = new MutationObserver(mutations => { const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => { for (const mutation of mutations) {
if (mutation.type === "attributes") { if (mutation.type === "attributes") {
const element = mutation.target as Element const element = mutation.target as Element
const version = element.getAttribute("browser-extension-installed") const version = element.getAttribute("browser-extension-installed")
if (version) setBrowserExtensionVersion(version) if (version) setBrowserExtensionVersion(version)
} }
}) }
}) })
observer.observe(document.documentElement, { observer.observe(document.documentElement, {

View File

@@ -1,4 +1,4 @@
import { type ViewMode } from "app/types" import type { ViewMode } from "app/types"
import useLocalStorage from "use-local-storage" import useLocalStorage from "use-local-storage"
export function useViewMode() { export function useViewMode() {

View File

@@ -1,4 +1,4 @@
import { i18n, type Messages } from "@lingui/core" import { type Messages, i18n } from "@lingui/core"
import { useAppSelector } from "app/store" import { useAppSelector } from "app/store"
import dayjs from "dayjs" import dayjs from "dayjs"
import { useEffect } from "react" import { useEffect } from "react"

View File

@@ -315,7 +315,7 @@ msgstr "输入您当前的密码以更改配置文件设置"
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Entry headers" msgid "Entry headers"
msgstr "" msgstr "条目头部"
#: src/components/Alert.tsx #: src/components/Alert.tsx
msgid "Error" msgid "Error"
@@ -578,7 +578,7 @@ msgstr "没有更多条目"
#: src/components/content/ShareButtons.tsx #: src/components/content/ShareButtons.tsx
msgid "No sharing options available." msgid "No sharing options available."
msgstr "" msgstr "没有可用的分享选项"
#: src/components/sidebar/TreeSearch.tsx #: src/components/sidebar/TreeSearch.tsx
msgid "Nothing found" msgid "Nothing found"
@@ -590,11 +590,11 @@ msgstr "最早的优先"
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "On desktop" msgid "On desktop"
msgstr "" msgstr "桌面端"
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "On mobile" msgid "On mobile"
msgstr "" msgstr "移动端"
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "On mobile, show action buttons at the bottom of the screen" msgid "On mobile, show action buttons at the bottom of the screen"
@@ -660,7 +660,7 @@ msgstr "OPML 文件"
#: src/components/content/add/ImportOpml.tsx #: src/components/content/add/ImportOpml.tsx
msgid "OPML file is required" msgid "OPML file is required"
msgstr "" msgstr "OPML 文件是必需的"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "Order" msgid "Order"
@@ -718,7 +718,7 @@ msgstr "此 CommaFeed 实例上的注册已关闭"
#: src/pages/app/AboutPage.tsx #: src/pages/app/AboutPage.tsx
msgid "REST API" msgid "REST API"
msgstr "" msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx
@@ -804,7 +804,7 @@ msgstr "显示条目菜单(移动端)"
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show external link icon" msgid "Show external link icon"
msgstr "" msgstr "显示外部链接图标"
#: 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"
@@ -820,7 +820,7 @@ msgstr "显示原生菜单(桌面端)"
#: src/components/settings/DisplaySettings.tsx #: src/components/settings/DisplaySettings.tsx
msgid "Show star icon" msgid "Show star icon"
msgstr "" msgstr "显示星标图标"
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx #: src/pages/auth/RegistrationPage.tsx

View File

@@ -1,4 +0,0 @@
html, body {
/* disable pull-to-refresh on mobile as it messes with vertical scrolling */
overscroll-behavior: none;
}

View File

@@ -3,7 +3,6 @@ import "@mantine/core/styles.css"
import "@mantine/notifications/styles.css" import "@mantine/notifications/styles.css"
import "@mantine/spotlight/styles.css" import "@mantine/spotlight/styles.css"
import "react-contexify/ReactContexify.css" import "react-contexify/ReactContexify.css"
import "main.css"
import { App } from "App" import { App } from "App"
import { store } from "app/store" import { store } from "app/store"
import dayjs from "dayjs" import dayjs from "dayjs"

View File

@@ -2,12 +2,12 @@ import { Trans } from "@lingui/macro"
import { ActionIcon, Box, Code, Container, Group, Table, Text, Title, useMantineTheme } from "@mantine/core" import { ActionIcon, Box, Code, Container, Group, Table, Text, Title, useMantineTheme } from "@mantine/core"
import { closeAllModals, openConfirmModal, openModal } from "@mantine/modals" import { closeAllModals, openConfirmModal, openModal } from "@mantine/modals"
import { client, errorToStrings } from "app/client" import { client, errorToStrings } from "app/client"
import { type UserModel } from "app/types" import type { UserModel } from "app/types"
import { UserEdit } from "components/admin/UserEdit"
import { Alert } from "components/Alert" import { Alert } from "components/Alert"
import { Loader } from "components/Loader" import { Loader } from "components/Loader"
import { RelativeDate } from "components/RelativeDate" import { RelativeDate } from "components/RelativeDate"
import { type ReactNode } from "react" import { UserEdit } from "components/admin/UserEdit"
import type { ReactNode } from "react"
import { useAsync, useAsyncCallback } from "react-async-hook" import { useAsync, useAsyncCallback } from "react-async-hook"
import { TbCheck, TbPencil, TbPlus, TbTrash, TbX } from "react-icons/tb" import { TbCheck, TbPencil, TbPlus, TbTrash, TbX } from "react-icons/tb"

View File

@@ -1,12 +1,13 @@
import { t, Trans } from "@lingui/macro" import { Trans, t } from "@lingui/macro"
import { Anchor, Box, Container, List, NativeSelect, SimpleGrid, Title } from "@mantine/core" import { Anchor, Box, Container, List, NativeSelect, SimpleGrid, Title } from "@mantine/core"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { redirectToApiDocumentation } from "app/redirect/thunks" import { redirectToApiDocumentation } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { CategorySelect } from "components/content/add/CategorySelect"
import { KeyboardShortcutsHelp } from "components/KeyboardShortcutsHelp" import { KeyboardShortcutsHelp } from "components/KeyboardShortcutsHelp"
import { CategorySelect } from "components/content/add/CategorySelect"
import { useBrowserExtension } from "hooks/useBrowserExtension" import { useBrowserExtension } from "hooks/useBrowserExtension"
import React, { useState } from "react" import type React from "react"
import { useState } from "react"
import { TbHelp, TbKeyboard, TbPuzzle, TbRocket } from "react-icons/tb" import { TbHelp, TbKeyboard, TbPuzzle, TbRocket } from "react-icons/tb"
import { tss } from "tss" import { tss } from "tss"

View File

@@ -7,11 +7,11 @@ import { Constants } from "app/constants"
import { redirectToRootCategory, redirectToSelectedSource } from "app/redirect/thunks" import { redirectToRootCategory, redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { reloadTree } from "app/tree/thunks" import { reloadTree } from "app/tree/thunks"
import { type CategoryModificationRequest } from "app/types" import type { CategoryModificationRequest } from "app/types"
import { flattenCategoryTree } from "app/utils" import { flattenCategoryTree } from "app/utils"
import { Alert } from "components/Alert" import { Alert } from "components/Alert"
import { CategorySelect } from "components/content/add/CategorySelect"
import { Loader } from "components/Loader" import { Loader } from "components/Loader"
import { CategorySelect } from "components/content/add/CategorySelect"
import { useEffect } from "react" import { useEffect } from "react"
import { useAsync, useAsyncCallback } from "react-async-hook" import { useAsync, useAsyncCallback } from "react-async-hook"
import { TbDeviceFloppy, TbTrash } from "react-icons/tb" import { TbDeviceFloppy, TbTrash } from "react-icons/tb"

View File

@@ -6,11 +6,11 @@ import { client, errorToStrings } from "app/client"
import { redirectToRootCategory, redirectToSelectedSource } from "app/redirect/thunks" import { redirectToRootCategory, redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { reloadTree } from "app/tree/thunks" import { reloadTree } from "app/tree/thunks"
import { type FeedModificationRequest } from "app/types" import type { FeedModificationRequest } from "app/types"
import { Alert } from "components/Alert" import { Alert } from "components/Alert"
import { CategorySelect } from "components/content/add/CategorySelect"
import { Loader } from "components/Loader" import { Loader } from "components/Loader"
import { RelativeDate } from "components/RelativeDate" import { RelativeDate } from "components/RelativeDate"
import { CategorySelect } from "components/content/add/CategorySelect"
import { useEffect } from "react" import { useEffect } from "react"
import { useAsync, useAsyncCallback } from "react-async-hook" import { useAsync, useAsyncCallback } from "react-async-hook"
import { TbDeviceFloppy, TbTrash } from "react-icons/tb" import { TbDeviceFloppy, TbTrash } from "react-icons/tb"

View File

@@ -2,7 +2,7 @@ import { Trans } from "@lingui/macro"
import { ActionIcon, Box, Center, Divider, Group, Title, useMantineTheme } from "@mantine/core" import { ActionIcon, Box, Center, Divider, Group, Title, useMantineTheme } from "@mantine/core"
import { useViewportSize } from "@mantine/hooks" import { useViewportSize } from "@mantine/hooks"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { type EntrySourceType } from "app/entries/slice" import type { EntrySourceType } from "app/entries/slice"
import { loadEntries } from "app/entries/thunks" import { loadEntries } from "app/entries/thunks"
import { redirectToCategoryDetails, redirectToFeedDetails, redirectToTagDetails } from "app/redirect/thunks" import { redirectToCategoryDetails, redirectToFeedDetails, redirectToTagDetails } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
@@ -62,6 +62,7 @@ export function FeedEntriesPage(props: FeedEntriesPageProps) {
} }
} }
// biome-ignore lint/correctness/useExhaustiveDependencies: we subscribe to state.timestamp because we want to reload entries even if the props are the same
useEffect(() => { useEffect(() => {
dispatch( dispatch(
loadEntries({ loadEntries({
@@ -72,7 +73,7 @@ export function FeedEntriesPage(props: FeedEntriesPageProps) {
clearSearch: true, clearSearch: true,
}) })
) )
}, [dispatch, props.sourceType, id, location.state]) }, [dispatch, props.sourceType, id, location.state?.timestamp])
const noSubscriptions = rootCategory && flattenCategoryTree(rootCategory).every(c => c.feeds.length === 0) const noSubscriptions = rootCategory && flattenCategoryTree(rootCategory).every(c => c.feeds.length === 0)
if (noSubscriptions) return <NoSubscriptionHelp /> if (noSubscriptions) return <NoSubscriptionHelp />

View File

@@ -104,7 +104,7 @@ export default function Layout(props: LayoutProps) {
label={mobileMenuOpen ? <Trans>Close menu</Trans> : <Trans>Open menu</Trans>} label={mobileMenuOpen ? <Trans>Close menu</Trans> : <Trans>Open menu</Trans>}
icon={mobileMenuOpen ? <TbX size={18} /> : <TbMenu2 size={18} />} icon={mobileMenuOpen ? <TbX size={18} /> : <TbMenu2 size={18} />}
onClick={() => dispatch(setMobileMenuOpen(!mobileMenuOpen))} onClick={() => dispatch(setMobileMenuOpen(!mobileMenuOpen))}
></ActionButton> />
) )
const addButton = ( const addButton = (
@@ -201,7 +201,7 @@ export default function Layout(props: LayoutProps) {
width: "10px", width: "10px",
cursor: "ew-resize", cursor: "ew-resize",
}} }}
></Box> />
</Draggable> </Draggable>
</OnDesktop> </OnDesktop>

View File

@@ -1,10 +1,10 @@
import { t, Trans } from "@lingui/macro" import { Trans, t } from "@lingui/macro"
import { Anchor, Box, Button, Center, Container, Group, Paper, PasswordInput, Stack, TextInput, Title } from "@mantine/core" import { Anchor, Box, Button, Center, Container, Group, Paper, PasswordInput, Stack, TextInput, Title } from "@mantine/core"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client" import { client, errorToStrings } from "app/client"
import { redirectToRootCategory } from "app/redirect/thunks" import { redirectToRootCategory } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { type LoginRequest } from "app/types" import type { LoginRequest } from "app/types"
import { Alert } from "components/Alert" import { Alert } from "components/Alert"
import { PageTitle } from "pages/PageTitle" import { PageTitle } from "pages/PageTitle"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"
@@ -50,6 +50,7 @@ export function LoginPage() {
} }
size="md" size="md"
required required
autoCapitalize="off"
/> />
<PasswordInput <PasswordInput
label={<Trans>Password</Trans>} label={<Trans>Password</Trans>}

View File

@@ -1,8 +1,8 @@
import { t, Trans } from "@lingui/macro" import { Trans, t } from "@lingui/macro"
import { Anchor, Box, Button, Center, Container, Group, Paper, Stack, TextInput, Title } from "@mantine/core" import { Anchor, Box, Button, Center, Container, Group, Paper, Stack, TextInput, Title } from "@mantine/core"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client" import { client, errorToStrings } from "app/client"
import { type PasswordResetRequest } from "app/types" import type { PasswordResetRequest } from "app/types"
import { Alert } from "components/Alert" import { Alert } from "components/Alert"
import { PageTitle } from "pages/PageTitle" import { PageTitle } from "pages/PageTitle"
import { useState } from "react" import { useState } from "react"

View File

@@ -1,10 +1,10 @@
import { t, Trans } from "@lingui/macro" import { Trans, t } from "@lingui/macro"
import { Anchor, Box, Button, Center, Container, Group, Paper, PasswordInput, Stack, TextInput, Title } from "@mantine/core" import { Anchor, Box, Button, Center, Container, Group, Paper, PasswordInput, Stack, TextInput, Title } from "@mantine/core"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client" import { client, errorToStrings } from "app/client"
import { redirectToRootCategory } from "app/redirect/thunks" import { redirectToRootCategory } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "app/store"
import { type RegistrationRequest } from "app/types" import type { RegistrationRequest } from "app/types"
import { Alert } from "components/Alert" import { Alert } from "components/Alert"
import { PageTitle } from "pages/PageTitle" import { PageTitle } from "pages/PageTitle"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"

View File

@@ -2,7 +2,7 @@ import { lingui } from "@lingui/vite-plugin"
import react from "@vitejs/plugin-react" import react from "@vitejs/plugin-react"
import { visualizer } from "rollup-plugin-visualizer" import { visualizer } from "rollup-plugin-visualizer"
import { defineConfig } from "vite" import { defineConfig } from "vite"
import eslint from "vite-plugin-eslint" import biomePlugin from "vite-plugin-biome"
import tsconfigPaths from "vite-tsconfig-paths" import tsconfigPaths from "vite-tsconfig-paths"
// https://vitejs.dev/config/ // https://vitejs.dev/config/
@@ -16,9 +16,12 @@ export default defineConfig(env => ({
}), }),
lingui(), lingui(),
// https://github.com/vitest-dev/vitest/issues/4055#issuecomment-1732994672 // https://github.com/vitest-dev/vitest/issues/4055#issuecomment-1732994672
env.mode !== "test" && eslint(),
tsconfigPaths(), tsconfigPaths(),
visualizer(), visualizer(),
biomePlugin({
mode: "check",
failOnError: true,
}),
], ],
base: "./", base: "./",
server: { server: {

View File

@@ -30,6 +30,8 @@
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$" /> <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$" />
</module> </module>
<module name="ParameterName" /> <module name="ParameterName" />
<module name="PatternVariableName" />
<module name="RecordComponentName" />
<module name="StaticVariableName" /> <module name="StaticVariableName" />
<module name="TypeName" /> <module name="TypeName" />
@@ -90,6 +92,9 @@
<module name="SimplifyBooleanReturn" /> <module name="SimplifyBooleanReturn" />
<module name="StringLiteralEquality" /> <module name="StringLiteralEquality" />
<module name="UnnecessaryParentheses" /> <module name="UnnecessaryParentheses" />
<module name="UnnecessarySemicolonAfterTypeMemberDeclaration" />
<module name="UnnecessarySemicolonInTryWithResources" />
<module name="UnusedLocalVariable" />
<!-- Checks for class design --> <!-- Checks for class design -->
<!-- See http://checkstyle.sf.net/config_design.html --> <!-- See http://checkstyle.sf.net/config_design.html -->

View File

@@ -6,14 +6,14 @@
<parent> <parent>
<groupId>com.commafeed</groupId> <groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId> <artifactId>commafeed</artifactId>
<version>4.4.0</version> <version>4.4.1</version>
</parent> </parent>
<artifactId>commafeed-server</artifactId> <artifactId>commafeed-server</artifactId>
<name>CommaFeed Server</name> <name>CommaFeed Server</name>
<properties> <properties>
<guice.version>7.0.0</guice.version> <guice.version>7.0.0</guice.version>
<querydsl.version>5.1.0</querydsl.version> <querydsl.version>6.3</querydsl.version>
<rome.version>2.1.0</rome.version> <rome.version>2.1.0</rome.version>
</properties> </properties>
@@ -36,12 +36,12 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version> <version>3.3.0</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId> <artifactId>maven-failsafe-plugin</artifactId>
<version>3.2.5</version> <version>3.3.0</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
@@ -54,7 +54,7 @@
<plugin> <plugin>
<groupId>io.github.git-commit-id</groupId> <groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId> <artifactId>git-commit-id-maven-plugin</artifactId>
<version>8.0.2</version> <version>9.0.0</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
@@ -72,7 +72,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.5.2</version> <version>3.6.0</version>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.kordamp.shade</groupId> <groupId>org.kordamp.shade</groupId>
@@ -121,7 +121,7 @@
<plugin> <plugin>
<groupId>io.swagger.core.v3</groupId> <groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-maven-plugin-jakarta</artifactId> <artifactId>swagger-maven-plugin-jakarta</artifactId>
<version>2.2.21</version> <version>2.2.22</version>
<?m2e ignore?> <?m2e ignore?>
<configuration> <configuration>
<outputPath>${project.build.directory}/classes/assets</outputPath> <outputPath>${project.build.directory}/classes/assets</outputPath>
@@ -145,7 +145,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>3.4.0</version> <version>3.4.1</version>
<configuration> <configuration>
<archive> <archive>
<manifest> <manifest>
@@ -157,7 +157,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId> <artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version> <version>3.4.0</version>
<executions> <executions>
<execution> <execution>
<id>validate</id> <id>validate</id>
@@ -211,7 +211,7 @@
<dependency> <dependency>
<groupId>com.commafeed</groupId> <groupId>com.commafeed</groupId>
<artifactId>commafeed-client</artifactId> <artifactId>commafeed-client</artifactId>
<version>4.4.0</version> <version>4.4.1</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -280,37 +280,27 @@
<dependency> <dependency>
<groupId>io.swagger.core.v3</groupId> <groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId> <artifactId>swagger-annotations</artifactId>
<version>2.2.21</version> <version>2.2.22</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.querydsl</groupId> <groupId>io.github.openfeign.querydsl</groupId>
<artifactId>querydsl-apt</artifactId> <artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version> <version>${querydsl.version}</version>
<scope>provided</scope> <scope>provided</scope>
<classifier>jakarta</classifier> <classifier>jakarta</classifier>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.querydsl</groupId> <groupId>io.github.openfeign.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId> <artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version> <version>${querydsl.version}</version>
<classifier>jakarta</classifier>
</dependency> </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.16.1</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId> <artifactId>commons-collections4</artifactId>
<version>4.4</version> <version>4.4</version>
</dependency> </dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId> <artifactId>commons-math3</artifactId>
@@ -336,7 +326,7 @@
<dependency> <dependency>
<groupId>redis.clients</groupId> <groupId>redis.clients</groupId>
<artifactId>jedis</artifactId> <artifactId>jedis</artifactId>
<version>5.1.2</version> <version>5.1.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.sun.mail</groupId> <groupId>com.sun.mail</groupId>
@@ -373,7 +363,7 @@
<dependency> <dependency>
<groupId>com.ibm.icu</groupId> <groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId> <artifactId>icu4j</artifactId>
<version>74.2</version> <version>75.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.sourceforge.cssparser</groupId> <groupId>net.sourceforge.cssparser</groupId>
@@ -397,13 +387,13 @@
<dependency> <dependency>
<groupId>io.github.hakky54</groupId> <groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart-for-apache5</artifactId> <artifactId>sslcontext-kickstart-for-apache5</artifactId>
<version>8.3.4</version> <version>8.3.6</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.apis</groupId> <groupId>com.google.apis</groupId>
<artifactId>google-api-services-youtube</artifactId> <artifactId>google-api-services-youtube</artifactId>
<version>v3-rev20240310-2.0.0</version> <version>v3-rev20240514-2.0.0</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -419,12 +409,12 @@
<dependency> <dependency>
<groupId>com.mysql</groupId> <groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId> <artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version> <version>8.4.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mariadb.jdbc</groupId> <groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId> <artifactId>mariadb-java-client</artifactId>
<version>3.3.3</version> <version>3.4.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.postgresql</groupId> <groupId>org.postgresql</groupId>
@@ -477,7 +467,7 @@
<dependency> <dependency>
<groupId>com.microsoft.playwright</groupId> <groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId> <artifactId>playwright</artifactId>
<version>1.43.0</version> <version>1.44.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>

View File

@@ -0,0 +1,29 @@
package com.commafeed.backend;
import java.nio.charset.StandardCharsets;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import lombok.experimental.UtilityClass;
@UtilityClass
@SuppressWarnings("deprecation")
public class Digests {
public static String sha1Hex(byte[] input) {
return hashBytesToHex(Hashing.sha1(), input);
}
public static String sha1Hex(String input) {
return hashBytesToHex(Hashing.sha1(), input.getBytes(StandardCharsets.UTF_8));
}
public static String md5Hex(String input) {
return hashBytesToHex(Hashing.md5(), input.getBytes(StandardCharsets.UTF_8));
}
private static String hashBytesToHex(HashFunction function, byte[] input) {
return function.hashBytes(input).toString();
}
}

View File

@@ -35,6 +35,7 @@ import jakarta.inject.Inject;
import jakarta.inject.Singleton; import jakarta.inject.Singleton;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import nl.altindag.ssl.SSLFactory; import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.apache5.util.Apache5SslUtils; import nl.altindag.ssl.apache5.util.Apache5SslUtils;
@@ -43,6 +44,7 @@ import nl.altindag.ssl.apache5.util.Apache5SslUtils;
* *
*/ */
@Singleton @Singleton
@Slf4j
public class HttpGetter { public class HttpGetter {
private final CloseableHttpClient client; private final CloseableHttpClient client;
@@ -70,8 +72,9 @@ public class HttpGetter {
* if the url hasn't changed since we asked for it last time * if the url hasn't changed since we asked for it last time
*/ */
public HttpResult getBinary(String url, String lastModified, String eTag, int timeout) throws IOException, NotModifiedException { public HttpResult getBinary(String url, String lastModified, String eTag, int timeout) throws IOException, NotModifiedException {
long start = System.currentTimeMillis(); log.debug("fetching {}", url);
long start = System.currentTimeMillis();
ClassicHttpRequest request = ClassicRequestBuilder.get(url).build(); ClassicHttpRequest request = ClassicRequestBuilder.get(url).build();
if (lastModified != null) { if (lastModified != null) {
request.addHeader(HttpHeaders.IF_MODIFIED_SINCE, lastModified); request.addHeader(HttpHeaders.IF_MODIFIED_SINCE, lastModified);

View File

@@ -3,8 +3,7 @@ package com.commafeed.backend.cache;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.apache.commons.codec.digest.DigestUtils; import com.commafeed.backend.Digests;
import com.commafeed.backend.feed.parser.FeedParserResult.Entry; import com.commafeed.backend.feed.parser.FeedParserResult.Entry;
import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedSubscription; import com.commafeed.backend.model.FeedSubscription;
@@ -20,7 +19,7 @@ public abstract class CacheService {
public abstract void setLastEntries(Feed feed, List<String> entries); public abstract void setLastEntries(Feed feed, List<String> entries);
public String buildUniqueEntryKey(Entry entry) { public String buildUniqueEntryKey(Entry entry) {
return DigestUtils.sha1Hex(entry.guid() + entry.url()); return Digests.sha1Hex(entry.guid() + entry.url());
} }
// user categories // user categories

View File

@@ -8,7 +8,7 @@ import com.commafeed.backend.model.FeedEntryContent;
import com.commafeed.backend.model.QFeedEntry; import com.commafeed.backend.model.QFeedEntry;
import com.commafeed.backend.model.QFeedEntryContent; import com.commafeed.backend.model.QFeedEntryContent;
import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.JPQLQuery; import com.querydsl.jpa.JPQLSubQuery;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Singleton; import jakarta.inject.Singleton;
@@ -29,7 +29,7 @@ public class FeedEntryContentDAO extends GenericDAO<FeedEntryContent> {
} }
public long deleteWithoutEntries(int max) { public long deleteWithoutEntries(int max) {
JPQLQuery<Integer> subQuery = JPAExpressions.selectOne().from(entry).where(entry.content.id.eq(content.id)); JPQLSubQuery<Integer> subQuery = JPAExpressions.selectOne().from(entry).where(entry.content.id.eq(content.id));
List<Long> ids = query().select(content.id).from(content).where(subQuery.notExists()).limit(max).fetch(); List<Long> ids = query().select(content.id).from(content).where(subQuery.notExists()).limit(max).fetch();
return deleteQuery(content).where(content.id.in(ids)).execute(); return deleteQuery(content).where(content.id.in(ids)).execute();

View File

@@ -16,15 +16,12 @@ 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 final JPAQueryFactory factory;
protected GenericDAO(SessionFactory sessionFactory) { protected GenericDAO(SessionFactory sessionFactory) {
super(sessionFactory); super(sessionFactory);
this.factory = new JPAQueryFactory(this::currentSession);
} }
protected JPAQueryFactory query() { protected JPAQueryFactory query() {
return factory; return new JPAQueryFactory(currentSession());
} }
protected JPAUpdateClause updateQuery(EntityPath<T> entityPath) { protected JPAUpdateClause updateQuery(EntityPath<T> entityPath) {

View File

@@ -5,8 +5,8 @@ import java.time.Instant;
import java.util.Set; import java.util.Set;
import org.apache.commons.codec.binary.StringUtils; import org.apache.commons.codec.binary.StringUtils;
import org.apache.commons.codec.digest.DigestUtils;
import com.commafeed.backend.Digests;
import com.commafeed.backend.HttpGetter; import com.commafeed.backend.HttpGetter;
import com.commafeed.backend.HttpGetter.HttpResult; import com.commafeed.backend.HttpGetter.HttpResult;
import com.commafeed.backend.HttpGetter.NotModifiedException; import com.commafeed.backend.HttpGetter.NotModifiedException;
@@ -68,7 +68,7 @@ public class FeedFetcher {
boolean lastModifiedHeaderValueChanged = !StringUtils.equals(lastModified, result.getLastModifiedSince()); boolean lastModifiedHeaderValueChanged = !StringUtils.equals(lastModified, result.getLastModifiedSince());
boolean etagHeaderValueChanged = !StringUtils.equals(eTag, result.getETag()); boolean etagHeaderValueChanged = !StringUtils.equals(eTag, result.getETag());
String hash = DigestUtils.sha1Hex(content); String hash = Digests.sha1Hex(content);
if (lastContentHash != 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",

View File

@@ -12,11 +12,11 @@ import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.codahale.metrics.Meter; import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.commafeed.backend.Digests;
import com.commafeed.backend.cache.CacheService; import com.commafeed.backend.cache.CacheService;
import com.commafeed.backend.dao.FeedSubscriptionDAO; import com.commafeed.backend.dao.FeedSubscriptionDAO;
import com.commafeed.backend.dao.UnitOfWork; import com.commafeed.backend.dao.UnitOfWork;
@@ -89,7 +89,7 @@ public class FeedRefreshUpdater {
// 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
Content content = entry.content(); Content content = entry.content();
String key2 = DigestUtils.sha1Hex(StringUtils.trimToEmpty(content.content() + content.title())); String key2 = Digests.sha1Hex(StringUtils.trimToEmpty(content.content() + content.title()));
Iterator<Lock> iterator = locks.bulkGet(Arrays.asList(key1, key2)).iterator(); Iterator<Lock> iterator = locks.bulkGet(Arrays.asList(key1, key2)).iterator();
Lock lock1 = iterator.next(); Lock lock1 = iterator.next();

View File

@@ -58,11 +58,11 @@ public class FeedEntryStatus extends AbstractModel {
} }
public FeedEntryStatus(User user, FeedSubscription subscription, FeedEntry entry) { public FeedEntryStatus(User user, FeedSubscription subscription, FeedEntry entry) {
setUser(user); this.user = user;
setSubscription(subscription); this.subscription = subscription;
setEntry(entry); this.entry = entry;
setEntryInserted(entry.getInserted()); this.entryInserted = entry.getInserted();
setEntryUpdated(entry.getUpdated()); this.entryUpdated = entry.getUpdated();
} }
} }

View File

@@ -2,9 +2,9 @@ package com.commafeed.backend.service;
import java.util.Optional; import java.util.Optional;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.commafeed.backend.Digests;
import com.commafeed.backend.dao.FeedEntryContentDAO; import com.commafeed.backend.dao.FeedEntryContentDAO;
import com.commafeed.backend.feed.FeedUtils; import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.feed.parser.FeedParserResult.Content; import com.commafeed.backend.feed.parser.FeedParserResult.Content;
@@ -42,8 +42,8 @@ public class FeedEntryContentService {
private FeedEntryContent buildContent(Content content, String baseUrl) { private FeedEntryContent buildContent(Content content, String baseUrl) {
FeedEntryContent entryContent = new FeedEntryContent(); FeedEntryContent entryContent = new FeedEntryContent();
entryContent.setTitleHash(DigestUtils.sha1Hex(StringUtils.trimToEmpty(content.title()))); entryContent.setTitleHash(Digests.sha1Hex(StringUtils.trimToEmpty(content.title())));
entryContent.setContentHash(DigestUtils.sha1Hex(StringUtils.trimToEmpty(content.content()))); entryContent.setContentHash(Digests.sha1Hex(StringUtils.trimToEmpty(content.content())));
entryContent.setTitle(FeedUtils.truncate(cleaningService.clean(content.title(), baseUrl, true), 2048)); entryContent.setTitle(FeedUtils.truncate(cleaningService.clean(content.title(), baseUrl, true), 2048));
entryContent.setContent(cleaningService.clean(content.content(), baseUrl, false)); entryContent.setContent(cleaningService.clean(content.content(), baseUrl, false));
entryContent.setAuthor(FeedUtils.truncate(cleaningService.clean(content.author(), baseUrl, true), 128)); entryContent.setAuthor(FeedUtils.truncate(cleaningService.clean(content.author(), baseUrl, true), 128));

View File

@@ -3,8 +3,7 @@ package com.commafeed.backend.service;
import java.time.Instant; import java.time.Instant;
import java.util.List; import java.util.List;
import org.apache.commons.codec.digest.DigestUtils; import com.commafeed.backend.Digests;
import com.commafeed.backend.cache.CacheService; import com.commafeed.backend.cache.CacheService;
import com.commafeed.backend.dao.FeedEntryDAO; import com.commafeed.backend.dao.FeedEntryDAO;
import com.commafeed.backend.dao.FeedEntryStatusDAO; import com.commafeed.backend.dao.FeedEntryStatusDAO;
@@ -41,7 +40,7 @@ public class FeedEntryService {
*/ */
public FeedEntry findOrCreate(Feed feed, Entry entry) { public FeedEntry findOrCreate(Feed feed, Entry entry) {
String guid = FeedUtils.truncate(entry.guid(), 2048); String guid = FeedUtils.truncate(entry.guid(), 2048);
String guidHash = DigestUtils.sha1Hex(entry.guid()); String guidHash = Digests.sha1Hex(entry.guid());
FeedEntry existing = feedEntryDAO.findExisting(guidHash, feed); FeedEntry existing = feedEntryDAO.findExisting(guidHash, feed);
if (existing != null) { if (existing != null) {
return existing; return existing;

View File

@@ -2,17 +2,17 @@ package com.commafeed.backend.service;
import java.io.IOException; import java.io.IOException;
import java.time.Instant; import java.time.Instant;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import org.apache.commons.codec.digest.DigestUtils; import com.commafeed.backend.Digests;
import org.apache.commons.io.IOUtils;
import com.commafeed.backend.dao.FeedDAO; import com.commafeed.backend.dao.FeedDAO;
import com.commafeed.backend.favicon.AbstractFaviconFetcher; import com.commafeed.backend.favicon.AbstractFaviconFetcher;
import com.commafeed.backend.favicon.Favicon; import com.commafeed.backend.favicon.Favicon;
import com.commafeed.backend.feed.FeedUtils; import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.Models; import com.commafeed.backend.model.Models;
import com.google.common.io.Resources;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Singleton; import jakarta.inject.Singleton;
@@ -31,7 +31,8 @@ public class FeedService {
this.faviconFetchers = faviconFetchers; this.faviconFetchers = faviconFetchers;
try { try {
defaultFavicon = new Favicon(IOUtils.toByteArray(getClass().getResource("/images/default_favicon.gif")), "image/gif"); defaultFavicon = new Favicon(
Resources.toByteArray(Objects.requireNonNull(getClass().getResource("/images/default_favicon.gif"))), "image/gif");
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("could not load default favicon", e); throw new RuntimeException("could not load default favicon", e);
} }
@@ -39,7 +40,7 @@ public class FeedService {
public synchronized Feed findOrCreate(String url) { public synchronized Feed findOrCreate(String url) {
String normalizedUrl = FeedUtils.normalizeURL(url); String normalizedUrl = FeedUtils.normalizeURL(url);
String normalizedUrlHash = DigestUtils.sha1Hex(normalizedUrl); String normalizedUrlHash = Digests.sha1Hex(normalizedUrl);
Feed feed = feedDAO.findByUrl(normalizedUrl, normalizedUrlHash); Feed feed = feedDAO.findByUrl(normalizedUrl, normalizedUrlHash);
if (feed == null) { if (feed == null) {
feed = new Feed(); feed = new Feed();
@@ -55,7 +56,7 @@ public class FeedService {
public void save(Feed feed) { public void save(Feed feed) {
String normalized = FeedUtils.normalizeURL(feed.getUrl()); String normalized = FeedUtils.normalizeURL(feed.getUrl());
feed.setNormalizedUrl(normalized); feed.setNormalizedUrl(normalized);
feed.setNormalizedUrlHash(DigestUtils.sha1Hex(normalized)); feed.setNormalizedUrlHash(Digests.sha1Hex(normalized));
feed.setLastUpdated(Instant.now()); feed.setLastUpdated(Instant.now());
feed.setEtagHeader(FeedUtils.truncate(feed.getEtagHeader(), 255)); feed.setEtagHeader(FeedUtils.truncate(feed.getEtagHeader(), 255));
feedDAO.saveOrUpdate(feed); feedDAO.saveOrUpdate(feed);

View File

@@ -8,11 +8,11 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.commafeed.CommaFeedApplication; import com.commafeed.CommaFeedApplication;
import com.commafeed.CommaFeedConfiguration; import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.Digests;
import com.commafeed.backend.dao.FeedCategoryDAO; import com.commafeed.backend.dao.FeedCategoryDAO;
import com.commafeed.backend.dao.FeedSubscriptionDAO; import com.commafeed.backend.dao.FeedSubscriptionDAO;
import com.commafeed.backend.dao.UserDAO; import com.commafeed.backend.dao.UserDAO;
@@ -94,7 +94,7 @@ public class UserService {
return Optional.empty(); return Optional.empty();
} }
String computedFeverApiKey = DigestUtils.md5Hex(user.getName() + ":" + user.getApiKey()); String computedFeverApiKey = Digests.md5Hex(user.getName() + ":" + user.getApiKey());
if (!computedFeverApiKey.equals(feverApiKey)) { if (!computedFeverApiKey.equals(feverApiKey)) {
return Optional.empty(); return Optional.empty();
} }
@@ -158,7 +158,7 @@ public class UserService {
public String generateApiKey(User user) { public String generateApiKey(User user) {
byte[] key = encryptionService.getEncryptedPassword(UUID.randomUUID().toString(), user.getSalt()); byte[] key = encryptionService.getEncryptedPassword(UUID.randomUUID().toString(), user.getSalt());
return DigestUtils.sha1Hex(key); return Digests.sha1Hex(key);
} }
public Set<Role> getRoles(User user) { public Set<Role> getRoles(User user) {

View File

@@ -11,7 +11,6 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.glassfish.jersey.media.multipart.FormDataParam; import org.glassfish.jersey.media.multipart.FormDataParam;
@@ -399,20 +398,20 @@ public class FeedREST {
Preconditions.checkNotNull(req.getTitle()); Preconditions.checkNotNull(req.getTitle());
Preconditions.checkNotNull(req.getUrl()); Preconditions.checkNotNull(req.getUrl());
String url = prependHttp(req.getUrl());
try { try {
url = fetchFeedInternal(url).getUrl();
FeedCategory category = null; FeedCategory category = null;
if (req.getCategoryId() != null && !CategoryREST.ALL.equals(req.getCategoryId())) { if (req.getCategoryId() != null && !CategoryREST.ALL.equals(req.getCategoryId())) {
category = feedCategoryDAO.findById(Long.valueOf(req.getCategoryId())); category = feedCategoryDAO.findById(Long.valueOf(req.getCategoryId()));
} }
FeedInfo info = fetchFeedInternal(url);
FeedInfo info = fetchFeedInternal(prependHttp(req.getUrl()));
long subscriptionId = feedSubscriptionService.subscribe(user, info.getUrl(), req.getTitle(), category); long subscriptionId = feedSubscriptionService.subscribe(user, info.getUrl(), req.getTitle(), category);
return Response.ok(subscriptionId).build(); return Response.ok(subscriptionId).build();
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to subscribe to URL {}: {}", url, e.getMessage(), e); log.error("Failed to subscribe to URL {}: {}", req.getUrl(), e.getMessage(), e);
return Response.status(Status.SERVICE_UNAVAILABLE).entity("Failed to subscribe to URL " + url + ": " + e.getMessage()).build(); return Response.status(Status.SERVICE_UNAVAILABLE)
.entity("Failed to subscribe to URL " + req.getUrl() + ": " + e.getMessage())
.build();
} }
} }
@@ -423,14 +422,9 @@ public class FeedREST {
@Timed @Timed
public Response subscribeFromUrl(@Parameter(hidden = true) @SecurityCheck User user, public Response subscribeFromUrl(@Parameter(hidden = true) @SecurityCheck User user,
@Parameter(description = "feed url", required = true) @QueryParam("url") String url) { @Parameter(description = "feed url", required = true) @QueryParam("url") String url) {
try { try {
Preconditions.checkNotNull(url); Preconditions.checkNotNull(url);
FeedInfo info = fetchFeedInternal(prependHttp(url));
url = prependHttp(url);
url = fetchFeedInternal(url).getUrl();
FeedInfo info = fetchFeedInternal(url);
feedSubscriptionService.subscribe(user, info.getUrl(), info.getTitle()); feedSubscriptionService.subscribe(user, info.getUrl(), info.getTitle());
} catch (Exception e) { } catch (Exception e) {
log.info("Could not subscribe to url {} : {}", url, e.getMessage()); log.info("Could not subscribe to url {} : {}", url, e.getMessage());
@@ -529,7 +523,7 @@ public class FeedREST {
return Response.status(Status.FORBIDDEN).entity("Import is disabled for the demo account").build(); return Response.status(Status.FORBIDDEN).entity("Import is disabled for the demo account").build();
} }
try { try {
String opml = IOUtils.toString(input, StandardCharsets.UTF_8); String opml = new String(input.readAllBytes(), StandardCharsets.UTF_8);
opmlImporter.importOpml(user, opml); opmlImporter.importOpml(user, opml);
} catch (Exception e) { } catch (Exception e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);

View File

@@ -6,7 +6,6 @@ import java.util.Collections;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.net.URIBuilder;
@@ -14,6 +13,7 @@ import org.apache.hc.core5.net.URIBuilder;
import com.codahale.metrics.annotation.Timed; import com.codahale.metrics.annotation.Timed;
import com.commafeed.CommaFeedApplication; import com.commafeed.CommaFeedApplication;
import com.commafeed.CommaFeedConfiguration; import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.Digests;
import com.commafeed.backend.dao.UserDAO; import com.commafeed.backend.dao.UserDAO;
import com.commafeed.backend.dao.UserRoleDAO; import com.commafeed.backend.dao.UserRoleDAO;
import com.commafeed.backend.dao.UserSettingsDAO; import com.commafeed.backend.dao.UserSettingsDAO;
@@ -134,8 +134,8 @@ public class UserREST {
s.setLanguage("en"); s.setLanguage("en");
s.setScrollSpeed(400); s.setScrollSpeed(400);
s.setScrollMode(ScrollMode.if_needed.name()); s.setScrollMode(ScrollMode.if_needed.name());
s.setStarIconDisplayMode(IconDisplayMode.always.name()); s.setStarIconDisplayMode(IconDisplayMode.on_desktop.name());
s.setExternalLinkIconDisplayMode(IconDisplayMode.always.name()); s.setExternalLinkIconDisplayMode(IconDisplayMode.on_desktop.name());
s.setMarkAllAsReadConfirmation(true); s.setMarkAllAsReadConfirmation(true);
s.setCustomContextMenu(true); s.setCustomContextMenu(true);
s.setMobileFooter(false); s.setMobileFooter(false);
@@ -292,7 +292,7 @@ public class UserREST {
} }
try { try {
user.setRecoverPasswordToken(DigestUtils.sha1Hex(UUID.randomUUID().toString())); user.setRecoverPasswordToken(Digests.sha1Hex(UUID.randomUUID().toString()));
user.setRecoverPasswordTokenDate(Instant.now()); user.setRecoverPasswordTokenDate(Instant.now());
userDAO.saveOrUpdate(user); userDAO.saveOrUpdate(user);
mailService.sendMail(user, "Password recovery", buildEmailContent(user)); mailService.sendMail(user, "Password recovery", buildEmailContent(user));

View File

@@ -20,9 +20,9 @@ abstract class AbstractCustomCodeServlet extends HttpServlet {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final UnitOfWork unitOfWork; private final transient UnitOfWork unitOfWork;
private final UserDAO userDAO; private final transient UserDAO userDAO;
private final UserSettingsDAO userSettingsDAO; private final transient UserSettingsDAO userSettingsDAO;
@Override @Override
protected final void doGet(final HttpServletRequest req, HttpServletResponse resp) throws IOException { protected final void doGet(final HttpServletRequest req, HttpServletResponse resp) throws IOException {

View File

@@ -0,0 +1,18 @@
package com.commafeed.backend;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class DigestsTest {
@Test
void sha1Hex() {
Assertions.assertEquals("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d", Digests.sha1Hex("hello"));
}
@Test
void md5Hex() {
Assertions.assertEquals("5d41402abc4b2a76b9719d911017c592", Digests.md5Hex("hello"));
}
}

View File

@@ -3,7 +3,6 @@ package com.commafeed.backend.feed;
import java.time.Instant; import java.time.Instant;
import java.util.Set; import java.util.Set;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -17,6 +16,7 @@ import com.commafeed.backend.HttpGetter.HttpResult;
import com.commafeed.backend.HttpGetter.NotModifiedException; import com.commafeed.backend.HttpGetter.NotModifiedException;
import com.commafeed.backend.feed.parser.FeedParser; import com.commafeed.backend.feed.parser.FeedParser;
import com.commafeed.backend.urlprovider.FeedURLProvider; import com.commafeed.backend.urlprovider.FeedURLProvider;
import com.google.gwt.thirdparty.guava.common.hash.Hashing;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class FeedFetcherTest { class FeedFetcherTest {
@@ -43,7 +43,7 @@ class FeedFetcherTest {
String lastModified = "last-modified-1"; String lastModified = "last-modified-1";
String etag = "etag-1"; String etag = "etag-1";
byte[] content = "content".getBytes(); byte[] content = "content".getBytes();
String lastContentHash = DigestUtils.sha1Hex(content); String lastContentHash = Hashing.sha1().hashBytes(content).toString();
Mockito.when(getter.getBinary(url, lastModified, etag, 20000)) Mockito.when(getter.getBinary(url, lastModified, etag, 20000))
.thenReturn(new HttpResult(content, "content-type", "last-modified-2", "etag-2", 20, null)); .thenReturn(new HttpResult(content, "content-type", "last-modified-2", "etag-2", 20, null));

View File

@@ -5,7 +5,9 @@ import org.junit.jupiter.api.extension.ExtendWith;
import com.commafeed.CommaFeedDropwizardAppExtension; import com.commafeed.CommaFeedDropwizardAppExtension;
import com.microsoft.playwright.Locator; import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.assertions.PlaywrightAssertions; import com.microsoft.playwright.assertions.PlaywrightAssertions;
import com.microsoft.playwright.options.AriaRole;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
@@ -17,10 +19,10 @@ class AuthentificationIT extends PlaywrightTestBase {
@Test @Test
void loginFail() { void loginFail() {
page.navigate(getLoginPageUrl()); page.navigate(getLoginPageUrl());
page.locator("[placeholder='User Name or E-mail']").fill("admin"); page.getByPlaceholder("User Name or E-mail").fill("admin");
page.locator("[placeholder='Password']").fill("wrong_password"); page.getByPlaceholder("Password").fill("wrong_password");
page.locator("button:has-text('Log in')").click(); page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Log in")).click();
PlaywrightAssertions.assertThat(page.locator("div[role='alert']")).containsText("wrong username or password"); PlaywrightAssertions.assertThat(page.getByRole(AriaRole.ALERT)).containsText("wrong username or password");
} }
@Test @Test
@@ -33,13 +35,13 @@ class AuthentificationIT extends PlaywrightTestBase {
@Test @Test
void registerFailPasswordTooSimple() { void registerFailPasswordTooSimple() {
page.navigate(getLoginPageUrl()); page.navigate(getLoginPageUrl());
page.locator("text=Sign up!").click(); page.getByText("Sign up!").click();
page.locator("[placeholder='User Name']").fill("user"); page.getByPlaceholder("User Name").fill("user");
page.locator("[placeholder='E-mail address']").fill("user@domain.com"); page.getByPlaceholder("E-mail address").fill("user@domain.com");
page.locator("[placeholder='Password']").fill("pass"); page.getByPlaceholder("Password").fill("pass");
page.locator("button:has-text('Sign up')").click(); page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Sign up")).click();
Locator alert = page.locator("div[role='alert']"); Locator alert = page.getByRole(AriaRole.ALERT);
PlaywrightAssertions.assertThat(alert).containsText("Password must be 8 or more characters in length."); PlaywrightAssertions.assertThat(alert).containsText("Password must be 8 or more characters in length.");
PlaywrightAssertions.assertThat(alert).containsText("Password must contain 1 or more uppercase characters."); PlaywrightAssertions.assertThat(alert).containsText("Password must contain 1 or more uppercase characters.");
PlaywrightAssertions.assertThat(alert).containsText("Password must contain 1 or more digit characters."); PlaywrightAssertions.assertThat(alert).containsText("Password must contain 1 or more digit characters.");
@@ -49,11 +51,11 @@ class AuthentificationIT extends PlaywrightTestBase {
@Test @Test
void registerSuccess() { void registerSuccess() {
page.navigate(getLoginPageUrl()); page.navigate(getLoginPageUrl());
page.locator("text=Sign up!").click(); page.getByText("Sign up!").click();
page.locator("[placeholder='User Name']").fill("user"); page.getByPlaceholder("User Name").fill("user");
page.locator("[placeholder='E-mail address']").fill("user@domain.com"); page.getByPlaceholder("E-mail address").fill("user@domain.com");
page.locator("[placeholder='Password']").fill("MyPassword1!"); page.getByPlaceholder("Password").fill("MyPassword1!");
page.locator("button:has-text('Sign up')").click(); page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Sign up")).click();
PlaywrightAssertions.assertThat(page).hasURL("http://localhost:" + EXT.getLocalPort() + "/#/app/category/all"); PlaywrightAssertions.assertThat(page).hasURL("http://localhost:" + EXT.getLocalPort() + "/#/app/category/all");
} }

View File

@@ -3,7 +3,7 @@ package com.commafeed.e2e;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.time.Instant; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Optional; import java.util.Optional;
@@ -34,7 +34,7 @@ import com.microsoft.playwright.Tracing;
* *
*/ */
@ExtendWith(PlaywrightTestBase.SaveArtifactsOnTestFailed.class) @ExtendWith(PlaywrightTestBase.SaveArtifactsOnTestFailed.class)
public class PlaywrightTestBase { public abstract class PlaywrightTestBase {
private static Playwright playwright; private static Playwright playwright;
private static Browser browser; private static Browser browser;
@@ -115,7 +115,7 @@ public class PlaywrightTestBase {
private String getFileName(ExtensionContext context) { private String getFileName(ExtensionContext context) {
return String.format("%s.%s-%s", context.getRequiredTestClass().getSimpleName(), context.getRequiredTestMethod().getName(), return String.format("%s.%s-%s", context.getRequiredTestClass().getSimpleName(), context.getRequiredTestMethod().getName(),
DateTimeFormatter.ofPattern("yyyy-MM-dd--HH-mm-ss").format(Instant.now())); DateTimeFormatter.ofPattern("yyyy-MM-dd--HH-mm-ss").format(ZonedDateTime.now()));
} }
private void saveScreenshot(PlaywrightTestBase testInstance, String fileName) { private void saveScreenshot(PlaywrightTestBase testInstance, String fileName) {

View File

@@ -1,6 +1,8 @@
package com.commafeed.e2e; package com.commafeed.e2e;
import com.microsoft.playwright.Page; import com.microsoft.playwright.Page;
import com.microsoft.playwright.Page.GetByRoleOptions;
import com.microsoft.playwright.options.AriaRole;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
@@ -8,9 +10,9 @@ import lombok.experimental.UtilityClass;
public class PlaywrightTestUtils { public class PlaywrightTestUtils {
public static void login(Page page) { public static void login(Page page) {
page.locator("[placeholder='User Name or E-mail']").fill("admin"); page.getByPlaceholder("User Name or E-mail").fill("admin");
page.locator("[placeholder='Password']").fill("admin"); page.getByPlaceholder("Password").fill("admin");
page.locator("button:has-text('Log in')").click(); page.getByRole(AriaRole.BUTTON, new GetByRoleOptions().setName("Log in")).click();
} }
} }

Some files were not shown because too many files have changed in this diff Show More