Compare commits

...

105 Commits

Author SHA1 Message Date
ricoberger
8989442fc1 Update Description in README 2024-08-04 15:12:13 +02:00
ricoberger
befe5b5977 Prepare v1.4.0 Release 2024-08-04 14:38:08 +02:00
dependabot[bot]
11585f284c Bump the pub group in /app with 12 updates (#181)
* Bump the pub group in /app with 12 updates

Bumps the pub group in /app with 12 updates:

| Package | From | To |
| --- | --- | --- |
| [app_links](https://github.com/llfbandit/app_links) | `6.1.3` | `6.1.4` |
| [flutter_cache_manager](https://github.com/Baseflow/flutter_cache_manager) | `3.3.2` | `3.4.0` |
| [flutter_native_splash](https://github.com/jonbhanson/flutter_native_splash) | `2.4.0` | `2.4.1` |
| [http](https://github.com/dart-lang/http/tree/master/pkgs) | `1.2.1` | `1.2.2` |
| [just_audio](https://github.com/ryanheise/just_audio) | `0.9.38` | `0.9.39` |
| [just_audio_background](https://github.com/ryanheise/just_audio) | `0.0.1-beta.12` | `0.0.1-beta.13` |
| [just_audio_media_kit](https://github.com/Pato05/just_audio_media_kit) | `2.0.4` | `2.0.5` |
| [package_info_plus](https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus) | `8.0.0` | `8.0.1` |
| [purchases_flutter](https://github.com/RevenueCat/purchases-flutter) | `6.29.4` | `6.30.2` |
| [shared_preferences](https://github.com/flutter/packages/tree/main/packages/shared_preferences) | `2.2.3` | `2.3.0` |
| [supabase_flutter](https://github.com/supabase/supabase-flutter/tree/main/packages) | `2.5.6` | `2.5.11` |
| [timeago](https://github.com/andresaraujo/timeago.dart) | `3.6.1` | `3.7.0` |


Updates `app_links` from 6.1.3 to 6.1.4
- [Release notes](https://github.com/llfbandit/app_links/releases)
- [Commits](https://github.com/llfbandit/app_links/compare/6.1.3...6.1.4)

Updates `flutter_cache_manager` from 3.3.2 to 3.4.0
- [Commits](https://github.com/Baseflow/flutter_cache_manager/compare/flutter_cache_manager_v3.3.2...v3.4.0)

Updates `flutter_native_splash` from 2.4.0 to 2.4.1
- [Release notes](https://github.com/jonbhanson/flutter_native_splash/releases)
- [Changelog](https://github.com/jonbhanson/flutter_native_splash/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jonbhanson/flutter_native_splash/commits)

Updates `http` from 1.2.1 to 1.2.2
- [Release notes](https://github.com/dart-lang/http/releases)
- [Commits](https://github.com/dart-lang/http/commits/http-v1.2.2/pkgs)

Updates `just_audio` from 0.9.38 to 0.9.39
- [Release notes](https://github.com/ryanheise/just_audio/releases)
- [Commits](https://github.com/ryanheise/just_audio/compare/just_audio-v0.9.38...just_audio-v0.9.39)

Updates `just_audio_background` from 0.0.1-beta.12 to 0.0.1-beta.13
- [Release notes](https://github.com/ryanheise/just_audio/releases)
- [Commits](https://github.com/ryanheise/just_audio/compare/just_audio_background-v0.0.1-beta.12...just_audio_background-v0.0.1-beta.13)

Updates `just_audio_media_kit` from 2.0.4 to 2.0.5
- [Changelog](https://github.com/Pato05/just_audio_media_kit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Pato05/just_audio_media_kit/compare/v2.0.4...v2.0.5)

Updates `package_info_plus` from 8.0.0 to 8.0.1
- [Release notes](https://github.com/fluttercommunity/plus_plugins/releases)
- [Commits](https://github.com/fluttercommunity/plus_plugins/commits/package_info_plus-v8.0.1/packages/package_info_plus)

Updates `purchases_flutter` from 6.29.4 to 6.30.2
- [Release notes](https://github.com/RevenueCat/purchases-flutter/releases)
- [Changelog](https://github.com/RevenueCat/purchases-flutter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/RevenueCat/purchases-flutter/compare/6.29.4...6.30.2)

Updates `shared_preferences` from 2.2.3 to 2.3.0
- [Release notes](https://github.com/flutter/packages/releases)
- [Commits](https://github.com/flutter/packages/commits/shared_preferences-v2.3.0/packages/shared_preferences)

Updates `supabase_flutter` from 2.5.6 to 2.5.11
- [Changelog](https://github.com/supabase/supabase-flutter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/supabase/supabase-flutter/commits/supabase_flutter-v2.5.11/packages)

Updates `timeago` from 3.6.1 to 3.7.0
- [Commits](https://github.com/andresaraujo/timeago.dart/commits)

---
updated-dependencies:
- dependency-name: app_links
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: flutter_cache_manager
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: flutter_native_splash
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: http
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: just_audio
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: just_audio_background
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: just_audio_media_kit
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: package_info_plus
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: purchases_flutter
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: shared_preferences
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: supabase_flutter
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: timeago
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update `Podfile.lock`

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ricoberger <mail@ricoberger.de>
2024-08-04 14:17:42 +02:00
dependabot[bot]
9503943b71 Bump the npm group in /landing with 8 updates (#180)
* Bump the npm group in /landing with 8 updates

Bumps the npm group in /landing with 8 updates:

| Package | From | To |
| --- | --- | --- |
| [@headlessui/react](https://github.com/tailwindlabs/headlessui/tree/HEAD/packages/@headlessui-react) | `2.1.1` | `2.1.2` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.14.9` | `22.0.2` |
| [eslint](https://github.com/eslint/eslint) | `8.57.0` | `9.8.0` |
| [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `14.2.4` | `14.2.5` |
| [next](https://github.com/vercel/next.js) | `14.2.4` | `14.2.5` |
| [postcss](https://github.com/postcss/postcss) | `8.4.39` | `8.4.40` |
| [tailwindcss](https://github.com/tailwindlabs/tailwindcss) | `3.4.4` | `3.4.7` |
| [typescript](https://github.com/Microsoft/TypeScript) | `5.5.2` | `5.5.4` |


Updates `@headlessui/react` from 2.1.1 to 2.1.2
- [Release notes](https://github.com/tailwindlabs/headlessui/releases)
- [Changelog](https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-react/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/headlessui/commits/@headlessui/react@v2.1.2/packages/@headlessui-react)

Updates `@types/node` from 20.14.9 to 22.0.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `eslint` from 8.57.0 to 9.8.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.57.0...v9.8.0)

Updates `eslint-config-next` from 14.2.4 to 14.2.5
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.2.5/packages/eslint-config-next)

Updates `next` from 14.2.4 to 14.2.5
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.4...v14.2.5)

Updates `postcss` from 8.4.39 to 8.4.40
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.39...8.4.40)

Updates `tailwindcss` from 3.4.4 to 3.4.7
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/v3.4.7/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v3.4.4...v3.4.7)

Updates `typescript` from 5.5.2 to 5.5.4
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.5.2...v5.5.4)

---
updated-dependencies:
- dependency-name: "@headlessui/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: npm
- dependency-name: eslint
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: npm
- dependency-name: eslint-config-next
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: postcss
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: tailwindcss
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: typescript
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>

* Revert `eslint` Update

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ricoberger <mail@ricoberger.de>
2024-08-04 14:10:17 +02:00
ricoberger
d38cf80818 Merge branch 'main' of github.com:feeddeck/feeddeck 2024-07-27 10:15:32 +02:00
ricoberger
3e03f15825 Update Dependencies for Email Templates 2024-07-27 10:15:06 +02:00
dependabot[bot]
99e0538596 Bump braces from 3.0.2 to 3.0.3 in /landing (#178)
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>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-27 10:13:20 +02:00
dependabot[bot]
2a209519e1 Bump the npm group in /landing with 8 updates (#174)
* Bump the npm group in /landing with 8 updates

Bumps the npm group in /landing with 8 updates:

| Package | From | To |
| --- | --- | --- |
| [@headlessui/react](https://github.com/tailwindlabs/headlessui/tree/HEAD/packages/@headlessui-react) | `2.0.4` | `2.1.1` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.13.0` | `20.14.9` |
| [eslint](https://github.com/eslint/eslint) | `8.57.0` | `9.6.0` |
| [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `14.2.3` | `14.2.4` |
| [next](https://github.com/vercel/next.js) | `14.2.3` | `14.2.4` |
| [postcss](https://github.com/postcss/postcss) | `8.4.38` | `8.4.39` |
| [tailwindcss](https://github.com/tailwindlabs/tailwindcss) | `3.4.3` | `3.4.4` |
| [typescript](https://github.com/Microsoft/TypeScript) | `5.4.5` | `5.5.2` |


Updates `@headlessui/react` from 2.0.4 to 2.1.1
- [Release notes](https://github.com/tailwindlabs/headlessui/releases)
- [Changelog](https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-react/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/headlessui/commits/@headlessui/react@v2.1.1/packages/@headlessui-react)

Updates `@types/node` from 20.13.0 to 20.14.9
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `eslint` from 8.57.0 to 9.6.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.57.0...v9.6.0)

Updates `eslint-config-next` from 14.2.3 to 14.2.4
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.2.4/packages/eslint-config-next)

Updates `next` from 14.2.3 to 14.2.4
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.3...v14.2.4)

Updates `postcss` from 8.4.38 to 8.4.39
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.38...8.4.39)

Updates `tailwindcss` from 3.4.3 to 3.4.4
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/v3.4.4/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v3.4.3...v3.4.4)

Updates `typescript` from 5.4.5 to 5.5.2
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.4.5...v5.5.2)

---
updated-dependencies:
- dependency-name: "@headlessui/react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: eslint
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: npm
- dependency-name: eslint-config-next
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: postcss
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: tailwindcss
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: typescript
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>

* Revert `eslint` Update

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ricoberger <mail@ricoberger.de>
2024-07-27 10:09:22 +02:00
dependabot[bot]
461dc6f384 Bump the pub group in /app with 6 updates (#176)
* Bump the pub group in /app with 6 updates

Bumps the pub group in /app with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [app_links](https://github.com/llfbandit/app_links) | `6.1.1` | `6.1.3` |
| [flutter_markdown](https://github.com/flutter/packages/tree/main/packages) | `0.7.1` | `0.7.3` |
| [purchases_flutter](https://github.com/RevenueCat/purchases-flutter) | `6.29.2` | `6.29.4` |
| [sign_in_with_apple](https://github.com/aboutyou/dart_packages) | `6.1.0` | `6.1.1` |
| [supabase_flutter](https://github.com/supabase/supabase-flutter/tree/main/packages) | `2.5.4` | `2.5.6` |
| [url_launcher](https://github.com/flutter/packages/tree/main/packages/url_launcher) | `6.2.6` | `6.3.0` |


Updates `app_links` from 6.1.1 to 6.1.3
- [Release notes](https://github.com/llfbandit/app_links/releases)
- [Commits](https://github.com/llfbandit/app_links/compare/6.1.1...6.1.3)

Updates `flutter_markdown` from 0.7.1 to 0.7.3
- [Release notes](https://github.com/flutter/packages/releases)
- [Commits](https://github.com/flutter/packages/commits/flutter_markdown-v0.7.3/packages)

Updates `purchases_flutter` from 6.29.2 to 6.29.4
- [Release notes](https://github.com/RevenueCat/purchases-flutter/releases)
- [Changelog](https://github.com/RevenueCat/purchases-flutter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/RevenueCat/purchases-flutter/compare/6.29.2...6.29.4)

Updates `sign_in_with_apple` from 6.1.0 to 6.1.1
- [Release notes](https://github.com/aboutyou/dart_packages/releases)
- [Commits](https://github.com/aboutyou/dart_packages/commits)

Updates `supabase_flutter` from 2.5.4 to 2.5.6
- [Changelog](https://github.com/supabase/supabase-flutter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/supabase/supabase-flutter/commits/supabase_flutter-v2.5.6/packages)

Updates `url_launcher` from 6.2.6 to 6.3.0
- [Release notes](https://github.com/flutter/packages/releases)
- [Commits](https://github.com/flutter/packages/commits/url_launcher-v6.3.0/packages/url_launcher)

---
updated-dependencies:
- dependency-name: app_links
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: flutter_markdown
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: purchases_flutter
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: sign_in_with_apple
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: supabase_flutter
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: url_launcher
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update Podfile.lock

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ricoberger <mail@ricoberger.de>
2024-07-27 10:07:31 +02:00
dependabot[bot]
409aa51f1c Bump docker/build-push-action from 5 to 6 in the github-actions group (#175)
Bumps the github-actions group with 1 update: [docker/build-push-action](https://github.com/docker/build-push-action).


Updates `docker/build-push-action` from 5 to 6
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-27 10:02:17 +02:00
Rico Berger
3620fcb5b0 [core] Wrap Actions in SafeArea Widget (#172)
With this commit we are wrapping all action widgets in a `SafeArea`
widget, to improve the alignment of the actions.
2024-06-08 18:30:04 +02:00
Rico Berger
ca7c22b0d7 Fix ESLint Dependency Mismatch (#171) 2024-06-08 18:20:26 +02:00
dependabot[bot]
318aeb4187 Bump the pub group across 1 directory with 17 updates (#167)
* Bump the pub group across 1 directory with 17 updates

Bumps the pub group with 16 updates in the /app directory:

| Package | From | To |
| --- | --- | --- |
| [app_links](https://github.com/llfbandit/app_links) | `3.5.1` | `6.1.1` |
| [cupertino_icons](https://github.com/flutter/packages/tree/main/third_party/packages) | `1.0.6` | `1.0.8` |
| [flutter_cache_manager](https://github.com/Baseflow/flutter_cache_manager) | `3.3.1` | `3.3.2` |
| [flutter_lints](https://github.com/flutter/packages/tree/main/packages) | `3.0.2` | `4.0.0` |
| [flutter_markdown](https://github.com/flutter/packages/tree/main/packages) | `0.6.22` | `0.7.1` |
| [html2md](https://github.com/jarontai/html2md) | `1.3.1` | `1.3.2` |
| [just_audio](https://github.com/ryanheise/just_audio) | `0.9.37` | `0.9.38` |
| [just_audio_background](https://github.com/ryanheise/just_audio) | `0.0.1-beta.11` | `0.0.1-beta.12` |
| [just_audio_media_kit](https://github.com/Pato05/just_audio_media_kit) | `2.0.2` | `2.0.4` |
| [package_info_plus](https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus) | `6.0.0` | `8.0.0` |
| [purchases_flutter](https://github.com/RevenueCat/purchases-flutter) | `6.24.0` | `6.29.2` |
| [shared_preferences](https://github.com/flutter/packages/tree/main/packages/shared_preferences) | `2.2.2` | `2.2.3` |
| [sign_in_with_apple](https://github.com/aboutyou/dart_packages) | `6.0.0` | `6.1.0` |
| [url_launcher](https://github.com/flutter/packages/tree/main/packages/url_launcher) | `6.2.5` | `6.2.6` |
| [window_manager](https://github.com/leanflutter/window_manager) | `0.3.8` | `0.3.9` |
| [youtube_explode_dart](https://github.com/Hexer10/youtube_explode_dart) | `2.1.0` | `2.2.1` |



Updates `app_links` from 3.5.1 to 6.1.1
- [Release notes](https://github.com/llfbandit/app_links/releases)
- [Changelog](https://github.com/llfbandit/app_links/blob/master/CHANGELOG.md)
- [Commits](https://github.com/llfbandit/app_links/compare/3.5.1...6.1.1)

Updates `cupertino_icons` from 1.0.6 to 1.0.8
- [Release notes](https://github.com/flutter/packages/releases)
- [Commits](https://github.com/flutter/packages/commits/cupertino_icons-v1.0.8/third_party/packages)

Updates `flutter_cache_manager` from 3.3.1 to 3.3.2
- [Commits](https://github.com/Baseflow/flutter_cache_manager/commits/flutter_cache_manager_v3.3.2)

Updates `flutter_lints` from 3.0.2 to 4.0.0
- [Release notes](https://github.com/flutter/packages/releases)
- [Commits](https://github.com/flutter/packages/commits/flutter_lints-v4.0.0/packages)

Updates `flutter_markdown` from 0.6.22 to 0.7.1
- [Release notes](https://github.com/flutter/packages/releases)
- [Commits](https://github.com/flutter/packages/commits/flutter_markdown-v0.7.1/packages)

Updates `html2md` from 1.3.1 to 1.3.2
- [Changelog](https://github.com/jarontai/html2md/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jarontai/html2md/commits)

Updates `just_audio` from 0.9.37 to 0.9.38
- [Release notes](https://github.com/ryanheise/just_audio/releases)
- [Commits](https://github.com/ryanheise/just_audio/compare/just_audio-v0.9.37...just_audio-v0.9.38)

Updates `just_audio_background` from 0.0.1-beta.11 to 0.0.1-beta.12
- [Release notes](https://github.com/ryanheise/just_audio/releases)
- [Commits](https://github.com/ryanheise/just_audio/compare/just_audio_background-v0.0.1-beta.11...just_audio_background-v0.0.1-beta.12)

Updates `just_audio_media_kit` from 2.0.2 to 2.0.4
- [Changelog](https://github.com/Pato05/just_audio_media_kit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Pato05/just_audio_media_kit/compare/v2.0.2...v2.0.4)

Updates `package_info_plus` from 6.0.0 to 8.0.0
- [Release notes](https://github.com/fluttercommunity/plus_plugins/releases)
- [Commits](https://github.com/fluttercommunity/plus_plugins/commits/package_info_plus-v8.0.0/packages/package_info_plus)

Updates `purchases_flutter` from 6.24.0 to 6.29.2
- [Release notes](https://github.com/RevenueCat/purchases-flutter/releases)
- [Changelog](https://github.com/RevenueCat/purchases-flutter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/RevenueCat/purchases-flutter/compare/6.24.0...6.29.2)

Updates `shared_preferences` from 2.2.2 to 2.2.3
- [Release notes](https://github.com/flutter/packages/releases)
- [Commits](https://github.com/flutter/packages/commits/shared_preferences-v2.2.3/packages/shared_preferences)

Updates `sign_in_with_apple` from 6.0.0 to 6.1.0
- [Release notes](https://github.com/aboutyou/dart_packages/releases)
- [Commits](https://github.com/aboutyou/dart_packages/commits)

Updates `supabase_flutter` from 2.3.4 to 2.5.4
- [Changelog](https://github.com/supabase/supabase-flutter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/supabase/supabase-flutter/commits/supabase_flutter-v2.5.4/packages)

Updates `url_launcher` from 6.2.5 to 6.2.6
- [Release notes](https://github.com/flutter/packages/releases)
- [Commits](https://github.com/flutter/packages/commits/url_launcher-v6.2.6/packages/url_launcher)

Updates `window_manager` from 0.3.8 to 0.3.9
- [Release notes](https://github.com/leanflutter/window_manager/releases)
- [Changelog](https://github.com/leanflutter/window_manager/blob/main/CHANGELOG.md)
- [Commits](https://github.com/leanflutter/window_manager/commits/v0.3.9)

Updates `youtube_explode_dart` from 2.1.0 to 2.2.1
- [Release notes](https://github.com/Hexer10/youtube_explode_dart/releases)
- [Changelog](https://github.com/Hexer10/youtube_explode_dart/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Hexer10/youtube_explode_dart/compare/v2.1.0...v2.2.1)

---
updated-dependencies:
- dependency-name: app_links
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: pub
- dependency-name: cupertino_icons
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: flutter_cache_manager
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: flutter_lints
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: pub
- dependency-name: flutter_markdown
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: html2md
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: just_audio
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: just_audio_background
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: just_audio_media_kit
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: package_info_plus
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: pub
- dependency-name: purchases_flutter
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: shared_preferences
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: sign_in_with_apple
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: supabase_flutter
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: url_launcher
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: window_manager
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: youtube_explode_dart
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update Flutter Version and Podfile

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ricoberger <mail@ricoberger.de>
2024-06-08 17:59:59 +02:00
dependabot[bot]
1fb3bf63da Bump the npm group in /landing with 10 updates (#168)
Bumps the npm group in /landing with 10 updates:

| Package | From | To |
| --- | --- | --- |
| [@headlessui/react](https://github.com/tailwindlabs/headlessui/tree/HEAD/packages/@headlessui-react) | `1.7.18` | `2.0.4` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.12.2` | `20.13.0` |
| [eslint](https://github.com/eslint/eslint) | `8.57.0` | `9.4.0` |
| [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `14.1.4` | `14.2.3` |
| [next](https://github.com/vercel/next.js) | `14.1.4` | `14.2.3` |
| [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `18.2.0` | `18.3.1` |
| [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `18.2.73` | `18.3.3` |
| [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) | `18.2.0` | `18.3.1` |
| [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `18.2.23` | `18.3.0` |
| [typescript](https://github.com/Microsoft/TypeScript) | `5.4.3` | `5.4.5` |


Updates `@headlessui/react` from 1.7.18 to 2.0.4
- [Release notes](https://github.com/tailwindlabs/headlessui/releases)
- [Changelog](https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-react/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/headlessui/commits/@headlessui/react@v2.0.4/packages/@headlessui-react)

Updates `@types/node` from 20.12.2 to 20.13.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `eslint` from 8.57.0 to 9.4.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.57.0...v9.4.0)

Updates `eslint-config-next` from 14.1.4 to 14.2.3
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.2.3/packages/eslint-config-next)

Updates `next` from 14.1.4 to 14.2.3
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.1.4...v14.2.3)

Updates `react` 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/v18.3.1/packages/react)

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

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/v18.3.1/packages/react-dom)

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

Updates `typescript` from 5.4.3 to 5.4.5
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.4.3...v5.4.5)

---
updated-dependencies:
- dependency-name: "@headlessui/react"
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: npm
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: eslint
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: npm
- dependency-name: eslint-config-next
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: react
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: "@types/react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: react-dom
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: "@types/react-dom"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: typescript
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-08 16:59:05 +02:00
Rico Berger
aa5c4861db Revert "Bump the npm group in /landing with 10 updates (#165)" (#166)
This reverts commit a7c5afdd83.
2024-05-01 21:08:28 +02:00
dependabot[bot]
a7c5afdd83 Bump the npm group in /landing with 10 updates (#165)
Bumps the npm group in /landing with 10 updates:

| Package | From | To |
| --- | --- | --- |
| [@headlessui/react](https://github.com/tailwindlabs/headlessui/tree/HEAD/packages/@headlessui-react) | `1.7.18` | `1.7.19` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.12.2` | `20.12.7` |
| [eslint](https://github.com/eslint/eslint) | `8.57.0` | `9.1.1` |
| [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `14.1.4` | `14.2.3` |
| [next](https://github.com/vercel/next.js) | `14.1.4` | `14.2.3` |
| [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `18.2.0` | `18.3.1` |
| [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `18.2.73` | `18.3.1` |
| [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) | `18.2.0` | `18.3.1` |
| [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `18.2.23` | `18.3.0` |
| [typescript](https://github.com/Microsoft/TypeScript) | `5.4.3` | `5.4.5` |


Updates `@headlessui/react` from 1.7.18 to 1.7.19
- [Release notes](https://github.com/tailwindlabs/headlessui/releases)
- [Changelog](https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-react/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/headlessui/commits/@headlessui/react@v1.7.19/packages/@headlessui-react)

Updates `@types/node` from 20.12.2 to 20.12.7
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `eslint` from 8.57.0 to 9.1.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.57.0...v9.1.1)

Updates `eslint-config-next` from 14.1.4 to 14.2.3
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.2.3/packages/eslint-config-next)

Updates `next` from 14.1.4 to 14.2.3
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.1.4...v14.2.3)

Updates `react` 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/v18.3.1/packages/react)

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

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/v18.3.1/packages/react-dom)

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

Updates `typescript` from 5.4.3 to 5.4.5
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.4.3...v5.4.5)

---
updated-dependencies:
- dependency-name: "@headlessui/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: eslint
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: npm
- dependency-name: eslint-config-next
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: react
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: "@types/react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: react-dom
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: "@types/react-dom"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: typescript
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-01 20:51:20 +02:00
Rico Berger
38fc655a53 [nitter] Disable Adding of new Sources (#163)
This commit disables the adding of new Nitter sources, because Nitter is
deprecated 😞 and adding new sources is not working anymore. Nitter
sources are also not updated anymore, because the update for Nitter
sources isn't working as well. To ensure that the Nitter sources are not
updated anymore the sources with type `nitter` are skipped in the
scheduler.

The Nitter source isn't removed completly, because we would also have to
cleanup the database and their might be items, which are bookmarked by
users.

See https://github.com/zedeus/nitter/issues/1155#issuecomment-1913361757
2024-04-06 17:22:27 +02:00
Rico Berger
4c34d33ac0 [x] Remove X Source (#162)
X (Twitter) was never supported and the code was never used to add a
source. We kept it in the hope that it will be possible again to use X
within the app. Since this isn't the case it is time to remove the code.
2024-04-06 13:00:12 +02:00
Rico Berger
eb9f84f650 [core] Update Flutter Version to 3.19.5 (#161) 2024-04-06 11:50:22 +02:00
dependabot[bot]
443e2db167 Bump the pub group in /app with 9 updates (#160)
* Bump the pub group in /app with 9 updates

Bumps the pub group in /app with 9 updates:

| Package | From | To |
| --- | --- | --- |
| [app_links](https://github.com/llfbandit/app_links) | `3.5.0` | `3.5.1` |
| [flutter_lints](https://github.com/flutter/packages/tree/main/packages) | `3.0.1` | `3.0.2` |
| [flutter_markdown](https://github.com/flutter/packages/tree/main/packages) | `0.6.20+1` | `0.6.22` |
| [flutter_native_splash](https://github.com/jonbhanson/flutter_native_splash) | `2.3.10` | `2.4.0` |
| [http](https://github.com/dart-lang/http/tree/master/pkgs) | `1.2.0` | `1.2.1` |
| [package_info_plus](https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus) | `5.0.1` | `6.0.0` |
| [just_audio](https://github.com/ryanheise/just_audio) | `0.9.36` | `0.9.37` |
| [just_audio_media_kit](https://github.com/Pato05/just_audio_media_kit) | `2.0.1` | `2.0.2` |
| [package_info_plus](https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus) | `5.0.1` | `6.0.0` |
| [purchases_flutter](https://github.com/RevenueCat/purchases-flutter) | `6.22.0` | `6.24.0` |


Updates `app_links` from 3.5.0 to 3.5.1
- [Release notes](https://github.com/llfbandit/app_links/releases)
- [Changelog](https://github.com/llfbandit/app_links/blob/master/CHANGELOG.md)
- [Commits](https://github.com/llfbandit/app_links/commits/3.5.1)

Updates `flutter_lints` from 3.0.1 to 3.0.2
- [Release notes](https://github.com/flutter/packages/releases)
- [Commits](https://github.com/flutter/packages/commits/flutter_lints-v3.0.2/packages)

Updates `flutter_markdown` from 0.6.20+1 to 0.6.22
- [Release notes](https://github.com/flutter/packages/releases)
- [Commits](https://github.com/flutter/packages/commits/flutter_markdown-v0.6.22/packages)

Updates `flutter_native_splash` from 2.3.10 to 2.4.0
- [Release notes](https://github.com/jonbhanson/flutter_native_splash/releases)
- [Changelog](https://github.com/jonbhanson/flutter_native_splash/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jonbhanson/flutter_native_splash/compare/v2.3.10...v2.4.0)

Updates `http` from 1.2.0 to 1.2.1
- [Release notes](https://github.com/dart-lang/http/releases)
- [Commits](https://github.com/dart-lang/http/commits/http-v1.2.1/pkgs)

Updates `package_info_plus` from 5.0.1 to 6.0.0
- [Release notes](https://github.com/fluttercommunity/plus_plugins/releases)
- [Commits](https://github.com/fluttercommunity/plus_plugins/commits/package_info_plus-v6.0.0/packages/package_info_plus)

Updates `just_audio` from 0.9.36 to 0.9.37
- [Release notes](https://github.com/ryanheise/just_audio/releases)
- [Commits](https://github.com/ryanheise/just_audio/commits)

Updates `just_audio_media_kit` from 2.0.1 to 2.0.2
- [Changelog](https://github.com/Pato05/just_audio_media_kit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Pato05/just_audio_media_kit/compare/v2.0.1...v2.0.2)

Updates `package_info_plus` from 5.0.1 to 6.0.0
- [Release notes](https://github.com/fluttercommunity/plus_plugins/releases)
- [Commits](https://github.com/fluttercommunity/plus_plugins/commits/package_info_plus-v6.0.0/packages/package_info_plus)

Updates `purchases_flutter` from 6.22.0 to 6.24.0
- [Release notes](https://github.com/RevenueCat/purchases-flutter/releases)
- [Changelog](https://github.com/RevenueCat/purchases-flutter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/RevenueCat/purchases-flutter/compare/6.22.0...6.24.0)

---
updated-dependencies:
- dependency-name: app_links
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: flutter_lints
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: flutter_markdown
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: flutter_native_splash
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: http
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: package_info_plus
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: pub
- dependency-name: just_audio
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: just_audio_media_kit
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: package_info_plus
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: pub
- dependency-name: purchases_flutter
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix Podfile.lock

* Update Java and Gradle Version

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ricoberger <mail@ricoberger.de>
2024-04-01 18:33:26 +02:00
dependabot[bot]
347c570cde Bump the github-actions group with 1 update (#159)
Bumps the github-actions group with 1 update: [actions/configure-pages](https://github.com/actions/configure-pages).


Updates `actions/configure-pages` from 4 to 5
- [Release notes](https://github.com/actions/configure-pages/releases)
- [Commits](https://github.com/actions/configure-pages/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/configure-pages
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 17:18:29 +02:00
dependabot[bot]
f8e2fa6128 Bump the npm group in /landing with 9 updates (#158)
Bumps the npm group in /landing with 9 updates:

| Package | From | To |
| --- | --- | --- |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.11.24` | `20.12.2` |
| [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `18.2.61` | `18.2.73` |
| [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `18.2.19` | `18.2.23` |
| [autoprefixer](https://github.com/postcss/autoprefixer) | `10.4.18` | `10.4.19` |
| [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `14.1.1` | `14.1.4` |
| [next](https://github.com/vercel/next.js) | `14.1.1` | `14.1.4` |
| [postcss](https://github.com/postcss/postcss) | `8.4.35` | `8.4.38` |
| [tailwindcss](https://github.com/tailwindlabs/tailwindcss) | `3.4.1` | `3.4.3` |
| [typescript](https://github.com/Microsoft/TypeScript) | `5.3.3` | `5.4.3` |


Updates `@types/node` from 20.11.24 to 20.12.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

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

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

Updates `autoprefixer` from 10.4.18 to 10.4.19
- [Release notes](https://github.com/postcss/autoprefixer/releases)
- [Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/autoprefixer/compare/10.4.18...10.4.19)

Updates `eslint-config-next` from 14.1.1 to 14.1.4
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.1.4/packages/eslint-config-next)

Updates `next` from 14.1.1 to 14.1.4
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.1.1...v14.1.4)

Updates `postcss` from 8.4.35 to 8.4.38
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.35...8.4.38)

Updates `tailwindcss` from 3.4.1 to 3.4.3
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/v3.4.3/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v3.4.1...v3.4.3)

Updates `typescript` from 5.3.3 to 5.4.3
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.3.3...v5.4.3)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: "@types/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@types/react-dom"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: autoprefixer
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: eslint-config-next
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: postcss
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: tailwindcss
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: typescript
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 17:18:06 +02:00
Rico Berger
9dbb979764 [core] Add Indexes to Improve Query Performance (#157)
Until now it could happen that the items for a column could not be
retrieved from the database, because of the set query timeout. In this
case we received the following error:

```
PostgrestException(message: canceling statement due to statement timeout, code: 57014, details: Internal Server Error, hint: null)
```

To fix this issue we added an `items_columnId_idx` index, so that the
items for a column are retrieved fast.

We also added some other useful indexes to improve the overall
performance of our queries.

Last but not least we also added an index to the `userId` columns as it
is recommended by Supabase to improve the performance of our RLS
policies.
2024-03-13 17:51:47 +01:00
dependabot[bot]
062db5d13a Bump the pub group in /app with 3 updates (#151)
* Bump the pub group in /app with 3 updates

Bumps the pub group in /app with 3 updates: [provider](https://github.com/rrousselGit/provider), [purchases_flutter](https://github.com/RevenueCat/purchases-flutter) and [sign_in_with_apple](https://github.com/aboutyou/dart_packages).


Updates `provider` from 6.1.1 to 6.1.2
- [Commits](https://github.com/rrousselGit/provider/compare/provider-v6.1.1...provider-v6.1.2)

Updates `purchases_flutter` from 6.21.1 to 6.22.0
- [Release notes](https://github.com/RevenueCat/purchases-flutter/releases)
- [Changelog](https://github.com/RevenueCat/purchases-flutter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/RevenueCat/purchases-flutter/compare/6.21.1...6.22.0)

Updates `sign_in_with_apple` from 5.0.0 to 6.0.0
- [Release notes](https://github.com/aboutyou/dart_packages/releases)
- [Commits](https://github.com/aboutyou/dart_packages/commits)

---
updated-dependencies:
- dependency-name: provider
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: purchases_flutter
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: sign_in_with_apple
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: pub
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update Flutter Version and Podfile

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ricoberger <mail@ricoberger.de>
2024-03-02 11:08:50 +01:00
dependabot[bot]
788c7c9440 Bump the npm group in /landing with 2 updates (#156)
Bumps the npm group in /landing with 2 updates: [autoprefixer](https://github.com/postcss/autoprefixer) and [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next).


Updates `autoprefixer` from 10.4.17 to 10.4.18
- [Release notes](https://github.com/postcss/autoprefixer/releases)
- [Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/autoprefixer/compare/10.4.17...10.4.18)

Updates `eslint-config-next` from 14.1.0 to 14.1.1
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.1.1/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: autoprefixer
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: eslint-config-next
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-02 00:50:41 +01:00
ricoberger
aed602c55e [core] Adjust Dependabot Configuration
Adjust the Dependabot configuration to not update the
`/supabase/email-templates` folder anymore. This is done, because
Dependabot couldn't handle the `package.json` and `package-lock.json`
file very well and always wants to delete some required stuff.

Since the folder only contains our email templates it is also not
critical if the dependencies are not up to date.
2024-03-02 00:45:39 +01:00
dependabot[bot]
24f4648274 Bump the docker group in /supabase/functions/_cmd with 1 update (#153)
Bumps the docker group in /supabase/functions/_cmd with 1 update: lukechannings/deno.


Updates `lukechannings/deno` from v1.40.3 to v1.40.5

---
updated-dependencies:
- dependency-name: lukechannings/deno
  dependency-type: direct:production
  dependency-group: docker
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-02 00:22:45 +01:00
dependabot[bot]
d29d601513 Bump the github-actions group with 1 update (#152)
Bumps the github-actions group with 1 update: [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter).


Updates `release-drafter/release-drafter` from 5 to 6
- [Release notes](https://github.com/release-drafter/release-drafter/releases)
- [Commits](https://github.com/release-drafter/release-drafter/compare/v5...v6)

---
updated-dependencies:
- dependency-name: release-drafter/release-drafter
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-02 00:22:11 +01:00
dependabot[bot]
f21d38b080 Bump the npm-landing group in /landing with 6 updates (#150)
Bumps the npm-landing group in /landing with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.11.15` | `20.11.24` |
| [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `18.2.51` | `18.2.61` |
| [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `18.2.18` | `18.2.19` |
| [eslint](https://github.com/eslint/eslint) | `8.56.0` | `8.57.0` |
| [next](https://github.com/vercel/next.js) | `14.1.0` | `14.1.1` |
| [postcss](https://github.com/postcss/postcss) | `8.4.33` | `8.4.35` |


Updates `@types/node` from 20.11.15 to 20.11.24
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

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

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

Updates `eslint` from 8.56.0 to 8.57.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.56.0...v8.57.0)

Updates `next` from 14.1.0 to 14.1.1
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.1.0...v14.1.1)

Updates `postcss` from 8.4.33 to 8.4.35
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.33...8.4.35)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: "@types/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: "@types/react-dom"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: eslint
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-landing
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: postcss
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-02 00:21:33 +01:00
ricoberger
a0c64508c9 Update License 2024-02-25 16:36:47 +01:00
Rico Berger
11084f04e9 [core] Upgrade Flutter Version (#149)
Upgrade the used version of Flutter to 3.19.1 and all used packages to
their latest version.

For the Android Version of the app, we also had to adjust some files,
because of the "Deprecated imperative apply of Flutter's Gradle
plugins". The steps we had to run for this can be found at
https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply
2024-02-25 16:23:33 +01:00
dependabot[bot]
2a8aa5f3c1 Bump the npm-email-templates group in /supabase/email-templates with 2 updates (#115)
* Bump the npm-email-templates group

Bumps the npm-email-templates group in /supabase/email-templates with 2 updates: [@react-email/components](https://github.com/resend/react-email/tree/HEAD/packages/components) and [react-email](https://github.com/resend/react-email/tree/HEAD/packages/react-email).


Updates `@react-email/components` from 0.0.11 to 0.0.14
- [Release notes](https://github.com/resend/react-email/releases)
- [Commits](https://github.com/resend/react-email/commits/v0.0.14/packages/components)

Updates `react-email` from 1.9.5 to 2.0.0
- [Release notes](https://github.com/resend/react-email/releases)
- [Commits](https://github.com/resend/react-email/commits/HEAD/packages/react-email)

---
updated-dependencies:
- dependency-name: "@react-email/components"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-email-templates
- dependency-name: react-email
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: npm-email-templates
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump the npm-email-templates group

Bumps the npm-email-templates group in /supabase/email-templates with 2 updates: [@react-email/components](https://github.com/resend/react-email/tree/HEAD/packages/components) and [react-email](https://github.com/resend/react-email/tree/HEAD/packages/react-email).


Updates `@react-email/components` from 0.0.11 to 0.0.14
- [Release notes](https://github.com/resend/react-email/releases)
- [Commits](https://github.com/resend/react-email/commits/v0.0.14/packages/components)

Updates `react-email` from 1.9.5 to 2.0.0
- [Release notes](https://github.com/resend/react-email/releases)
- [Commits](https://github.com/resend/react-email/commits/HEAD/packages/react-email)

---
updated-dependencies:
- dependency-name: "@react-email/components"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-email-templates
- dependency-name: react-email
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: npm-email-templates
...

Signed-off-by: dependabot[bot] <support@github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ricoberger <mail@ricoberger.de>
2024-02-24 09:09:40 +01:00
Rico Berger
c64b8026ae [core] Fix Blockquote Style (#148)
This commit fixes the style of blockquotes when we render the item
description as Markdown. Until now blockquotes had a weird blue
background, so that the text wasn't readable.

Now we are using the secondary color as background and we add a border on
the left site with the primary color.

We would also like to use italic as font family, but there is currently
a bug, so that the defined blockquote style is not applied. See
https://github.com/flutter/flutter/issues/81720
2024-02-23 18:39:10 +01:00
Rico Berger
ed76d85fd8 [core] Use Upstream just_audio_media_kit Version (#147)
Use the newest upstream version of the `just_audio_media_kit` package,
which removes the hard dependencies on the `media_kit_libs_linux` and
`media_kit_libs_windows_audio` packages, so that it shouldn't conflict
anymore with the usage of the `media_kit_libs_video` package.
2024-02-22 20:47:34 +01:00
Rico Berger
b2b3723ba8 [4chan] Improve Options Parsing (#146)
A 4chan source can now also be added via the RSS source type in the
Flutter app. If a user provides the RSS feed of an 4chan board, we check
if the url is related to 4chan and if this is the case, we use the 4chan
  instead of the RSS source type, similar to how it is handled for other
  sources like Medium, Reddit, etc.
2024-02-16 19:17:59 +01:00
Rico Berger
ca9ce54061 [mastodon] Add Support for Piped Videos (#145)
If a Mastodon post contains a Piped video it can now be played directly
within the app, similar to how it is handled for Nitter posts.
2024-02-14 19:01:56 +01:00
Rico Berger
8199c451b1 [reddit] Add Support for Piped Videos (#144)
If a Reddit post contains a Piped video it can now be played directly
within the app, similar to how it is handled in Nitter posts.
2024-02-14 19:00:51 +01:00
Rico Berger
66173d5a38 [lemmy] Add Support for Piped Videos (#143)
If a Lemmy post contains a Piped video it can now by played directly in
the app, similar to how it is handled for Nitter posts.
2024-02-14 18:59:14 +01:00
ricoberger
a5fba341d3 [core] Sort Imports 2024-02-14 18:36:54 +01:00
Rico Berger
689d3bd39b [4chan] Add Support for 4chan (#142)
This commit adds support for 4chan. This means that 4chan can be
selected as a new source type. When the 4chan source is selected a user
can select a board from which he wants to get the RSS feed.
2024-02-14 13:05:10 +01:00
Rico Berger
0b077ae973 [rss] Fix Image Parsing (#141)
The images of an RSS feed can now also start with `http://`, which is
necessary for some feeds.
2024-02-14 13:03:13 +01:00
Rico Berger
9a6bb033bf [core] Remove referer Check in image-proxy-v1 Function (#140) 2024-02-12 22:51:47 +01:00
ricoberger
7b84bab217 Prepare v1.3.0 Release 2024-02-12 19:51:12 +01:00
Rico Berger
1a56a8996e [core] Increase Update Interval for Reddit and Nitter (#138)
Reddit and Nitter are heavlily rate limited from time to time, to not
effect our premium users, by these limits, we decided to increase the
interval in which the Reddit and Nitter sources are updated for users
which are on the free tier.
2024-02-12 19:44:14 +01:00
Rico Berger
37bcc5e026 [nitter] Add Support for Piped Videos (#136)
The Nitter source now supports playing Piped Videos directly within the
app. For this we are checking if the Nitter post contains a piped.video
url and if this is the case we are using the newly added
`ItemPipedVideo` widget to render the video player, to allow users to
directly play the video within the app.

To support Piped videos we had to create a new `ItemPipedVideo` widget,
which is very similar to the `ItemYoutubeVideo` widget. This means on
the web version of FeedDeck we show the Video via an iframe and on the
other platforms via our `ItemVideoPlayer` widget. The main difference
between Piped and YouTube widget is the different client we use to fetch
the video urls. Besides the Piped API returns two different stream one
for the video and one for the audio, so that we had to add an additional
`audio` paramter to the `ItemVideoPlayer` widget, which allows us to
specify an additional audio source for a video.

NOTE: We had to add support for Piped, because Nitter automatically
converts the YouTube urls to the corresponding Piped urls.
2024-02-11 21:52:15 +01:00
Rico Berger
6a158f5176 [landing] Add Feature Images for Mobile Screens (#137)
Until now we always displayed the same feature images on the landing
page and it didn't matter if the user was on a desktop or mobile device.
This is now changed so that we display different images on mobile
devices (depending on the screen resolution).

Besides that we also moved the deprecated `themeColor` from the
`Metadata` to the `Viewport` and added it to all pages.

Last but not least we added some missing Android icons, which were
referenced in the `manifest.json` file, but where the icon didn't exist.
2024-02-11 21:26:47 +01:00
Rico Berger
0b7ca6cb14 [mastodon] Add Support for YouTube Videos (#135)
The Mastodon source supports YouTube Videos now. This means that if a
Mastodon post contains a link to a YouTube video as user can now
directly watch the video within the app.

If the post contains a video we render the YouTube video and the
description instead of the description, images and videos.
2024-02-10 21:25:16 +01:00
Rico Berger
303f78c3bc [reddit] Add Support for YouTube Videos (#134)
The Reddit source supports YouTube videos now. This means if a Reddit
post contains a link to a YouTube video, we render the video in the item
details, so that a user can directly watch the video.
2024-02-10 21:04:09 +01:00
ricoberger
c0c87e2c10 [core] Sort Imports 2024-02-10 20:43:18 +01:00
Rico Berger
babce57c80 [core] Fix Password Validations (#133)
This commit fixes the password validations. In #130 we introduced some
stronger password policies to forbid weak passwords, but forgot to
change it in all places. Now the same rules are also applying when a
user changes his password or resets his password.

During the sign in we do not use the same rules, to not block users
which have already signed up, with a password which doesn't match the
rules.
2024-02-10 15:31:56 +01:00
Rico Berger
ca5866ac13 [core] Add Continuous Delivery Workflow for Linux arm64 (#132)
Until now we had to manually build the app for Linux arm64 when we
created a new release. Now we are using ubicloud and the
`ubicloud-standard-2-arm` GitHub Action runner to build the app for
Linux arm64.
2024-02-10 14:58:27 +01:00
ricoberger
508e255c8b [core] Fix File 2024-02-06 21:05:40 +01:00
Rico Berger
aeeea4fd95 [core] Update macOS GitHub Action Runners (#131)
Update the used GitHub Action runners to `macos-14`, see
https://github.blog/changelog/2024-01-30-github-actions-introducing-the-new-m1-macos-runner-available-to-open-source/
2024-02-02 17:36:46 +01:00
Rico Berger
6029ee539e [core] Forbid Weak Passwords (#130)
This commit improves our password policy, so that every user which signs
up must have a password with a minimum length of 8 characters, one upper
and lower case letter and one number.
2024-02-02 17:22:53 +01:00
Rico Berger
c9a596111c [rss] Do Not Remove HTML Tags (#129)
We do not remove HTML tags from the description of a RSS feed item
anymore, because:

- This didn't worked for all feeds, because we first removed the tags
  and then unescape the data, which didn't make sense
- We render the HTML to Makrdown in the frontend so that the description
  can contain HTML tags and is still properly rendered and in some case
  better rendered then before
2024-02-02 17:04:43 +01:00
Rico Berger
b8a73cc003 [core] Make Log Level Configurable (#128)
The log level is now configurable, via the `FEEDDECK_LOG_LEVEL`
environment variable. The environment variable can have the following
values: `debug`, `info`, `warning` or `error`.

With this change we also only log the response when getting and parsing
the feed fails when the log level is set to `debug`, also when the
actual message is still an error. This should reduce the noice in the
logs a lot and allows us to specially turn this on while debugging.
2024-02-02 16:52:14 +01:00
ricoberger
817eb4d9e8 [core] Update File 2024-02-02 16:06:44 +01:00
dependabot[bot]
20e3e736c2 Bump the docker group in /supabase/functions/_cmd with 1 update (#127)
Bumps the docker group in /supabase/functions/_cmd with 1 update: lukechannings/deno.


Updates `lukechannings/deno` from v1.40.2 to v1.40.3

---
updated-dependencies:
- dependency-name: lukechannings/deno
  dependency-type: direct:production
  dependency-group: docker
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-02 00:06:02 +01:00
dependabot[bot]
20352c0301 Bump the npm-landing group in /landing with 2 updates (#126)
Bumps the npm-landing group in /landing with 2 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react).


Updates `@types/node` from 20.11.13 to 20.11.15
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

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

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: "@types/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-02 00:05:29 +01:00
Rico Berger
e7b7000f46 [core] Add Open Link Action (#125)
This commit adds a new action to the menu shown when a user clicks
longer on the item in a column. The new "Open Link" action allows a user
to directly open the link of the item, so that the details modal must
not be opened anymore to open the link.
2024-01-31 21:09:34 +01:00
Rico Berger
fb3bec623a [core] Add Missing SafeArea Widget (#124)
Several widgets which where rendered within a modal bottom sheet didn't
used a `SafeArea` widget in the body of the `Scaffold` widget, so that
the action buttons on the bottom of the widget where not rendered in the
correct position.
2024-01-31 20:54:50 +01:00
dependabot[bot]
976c066004 Bump the pub group in /app with 2 updates (#121)
* Bump the pub group in /app with 2 updates

Bumps the pub group in /app with 2 updates: [purchases_flutter](https://github.com/RevenueCat/purchases-flutter) and [supabase_flutter](https://github.com/supabase/supabase-flutter/tree/main/packages).


Updates `purchases_flutter` from 6.18.0 to 6.19.0
- [Release notes](https://github.com/RevenueCat/purchases-flutter/releases)
- [Changelog](https://github.com/RevenueCat/purchases-flutter/blob/6.19.0/CHANGELOG.md)
- [Commits](https://github.com/RevenueCat/purchases-flutter/compare/6.18.0...6.19.0)

Updates `supabase_flutter` from 2.3.1 to 2.3.2
- [Changelog](https://github.com/supabase/supabase-flutter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/supabase/supabase-flutter/commits/HEAD/packages)

---
updated-dependencies:
- dependency-name: purchases_flutter
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: supabase_flutter
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update Flutter Version

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ricoberger <mail@ricoberger.de>
2024-01-31 20:28:49 +01:00
dependabot[bot]
304a9744a9 Bump the npm-landing group in /landing with 1 update (#120)
Bumps the npm-landing group in /landing with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@types/node` from 20.11.10 to 20.11.13
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-31 19:51:34 +01:00
Rico Berger
ee11cae8dc [landing] Add Lemmy Icon (#123)
Add an icon for Lemmy to the landing page. The new `lemmy` source type
was added in #94 and is now also added to the landing page for the next
release which should happen very soon.
2024-01-31 19:37:50 +01:00
Rico Berger
04314f116d [core] Fix Naming of Files (#122)
Fix the naming of some files: Instead of login we are always using the
term sign in and already renamed the `DesktopLoginManager` to
`DesktopSignInManager` in #106. During the renaming we forgot to rename
the file which is now done.

We also renamed the `sign_in_with_apple.dart` file to
`signin_with_apple.dart`, because the other files also do not contain an
underscore between sign and in.
2024-01-31 19:32:23 +01:00
ricoberger
b645244378 [core] Fix Assignees in Dependabot Configuration 2024-01-31 19:12:09 +01:00
dependabot[bot]
110ff56aa1 Bump the pub group in /app with 9 updates (#119)
* Bump the pub group in /app with 9 updates

Bumps the pub group in /app with 9 updates:

| Package | From | To |
| --- | --- | --- |
| [cached_network_image](https://github.com/Baseflow/flutter_cached_network_image) | `3.3.0` | `3.3.1` |
| [flutter_native_splash](https://github.com/jonbhanson/flutter_native_splash) | `2.3.8` | `2.3.10` |
| [http](https://github.com/dart-lang/http/tree/master/pkgs) | `1.1.2` | `1.2.0` |
| [intl](https://github.com/dart-lang/i18n/tree/main/pkgs) | `0.18.1` | `0.19.0` |
| [purchases_flutter](https://github.com/RevenueCat/purchases-flutter) | `6.5.1` | `6.18.0` |
| [supabase_flutter](https://github.com/supabase/supabase-flutter/tree/main/packages) | `2.0.2` | `2.3.1` |
| [url_launcher](https://github.com/flutter/packages/tree/main/packages/url_launcher) | `6.2.2` | `6.2.4` |
| [window_manager](https://github.com/leanflutter/window_manager) | `0.3.7` | `0.3.8` |
| [youtube_explode_dart](https://github.com/Hexer10/youtube_explode_dart) | `2.0.4` | `2.1.0` |


Updates `cached_network_image` from 3.3.0 to 3.3.1
- [Commits](https://github.com/Baseflow/flutter_cached_network_image/compare/v3.3.0...v3.3.1)

Updates `flutter_native_splash` from 2.3.8 to 2.3.10
- [Release notes](https://github.com/jonbhanson/flutter_native_splash/releases)
- [Changelog](https://github.com/jonbhanson/flutter_native_splash/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jonbhanson/flutter_native_splash/commits)

Updates `http` from 1.1.2 to 1.2.0
- [Release notes](https://github.com/dart-lang/http/releases)
- [Commits](https://github.com/dart-lang/http/commits/HEAD/pkgs)

Updates `intl` from 0.18.1 to 0.19.0
- [Release notes](https://github.com/dart-lang/i18n/releases)
- [Commits](https://github.com/dart-lang/i18n/commits/intl-v0.19.0/pkgs)

Updates `purchases_flutter` from 6.5.1 to 6.18.0
- [Release notes](https://github.com/RevenueCat/purchases-flutter/releases)
- [Changelog](https://github.com/RevenueCat/purchases-flutter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/RevenueCat/purchases-flutter/compare/6.5.1...6.18.0)

Updates `supabase_flutter` from 2.0.2 to 2.3.1
- [Changelog](https://github.com/supabase/supabase-flutter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/supabase/supabase-flutter/commits/HEAD/packages)

Updates `url_launcher` from 6.2.2 to 6.2.4
- [Release notes](https://github.com/flutter/packages/releases)
- [Commits](https://github.com/flutter/packages/commits/url_launcher-v6.2.4/packages/url_launcher)

Updates `window_manager` from 0.3.7 to 0.3.8
- [Release notes](https://github.com/leanflutter/window_manager/releases)
- [Changelog](https://github.com/leanflutter/window_manager/blob/main/CHANGELOG.md)
- [Commits](https://github.com/leanflutter/window_manager/commits)

Updates `youtube_explode_dart` from 2.0.4 to 2.1.0
- [Release notes](https://github.com/Hexer10/youtube_explode_dart/releases)
- [Changelog](https://github.com/Hexer10/youtube_explode_dart/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Hexer10/youtube_explode_dart/compare/v2.0.4...v2.1.0)

---
updated-dependencies:
- dependency-name: cached_network_image
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: flutter_native_splash
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: http
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: intl
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: purchases_flutter
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: supabase_flutter
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
- dependency-name: url_launcher
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: window_manager
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
- dependency-name: youtube_explode_dart
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pub
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix Podfile.lock

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ricoberger <mail@ricoberger.de>
2024-01-31 19:10:56 +01:00
ricoberger
bbbeb9524f [core] Remove Deprecate Edge Functions
This commit removes the deprecated edge functions `add-source-v1` and
`profile-v1` from the stage deployment.
2024-01-31 19:07:01 +01:00
Rico Berger
90fc7532ba [core] Add Client Side Scraping of Sources (#118)
It is now possible to add and update sources via client side scraping.
For that a new edge function `add-or-update-source-v1` was added and the
old `add-source-v1` function was deprecated.

The new function accepts a new `feedData` field, which can contain the
feed for a source. If the field is provided we will not try to get the
feed for a source within our edge function and instead use the provided
data.

Currently this function is only used to add a Reddit source. Later we
plan to extend it for other sources and want to use it to update source
via the app, when the source provider makes heavy use of rate limiting.
2024-01-30 21:59:42 +01:00
dependabot[bot]
fac622ef97 Bump the docker group in /supabase/functions/_cmd with 1 update (#114)
Bumps the docker group in /supabase/functions/_cmd with 1 update: lukechannings/deno.


Updates `lukechannings/deno` from v1.38.4 to v1.40.2

---
updated-dependencies:
- dependency-name: lukechannings/deno
  dependency-type: direct:production
  dependency-group: docker
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-30 21:49:44 +01:00
dependabot[bot]
e29b94a576 Bump the github-actions group with 4 updates (#110)
Bumps the github-actions group with 4 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact), [actions/configure-pages](https://github.com/actions/configure-pages), [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) and [actions/deploy-pages](https://github.com/actions/deploy-pages).


Updates `actions/upload-artifact` from 3 to 4
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

Updates `actions/configure-pages` from 3 to 4
- [Release notes](https://github.com/actions/configure-pages/releases)
- [Commits](https://github.com/actions/configure-pages/compare/v3...v4)

Updates `actions/upload-pages-artifact` from 2 to 3
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](https://github.com/actions/upload-pages-artifact/compare/v2...v3)

Updates `actions/deploy-pages` from 2 to 4
- [Release notes](https://github.com/actions/deploy-pages/releases)
- [Commits](https://github.com/actions/deploy-pages/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/configure-pages
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/upload-pages-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/deploy-pages
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-30 21:49:03 +01:00
dependabot[bot]
911b3691b3 Bump the npm-landing group in /landing with 11 updates (#116)
Bumps the npm-landing group in /landing with 11 updates:

| Package | From | To |
| --- | --- | --- |
| [@headlessui/react](https://github.com/tailwindlabs/headlessui/tree/HEAD/packages/@headlessui-react) | `1.7.17` | `1.7.18` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.10.1` | `20.11.10` |
| [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `18.2.39` | `18.2.48` |
| [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `18.2.17` | `18.2.18` |
| [autoprefixer](https://github.com/postcss/autoprefixer) | `10.4.16` | `10.4.17` |
| [eslint](https://github.com/eslint/eslint) | `8.54.0` | `8.56.0` |
| [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `14.0.3` | `14.1.0` |
| [next](https://github.com/vercel/next.js) | `14.0.3` | `14.1.0` |
| [postcss](https://github.com/postcss/postcss) | `8.4.31` | `8.4.33` |
| [tailwindcss](https://github.com/tailwindlabs/tailwindcss) | `3.3.5` | `3.4.1` |
| [typescript](https://github.com/Microsoft/TypeScript) | `5.3.2` | `5.3.3` |


Updates `@headlessui/react` from 1.7.17 to 1.7.18
- [Release notes](https://github.com/tailwindlabs/headlessui/releases)
- [Changelog](https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-react/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/headlessui/commits/@headlessui/react@v1.7.18/packages/@headlessui-react)

Updates `@types/node` from 20.10.1 to 20.11.10
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

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

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

Updates `autoprefixer` from 10.4.16 to 10.4.17
- [Release notes](https://github.com/postcss/autoprefixer/releases)
- [Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/autoprefixer/compare/10.4.16...10.4.17)

Updates `eslint` from 8.54.0 to 8.56.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.54.0...v8.56.0)

Updates `eslint-config-next` from 14.0.3 to 14.1.0
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.1.0/packages/eslint-config-next)

Updates `next` from 14.0.3 to 14.1.0
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.0.3...v14.1.0)

Updates `postcss` from 8.4.31 to 8.4.33
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.31...8.4.33)

Updates `tailwindcss` from 3.3.5 to 3.4.1
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v3.3.5...v3.4.1)

Updates `typescript` from 5.3.2 to 5.3.3
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.3.2...v5.3.3)

---
updated-dependencies:
- dependency-name: "@headlessui/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-landing
- dependency-name: "@types/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: "@types/react-dom"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: autoprefixer
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: eslint
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-landing
- dependency-name: eslint-config-next
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-landing
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-landing
- dependency-name: postcss
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: tailwindcss
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-landing
- dependency-name: typescript
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-30 21:48:09 +01:00
Rico Berger
3a84376223 Add Assignees to Dependabot Configuration (#113) 2024-01-30 19:41:03 +01:00
Rico Berger
0f5a8e44f1 [core] Add Additional Header for Web Deployment (#112)
This commit adds the `X-Content-Type-Options`, `X-Frame-Options` and
`Referrer-Policy` headers to the web deployment of FeedDeck via the
`_headers` file.

See https://developers.cloudflare.com/pages/configuration/headers/
2024-01-30 19:27:47 +01:00
Rico Berger
5753fb2714 Update Flutter to Version 3.16.5 (#106)
Update the used Flutter version to 3.16.5 and the used packages to their
latest version.

The Supabase package contained some breaking changes:
- `functionUrl` is not exported anymore, so that it must be generated by
  ourselfs
- `Provider` was renamed to `OAuthProvider`
- The `signInWithApple` method was removed and is now implemented by us
  via the `sign_in_with_apple` package.

We also renamed the `DesktopLoginManager` to `DesktopSignInManager` to
use the same naming as in other places of the app, where we are always
using sign in and not login.
2023-12-21 17:24:07 +01:00
ricoberger
4008660a35 [core] Fix deno test Commands in Contributing Guidelines 2023-12-20 09:47:27 +01:00
Rico Berger
1cb58e1e0f [core] Refactor Tools and add get-feed Tool (#105)
This commit refactors the existing tools, by moving the tools logic to a
new `tools.ts` file, so that the main `cmd.ts` file remains clear.

Besides that we also add a new tool `get-feed` which can be used to run
the `getFeed` function from the command line. The function is called
with a source and returns the generated source and items, as they are
saved in the database by the `add-source-v1` Supabase edge function.
2023-12-17 18:04:53 +01:00
ricoberger
08e9170a80 [nitter] Adjust Header Handling 2023-12-17 13:51:03 +01:00
Rico Berger
4198a5bac6 [core] Replace Deprecated serve Function (#104)
The used `serve` function from the `std/server` module is deprecated and
must be replaced with `Deno.serve`. This was done within this commit.
2023-12-17 12:37:00 +01:00
ricoberger
295ae13705 [core] Remove deno.lock File 2023-12-17 12:11:31 +01:00
Rico Berger
0894f0e777 [core] Improve Error Handling for Feed Edge Functions (#103)
This commit improve the error handling for the edge function to add a
new source and the worker, so that we get more insights why a request
fails. A user will now also get a more detailed error why a source could
not be added.
2023-12-17 12:10:04 +01:00
ricoberger
2966ecc651 [rss] Fix Test Name 2023-12-12 22:13:47 +01:00
Rico Berger
49c168b5b2 [core] Disable Right Click for Item Actions on Web (#102)
On the web the right click on an item to show the actions, doesn't work
properly, because the browsers right click menu will be shown first. So
it doesn't make sense to show also our right click menu.
2023-12-12 22:03:32 +01:00
Rico Berger
8e0017e928 [rss] Show Videos from RSS Feeds (#101)
If an RSS feed contains a video within the `attachments` field, the video
will now be added to the `options` field of the item. In the Flutter
code we then check if the video field is present in the options and show
the video instead of an image in the details view of the item.
2023-12-12 21:49:45 +01:00
Rico Berger
982add8fbb [core] Update Deno Modules (#100)
This commit updates all used Deno modules to their latest version.

Since some of the used modules / functions were deprecated we had to
adjust our encrypt / descrypt functions and the generation of the source
and item ids, where we have to use a new md5 function.
2023-12-12 20:32:44 +01:00
Rico Berger
8065e19c85 [rss] Parse Atom and RDF Feeds from Websites (#99)
Until now we only checked if a website contained a RSS feed which can be
used for FeedDeck. Now we are also checking if the website contains a
Atom or RDF feed when no RSS feed was found.

For this we are checking `link` tag with the `type="application/atom+xml"`
attribute or a link tag with the `type="application/rdf+xml"` attribute.
2023-12-12 20:22:30 +01:00
Rico Berger
9e59439226 [core] Add Tests for Sources (#98)
This commit adds tests for all available sources.

This commit also fixes the parsing of Atom feeds for the RSS source,
where the `dc:date` field must be used for the `publishedAt` field.
2023-12-12 18:50:29 +01:00
Rico Berger
5087c299d3 [core] Add Test Setup for Deno (#97)
This commit uses the "Continuous Integration" GitHub Action, to run
tests for the Deno code, which is used by the Supabase functions and our
Docker containers.

This commit also adds a first test so that the `deno test` command does
not fail.
2023-12-03 15:55:04 +01:00
Rico Berger
bddf5874d4 [core] Fix Converting of HTML to Plain Text in Description (#96)
This commit fixes the conversion of HTML to plain text in the
description for an item. Until now it could happen, that the there was
no whitespace between some words after the conversion. This is now fixed
so that there is always a whitespace between words in the plain text.
2023-12-02 17:58:44 +01:00
Rico Berger
6c469e5d0d [core] Improve ItemVideoPlayer Widget (#95)
This commit adds two improvements to the `ItemVideoPlayer` widget. These
improvements are:
1. The padding for the widget is now defined within the widget, so that
   is must not be defined in the parent widget. With this change the
   widget follows the styling of our other widgets like `ItemMedia`.
2. On iOS the quality selection had a large bottom padding, this is now
   fixed, by using a `Wrap` widget instead of a `ListView` like we are
   using in the other modal bottom sheets which are showing some
   actions.
2023-12-02 16:51:56 +01:00
Rico Berger
8c88ece3dc [lemmy] Add Support for Lemmy (#94)
This commit adds support to add Lemmy RSS feeds to FeedDeck. A user can
provide the url of an Lemmy instance, the url of a community or of an
user.

The special thing of the Lemmy source in opposite to the normal RSS
source is, that we parse the provided link form a feed item, to check if
it contains a image, video or YouTube url, to apply some special
formatting.
2023-12-02 15:52:50 +01:00
Rico Berger
eb28a44cc8 [core] Fix Index Reset for Tabs in Small Deck Layout (#93)
The index was not reset in the `DeckLayoutSmall` widget, when the user
selected a new deck in the settings widget. This was caused because the
`DefaultTabController` was not rebuild after a new deck was selected, so
that the `initialIndex` value was not used.

This is now fixed by adding a `key` to the `DefaultTabController`, which
corresponds to the selected deck. This means if the user selects a new
deck in the settings the widget will be rebuild and the initial selected
tab will be the first one. If a user selects the same deck or switches
between the small and large layout the tab will be the formerly selected
one.
2023-12-01 18:09:54 +01:00
ricoberger
240e9e93d9 [podcast] Add Comment for the _player.pause() Hack 2023-12-01 18:03:28 +01:00
dependabot[bot]
eebec73fd2 Bump the npm-email-templates group in /supabase/email-templates with 1 update (#89)
* Bump the npm-email-templates group

Bumps the npm-email-templates group in /supabase/email-templates with 1 update: [@react-email/components](https://github.com/resendlabs/react-email/tree/HEAD/packages/components).

- [Release notes](https://github.com/resendlabs/react-email/releases)
- [Changelog](https://github.com/resendlabs/react-email/blob/canary/docs/changelog.mdx)
- [Commits](https://github.com/resendlabs/react-email/commits/v0.0.11/packages/components)

---
updated-dependencies:
- dependency-name: "@react-email/components"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-email-templates
...

Signed-off-by: dependabot[bot] <support@github.com>

* Run `npm install`

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ricoberger <mail@ricoberger.de>
2023-12-01 17:38:16 +01:00
dependabot[bot]
1b226791b4 Bump the pub group in /app with 1 update (#90)
Bumps the pub group in /app with 1 update: [msix](https://github.com/YehudaKremer/msix).

- [Release notes](https://github.com/YehudaKremer/msix/releases)
- [Changelog](https://github.com/YehudaKremer/msix/blob/main/CHANGELOG.md)
- [Commits](https://github.com/YehudaKremer/msix/compare/v3.16.6...v3.16.7)

---
updated-dependencies:
- dependency-name: msix
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pub
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 16:48:48 +01:00
dependabot[bot]
ad9885ce92 Bump the npm-landing group in /landing with 7 updates (#91)
Bumps the npm-landing group in /landing with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.8.10` | `20.10.1` |
| [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `18.2.33` | `18.2.39` |
| [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `18.2.14` | `18.2.17` |
| [eslint](https://github.com/eslint/eslint) | `8.52.0` | `8.54.0` |
| [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `14.0.1` | `14.0.3` |
| [next](https://github.com/vercel/next.js) | `14.0.1` | `14.0.3` |
| [typescript](https://github.com/Microsoft/TypeScript) | `5.2.2` | `5.3.2` |


Updates `@types/node` from 20.8.10 to 20.10.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

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

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

Updates `eslint` from 8.52.0 to 8.54.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.52.0...v8.54.0)

Updates `eslint-config-next` from 14.0.1 to 14.0.3
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.0.3/packages/eslint-config-next)

Updates `next` from 14.0.1 to 14.0.3
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.0.1...v14.0.3)

Updates `typescript` from 5.2.2 to 5.3.2
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.2.2...v5.3.2)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-landing
- dependency-name: "@types/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: "@types/react-dom"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: eslint
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-landing
- dependency-name: eslint-config-next
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-landing
- dependency-name: typescript
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-landing
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 16:44:20 +01:00
dependabot[bot]
16418ab205 Bump the docker group in /supabase/functions/_cmd with 1 update (#92)
Bumps the docker group in /supabase/functions/_cmd with 1 update: lukechannings/deno.


---
updated-dependencies:
- dependency-name: lukechannings/deno
  dependency-type: direct:production
  dependency-group: docker
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 16:38:50 +01:00
Rico Berger
d9d82a1679 [core] Add Test Setup for Flutter (#87)
This commit adds a new "Continuous Integration" GitHub Action, which is
used to run the tests for the Flutter app (and later also for Deno).
This commit also adds a first test, so that the GitHub Action does not
fail.

This PR also removes the Visual Studio Code and Neovim configurations
from the contribution guide, since I'm not using it anymore and for that
I do not want to maintain it any longer.
2023-11-30 21:49:49 +01:00
ricoberger
ff52516324 Prepare v1.2.1 Release 2023-11-30 18:03:33 +01:00
Rico Berger
abd3c24f68 [core] Remove Blank in Item Preview Description (#86)
This commit removes all blank lines in the item preview description, so
that we do not render a blank line as the last line. This was done to
improve the style of the item previews, which looked ugly when the last
line was a blank line.
2023-11-29 21:47:53 +01:00
Rico Berger
04ef618295 [core] Add Right Click Support for Item Actions (#85)
Until now it was only possible to show the actions for an item by
pressing longer on the item. Now a user can also right click on the item
to display the actions which feels a bit more natural on desktop
devices.
2023-11-29 21:14:27 +01:00
Rico Berger
a58c93be8a [podcast] Stop Audio Playback on Windows and Linux (#84)
On Windows and Linux it could happen that the audio playback for a
podcast wasn't stopped when the item details view for a podcast item was
closed.

This commit "fixes" the problem, by pausing the audio player, before the
widget is disposed.

This commit also fixes the condition when the background audio services
should be initialized in the `main.dart` file. Instead of the macOS
check, we checked for iOS twice.
2023-11-29 21:06:06 +01:00
Rico Berger
5a8d6b34c1 [medium] Extend Filter Words List (#83) 2023-11-29 20:09:34 +01:00
ricoberger
9233e4d373 [core] Fix 2023-11-26 20:07:18 +01:00
Rico Berger
6e50af16a7 [core] Fix build.gradle File for Android Release (#82)
The change introduced in #71 so that we can run the Android build in a
GitHub action, broke the `flutter build appbundle` command to build the
Android version for the Google Play store. This commit should fix this,
so that we can build the Android version in a GitHub Action and for
Google Play.
2023-11-26 19:40:54 +01:00
170 changed files with 16913 additions and 4157 deletions

View File

@@ -4,6 +4,8 @@ updates:
directory: "/"
schedule:
interval: "monthly"
assignees:
- "ricoberger"
groups:
github-actions:
patterns:
@@ -13,6 +15,8 @@ updates:
directory: "/app"
schedule:
interval: "monthly"
assignees:
- "ricoberger"
groups:
pub:
patterns:
@@ -22,6 +26,8 @@ updates:
directory: "/supabase/functions/_cmd"
schedule:
interval: "monthly"
assignees:
- "ricoberger"
groups:
docker:
patterns:
@@ -31,16 +37,9 @@ updates:
directory: "/landing"
schedule:
interval: "monthly"
assignees:
- "ricoberger"
groups:
npm-landing:
patterns:
- "*"
- package-ecosystem: "npm"
directory: "/supabase/email-templates"
schedule:
interval: "monthly"
groups:
npm-email-templates:
npm:
patterns:
- "*"

View File

@@ -50,7 +50,7 @@ jobs:
- name: Build and Push Docker Image
id: docker_build
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
push: true
context: ./supabase/functions
@@ -87,11 +87,12 @@ jobs:
supabase db push
supabase functions deploy add-source-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
supabase functions deploy add-or-update-source-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
# supabase functions deploy add-source-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
supabase functions deploy delete-user-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
supabase functions deploy generate-magic-link-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
supabase functions deploy image-proxy-v1 --no-verify-jwt --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
supabase functions deploy profile-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
# supabase functions deploy profile-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
supabase functions deploy profile-v2 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
supabase functions deploy revenuecat-webhooks-v1 --no-verify-jwt --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
supabase functions deploy stripe-create-billing-portal-link-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
@@ -109,6 +110,7 @@ jobs:
supabase db push
supabase functions deploy add-or-update-source-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
supabase functions deploy add-source-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
supabase functions deploy delete-user-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
supabase functions deploy generate-magic-link-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
@@ -166,7 +168,7 @@ jobs:
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.16.0'
flutter-version: '3.22.2'
channel: 'stable'
cache: true
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'
@@ -194,7 +196,7 @@ jobs:
# runs for pull requests and when a new release is published.
macos:
name: macOS
runs-on: macos-latest
runs-on: macos-14
if: github.event_name == 'pull_request' || (github.event_name == 'release' && github.event.action == 'published')
permissions:
contents: write
@@ -209,7 +211,7 @@ jobs:
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.16.0'
flutter-version: '3.22.2'
channel: 'stable'
cache: true
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'
@@ -230,7 +232,7 @@ jobs:
- name: Upload Artifacts (PR)
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: feeddeck-macos-universal.zip
path: app/build/macos/Build/Products/Release/feeddeck-macos-universal.zip
@@ -243,10 +245,10 @@ jobs:
upload_url: ${{ github.event.release.upload_url }}
asset_path: app/build/macos/Build/Products/Release/feeddeck-macos-universal.zip
# The "Linux" job builds the Flutter Linux app and uploads it to the GitHub release or the pull request. The job only
# runs for pull requests and when a new release is published.
linux:
name: Linux
# The "Linux (x86_64)" job builds the Flutter Linux app and uploads it to the GitHub release or the pull request. The
# job only runs for pull requests and when a new release is published.
linux-x86_64:
name: Linux (x86_64)
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' || (github.event_name == 'release' && github.event.action == 'published')
permissions:
@@ -271,7 +273,7 @@ jobs:
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.16.0'
flutter-version: '3.22.2'
channel: 'stable'
cache: true
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'
@@ -297,7 +299,7 @@ jobs:
- name: Upload Artifacts (PR)
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: feeddeck-linux-x86_64.tar.gz
path: app/build/feeddeck-linux-x86_64.tar.gz
@@ -310,6 +312,77 @@ jobs:
upload_url: ${{ github.event.release.upload_url }}
asset_path: app/build/feeddeck-linux-x86_64.tar.gz
# The "Linux (arm64)" job builds the Flutter Linux app and uploads it to the GitHub release or the pull request. The
# job only runs for pull requests and when a new release is published.
#
# NOTE: Normally this job should run for every pull request and when a new release is published, but since we have to
# pay for the "ubicloud-standard-2-arm" runner, we only run the job when a new release is published.
linux-arm64:
name: Linux (arm64)
runs-on: ubicloud-standard-2-arm
if: github.event_name == 'release' && github.event.action == 'published'
# if: github.event_name == 'pull_request' || (github.event_name == 'release' && github.event.action == 'published')
permissions:
contents: write
defaults:
run:
working-directory: "app"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Packages
run: |
# Required for Flutter
sudo apt-get update -y
sudo apt-get install -y ninja-build libgtk-3-dev
# Required for Package "media_kit" which is used via "just_audio_media_kit" for Linux and Windows:
# See: https://pub.dev/packages/media_kit and https://pub.dev/packages/just_audio_media_kit
sudo apt-get install -y libmpv-dev mpv
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.2'
channel: 'master'
cache: true
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'
cache-path: '${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:'
- name: Install Dependencies
run: |
flutter pub get
- name: Build
run: |
flutter config --enable-linux-desktop
flutter build linux --release --dart-define SUPABASE_URL=${{ secrets.SUPABASE_PROD_URL }} --dart-define SUPABASE_ANON_KEY=${{ secrets.SUPABASE_PROD_ANON_KEY }} --dart-define SUPABASE_SITE_URL=${{ secrets.SUPABASE_PROD_SITE_URL }} --dart-define GOOGLE_CLIENT_ID=${{ secrets.SUPABASE_PROD_GOOGLE_CLIENT_ID }}
- name: Package
run: |
cp linux/flatpak/app.feeddeck.feeddeck.desktop build/linux/arm64/release/bundle/
cp linux/flatpak/app.feeddeck.feeddeck.metainfo.xml build/linux/arm64/release/bundle/
cp linux/flatpak/app.feeddeck.feeddeck.svg build/linux/arm64/release/bundle/
cd build
cp -r linux/arm64/release/bundle/ feeddeck-linux-arm64
tar -czf feeddeck-linux-arm64.tar.gz feeddeck-linux-arm64
- name: Upload Artifacts (PR)
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@v4
with:
name: feeddeck-linux-arm64.tar.gz
path: app/build/feeddeck-linux-arm64.tar.gz
if-no-files-found: error
- name: Upload Artifacts (Release)
uses: shogo82148/actions-upload-release-asset@v1
if: ${{ github.event_name == 'release' && github.event.action == 'published' }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: app/build/feeddeck-linux-arm64.tar.gz
# The "Windows" job builds the Flutter Windows app and uploads it to the GitHub release or the pull request. The job
# only runs for pull requests and when a new release is published.
windows:
@@ -329,7 +402,7 @@ jobs:
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.16.0'
flutter-version: '3.22.2'
channel: 'stable'
cache: true
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'
@@ -352,7 +425,7 @@ jobs:
- name: Upload Artifacts (PR)
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: feeddeck-windows-x86_64.zip
path: app/build/feeddeck-windows-x86_64.zip
@@ -369,7 +442,7 @@ jobs:
# app works. The artifact of the build isn't uploaded / used.
ios:
name: iOS
runs-on: macos-latest
runs-on: macos-14
if: github.event_name == 'pull_request' || (github.event_name == 'release' && github.event.action == 'published')
defaults:
run:
@@ -382,7 +455,7 @@ jobs:
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.16.0'
flutter-version: '3.22.2'
channel: 'stable'
cache: true
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'
@@ -411,10 +484,13 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
run: echo "JAVA_HOME=$JAVA_HOME_17_X64" >> $GITHUB_ENV
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.16.0'
flutter-version: '3.22.2'
channel: 'stable'
cache: true
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'

View File

@@ -0,0 +1,53 @@
name: Continuous Integration
on:
push:
branches:
- main
pull_request:
jobs:
flutter:
name: Flutter
runs-on: ubuntu-latest
defaults:
run:
working-directory: "app"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.2'
channel: 'stable'
cache: true
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'
cache-path: '${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:'
- name: Install Dependencies
run: |
flutter pub get
- name: Test
run: |
flutter test
deno:
name: Deno
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Deno
uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- name: Test
run: |
deno test --allow-env --import-map=supabase/functions/import_map.json supabase/functions

View File

@@ -16,7 +16,7 @@ jobs:
steps:
- name: Update Changelog
uses: release-drafter/release-drafter@v5
uses: release-drafter/release-drafter@v6
with:
config-name: release.yaml
disable-autolabeler: true
@@ -47,7 +47,7 @@ jobs:
cache-dependency-path: landing/package-lock.json
- name: Setup Pages
uses: actions/configure-pages@v3
uses: actions/configure-pages@v5
- name: Install Dependencies
run: |
@@ -58,7 +58,7 @@ jobs:
npm run build
- name: Upload Artifact
uses: actions/upload-pages-artifact@v2
uses: actions/upload-pages-artifact@v3
with:
path: ./landing/out
@@ -78,4 +78,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
uses: actions/deploy-pages@v4

10
.gitignore vendored
View File

@@ -1,11 +1,11 @@
# Visual Studio Code Launch Configurations
.vscode/launch.json
# Neovim
.nvim.lua
# Visual Studio Code
.vscode
# Environment Variables
/supabase/.env.local
/supabase/.env.dev
/supabase/.env.stage
/supabase/.env.prod
# Deno
/coverage_deno

17
.vscode/settings.json vendored
View File

@@ -1,17 +0,0 @@
{
"deno.enable": true,
"deno.unstable": true,
"deno.lint": true,
"deno.enablePaths": [
"./supabase/functions"
],
"deno.importMap": "./supabase/functions/import_map.json",
"editor.formatOnSave": true,
"editor.defaultFormatter": "denoland.vscode-deno",
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[html]": {
"editor.defaultFormatter": "vscode.html-language-features"
}
}

View File

@@ -57,16 +57,16 @@ check your installed version:
```sh
$ flutter --version
Flutter 3.16.0 • channel stable • https://github.com/flutter/flutter.git
Framework • revision db7ef5bf9f (9 days ago) • 2023-11-15 11:25:44 -0800
Engine • revision 74d16627b9
Tools • Dart 3.2.0 • DevTools 2.28.2
Flutter 3.22.2 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 761747bfc5 (3 days ago) • 2024-06-05 22:15:13 +0200
Engine • revision edd8546116
Tools • Dart 3.4.3 • DevTools 2.34.3
$ deno --version
deno 1.36.4 (release, aarch64-apple-darwin)
v8 11.6.189.12
typescript 5.1.6
deno 1.40.2 (release, aarch64-apple-darwin)
v8 12.1.285.6
typescript 5.3.3
```
### Working with Flutter
@@ -79,131 +79,25 @@ required variables to the `flutter run` command:
./run.sh --device="chrome" --environment="local"
```
Alternative you can also run the project from Visual Studio Code or Neovim
with the following configuration files. Within the different configurations you
have to provide the following arguments:
`--dart-define SUPABASE_URL=<SUPABASE_URL>`,
`--dart-define SUPABASE_ANON_KEY=<SUPABASE_ANON_KEY>`,
`--dart-define SUPABASE_SITE_URL=<SUPABASE_SITE_URL>` and
`--dart-define GOOGLE_CLIENT_ID=<GOOGLE_CLIENT_ID>`.
To run the tests the following command can be used:
<details>
<summary>Visual Studio Code: `.vscode/launch.json`</summary>
```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Local - Chrome",
"type": "dart",
"request": "launch",
"program": "app/lib/main.dart",
"args": [
"-d",
"chrome",
"--web-port",
"3000",
"--web-browser-flag=--disable-web-security",
"--dart-define",
"SUPABASE_URL=<SUPABASE_URL>",
"--dart-define",
"SUPABASE_ANON_KEY=<SUPABASE_ANON_KEY>",
"--dart-define",
"SUPABASE_SITE_URL=<SUPABASE_SITE_URL>",
"--dart-define",
"GOOGLE_CLIENT_ID=<GOOGLE_CLIENT_ID>"
]
},
{
"name": "Local - iOS Simulator",
"type": "dart",
"request": "launch",
"program": "app/lib/main.dart",
"args": [
"-d",
"iPhone 14 Pro Max",
"--dart-define",
"SUPABASE_URL=<SUPABASE_URL>",
"--dart-define",
"SUPABASE_ANON_KEY=<SUPABASE_ANON_KEY>",
"--dart-define",
"SUPABASE_SITE_URL=<SUPABASE_SITE_URL>",
"--dart-define",
"GOOGLE_CLIENT_ID=<GOOGLE_CLIENT_ID>"
]
},
{
"name": "Local - macOS",
"type": "dart",
"request": "launch",
"program": "app/lib/main.dart",
"args": [
"-d",
"macOS",
"--dart-define",
"SUPABASE_URL=<SUPABASE_URL>",
"--dart-define",
"SUPABASE_ANON_KEY=<SUPABASE_ANON_KEY>",
"--dart-define",
"SUPABASE_SITE_URL=<SUPABASE_SITE_URL>",
"--dart-define",
"GOOGLE_CLIENT_ID=<GOOGLE_CLIENT_ID>"
]
}
]
}
```sh
flutter test
```
</details>
To check the test coverage the `--coverage` flag can be added to the command and
an HTML report can be generated:
<details>
<summary>Neovim: `.nvim.lua`</summary>
```sh
flutter test --coverage
# To generate the HTML report lcov is required, which can be installed via Homebrew:
brew install lcov
```lua
require('flutter-tools').setup_project({
{
name = 'Local - Chrome',
flavor = 'debug',
target = 'lib/main.dart',
device = 'chrome',
dart_define = {
SUPABASE_URL = 'http://localhost:54321',
SUPABASE_ANON_KEY = '<SUPABASE_ANON_KEY'
SUPABASE_SITE_URL = '<SUPABASE_SITE_URL>',
GOOGLE_CLIENT_ID = '<GOOGLE_CLIENT_ID',
},
},
{
name = 'Local - iOS Simulator',
flavor = 'debug',
target = 'lib/main.dart',
device = 'iPhone 14 Pro Max',
dart_define = {
SUPABASE_URL = 'http://localhost:54321',
SUPABASE_ANON_KEY = '<SUPABASE_ANON_KEY'
SUPABASE_SITE_URL = '<SUPABASE_SITE_URL>',
GOOGLE_CLIENT_ID = '<GOOGLE_CLIENT_ID',
},
},
{
name = 'Local - Chrome',
flavor = 'debug',
target = 'lib/main.dart',
device = 'macOS',
dart_define = {
SUPABASE_URL = 'http://localhost:54321',
SUPABASE_ANON_KEY = '<SUPABASE_ANON_KEY'
SUPABASE_SITE_URL = '<SUPABASE_SITE_URL>',
GOOGLE_CLIENT_ID = '<GOOGLE_CLIENT_ID',
},
},
})
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.html
```
</details>
#### Sort all Imports
To sort all imports in the Dart code in a uniformly way you have to run the
@@ -336,6 +230,38 @@ cd supabase/functions/_cmd
docker-compose up --build
```
To build the Docker image, the following commands can be run:
```sh
docker build -f supabase/functions/_cmd/Dockerfile -t ghcr.io/feeddeck/feeddeck:dev supabase/functions
# To build the Docker image for another platform use the following:
docker buildx build --platform linux/amd64 -f supabase/functions/_cmd/Dockerfile -t ghcr.io/feeddeck/feeddeck:dev supabase/functions
# The Docker image can then be used to run the scheduler, worker or tools, e.g.
docker run ghcr.io/feeddeck/feeddeck:dev tools get-feed '{"type": "reddit", "options": {"reddit": "/r/kubernetes"}}'
```
To run the tests for our code, the following command can be used:
```sh
deno test --allow-env --import-map=supabase/functions/import_map.json supabase/functions
```
To check the test coverage the `--coverage` flag can be added to the command and
an HTML report can be generated:
```sh
deno test --allow-env --import-map=supabase/functions/import_map.json supabase/functions --coverage=coverage_deno
# To generate the HTML report lcov is required, which can be installed via Homebrew:
brew install lcov
deno coverage coverage_deno --lcov --output=coverage_deno/coverage_deno.lcov
genhtml -o coverage_deno/html coverage_deno/coverage_deno.lcov
open coverage_deno/html/index.html
```
## Hosting
FeedDeck uses Supabase as backend. For Supabase we can use
@@ -357,6 +283,7 @@ supabase secrets set --env-file supabase/.env
supabase secrets list
# Deploy all functions
supabase functions deploy add-or-update-source-v1 --project-ref <PROJECT-ID> --import-map supabase/functions/import_map.json
supabase functions deploy add-source-v1 --project-ref <PROJECT-ID> --import-map supabase/functions/import_map.json
supabase functions deploy delete-user-v1 --project-ref <PROJECT-ID> --import-map supabase/functions/import_map.json
supabase functions deploy generate-magic-link-v1 --project-ref <PROJECT-ID> --import-map supabase/functions/import_map.json
@@ -458,21 +385,8 @@ Android, macOS, Windows and Linux if you do not want to use the official ones.
5. Build the app for Web by running `flutter build web`. The build can be found
at `app/build/web` and must be uploaded to your hosting provider.
6. Build the app for Linux by running `flutter build linux --release`. To build
the `arm64` version the following commands can be run on a Raspberry Pi. Once
the `feeddeck-linux-arm64.tar.gz` archive was created it can be uploaded to
the GitHub release.
```sh
cp linux/flatpak/app.feeddeck.feeddeck.desktop build/linux/arm64/release/bundle/
cp linux/flatpak/app.feeddeck.feeddeck.metainfo.xml build/linux/arm64/release/bundle/
cp linux/flatpak/app.feeddeck.feeddeck.svg build/linux/arm64/release/bundle/
cd build
cp -r linux/arm64/release/bundle/ feeddeck-linux-arm64
tar -czf feeddeck-linux-arm64.tar.gz feeddeck-linux-arm64
```
Update the `app.feeddeck.feeddeck.yml` file at
6. Build the app for Linux by running `flutter build linux --release`. Update
the `app.feeddeck.feeddeck.yml` file at
[github.com/flathub/app.feeddeck.feeddeck](https://github.com/flathub/app.feeddeck.feeddeck)
with the new release.

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2023 Rico Berger
Copyright (c) 2024 Rico Berger
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View File

@@ -31,8 +31,8 @@ platforms. FeedDeck is written in [Flutter](https://flutter.dev/) and uses
- **RSS and Social Media Feeds:** Follow your favorite RSS and social media
feeds.
- **News:** Get the latest news from your favorite RSS feeds and Google News.
- **Social Media:** Follow your friends and favorite topics on Medium, Nitter,
Reddit, Tumblr and X.
- **Social Media:** Follow your friends and favorite topics on Medium, Reddit
and Tumblr.
- **GitHub:** Get your GitHub notifications and follow your repository
activities.
- **Podcasts:** Follow and listen to your favorite podcasts, via the built-in

1
app/.gitignore vendored
View File

@@ -31,6 +31,7 @@ migrate_working_dir/
.pub-cache/
.pub/
/build/
/coverage
# Symbolication related
app.*.symbols

View File

@@ -1,3 +1,9 @@
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
@@ -12,11 +18,6 @@ if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@@ -27,21 +28,18 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
namespace "app.feeddeck.feeddeck"
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = '17'
}
sourceSets {
@@ -55,7 +53,7 @@ android {
// minSdkVersion flutter.minSdkVersion
// targetSdkVersion flutter.targetSdkVersion
minSdkVersion 21
targetSdkVersion 33
targetSdkVersion 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
@@ -71,7 +69,7 @@ android {
buildTypes {
release {
if (project.hasProperty("keyStoreFile")) {
if (keystorePropertiesFile.exists()) {
signingConfig signingConfigs.release
} else {
// For testing purposes we sign with dummy credentials if no key properties are given.
@@ -86,6 +84,5 @@ flutter {
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.google.android.gms:play-services-auth:20.6.0'
}

View File

@@ -1,16 +1,3 @@
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip

View File

@@ -1,11 +1,26 @@
include ':app'
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}
settings.ext.flutterSdkPath = flutterSdkPath()
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.1.2" apply false
id "org.jetbrains.kotlin.android" version "1.9.10" apply false
}
include ":app"

Binary file not shown.

View File

@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>12.0</string>
</dict>
</plist>

View File

@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
platform :ios, '11.0'
platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@@ -8,9 +8,6 @@ PODS:
- Flutter (1.0.0)
- flutter_native_splash (0.0.1):
- Flutter
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- just_audio (0.0.1):
- Flutter
- media_kit_libs_ios_video (1.0.4):
@@ -24,12 +21,12 @@ PODS:
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- purchases_flutter (6.4.0):
- purchases_flutter (6.30.2):
- Flutter
- PurchasesHybridCommon (= 8.0.0)
- PurchasesHybridCommon (8.0.0):
- RevenueCat (= 4.30.5)
- RevenueCat (4.30.5)
- PurchasesHybridCommon (= 11.1.0)
- PurchasesHybridCommon (11.1.0):
- RevenueCat (= 4.43.2)
- RevenueCat (4.43.2)
- screen_brightness_ios (0.1.0):
- Flutter
- shared_preferences_foundation (0.0.1):
@@ -39,15 +36,13 @@ PODS:
- Flutter
- sqflite (0.0.3):
- Flutter
- FMDB (>= 2.7.5)
- FlutterMacOS
- url_launcher_ios (0.0.1):
- Flutter
- volume_controller (0.0.1):
- Flutter
- wakelock_plus (0.0.1):
- Flutter
- webview_flutter_wkwebview (0.0.1):
- Flutter
DEPENDENCIES:
- app_links (from `.symlinks/plugins/app_links/ios`)
@@ -65,15 +60,13 @@ DEPENDENCIES:
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- volume_controller (from `.symlinks/plugins/volume_controller/ios`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
SPEC REPOS:
trunk:
- FMDB
- PurchasesHybridCommon
- RevenueCat
@@ -109,41 +102,37 @@ EXTERNAL SOURCES:
sign_in_with_apple:
:path: ".symlinks/plugins/sign_in_with_apple/ios"
sqflite:
:path: ".symlinks/plugins/sqflite/ios"
:path: ".symlinks/plugins/sqflite/darwin"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
volume_controller:
:path: ".symlinks/plugins/volume_controller/ios"
wakelock_plus:
:path: ".symlinks/plugins/wakelock_plus/ios"
webview_flutter_wkwebview:
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
SPEC CHECKSUMS:
app_links: 5ef33d0d295a89d9d16bb81b0e3b0d5f70d6c875
app_links: e70ca16b4b0f88253b3b3660200d4a10b4ea9795
audio_service: f509d65da41b9521a61f1c404dd58651f265a567
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
purchases_flutter: a428f3e8ac54dfb499ff190efa99d6701094bc32
PurchasesHybridCommon: 80262c5ffe6621e3cf3812e6103170f6d7fbcb79
RevenueCat: c1e33f4e1f1fd239ba461652f02928e220becc31
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
purchases_flutter: 42d5544e7730ea89a88cc2f008b7c700fd147052
PurchasesHybridCommon: 4022d5944cb30ec44ba5159e42aa161fe0e30175
RevenueCat: 3d934653b7e8b09af88fd47e9e84cfaf5d0a89ba
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
PODFILE CHECKSUM: ec83c31511fbc978a9918c6fda235238118483f5
PODFILE CHECKSUM: 016564c560c4c9dbcb210e12c7aa6039072645f1
COCOAPODS: 1.14.2
COCOAPODS: 1.15.2

View File

@@ -161,7 +161,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@@ -348,7 +348,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -428,7 +428,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -477,7 +477,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@@ -57,7 +57,7 @@ void main() async {
/// We can not initialize the [just_audio_background] package on Windows and
/// Linux, because then the returned duration in the `_player.durationStream`
/// isn't working correctly in the [ItemAudioPlayer] widget.
if (kIsWeb || Platform.isAndroid || Platform.isIOS || Platform.isIOS) {
if (kIsWeb || Platform.isAndroid || Platform.isIOS || Platform.isMacOS) {
await JustAudioBackground.init(
androidNotificationChannelId: 'com.ryanheise.bg_demo.channel.audio',
androidNotificationChannelName: 'Audio playback',

View File

@@ -8,8 +8,10 @@ import 'package:feeddeck/utils/fd_icons.dart';
/// [FDSourceType] is a enum value which defines the source type. A source can
/// have one of the following types:
/// - [fourchan]
/// - [github]
/// - [googlenews]
/// - [lemmy]
/// - [mastodon]
/// - [medium]
/// - [nitter]
@@ -19,7 +21,6 @@ import 'package:feeddeck/utils/fd_icons.dart';
/// - [rss]
/// - [stackoverflow]
/// - [tumblr]
/// - [x]
/// - [youtube]
///
/// The [none] value is not valid and just here as a fallback in case sth. odd
@@ -27,8 +28,10 @@ import 'package:feeddeck/utils/fd_icons.dart';
/// the list, so that we can loop though the types in a ListView / GridView
/// builder via `FDSourceType.values.length - 1`.
enum FDSourceType {
fourchan,
github,
googlenews,
lemmy,
mastodon,
medium,
nitter,
@@ -38,7 +41,6 @@ enum FDSourceType {
rss,
stackoverflow,
tumblr,
// x,
youtube,
none,
}
@@ -55,10 +57,14 @@ extension FDSourceTypeExtension on FDSourceType {
/// [toLocalizedString] returns a localized string for a source type.
String toLocalizedString() {
switch (this) {
case FDSourceType.fourchan:
return '4chan';
case FDSourceType.github:
return 'GitHub';
case FDSourceType.googlenews:
return 'Google News';
case FDSourceType.lemmy:
return 'Lemmy';
case FDSourceType.mastodon:
return 'Mastodon';
case FDSourceType.medium:
@@ -77,8 +83,6 @@ extension FDSourceTypeExtension on FDSourceType {
return 'StackOverflow';
case FDSourceType.tumblr:
return 'Tumblr';
// case FDSourceType.x:
// return 'X';
case FDSourceType.youtube:
return 'YouTube';
default:
@@ -89,10 +93,14 @@ extension FDSourceTypeExtension on FDSourceType {
/// [icon] returns the icon for a source.
IconData get icon {
switch (this) {
case FDSourceType.fourchan:
return FDIcons.fourchan;
case FDSourceType.github:
return FDIcons.github;
case FDSourceType.googlenews:
return FDIcons.googlenews;
case FDSourceType.lemmy:
return FDIcons.lemmy;
case FDSourceType.mastodon:
return FDIcons.mastodon;
case FDSourceType.medium:
@@ -111,8 +119,6 @@ extension FDSourceTypeExtension on FDSourceType {
return FDIcons.stackoverflow;
case FDSourceType.tumblr:
return FDIcons.tumblr;
// case FDSourceType.x:
// return FDIcons.x;
case FDSourceType.youtube:
return FDIcons.youtube;
default:
@@ -123,10 +129,14 @@ extension FDSourceTypeExtension on FDSourceType {
/// [bgColor] returns the background color for the source icon.
Color get bgColor {
switch (this) {
case FDSourceType.fourchan:
return const Color(0xff880000);
case FDSourceType.github:
return const Color(0xff000000);
case FDSourceType.googlenews:
return const Color(0xff4285f4);
case FDSourceType.lemmy:
return const Color(0xff00bc8c);
case FDSourceType.mastodon:
return const Color(0xff6364ff);
case FDSourceType.medium:
@@ -145,8 +155,6 @@ extension FDSourceTypeExtension on FDSourceType {
return const Color(0xffef8236);
case FDSourceType.tumblr:
return const Color(0xff34526f);
// case FDSourceType.x:
// return const Color(0xff000000);
case FDSourceType.youtube:
return const Color(0xffff0000);
default:
@@ -158,10 +166,14 @@ extension FDSourceTypeExtension on FDSourceType {
/// used toether with the [bgColor].
Color get fgColor {
switch (this) {
case FDSourceType.fourchan:
return const Color(0xffffffff);
case FDSourceType.github:
return const Color(0xffffffff);
case FDSourceType.googlenews:
return const Color(0xffffffff);
case FDSourceType.lemmy:
return const Color(0xffffffff);
case FDSourceType.mastodon:
return const Color(0xffffffff);
case FDSourceType.medium:
@@ -180,8 +192,6 @@ extension FDSourceTypeExtension on FDSourceType {
return const Color(0xffffffff);
case FDSourceType.tumblr:
return const Color(0xffffffff);
// case FDSourceType.x:
// return const Color(0xffffffff);
case FDSourceType.youtube:
return const Color(0xffffffff);
default:
@@ -258,8 +268,10 @@ class FDSource {
/// [FDSourceOptions] defines all options for the different source types which
/// are available.
class FDSourceOptions {
String? fourchan;
FDGitHubOptions? github;
FDGoogleNewsOptions? googlenews;
String? lemmy;
String? mastodon;
String? medium;
String? nitter;
@@ -269,12 +281,13 @@ class FDSourceOptions {
String? rss;
FDStackOverflowOptions? stackoverflow;
String? tumblr;
String? x;
String? youtube;
FDSourceOptions({
this.fourchan,
this.github,
this.googlenews,
this.lemmy,
this.mastodon,
this.medium,
this.nitter,
@@ -284,12 +297,15 @@ class FDSourceOptions {
this.rss,
this.stackoverflow,
this.tumblr,
this.x,
this.youtube,
});
factory FDSourceOptions.fromJson(Map<String, dynamic> responseData) {
return FDSourceOptions(
fourchan: responseData.containsKey('fourchan') &&
responseData['fourchan'] != null
? responseData['fourchan']
: null,
github:
responseData.containsKey('github') && responseData['github'] != null
? FDGitHubOptions.fromJson(responseData['github'])
@@ -298,6 +314,9 @@ class FDSourceOptions {
responseData['googlenews'] != null
? FDGoogleNewsOptions.fromJson(responseData['googlenews'])
: null,
lemmy: responseData.containsKey('lemmy') && responseData['lemmy'] != null
? responseData['lemmy']
: null,
mastodon: responseData.containsKey('mastodon') &&
responseData['mastodon'] != null
? responseData['mastodon']
@@ -333,9 +352,6 @@ class FDSourceOptions {
responseData.containsKey('tumblr') && responseData['tumblr'] != null
? responseData['tumblr']
: null,
x: responseData.containsKey('x') && responseData['x'] != null
? responseData['x']
: null,
youtube:
responseData.containsKey('youtube') && responseData['youtube'] != null
? responseData['youtube']
@@ -345,8 +361,10 @@ class FDSourceOptions {
Map<String, dynamic> toJson() {
return {
'fourchan': fourchan,
'github': github?.toJson(),
'googlenews': googlenews?.toJson(),
'lemmy': lemmy,
'mastodon': mastodon,
'medium': medium,
'nitter': nitter,
@@ -356,7 +374,6 @@ class FDSourceOptions {
'rss': rss,
'stackoverflow': stackoverflow?.toJson(),
'tumblr': tumblr,
'x': x,
'youtube': youtube,
};
}

View File

@@ -376,21 +376,33 @@ class AppRepository with ChangeNotifier {
}
/// [addSource] is called to add a source to the column with the provided
/// [columnId]. The function takes a [source] as parameter. The function calls
/// the `add-source-v1` edge function via the Supabase client to create the
/// source. When the source was created the newly returned source is added to
/// the list of sources of the column with the provided [columnId].
/// [columnId]. Next to [columnId] a user must also provide the [type] and
/// [options] for the source. The function calls the `add-or-update-source-v1`
/// edge function via the Supabase client to create the source. When the
/// source was created the newly returned source is added to the list of
/// sources of the column with the provided [columnId].
///
/// The optional [feedData] parameter is used to provide the feed data for the
/// source. This is can be used to scrape the source data via the client (app)
/// instead of the server (scheduler / worker).
Future<void> addSource(
String columnId,
FDSourceType type,
FDSourceOptions options,
) async {
FDSourceOptions options, [
String? feedData,
]) async {
final result = await Supabase.instance.client.functions.invoke(
'add-source-v1',
'add-or-update-source-v1',
body: {
'columnId': columnId,
'type': type.toShortString(),
'options': options.toJson(),
'source': {
'id': '',
'columnId': columnId,
'userId': '',
'type': type.toShortString(),
'title': '',
'options': options.toJson(),
},
'feedData': feedData,
},
);

View File

@@ -141,7 +141,7 @@ class ItemsRepository with ChangeNotifier {
/// selected source which is stored in the [_filters.sourceIdFilter]
/// field.
if (_filters.sourceIdFilter != '') {
filter = filter.eq('sourceId', sourceIdFilter);
filter = filter.eq('sourceId', _filters.sourceIdFilter);
}
filter = filter.lte('createdAt', _filters.createdAtFilter);
@@ -305,7 +305,7 @@ class ItemsRepository with ChangeNotifier {
for (var i = 0; i < chunks.length; i++) {
await Supabase.instance.client
.from('items')
.update({'isRead': read}).in_('id', chunks[i]);
.update({'isRead': read}).inFilter('id', chunks[i]);
for (var j = 0; j < _items.length; j++) {
if (chunks[i].contains(_items[j].id)) {
_items[j].isRead = read;

View File

@@ -56,15 +56,15 @@ const _htmlAuthFinished = '''
</html>
''';
/// The [DesktopLoginManager] is used to authenticate a user with the provided
/// The [DesktopSignInManager] is used to authenticate a user with the provided
/// OAuth [provider] on desktop platforms.
class DesktopLoginManager {
final supabase.Provider provider;
class DesktopSignInManager {
final supabase.OAuthProvider provider;
final Map<String, String>? queryParams;
HttpServer? redirectServer;
DesktopLoginManager({
DesktopSignInManager({
required this.provider,
required this.queryParams,
});

View File

@@ -1,7 +1,7 @@
import 'package:flutter/widgets.dart';
/// Flutter icons [FDIcons]
/// Copyright (C) 2023 by original authors @ fluttericon.com, fontello.com
/// Flutter icons FDIcons
/// Copyright (C) 2024 by original authors @ fluttericon.com, fontello.com
/// This font was generated by FlutterIcon.com, which is derived from Fontello.
///
/// To use this font, place it in your fonts/ directory and include the
@@ -63,4 +63,8 @@ class FDIcons {
IconData(0xe814, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData mastodon =
IconData(0xe815, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData lemmy =
IconData(0xe816, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData fourchan =
IconData(0xe817, fontFamily: _kFontFam, fontPackage: _kFontPkg);
}

View File

@@ -0,0 +1,55 @@
import 'package:http/http.dart' as http;
import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/utils/api_exception.dart';
/// [getFeed] returns the feed for the provided [sourceType] and [options]. It
/// can be used to fetch the feed for a source on the client side (app) instead
/// of via the corresponding `add-or-update-source-v1` edge function or via our
/// worker.
///
/// The functions for the different sources must implement the same parsing for
/// the source options as it is done in the edge function.
Future<String> getFeed(FDSourceType sourceType, FDSourceOptions options) async {
switch (sourceType) {
case FDSourceType.reddit:
return getFeedReddit(options.reddit);
default:
throw const ApiException('Unknown source type', 400);
}
}
/// [getFeedReddit] returns the feed for the provided [input]. It is used to
/// fetch the RSS feed for a Reddit source, which can be passed to the
/// `add-or-update-source-v1` edge function.
///
/// The function must implement the same parsing logic as it is done in the
/// `supabase/functions/_shared/feed/reddit.ts` file.
Future<String> getFeedReddit(String? input) async {
if (input == null || input.isEmpty) {
throw const ApiException('No input provided', 400);
}
String url = '';
try {
if (input.startsWith('/r/') || input.startsWith('/u/')) {
url = 'https://www.reddit.com$input.rss';
} else {
final inputUri = Uri.parse(input);
if (inputUri.host.endsWith('reddit.com')) {
if (input.endsWith('.rss')) {
url = input;
} else {
url = '$input.rss';
}
} else {
throw const ApiException('Invalid input', 400);
}
}
} catch (err) {
throw const ApiException('Invalid input', 400);
}
final response = await http.get(Uri.parse(url));
return response.body;
}

View File

@@ -0,0 +1,33 @@
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
/// [signInWithApple] performs Apple sign in on iOS and macOS.
/// See https://supabase.com/docs/guides/auth/social-login/auth-apple?platform=flutter#using-native-sign-in-with-apple-in-flutter
Future<AuthResponse> signInWithApple() async {
final rawNonce = Supabase.instance.client.auth.generateRawNonce();
final hashedNonce = sha256.convert(utf8.encode(rawNonce)).toString();
final credential = await SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
],
nonce: hashedNonce,
);
final idToken = credential.identityToken;
if (idToken == null) {
throw const AuthException(
'Could not find ID Token from generated credential.',
);
}
return Supabase.instance.client.auth.signInWithIdToken(
provider: OAuthProvider.apple,
idToken: idToken,
nonce: rawNonce,
);
}

View File

@@ -149,6 +149,7 @@ class DeckLayoutSmall extends StatelessWidget {
AppRepository app = Provider.of<AppRepository>(context, listen: true);
return DefaultTabController(
key: ValueKey(app.activeDeckId),
initialIndex: _getInitialIndex(context, app.columns.length),
length: app.columns.length,
child: Scaffold(
@@ -175,6 +176,7 @@ class DeckLayoutSmall extends StatelessWidget {
child: TabBar(
isScrollable: true,
tabAlignment: TabAlignment.start,
dividerHeight: 0,
onTap: (int index) {
/// When the user clicks on a tab we update the index in
/// the [LayoutRepository] so that we can use it as

View File

@@ -39,7 +39,7 @@ class _HomeState extends State<Home> {
});
if (!kIsWeb && (Platform.isIOS || Platform.isAndroid)) {
_appLinks.allUriLinkStream.listen((uri) {
_appLinks.uriLinkStream.listen((uri) {
if (uri
.toString()
.startsWith('app.feeddeck.feeddeck://signin-callback/')) {

View File

@@ -7,6 +7,8 @@ import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/repositories/items_repository.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/utils/openurl.dart';
import 'package:feeddeck/widgets/item/details/item_details_fourchan.dart';
import 'package:feeddeck/widgets/item/details/item_details_lemmy.dart';
import 'package:feeddeck/widgets/item/details/item_details_mastodon.dart';
import 'package:feeddeck/widgets/item/details/item_details_medium.dart';
import 'package:feeddeck/widgets/item/details/item_details_nitter.dart';
@@ -60,6 +62,12 @@ class ItemDetails extends StatelessWidget {
Widget _buildDetails() {
switch (source.type) {
case FDSourceType.fourchan:
return ItemDetailsFourChan(
item: item,
source: source,
);
/// Sources with type [FDSourceType.github] do not provide a details view,
/// because we directly open the link, when the user clicks on the
/// corresponding preview item.
@@ -71,6 +79,11 @@ class ItemDetails extends StatelessWidget {
/// corresponding preview item.
case FDSourceType.googlenews:
return Container();
case FDSourceType.lemmy:
return ItemDetailsLemmy(
item: item,
source: source,
);
case FDSourceType.mastodon:
return ItemDetailsMastodon(
item: item,
@@ -116,11 +129,6 @@ class ItemDetails extends StatelessWidget {
item: item,
source: source,
);
// case FDSourceType.x:
// return ItemDetailsX(
// item: item,
// source: source,
// );
case FDSourceType.youtube:
return ItemDetailsYoutube(
item: item,
@@ -202,7 +210,7 @@ class ItemDetails extends StatelessWidget {
Constants.elevatedButtonSize,
),
),
label: const Text('Open link'),
label: const Text('Open Link'),
onPressed: () => _openUrl(item.link),
icon: const Icon(Icons.launch),
),

View File

@@ -2,13 +2,12 @@ import 'package:flutter/material.dart';
import 'package:feeddeck/models/item.dart';
import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/widgets/item/details/utils/item_description.dart';
import 'package:feeddeck/widgets/item/details/utils/item_media_gallery.dart';
import 'package:feeddeck/widgets/item/details/utils/item_subtitle.dart';
import 'package:feeddeck/widgets/item/details/utils/item_title.dart';
class ItemDetailsX extends StatelessWidget {
const ItemDetailsX({
class ItemDetailsFourChan extends StatelessWidget {
const ItemDetailsFourChan({
super.key,
required this.item,
required this.source,
@@ -23,6 +22,9 @@ class ItemDetailsX extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: [
ItemTitle(
itemTitle: item.title,
),
ItemSubtitle(
item: item,
source: source,
@@ -32,16 +34,6 @@ class ItemDetailsX extends StatelessWidget {
sourceFormat: DescriptionFormat.html,
tagetFormat: DescriptionFormat.markdown,
),
const SizedBox(
height: Constants.spacingExtraSmall,
),
ItemMediaGallery(
itemMedias: item.options != null && item.options!.containsKey('media')
? (item.options!['media'] as List)
.map((item) => item as String)
.toList()
: null,
),
],
);
}

View File

@@ -0,0 +1,90 @@
import 'package:flutter/material.dart';
import 'package:feeddeck/models/item.dart';
import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/widgets/item/details/utils/item_description.dart';
import 'package:feeddeck/widgets/item/details/utils/item_media.dart';
import 'package:feeddeck/widgets/item/details/utils/item_piped/item_piped_video.dart';
import 'package:feeddeck/widgets/item/details/utils/item_subtitle.dart';
import 'package:feeddeck/widgets/item/details/utils/item_title.dart';
import 'package:feeddeck/widgets/item/details/utils/item_videos.dart';
import 'package:feeddeck/widgets/item/details/utils/item_youtube/item_youtube_video.dart';
class ItemDetailsLemmy extends StatelessWidget {
const ItemDetailsLemmy({
super.key,
required this.item,
required this.source,
});
final FDItem item;
final FDSource source;
/// [_buildMedia] builds the media widget for the item. The media widget can
/// display an image, a video or y YouTube video.
///
/// See the `getMedia` function in the `lemmy.ts` file, for a list of
/// extension which are a image / video.
Widget _buildMedia() {
if (item.media != null && item.media! != '') {
final mediaUrl = Uri.parse(item.media!);
if (mediaUrl.path.endsWith('.jpg') ||
mediaUrl.path.endsWith('.jpeg') ||
mediaUrl.path.endsWith('.png') ||
mediaUrl.path.endsWith('.gif')) {
return ItemMedia(
itemMedia: item.media,
);
}
if (mediaUrl.path.endsWith('.mp4')) {
return ItemVideoPlayer(
video: item.media!,
);
}
if (item.media!.startsWith('https://youtu.be/') ||
item.media!.startsWith('https://www.youtube.com/watch?') ||
item.media!.startsWith('https://m.youtube.com/watch?')) {
return ItemYoutubeVideo(
null,
item.media!,
);
}
if (item.media!.startsWith('https://piped.video/watch?v=') ||
item.media!.startsWith('https://piped.video/')) {
return ItemPipedVideo(
null,
item.media!,
);
}
}
return Container();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: [
ItemTitle(
itemTitle: item.title,
),
ItemSubtitle(
item: item,
source: source,
),
_buildMedia(),
ItemDescription(
itemDescription: item.description,
sourceFormat: DescriptionFormat.html,
tagetFormat: DescriptionFormat.markdown,
),
],
);
}
}

View File

@@ -5,8 +5,10 @@ import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/widgets/item/details/utils/item_description.dart';
import 'package:feeddeck/widgets/item/details/utils/item_media_gallery.dart';
import 'package:feeddeck/widgets/item/details/utils/item_piped/item_piped_video.dart';
import 'package:feeddeck/widgets/item/details/utils/item_subtitle.dart';
import 'package:feeddeck/widgets/item/details/utils/item_videos.dart';
import 'package:feeddeck/widgets/item/details/utils/item_youtube/item_youtube_video.dart';
class ItemDetailsMastodon extends StatelessWidget {
const ItemDetailsMastodon({
@@ -18,6 +20,116 @@ class ItemDetailsMastodon extends StatelessWidget {
final FDItem item;
final FDSource source;
/// [_getYoutubeUrl] returns a YouTube url when the provided [description]
/// contains a YouTube link. If the [description] does not contain a YouTube
/// link, the function returns `null`.
String? _getYoutubeUrl(String description) {
final exp = RegExp(r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+');
final matches = exp.allMatches(description);
for (var match in matches) {
final url = description.substring(match.start, match.end);
if (url.startsWith('https://youtu.be/') ||
url.startsWith('https://www.youtube.com/watch?') ||
url.startsWith('https://m.youtube.com/watch?')) {
return url;
}
}
return null;
}
/// [_getPipedUrl] returns a Piped url when the provided [description]
/// contains a Piped link. If the [description] does not contain a Piped link,
/// the function returns `null`.
String? _getPipedUrl(String description) {
final exp = RegExp(r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+');
final matches = exp.allMatches(description);
for (var match in matches) {
final url = description.substring(match.start, match.end);
if (url.startsWith('https://piped.video/watch?v=') ||
url.startsWith('https://piped.video/')) {
return url;
}
}
return null;
}
/// [_buildDescription] builds the description widget for the item. If the
/// description contains a YouTube link, we render the [ItemYoutubeVideo]
/// and the [ItemDescription] widgets. If the description does not contain a
/// YouTube link, we render the [ItemDescription], [ItemMediaGallery] and
/// [ItemVideos] widget.
List<Widget> _buildDescription() {
final youtubeUrl =
item.description != null ? _getYoutubeUrl(item.description!) : null;
if (youtubeUrl != null) {
return [
ItemYoutubeVideo(
item.media,
youtubeUrl,
),
ItemDescription(
itemDescription: item.description,
sourceFormat: DescriptionFormat.html,
tagetFormat: DescriptionFormat.markdown,
disableImages: true,
),
];
}
final pipedUrl =
item.description != null ? _getPipedUrl(item.description!) : null;
if (pipedUrl != null) {
return [
ItemPipedVideo(
item.media,
pipedUrl,
),
ItemDescription(
itemDescription: item.description,
sourceFormat: DescriptionFormat.html,
tagetFormat: DescriptionFormat.markdown,
disableImages: true,
),
];
}
return [
ItemDescription(
itemDescription: item.description,
sourceFormat: DescriptionFormat.html,
tagetFormat: DescriptionFormat.markdown,
disableImages: true,
),
const SizedBox(
height: Constants.spacingExtraSmall,
),
ItemMediaGallery(
itemMedias: item.options != null &&
item.options!.containsKey('media') &&
item.options!['media'] != null
? (item.options!['media'] as List)
.map((item) => item as String)
.toList()
: null,
),
ItemVideos(
videos: item.options != null &&
item.options!.containsKey('videos') &&
item.options!['videos'] != null
? (item.options!['videos'] as List)
.map((item) => item as String)
.toList()
: null,
),
];
}
@override
Widget build(BuildContext context) {
return Column(
@@ -28,32 +140,7 @@ class ItemDetailsMastodon extends StatelessWidget {
item: item,
source: source,
),
ItemDescription(
itemDescription: item.description,
sourceFormat: DescriptionFormat.html,
tagetFormat: DescriptionFormat.markdown,
),
const SizedBox(
height: Constants.spacingExtraSmall,
),
ItemMediaGallery(
itemMedias: item.options != null &&
item.options!.containsKey('media') &&
item.options!['media'] != null
? (item.options!['media'] as List)
.map((item) => item as String)
.toList()
: null,
),
ItemVideos(
videos: item.options != null &&
item.options!.containsKey('videos') &&
item.options!['videos'] != null
? (item.options!['videos'] as List)
.map((item) => item as String)
.toList()
: null,
),
..._buildDescription(),
],
);
}

View File

@@ -5,8 +5,8 @@ import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/widgets/item/details/utils/item_description.dart';
import 'package:feeddeck/widgets/item/details/utils/item_media_gallery.dart';
import 'package:feeddeck/widgets/item/details/utils/item_piped/item_piped_video.dart';
import 'package:feeddeck/widgets/item/details/utils/item_subtitle.dart';
import 'package:feeddeck/widgets/item/details/utils/item_title.dart';
class ItemDetailsNitter extends StatelessWidget {
const ItemDetailsNitter({
@@ -18,18 +18,37 @@ class ItemDetailsNitter extends StatelessWidget {
final FDItem item;
final FDSource source;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: [
ItemTitle(
itemTitle: item.title,
),
ItemSubtitle(
item: item,
source: source,
/// [_getPipedUrl] returns a Piped url when the provided [description]
/// contains a Piped link. If the [description] does not contain a Piped link,
/// the function returns `null`.
String? _getPipedUrl(String description) {
final exp = RegExp(r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+');
final matches = exp.allMatches(description);
for (var match in matches) {
final url = description.substring(match.start, match.end);
if (url.startsWith('https://piped.video/watch?v=') ||
url.startsWith('https://piped.video/')) {
return url;
}
}
return null;
}
/// [_buildDescription] builds the description widget for the item. If the
/// description contains a Piped link, we render the [ItemPipedVideo] and the
/// [ItemDescription] widgets. If the description does not contain a Piped
/// link, we render the [ItemDescription] and [ItemMediaGallery] widget.
List<Widget> _buildDescription() {
final pipedUrl =
item.description != null ? _getPipedUrl(item.description!) : null;
if (pipedUrl != null) {
return [
ItemPipedVideo(
item.media,
pipedUrl,
),
ItemDescription(
itemDescription: item.description,
@@ -37,16 +56,40 @@ class ItemDetailsNitter extends StatelessWidget {
tagetFormat: DescriptionFormat.markdown,
disableImages: true,
),
const SizedBox(
height: Constants.spacingExtraSmall,
),
ItemMediaGallery(
itemMedias: item.options != null && item.options!.containsKey('media')
? (item.options!['media'] as List)
.map((item) => item as String)
.toList()
: null,
];
}
return [
ItemDescription(
itemDescription: item.description,
sourceFormat: DescriptionFormat.html,
tagetFormat: DescriptionFormat.markdown,
disableImages: true,
),
const SizedBox(
height: Constants.spacingExtraSmall,
),
ItemMediaGallery(
itemMedias: item.options != null && item.options!.containsKey('media')
? (item.options!['media'] as List)
.map((item) => item as String)
.toList()
: null,
),
];
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: [
ItemSubtitle(
item: item,
source: source,
),
..._buildDescription(),
],
);
}

View File

@@ -3,8 +3,10 @@ import 'package:flutter/material.dart';
import 'package:feeddeck/models/item.dart';
import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/widgets/item/details/utils/item_description.dart';
import 'package:feeddeck/widgets/item/details/utils/item_piped/item_piped_video.dart';
import 'package:feeddeck/widgets/item/details/utils/item_subtitle.dart';
import 'package:feeddeck/widgets/item/details/utils/item_title.dart';
import 'package:feeddeck/widgets/item/details/utils/item_youtube/item_youtube_video.dart';
class ItemDetailsReddit extends StatelessWidget {
const ItemDetailsReddit({
@@ -16,6 +18,98 @@ class ItemDetailsReddit extends StatelessWidget {
final FDItem item;
final FDSource source;
/// [_getYoutubeUrl] returns a YouTube url when the provided [description]
/// contains a YouTube link. If the [description] does not contain a YouTube
/// link, the function returns `null`.
String? _getYoutubeUrl(String description) {
final exp = RegExp(r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+');
final matches = exp.allMatches(description);
for (var match in matches) {
final url = description.substring(match.start, match.end);
if (url.startsWith('https://youtu.be/') ||
url.startsWith('https://www.youtube.com/watch?') ||
url.startsWith('https://m.youtube.com/watch?')) {
return url;
}
}
return null;
}
/// [_getPipedUrl] returns a Piped url when the provided [description]
/// contains a Piped link. If the [description] does not contain a Piped link,
/// the function returns `null`.
String? _getPipedUrl(String description) {
final exp = RegExp(r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+');
final matches = exp.allMatches(description);
for (var match in matches) {
final url = description.substring(match.start, match.end);
if (url.startsWith('https://piped.video/watch?v=') ||
url.startsWith('https://piped.video/')) {
return url;
}
}
return null;
}
/// [_buildDescription] builds the description widget for the item. If the
/// description contains a YouTube link, we render the [ItemYoutubeVideo]
/// and the [ItemDescription] widgets. If the description contains a Piped
/// link, we render the [ItemPipedVideo] and the [ItemDescription] widget. If
/// the description does not contain a YouTube or Piped link, we only render
/// the [ItemDescription] widget.
///
/// If the description containes a YouTube link we also have to disable the
/// rendering of images within the [ItemDescription] widget.
List<Widget> _buildDescription() {
final youtubeUrl =
item.description != null ? _getYoutubeUrl(item.description!) : null;
if (youtubeUrl != null) {
return [
ItemYoutubeVideo(
item.media,
youtubeUrl,
),
ItemDescription(
itemDescription: item.description,
sourceFormat: DescriptionFormat.html,
tagetFormat: DescriptionFormat.markdown,
disableImages: true,
),
];
}
final pipedUrl =
item.description != null ? _getPipedUrl(item.description!) : null;
if (pipedUrl != null) {
return [
ItemPipedVideo(
item.media,
pipedUrl,
),
ItemDescription(
itemDescription: item.description,
sourceFormat: DescriptionFormat.html,
tagetFormat: DescriptionFormat.markdown,
disableImages: true,
),
];
}
return [
ItemDescription(
itemDescription: item.description,
sourceFormat: DescriptionFormat.html,
tagetFormat: DescriptionFormat.markdown,
),
];
}
@override
Widget build(BuildContext context) {
return Column(
@@ -29,11 +123,7 @@ class ItemDetailsReddit extends StatelessWidget {
item: item,
source: source,
),
ItemDescription(
itemDescription: item.description,
sourceFormat: DescriptionFormat.html,
tagetFormat: DescriptionFormat.markdown,
),
..._buildDescription(),
],
);
}

View File

@@ -8,6 +8,7 @@ import 'package:feeddeck/widgets/item/details/utils/item_description.dart';
import 'package:feeddeck/widgets/item/details/utils/item_media.dart';
import 'package:feeddeck/widgets/item/details/utils/item_subtitle.dart';
import 'package:feeddeck/widgets/item/details/utils/item_title.dart';
import 'package:feeddeck/widgets/item/details/utils/item_videos.dart';
class ItemDetailsRSS extends StatelessWidget {
const ItemDetailsRSS({
@@ -19,10 +20,23 @@ class ItemDetailsRSS extends StatelessWidget {
final FDItem item;
final FDSource source;
/// [_buildImage] renders the [item.media] when the [shouldBeRendered] is
/// `true`. If it is `false` an empty container is returned.
Widget _buildImage(bool shouldBeRendered) {
if (!shouldBeRendered) {
/// [_buildMedia] renders an image or video for the item. If the description
/// of the item contains an image we do not render the image, because it could
/// already be rendered via the description.
///
/// Videos are currently always rendered, because they will not be rendered,
/// by the [MarkdownBody] widget.
Widget _buildMedia() {
if (item.options != null &&
item.options!.containsKey('video') &&
item.options!['video'] != null) {
return ItemVideos(videos: [item.options!['video']]);
}
/// Check if the description of the RSS feed contains an image. If this is
/// the case we do not render the image from the [item.media] because the
/// image is already rendered in the [ItemDescription] widget.
if (parse(item.description).querySelectorAll('img').isNotEmpty) {
return Container();
}
@@ -33,12 +47,6 @@ class ItemDetailsRSS extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// Check if the description of the RSS feed contains an image. If this is
/// the case we do not render the image from the [item.media] because the
/// image is already rendered in the [ItemDescription] widget.
final descriptionContainImage =
parse(item.description).querySelectorAll('img').isNotEmpty;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
@@ -50,7 +58,7 @@ class ItemDetailsRSS extends StatelessWidget {
item: item,
source: source,
),
_buildImage(!descriptionContainImage),
_buildMedia(),
ItemDescription(
itemDescription: item.description,
sourceFormat: DescriptionFormat.html,

View File

@@ -65,6 +65,13 @@ class _ItemAudioPlayerState extends State<ItemAudioPlayer> {
@override
void dispose() {
/// We have to dispose the [_player] when the widget is disposed, otherwise
/// the audio will continue to play in the background.
///
/// On Linux and Windows the audio will continue to play even if the
/// [_player] is disposed, so that we also call the `pause` method of the
/// [_player] to stop the audio.
_player.pause();
_player.dispose();
super.dispose();
}

View File

@@ -54,6 +54,15 @@ class ItemDescription extends StatelessWidget {
codeblockDecoration: const BoxDecoration(
color: Constants.secondary,
),
blockquoteDecoration: const BoxDecoration(
color: Constants.secondary,
border: Border(
left: BorderSide(
color: Constants.primary,
width: 1,
),
),
),
),
onTapLink: (text, href, title) {
if (href != null) {
@@ -158,7 +167,9 @@ class ItemDescription extends StatelessWidget {
if (sourceFormat == DescriptionFormat.html &&
tagetFormat == DescriptionFormat.plain) {
return _buildPlain(
itemDescription!.replaceAll(RegExp(r'<[^>]*>|&[^;]+;'), ''),
itemDescription!
.replaceAll(RegExp(r'<[^>]*>|&[^;]+;'), ' ')
.replaceAll(RegExp('\\s+'), ' '),
);
}

View File

@@ -0,0 +1,20 @@
library item_piped_video;
import 'package:flutter/material.dart';
import 'item_piped_video_stub.dart'
if (dart.library.io) 'item_piped_video_native.dart'
if (dart.library.html) 'item_piped_video_web.dart';
/// The [ItemPipedVideo] class implements a widget that displays a video from
/// Piped.
///
/// This is required because we are using different implementations for the web
/// and for all other target platforms (Android, iOS, macOS, Windows, Linux). On
/// the web we display the Piped video via an `iframe` element. On all other
/// platforms we are using the [piped_client] package to fetch the url of the
/// Piped video, which can then be displayed via our [ItemVideoPlayer] widget.
abstract class ItemPipedVideo implements StatefulWidget {
factory ItemPipedVideo(String? imageUrl, String videoUrl) =>
getItemPipedVideo(imageUrl, videoUrl);
}

View File

@@ -0,0 +1,125 @@
import 'package:flutter/material.dart';
import 'package:piped_client/piped_client.dart';
import 'package:feeddeck/widgets/item/details/utils/item_media.dart';
import 'package:feeddeck/widgets/item/details/utils/item_videos.dart';
import 'item_piped_video.dart';
/// The [ItemVideoQuality] class represents a list of video qualities for the
/// requested Piped video and the corresponding audio stream.
class ItemVideoQualitiesAndAudio {
const ItemVideoQualitiesAndAudio({
required this.qualities,
required this.audio,
});
final List<ItemVideoQuality> qualities;
final String audio;
}
/// [_getVideoId] returns the id of the provide video url, which can be used to
/// get the video streams via the Piped API.
String _getVideoId(String videoUrl) {
if (videoUrl.startsWith('https://piped.video/watch?v=')) {
return videoUrl.replaceFirst(
'https://piped.video/watch?v=',
'',
);
}
if (videoUrl.startsWith('https://piped.video/')) {
return videoUrl.replaceFirst(
'https://piped.video/',
'',
);
}
return videoUrl;
}
class ItemPipedVideoNative extends StatefulWidget implements ItemPipedVideo {
const ItemPipedVideoNative({
super.key,
required this.imageUrl,
required this.videoUrl,
});
final String? imageUrl;
final String videoUrl;
@override
State<ItemPipedVideoNative> createState() => _ItemPipedVideoNativeState();
}
class _ItemPipedVideoNativeState extends State<ItemPipedVideoNative> {
final piped = PipedClient();
late Future<ItemVideoQualitiesAndAudio> _futureFetchVideoAndAudioUrls;
/// [_fetchVideoAndAudioUrls] fetches the video and audio urls for the
/// requested Piped video. Since the video streams do not contain the audio
/// stream, we have to fetch the audio stream separately.
Future<ItemVideoQualitiesAndAudio> _fetchVideoAndAudioUrls() async {
final streams = await piped.streams(_getVideoId(widget.videoUrl));
return ItemVideoQualitiesAndAudio(
qualities: streams.videoStreams
.where(
(element) =>
element.mimeType == 'video/mp4' &&
element.format == PipedVideoStreamFormat.mp4,
)
.map(
(element) => ItemVideoQuality(
quality: element.quality,
video: element.url,
),
)
.toList(),
audio: streams.audioStreams
.where((element) => element.mimeType == 'audio/mp4')
.map((element) => element.url)
.first,
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
setState(() {
_futureFetchVideoAndAudioUrls = _fetchVideoAndAudioUrls();
});
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _futureFetchVideoAndAudioUrls,
builder: (
BuildContext context,
AsyncSnapshot<ItemVideoQualitiesAndAudio> snapshot,
) {
if (snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState == ConnectionState.waiting ||
snapshot.hasError ||
snapshot.data == null ||
snapshot.data!.qualities.isEmpty ||
snapshot.data!.audio.isEmpty) {
return ItemMedia(itemMedia: widget.imageUrl);
}
return ItemVideoPlayer(
video: snapshot.data!.qualities.first.video,
audio: snapshot.data!.audio,
qualities: snapshot.data!.qualities,
);
},
);
}
}
ItemPipedVideo getItemPipedVideo(String? imageUrl, String videoUrl) =>
ItemPipedVideoNative(
imageUrl: imageUrl,
videoUrl: videoUrl,
);

View File

@@ -0,0 +1,6 @@
import 'item_piped_video.dart';
ItemPipedVideo getItemPipedVideo(String? imageUrl, String videoUrl) =>
throw UnsupportedError(
'Can not ItemPipedVideo without the packages dart:html or dart:io',
);

View File

@@ -0,0 +1,90 @@
import 'dart:html'; // ignore: avoid_web_libraries_in_flutter
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:feeddeck/utils/constants.dart';
import 'item_piped_video.dart';
/// [_convertVideoUrl] converts the video url to a format that can be used to
/// embed the video in an iframe.
String _convertVideoUrl(String videoUrl) {
if (videoUrl.startsWith('https://piped.video/watch?v=')) {
return videoUrl.replaceFirst(
'https://piped.video/watch?v=',
'https://piped.video/embed/',
);
}
if (videoUrl.startsWith('https://piped.video/')) {
return videoUrl.replaceFirst(
'https://piped.video/',
'https://piped.video/embed/',
);
}
return videoUrl;
}
class ItemPipedVideoWeb extends StatefulWidget implements ItemPipedVideo {
const ItemPipedVideoWeb({
super.key,
required this.imageUrl,
required this.videoUrl,
});
final String? imageUrl;
final String videoUrl;
@override
State<ItemPipedVideoWeb> createState() => _ItemPipedVideoWebState();
}
class _ItemPipedVideoWebState extends State<ItemPipedVideoWeb> {
final IFrameElement _iframeElement = IFrameElement();
@override
void initState() {
super.initState();
_iframeElement.src = _convertVideoUrl(widget.videoUrl);
_iframeElement.style.border = 'none';
_iframeElement.allowFullscreen = true;
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(
widget.videoUrl,
(int viewId) => _iframeElement,
);
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.only(
bottom: Constants.spacingMiddle,
),
child: LayoutBuilder(
builder: (context, constraints) {
return Center(
child: SizedBox(
width: constraints.maxWidth,
height: constraints.maxWidth * 9.0 / 16.0,
child: HtmlElementView(
key: Key(widget.videoUrl),
viewType: widget.videoUrl,
),
),
);
},
),
);
}
}
ItemPipedVideo getItemPipedVideo(String? imageUrl, String videoUrl) =>
ItemPipedVideoWeb(
imageUrl: imageUrl,
videoUrl: videoUrl,
);

View File

@@ -25,22 +25,12 @@ class ItemVideos extends StatelessWidget {
return Container();
}
return Container(
padding: const EdgeInsets.only(
bottom: Constants.spacingMiddle,
),
child: ListView.separated(
shrinkWrap: true,
separatorBuilder: (context, index) {
return const SizedBox(
height: Constants.spacingMiddle,
);
},
itemCount: videos!.length,
itemBuilder: (context, index) {
return ItemVideoPlayer(video: videos![index]);
},
),
return ListView.builder(
shrinkWrap: true,
itemCount: videos!.length,
itemBuilder: (context, index) {
return ItemVideoPlayer(video: videos![index]);
},
);
}
}
@@ -61,6 +51,9 @@ class ItemVideoQuality {
/// by the user. It should be used in combination with the [ItemVideos] widget
/// and is responsible for the actual implementation of the video player.
///
/// If the provided [video] doesn't contain the audio stream it can be passed
/// via the [audio] parameter.
///
/// The optional [qualities] parameter can be used to display a list of
/// different qualities for the video, so that a user can select a lower quality
/// if the video is not loading fast enough.
@@ -68,10 +61,12 @@ class ItemVideoPlayer extends StatefulWidget {
const ItemVideoPlayer({
super.key,
required this.video,
this.audio,
this.qualities,
});
final String video;
final String? audio;
final List<ItemVideoQuality>? qualities;
@override
@@ -99,43 +94,60 @@ class _ItemVideoPlayerState extends State<ItemVideoPlayer> {
maxWidth: Constants.centeredFormMaxWidth,
),
builder: (BuildContext context) {
return Container(
margin: const EdgeInsets.all(
Constants.spacingMiddle,
),
padding: const EdgeInsets.only(
left: Constants.spacingMiddle,
right: Constants.spacingMiddle,
),
decoration: const BoxDecoration(
color: Constants.background,
borderRadius: BorderRadius.all(
Radius.circular(Constants.spacingMiddle),
return SafeArea(
child: Container(
margin: const EdgeInsets.all(
Constants.spacingMiddle,
),
padding: const EdgeInsets.only(
left: Constants.spacingMiddle,
right: Constants.spacingMiddle,
),
decoration: const BoxDecoration(
color: Constants.background,
borderRadius: BorderRadius.all(
Radius.circular(Constants.spacingMiddle),
),
),
child: Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: widget.qualities!
.asMap()
.entries
.map((quality) {
if (quality.key == widget.qualities!.length - 1) {
return [
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () async {
Navigator.of(context).pop();
await _playerOpen(quality.value.video);
},
title: Text(quality.value.quality),
),
];
}
return [
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () async {
Navigator.of(context).pop();
await _playerOpen(quality.value.video);
},
title: Text(quality.value.quality),
),
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
];
})
.expand((e) => e)
.toList(),
),
),
child: ListView.separated(
shrinkWrap: true,
separatorBuilder: (context, index) {
return const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
);
},
itemCount: widget.qualities!.length,
itemBuilder: (context, index) {
return ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () {
Navigator.of(context).pop();
player.open(
Media(widget.qualities![index].video),
play: true,
);
},
title: Text(widget.qualities![index].quality),
);
},
),
);
},
@@ -194,13 +206,32 @@ class _ItemVideoPlayerState extends State<ItemVideoPlayer> {
];
}
/// [_playerOpen] opens the video player with the provided [video] and sets
/// the audio track if it is provided via the [audio] parameter.
Future<void> _playerOpen(String video) async {
await player.open(
Media(video),
play: false,
);
/// Load an external audio track when it is provided via the [audio]
/// parameter.
/// See: https://github.com/media-kit/media-kit?tab=readme-ov-file#load-external-audio-track
if (widget.audio != null) {
await player.setAudioTrack(
AudioTrack.uri(
widget.audio!,
),
);
}
}
@override
void initState() {
super.initState();
player.open(
Media(widget.video),
play: false,
);
WidgetsBinding.instance.addPostFrameCallback((_) {
_playerOpen(widget.video);
});
}
@override
@@ -211,46 +242,51 @@ class _ItemVideoPlayerState extends State<ItemVideoPlayer> {
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
return Center(
child: SizedBox(
width: constraints.maxWidth,
height: constraints.maxWidth * 9.0 / 16.0,
child: MaterialDesktopVideoControlsTheme(
normal: MaterialDesktopVideoControlsThemeData(
bottomButtonBar: _buildBottomButtonBar(false),
seekBarPositionColor: Constants.primary,
seekBarThumbColor: Constants.primary,
),
fullscreen: const MaterialDesktopVideoControlsThemeData(
seekBarPositionColor: Constants.primary,
seekBarThumbColor: Constants.primary,
),
child: MaterialVideoControlsTheme(
normal: MaterialVideoControlsThemeData(
bottomButtonBar: _buildBottomButtonBar(true),
return Container(
padding: const EdgeInsets.only(
bottom: Constants.spacingMiddle,
),
child: LayoutBuilder(
builder: (context, constraints) {
return Center(
child: SizedBox(
width: constraints.maxWidth,
height: constraints.maxWidth * 9.0 / 16.0,
child: MaterialDesktopVideoControlsTheme(
normal: MaterialDesktopVideoControlsThemeData(
bottomButtonBar: _buildBottomButtonBar(false),
seekBarPositionColor: Constants.primary,
seekBarThumbColor: Constants.primary,
),
fullscreen: const MaterialVideoControlsThemeData(
fullscreen: const MaterialDesktopVideoControlsThemeData(
seekBarPositionColor: Constants.primary,
seekBarThumbColor: Constants.primary,
),
child: Video(
controller: controller,
controls: kIsWeb ||
Platform.isLinux ||
Platform.isMacOS ||
Platform.isWindows
? MaterialDesktopVideoControls
: MaterialVideoControls,
child: MaterialVideoControlsTheme(
normal: MaterialVideoControlsThemeData(
bottomButtonBar: _buildBottomButtonBar(true),
seekBarPositionColor: Constants.primary,
seekBarThumbColor: Constants.primary,
),
fullscreen: const MaterialVideoControlsThemeData(
seekBarPositionColor: Constants.primary,
seekBarThumbColor: Constants.primary,
),
child: Video(
controller: controller,
controls: kIsWeb ||
Platform.isLinux ||
Platform.isMacOS ||
Platform.isWindows
? MaterialDesktopVideoControls
: MaterialVideoControls,
),
),
),
),
),
);
},
);
},
),
);
}
}

View File

@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/widgets/item/details/utils/item_media.dart';
import 'package:feeddeck/widgets/item/details/utils/item_videos.dart';
import 'item_youtube_video.dart';
@@ -66,18 +65,14 @@ class _ItemYoutubeVideoNativeState extends State<ItemYoutubeVideoNative> {
if (snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState == ConnectionState.waiting ||
snapshot.hasError ||
snapshot.data == null) {
snapshot.data == null ||
snapshot.data!.isEmpty) {
return ItemMedia(itemMedia: widget.imageUrl);
}
return Container(
padding: const EdgeInsets.only(
bottom: Constants.spacingMiddle,
),
child: ItemVideoPlayer(
video: snapshot.data!.first.video,
qualities: snapshot.data,
),
return ItemVideoPlayer(
video: snapshot.data!.first.video,
qualities: snapshot.data,
);
},
);

View File

@@ -7,6 +7,33 @@ import 'package:flutter/material.dart';
import 'package:feeddeck/utils/constants.dart';
import 'item_youtube_video.dart';
/// [_convertVideoUrl] converts the video url to a format that can be used to
/// embed the video in an iframe.
String _convertVideoUrl(String videoUrl) {
if (videoUrl.startsWith('https://youtu.be/')) {
return videoUrl.replaceFirst(
'https://youtu.be/',
'https://www.youtube-nocookie.com/embed/',
);
}
if (videoUrl.startsWith('https://www.youtube.com/watch?v=')) {
return 'https://www.youtube-nocookie.com/embed/${videoUrl.replaceFirst(
'https://www.youtube.com/watch?v=',
'',
)}';
}
if (videoUrl.startsWith('https://m.youtube.com/watch?v=')) {
return 'https://www.youtube-nocookie.com/embed/${videoUrl.replaceFirst(
'https://m.youtube.com/watch?v=',
'',
)}';
}
return videoUrl;
}
class ItemYoutubeVideoWeb extends StatefulWidget implements ItemYoutubeVideo {
const ItemYoutubeVideoWeb({
super.key,
@@ -27,11 +54,8 @@ class _ItemYoutubeVideoWebState extends State<ItemYoutubeVideoWeb> {
@override
void initState() {
super.initState();
_iframeElement.src =
'https://www.youtube-nocookie.com/embed/${widget.videoUrl.replaceFirst(
'https://www.youtube.com/watch?v=',
'',
)}';
_iframeElement.src = _convertVideoUrl(widget.videoUrl);
_iframeElement.style.border = 'none';
_iframeElement.allowFullscreen = true;

View File

@@ -5,8 +5,10 @@ import 'package:provider/provider.dart';
import 'package:feeddeck/models/item.dart';
import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/repositories/items_repository.dart';
import 'package:feeddeck/widgets/item/preview/item_preview_fourchan.dart';
import 'package:feeddeck/widgets/item/preview/item_preview_github.dart';
import 'package:feeddeck/widgets/item/preview/item_preview_googlenews.dart';
import 'package:feeddeck/widgets/item/preview/item_preview_lemmy.dart';
import 'package:feeddeck/widgets/item/preview/item_preview_mastodon.dart';
import 'package:feeddeck/widgets/item/preview/item_preview_medium.dart';
import 'package:feeddeck/widgets/item/preview/item_preview_nitter.dart';
@@ -43,6 +45,11 @@ class ItemPreview extends StatelessWidget {
/// Based on the [source.type] we display a different preview. The preview
/// for each source type is implemented in a separate widget.
switch (source.type) {
case FDSourceType.fourchan:
return ItemPreviewFourChan(
item: item,
source: source,
);
case FDSourceType.github:
return ItemPreviewGithub(
item: item,
@@ -53,6 +60,11 @@ class ItemPreview extends StatelessWidget {
item: item,
source: source,
);
case FDSourceType.lemmy:
return ItemPreviewLemmy(
item: item,
source: source,
);
case FDSourceType.mastodon:
return ItemPreviewMastodon(
item: item,
@@ -98,11 +110,6 @@ class ItemPreview extends StatelessWidget {
item: item,
source: source,
);
// case FDSourceType.x:
// return ItemPreviewX(
// item: item,
// source: source,
// );
case FDSourceType.youtube:
return ItemPreviewYoutube(
item: item,

View File

@@ -5,11 +5,12 @@ import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/widgets/item/preview/utils/details.dart';
import 'package:feeddeck/widgets/item/preview/utils/item_actions.dart';
import 'package:feeddeck/widgets/item/preview/utils/item_description.dart';
import 'package:feeddeck/widgets/item/preview/utils/item_media_gallery.dart';
import 'package:feeddeck/widgets/item/preview/utils/item_media.dart';
import 'package:feeddeck/widgets/item/preview/utils/item_source.dart';
import 'package:feeddeck/widgets/item/preview/utils/item_title.dart';
class ItemPreviewX extends StatelessWidget {
const ItemPreviewX({
class ItemPreviewFourChan extends StatelessWidget {
const ItemPreviewFourChan({
super.key,
required this.item,
required this.source,
@@ -25,24 +26,23 @@ class ItemPreviewX extends StatelessWidget {
onTap: () => showDetails(context, item, source),
children: [
ItemSource(
sourceTitle: item.author ?? '',
sourceSubtitle: '${source.type.toLocalizedString()}: ${source.title}',
sourceTitle: source.title,
sourceSubtitle: source.type.toLocalizedString(),
sourceType: source.type,
sourceIcon: source.icon,
itemPublishedAt: item.publishedAt,
itemIsRead: item.isRead,
),
ItemTitle(
itemTitle: item.title,
),
ItemMedia(
itemMedia: item.media,
),
ItemDescription(
itemDescription: item.description,
sourceFormat: DescriptionFormat.html,
tagetFormat: DescriptionFormat.markdown,
),
ItemMediaGallery(
itemMedias: item.options != null && item.options!.containsKey('media')
? (item.options!['media'] as List)
.map((item) => item as String)
.toList()
: null,
tagetFormat: DescriptionFormat.plain,
),
],
);

View File

@@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
import 'package:feeddeck/models/item.dart';
import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/widgets/item/preview/utils/details.dart';
import 'package:feeddeck/widgets/item/preview/utils/item_actions.dart';
import 'package:feeddeck/widgets/item/preview/utils/item_description.dart';
import 'package:feeddeck/widgets/item/preview/utils/item_media.dart';
import 'package:feeddeck/widgets/item/preview/utils/item_source.dart';
import 'package:feeddeck/widgets/item/preview/utils/item_title.dart';
class ItemPreviewLemmy extends StatelessWidget {
const ItemPreviewLemmy({
super.key,
required this.item,
required this.source,
});
final FDItem item;
final FDSource source;
/// [_buildMedia] returns the media of the item if the item has media file.
/// Since we save images and videos within the media property we have to
/// filter out all videos.
///
/// See the `getMedia` function in the `lemmy.ts` file, for a list of
/// extension which are a image / video.
Widget _buildMedia() {
if (item.media != null && item.media! != '') {
final mediaUrl = Uri.parse(item.media!);
if (mediaUrl.path.endsWith('.jpg') ||
mediaUrl.path.endsWith('.jpeg') ||
mediaUrl.path.endsWith('.png') ||
mediaUrl.path.endsWith('.gif')) {
return ItemMedia(
itemMedia: item.media,
);
}
}
return Container();
}
@override
Widget build(BuildContext context) {
return ItemActions(
item: item,
onTap: () => showDetails(context, item, source),
children: [
ItemSource(
sourceTitle: source.title,
sourceSubtitle: source.type.toLocalizedString(),
sourceType: source.type,
sourceIcon: source.icon,
itemPublishedAt: item.publishedAt,
itemIsRead: item.isRead,
),
ItemTitle(
itemTitle: item.title,
),
_buildMedia(),
ItemDescription(
itemDescription: item.description,
sourceFormat: DescriptionFormat.html,
tagetFormat: DescriptionFormat.plain,
),
],
);
}
}

View File

@@ -9,6 +9,7 @@ import 'package:provider/provider.dart';
import 'package:feeddeck/models/item.dart';
import 'package:feeddeck/repositories/items_repository.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/utils/openurl.dart';
/// The [ItemActions] widget provides an actions menu for an item, which can be
/// used to quickly mark an item as read or unread and to add or remove a
@@ -62,6 +63,14 @@ class _ItemActionsState extends State<ItemActions> {
} catch (_) {}
}
/// [_openUrl] opens the item url in the default browser of the current
/// device.
Future<void> _openUrl() async {
try {
await openUrl(widget.item.link);
} catch (_) {}
}
/// [_getTapPositionLarge] set the [_tapPosition] which will be used for the
/// actions menu.
void _getTapPositionLarge(TapDownDetails details) {
@@ -70,10 +79,10 @@ class _ItemActionsState extends State<ItemActions> {
});
}
/// [_showActionsMenuLarge] shows a popup menu with all available aactions for
/// [_showActionsMenuLarge] shows a popup menu with all available actions for
/// an item. This means the user can mark an item as read or unread or a user
/// can add or remove a bookmark for an item.
void _showActionsMenuLarge(BuildContext context) async {
void _showActionsMenuLarge() async {
HapticFeedback.heavyImpact();
final RenderObject? overlay =
@@ -126,6 +135,15 @@ class _ItemActionsState extends State<ItemActions> {
: const Text('Add Bookmark'),
),
),
const PopupMenuItem(
value: 'openlink',
child: ListTile(
leading: Icon(
Icons.launch,
),
title: Text('Open Link'),
),
),
],
);
@@ -140,9 +158,19 @@ class _ItemActionsState extends State<ItemActions> {
await _bookmark(context);
}
break;
case 'openlink':
if (mounted) {
await _openUrl();
}
break;
}
}
/// [_showActionsMenuSmall] shows a modal bottom sheet with all available
/// actions for an item. This means the user can mark an item as read or
/// unread or a user can add or remove a bookmark for an item. The actions are
/// the same as we show on large screens via [_showActionsMenuLarge], but the
/// modal bottom sheet is optiomized for small screens.
void _showActionsMenuSmall(BuildContext mainContext) async {
HapticFeedback.heavyImpact();
@@ -157,56 +185,72 @@ class _ItemActionsState extends State<ItemActions> {
maxWidth: Constants.centeredFormMaxWidth,
),
builder: (BuildContext context) {
return Container(
margin: const EdgeInsets.all(
Constants.spacingMiddle,
),
padding: const EdgeInsets.only(
left: Constants.spacingMiddle,
right: Constants.spacingMiddle,
),
decoration: const BoxDecoration(
color: Constants.background,
borderRadius: BorderRadius.all(
Radius.circular(Constants.spacingMiddle),
return SafeArea(
child: Container(
margin: const EdgeInsets.all(
Constants.spacingMiddle,
),
),
child: Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () async {
Navigator.of(context).pop();
_read(mainContext);
},
leading: widget.item.isRead
? const Icon(Icons.visibility_off)
: const Icon(Icons.visibility),
title: widget.item.isRead
? const Text('Mark as Unread')
: const Text('Mark as Read'),
padding: const EdgeInsets.only(
left: Constants.spacingMiddle,
right: Constants.spacingMiddle,
),
decoration: const BoxDecoration(
color: Constants.background,
borderRadius: BorderRadius.all(
Radius.circular(Constants.spacingMiddle),
),
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () async {
Navigator.of(context).pop();
_bookmark(mainContext);
},
leading: widget.item.isBookmarked
? const Icon(Icons.bookmark)
: const Icon(Icons.bookmark_outline),
title: widget.item.isBookmarked
? const Text('Remove Bookmark')
: const Text('Add Bookmark'),
),
],
),
child: Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () async {
Navigator.of(context).pop();
_read(mainContext);
},
leading: widget.item.isRead
? const Icon(Icons.visibility_off)
: const Icon(Icons.visibility),
title: widget.item.isRead
? const Text('Mark as Unread')
: const Text('Mark as Read'),
),
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () async {
Navigator.of(context).pop();
_bookmark(mainContext);
},
leading: widget.item.isBookmarked
? const Icon(Icons.bookmark)
: const Icon(Icons.bookmark_outline),
title: widget.item.isBookmarked
? const Text('Remove Bookmark')
: const Text('Add Bookmark'),
),
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () async {
Navigator.of(context).pop();
_openUrl();
},
leading: const Icon(Icons.launch),
title: const Text('Open Link'),
),
],
),
),
);
},
@@ -326,12 +370,17 @@ class _ItemActionsState extends State<ItemActions> {
/// On large screens we show an actions menu via `_showActionsMenuLarge`,
/// which is rendered directly at the point where the user pressed on the
/// item.
/// The menu can be opened by a long press or by a secondary tap (right
/// click) on the item.
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: widget.onTap,
onTapDown: (details) => _getTapPositionLarge(details),
onLongPress: () => _showActionsMenuLarge(context),
onLongPress: () => _showActionsMenuLarge(),
onSecondaryTapDown:
kIsWeb ? null : (details) => _getTapPositionLarge(details),
onSecondaryTap: kIsWeb ? null : () => _showActionsMenuLarge(),
child: Container(
width: double.infinity,
decoration: const BoxDecoration(

View File

@@ -55,6 +55,15 @@ class ItemDescription extends StatelessWidget {
codeblockDecoration: const BoxDecoration(
color: Constants.secondary,
),
blockquoteDecoration: const BoxDecoration(
color: Constants.secondary,
border: Border(
left: BorderSide(
color: Constants.primary,
width: 1,
),
),
),
),
onTapLink: (text, href, title) {
if (href != null) {
@@ -81,6 +90,10 @@ class ItemDescription extends StatelessWidget {
}
/// [_buildPlain] renders the provided [content] as plain text.
///
/// To not have some trailing newlines, the [content] is trimmed and splitted
/// on newline characters, so that we can filter out empty lines, before the
/// the content is rendered.
Widget _buildPlain(String content) {
if (content == '') {
return Container();
@@ -91,7 +104,7 @@ class ItemDescription extends StatelessWidget {
bottom: Constants.spacingExtraSmall,
),
child: Text(
content.trim(),
content.trim().split('\n').where((line) => line != '').join('\n'),
maxLines: 5,
style: const TextStyle(
overflow: TextOverflow.ellipsis,
@@ -121,7 +134,9 @@ class ItemDescription extends StatelessWidget {
if (sourceFormat == DescriptionFormat.html &&
tagetFormat == DescriptionFormat.plain) {
return _buildPlain(
itemDescription!.replaceAll(RegExp(r'<[^>]*>|&[^;]+;'), ''),
itemDescription!
.replaceAll(RegExp(r'<[^>]*>|&[^;]+;'), ' ')
.replaceAll(RegExp('\\s+'), ' '),
);
}

View File

@@ -27,14 +27,22 @@ class _ResetPasswordState extends State<ResetPassword> {
/// [_validatePassword] validates the email address provided via the
/// [TextField] of the [_passwordController]. The password field can not be
/// empty and must have a minimum length of 6 characters.
/// empty and must have a minimum length of 8 characters. The password must
/// also contain at least one upper case letter, one lower case letter and one
/// number.
String? _validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
if (value.length < 6) {
return 'Password must be a least 6 characters long';
if (value.length < 8) {
return 'Password must be a least 8 characters long';
}
String pattern = r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9]).{8,}$';
RegExp regExp = RegExp(pattern);
if (!regExp.hasMatch(value)) {
return 'Password must contain at least one upper case letter, one lower case letter and one number';
}
return null;

View File

@@ -232,78 +232,80 @@ class _SettingsAccountsGithubAddState extends State<SettingsAccountsGithubAdd> {
),
],
),
body: Column(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(Constants.spacingMiddle),
child: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MarkdownBody(
selectable: true,
data: _helpText,
onTapLink: (text, href, title) {
try {
if (href != null) {
openUrl(href);
}
} catch (_) {}
},
),
const SizedBox(
height: Constants.spacingMiddle,
),
TextFormField(
controller: _tokenController,
keyboardType: TextInputType.text,
autocorrect: false,
enableSuggestions: true,
maxLines: 1,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Token',
body: SafeArea(
child: Column(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(Constants.spacingMiddle),
child: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MarkdownBody(
selectable: true,
data: _helpText,
onTapLink: (text, href, title) {
try {
if (href != null) {
openUrl(href);
}
} catch (_) {}
},
),
validator: (value) => _validateToken(value),
onFieldSubmitted: (value) => _addAccount(),
),
],
const SizedBox(
height: Constants.spacingMiddle,
),
TextFormField(
controller: _tokenController,
keyboardType: TextInputType.text,
autocorrect: false,
enableSuggestions: true,
maxLines: 1,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Token',
),
validator: (value) => _validateToken(value),
onFieldSubmitted: (value) => _addAccount(),
),
],
),
),
),
),
),
),
const SizedBox(
height: Constants.spacingSmall,
),
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
_buildError(),
Padding(
padding: const EdgeInsets.all(Constants.spacingMiddle),
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
maximumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
minimumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
),
label: const Text('Add Account'),
onPressed: _isLoading ? null : _addAccount,
icon: _isLoading
? const ElevatedButtonProgressIndicator()
: const Icon(Icons.add),
const SizedBox(
height: Constants.spacingSmall,
),
),
],
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
_buildError(),
Padding(
padding: const EdgeInsets.all(Constants.spacingMiddle),
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
maximumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
minimumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
),
label: const Text('Add Account'),
onPressed: _isLoading ? null : _addAccount,
icon: _isLoading
? const ElevatedButtonProgressIndicator()
: const Icon(Icons.add),
),
),
],
),
),
);
}

View File

@@ -18,60 +18,62 @@ class SettingsAccountsActions extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(
Constants.spacingMiddle,
),
padding: const EdgeInsets.only(
left: Constants.spacingMiddle,
right: Constants.spacingMiddle,
),
decoration: const BoxDecoration(
color: Constants.background,
borderRadius: BorderRadius.all(
Radius.circular(Constants.spacingMiddle),
return SafeArea(
child: Container(
margin: const EdgeInsets.all(
Constants.spacingMiddle,
),
),
child: Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () {
Navigator.of(context).pop();
reconnect();
},
leading: const Icon(
Icons.link,
),
title: const Text(
'Re-Connect',
),
padding: const EdgeInsets.only(
left: Constants.spacingMiddle,
right: Constants.spacingMiddle,
),
decoration: const BoxDecoration(
color: Constants.background,
borderRadius: BorderRadius.all(
Radius.circular(Constants.spacingMiddle),
),
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () {
Navigator.of(context).pop();
delete();
},
leading: const Icon(
Icons.delete,
color: Constants.error,
),
title: const Text(
'Delete',
style: TextStyle(
color: Constants.error,
),
child: Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () {
Navigator.of(context).pop();
reconnect();
},
leading: const Icon(
Icons.link,
),
title: const Text(
'Re-Connect',
),
),
),
],
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () {
Navigator.of(context).pop();
delete();
},
leading: const Icon(
Icons.delete,
color: Constants.error,
),
title: const Text(
'Delete',
style: TextStyle(
color: Constants.error,
),
),
),
],
),
),
);
}

View File

@@ -166,28 +166,29 @@ class _SettingsPremiumInAppState extends State<SettingsPremiumInApp> {
),
],
),
body: FutureBuilder(
future: _futureFetchOfferings,
builder: (
BuildContext context,
AsyncSnapshot<Offering?> snapshot,
) {
return Column(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(Constants.spacingMiddle),
child: SingleChildScrollView(
child: snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState ==
ConnectionState.waiting ||
snapshot.hasError ||
snapshot.data == null ||
snapshot.data?.monthly == null
? const Text('Loading ...')
: MarkdownBody(
selectable: true,
data: '''
body: SafeArea(
child: FutureBuilder(
future: _futureFetchOfferings,
builder: (
BuildContext context,
AsyncSnapshot<Offering?> snapshot,
) {
return Column(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(Constants.spacingMiddle),
child: SingleChildScrollView(
child: snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState ==
ConnectionState.waiting ||
snapshot.hasError ||
snapshot.data == null ||
snapshot.data?.monthly == null
? const Text('Loading ...')
: MarkdownBody(
selectable: true,
data: '''
You are currently using the free version of FeedDeck, which allows you to add up
to 10 sources for the first 7 days. After that trial period your sources will
not be updated anymore.
@@ -197,57 +198,61 @@ upgrade to a premium account. The premium account costs
${snapshot.data?.monthly?.storeProduct.priceString} per month and can be
canceled at any time.
''',
),
),
),
),
const SizedBox(
height: Constants.spacingSmall,
),
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
Padding(
padding: const EdgeInsets.all(Constants.spacingMiddle),
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Constants.primary,
foregroundColor: Constants.onPrimary,
maximumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
minimumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
),
),
label: Text(
snapshot.data?.monthly?.storeProduct.priceString != null
? 'Subscribe to FeedDeck Premium for ${snapshot.data?.monthly?.storeProduct.priceString}'
: 'Subscribe to FeedDeck Premium',
),
onPressed: snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState == ConnectionState.waiting ||
snapshot.hasError ||
snapshot.data == null ||
snapshot.data?.monthly == null ||
_isLoading
? null
: () => _purchase(snapshot.data!.monthly!),
icon: snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState == ConnectionState.waiting ||
snapshot.hasError ||
snapshot.data == null ||
snapshot.data?.monthly == null ||
_isLoading
? const ElevatedButtonProgressIndicator()
: const Icon(FDIcons.feeddeck),
),
),
],
);
},
const SizedBox(
height: Constants.spacingSmall,
),
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
Padding(
padding: const EdgeInsets.all(Constants.spacingMiddle),
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Constants.primary,
foregroundColor: Constants.onPrimary,
maximumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
minimumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
),
label: Text(
snapshot.data?.monthly?.storeProduct.priceString != null
? 'Subscribe to FeedDeck Premium for ${snapshot.data?.monthly?.storeProduct.priceString}'
: 'Subscribe to FeedDeck Premium',
),
onPressed:
snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState ==
ConnectionState.waiting ||
snapshot.hasError ||
snapshot.data == null ||
snapshot.data?.monthly == null ||
_isLoading
? null
: () => _purchase(snapshot.data!.monthly!),
icon: snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState ==
ConnectionState.waiting ||
snapshot.hasError ||
snapshot.data == null ||
snapshot.data?.monthly == null ||
_isLoading
? const ElevatedButtonProgressIndicator()
: const Icon(FDIcons.feeddeck),
),
),
],
);
},
),
),
);
}

View File

@@ -87,62 +87,67 @@ class _SettingsPremiumStripeState extends State<SettingsPremiumStripe> {
),
],
),
body: FutureBuilder(
future: _futureFetchCheckoutSessionLink,
builder: (
BuildContext context,
AsyncSnapshot<String> snapshot,
) {
return Column(
children: [
const Expanded(
child: Padding(
padding: EdgeInsets.all(Constants.spacingMiddle),
child: SingleChildScrollView(
child: MarkdownBody(
selectable: true,
data: _settingsPremiumStripeText,
body: SafeArea(
child: FutureBuilder(
future: _futureFetchCheckoutSessionLink,
builder: (
BuildContext context,
AsyncSnapshot<String> snapshot,
) {
return Column(
children: [
const Expanded(
child: Padding(
padding: EdgeInsets.all(Constants.spacingMiddle),
child: SingleChildScrollView(
child: MarkdownBody(
selectable: true,
data: _settingsPremiumStripeText,
),
),
),
),
),
const SizedBox(
height: Constants.spacingSmall,
),
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
Padding(
padding: const EdgeInsets.all(Constants.spacingMiddle),
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Constants.primary,
foregroundColor: Constants.onPrimary,
maximumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
minimumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
),
label: const Text('Subscribe to FeedDeck Premium'),
onPressed: snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState == ConnectionState.waiting ||
snapshot.hasError
? null
: () => _openUrl(snapshot.data),
icon: snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState == ConnectionState.waiting ||
snapshot.hasError
? const ElevatedButtonProgressIndicator()
: const Icon(FDIcons.feeddeck),
const SizedBox(
height: Constants.spacingSmall,
),
),
],
);
},
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
Padding(
padding: const EdgeInsets.all(Constants.spacingMiddle),
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Constants.primary,
foregroundColor: Constants.onPrimary,
maximumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
minimumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
),
label: const Text('Subscribe to FeedDeck Premium'),
onPressed:
snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState ==
ConnectionState.waiting ||
snapshot.hasError
? null
: () => _openUrl(snapshot.data),
icon: snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState ==
ConnectionState.waiting ||
snapshot.hasError
? const ElevatedButtonProgressIndicator()
: const Icon(FDIcons.feeddeck),
),
),
],
);
},
),
),
);
}

View File

@@ -180,60 +180,65 @@ class _SettingsProfileCustomerPortalModalState
),
],
),
body: FutureBuilder(
future: _futureFetchCustomerPortalLink,
builder: (
BuildContext context,
AsyncSnapshot<String> snapshot,
) {
return Column(
children: [
const Expanded(
child: Padding(
padding: EdgeInsets.all(Constants.spacingMiddle),
child: SingleChildScrollView(
child: MarkdownBody(
selectable: true,
data: _settingsProfileCustomerPortalText,
body: SafeArea(
child: FutureBuilder(
future: _futureFetchCustomerPortalLink,
builder: (
BuildContext context,
AsyncSnapshot<String> snapshot,
) {
return Column(
children: [
const Expanded(
child: Padding(
padding: EdgeInsets.all(Constants.spacingMiddle),
child: SingleChildScrollView(
child: MarkdownBody(
selectable: true,
data: _settingsProfileCustomerPortalText,
),
),
),
),
),
const SizedBox(
height: Constants.spacingSmall,
),
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
Padding(
padding: const EdgeInsets.all(Constants.spacingMiddle),
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
maximumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
minimumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
),
label: const Text('Open Customer Portal'),
onPressed: snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState == ConnectionState.waiting ||
snapshot.hasError
? null
: () => _openUrl(snapshot.data),
icon: snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState == ConnectionState.waiting ||
snapshot.hasError
? const ElevatedButtonProgressIndicator()
: const Icon(Icons.receipt),
const SizedBox(
height: Constants.spacingSmall,
),
),
],
);
},
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
Padding(
padding: const EdgeInsets.all(Constants.spacingMiddle),
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
maximumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
minimumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),
),
label: const Text('Open Customer Portal'),
onPressed:
snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState ==
ConnectionState.waiting ||
snapshot.hasError
? null
: () => _openUrl(snapshot.data),
icon: snapshot.connectionState == ConnectionState.none ||
snapshot.connectionState ==
ConnectionState.waiting ||
snapshot.hasError
? const ElevatedButtonProgressIndicator()
: const Icon(Icons.receipt),
),
),
],
);
},
),
),
);
}

View File

@@ -24,14 +24,22 @@ class _SettingsProfilePasswordState extends State<SettingsProfilePassword> {
/// [_validatePassword] validates the email address provided via the
/// [TextField] of the [_newPasswordController]. The password field can not be
/// empty and must have a minimum length of 6 characters.
/// empty and must have a minimum length of 8 characters. The password must
/// also contain at least one upper case letter, one lower case letter and one
/// number.
String? _validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
if (value.length < 6) {
return 'Password must be a least 6 characters long';
if (value.length < 8) {
return 'Password must be a least 8 characters long';
}
String pattern = r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9]).{8,}$';
RegExp regExp = RegExp(pattern);
if (!regExp.hasMatch(value)) {
return 'Password must contain at least one upper case letter, one lower case letter and one number';
}
return null;

View File

@@ -152,60 +152,62 @@ class SettingsProfileSignOutActions extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(
Constants.spacingMiddle,
),
padding: const EdgeInsets.only(
left: Constants.spacingMiddle,
right: Constants.spacingMiddle,
),
decoration: const BoxDecoration(
color: Constants.background,
borderRadius: BorderRadius.all(
Radius.circular(Constants.spacingMiddle),
return SafeArea(
child: Container(
margin: const EdgeInsets.all(
Constants.spacingMiddle,
),
),
child: Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () {
Navigator.of(context).pop();
signOut(supabase.SignOutScope.local);
},
leading: const Icon(
Icons.logout,
),
title: const Text(
'From current device',
),
padding: const EdgeInsets.only(
left: Constants.spacingMiddle,
right: Constants.spacingMiddle,
),
decoration: const BoxDecoration(
color: Constants.background,
borderRadius: BorderRadius.all(
Radius.circular(Constants.spacingMiddle),
),
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () {
Navigator.of(context).pop();
signOut(supabase.SignOutScope.global);
},
leading: const Icon(
Icons.logout,
color: Constants.error,
),
title: const Text(
'From all devices',
style: TextStyle(
color: Constants.error,
),
child: Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () {
Navigator.of(context).pop();
signOut(supabase.SignOutScope.local);
},
leading: const Icon(
Icons.logout,
),
title: const Text(
'From current device',
),
),
),
],
const Divider(
color: Constants.dividerColor,
height: 1,
thickness: 1,
),
ListTile(
mouseCursor: SystemMouseCursors.click,
onTap: () {
Navigator.of(context).pop();
signOut(supabase.SignOutScope.global);
},
leading: const Icon(
Icons.logout,
color: Constants.error,
),
title: const Text(
'From all devices',
style: TextStyle(
color: Constants.error,
),
),
),
],
),
),
);
}

View File

@@ -10,8 +10,9 @@ import 'package:supabase_flutter/supabase_flutter.dart' as supabase;
import 'package:feeddeck/repositories/app_repository.dart';
import 'package:feeddeck/repositories/settings_repository.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/utils/desktop_login_manager.dart';
import 'package:feeddeck/utils/desktop_signin_manager.dart';
import 'package:feeddeck/utils/fd_icons.dart';
import 'package:feeddeck/utils/signin_with_apple.dart';
import 'package:feeddeck/widgets/deck/deck_layout.dart';
import 'package:feeddeck/widgets/general/elevated_button_progress_indicator.dart';
import 'package:feeddeck/widgets/general/logo.dart';
@@ -65,7 +66,7 @@ class _SignInState extends State<SignIn> {
);
await supabase.Supabase.instance.client.auth.signInWithIdToken(
provider: supabase.Provider.google,
provider: supabase.OAuthProvider.google,
idToken: idToken,
);
@@ -90,17 +91,17 @@ class _SignInState extends State<SignIn> {
);
} else if (!kIsWeb &&
(Platform.isLinux || Platform.isMacOS || Platform.isWindows)) {
/// On Linux, macOS and Windows we have to use the [DesktopLoginManager]
/// to handle the login via the users Google account. Once the sing in
/// process is finished we have to call the init method of the
/// [AppRepository] to load the users data.
/// On Linux, macOS and Windows we have to use the
/// [DesktopSignInManager] to handle the login via the users Google
/// account. Once the sing in process is finished we have to call the
/// init method of the [AppRepository] to load the users data.
setState(() {
_isLoading = true;
_error = '';
});
await DesktopLoginManager(
provider: supabase.Provider.google,
await DesktopSignInManager(
provider: supabase.OAuthProvider.google,
queryParams: {
'access_type': 'offline',
'prompt': 'consent',
@@ -134,7 +135,7 @@ class _SignInState extends State<SignIn> {
/// method of the [AppRepository] is automatically called. On iOS
/// the authentication is the handled via the `singin-callback` route.
await supabase.Supabase.instance.client.auth.signInWithOAuth(
supabase.Provider.google,
supabase.OAuthProvider.google,
queryParams: {
'access_type': 'offline',
'prompt': 'consent',
@@ -165,7 +166,7 @@ class _SignInState extends State<SignIn> {
_error = '';
});
await supabase.Supabase.instance.client.auth.signInWithApple();
await signInWithApple();
if (!mounted) return;
await Provider.of<AppRepository>(
@@ -187,7 +188,7 @@ class _SignInState extends State<SignIn> {
(route) => false,
);
} else if (!kIsWeb && (Platform.isLinux || Platform.isWindows)) {
/// On Linux and Windows we have to use the [DesktopLoginManager] to
/// On Linux and Windows we have to use the [DesktopSignInManager] to
/// handle the login via the users Apple account. Once the sing in
/// process is finished we have to call the init method of the
/// [AppRepository] to load the users data.
@@ -196,8 +197,8 @@ class _SignInState extends State<SignIn> {
_error = '';
});
await DesktopLoginManager(
provider: supabase.Provider.apple,
await DesktopSignInManager(
provider: supabase.OAuthProvider.apple,
queryParams: null,
).signIn();
@@ -228,7 +229,7 @@ class _SignInState extends State<SignIn> {
/// method of the [AppRepository] is automatically called. On Android
/// the authentication is the handled via the `singin-callback` route.
await supabase.Supabase.instance.client.auth.signInWithOAuth(
supabase.Provider.apple,
supabase.OAuthProvider.apple,
redirectTo:
kIsWeb ? null : 'app.feeddeck.feeddeck://signin-callback/',
);

View File

@@ -47,18 +47,15 @@ class _SignInWithFeedDeckState extends State<SignInWithFeedDeck> {
return null;
}
/// [_validatePassword] validates the email address provided via the
/// [TextField] of the [_passwordController]. The password field can not be
/// empty and must have a minimum length of 6 characters.
/// [_validatePassword] validates the password provided via the [TextField] of
/// the [_passwordController]. In opposite to the sign up, reset password and
/// change password validations, we just check that the password field is not
/// empty.
String? _validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
if (value.length < 6) {
return 'Password must be a least 6 characters long';
}
return null;
}

View File

@@ -56,14 +56,22 @@ class _SignUpState extends State<SignUp> {
/// [_validatePassword] validates the email address provided via the
/// [TextField] of the [_passwordController]. The password field can not be
/// empty and must have a minimum length of 6 characters.
/// empty and must have a minimum length of 8 characters. The password must
/// also contain at least one upper case letter, one lower case letter and one
/// number.
String? _validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
if (value.length < 6) {
return 'Password must be a least 6 characters long';
if (value.length < 8) {
return 'Password must be a least 8 characters long';
}
String pattern = r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9]).{8,}$';
RegExp regExp = RegExp(pattern);
if (!regExp.hasMatch(value)) {
return 'Password must contain at least one upper case letter, one lower case letter and one number';
}
return null;

View File

@@ -1,10 +1,14 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:feeddeck/models/column.dart';
import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/widgets/source/add/add_source_fourchan.dart';
import 'package:feeddeck/widgets/source/add/add_source_github.dart';
import 'package:feeddeck/widgets/source/add/add_source_googlenews.dart';
import 'package:feeddeck/widgets/source/add/add_source_lemmy.dart';
import 'package:feeddeck/widgets/source/add/add_source_mastodon.dart';
import 'package:feeddeck/widgets/source/add/add_source_medium.dart';
import 'package:feeddeck/widgets/source/add/add_source_nitter.dart';
@@ -37,6 +41,9 @@ class AddSource extends StatefulWidget {
}
class _AddSourceState extends State<AddSource> {
final List<FDSourceType> _sourceTypeValues = FDSourceType.values
.whereNot((e) => e == FDSourceType.nitter || e == FDSourceType.none)
.toList();
FDSourceType _sourceType = FDSourceType.none;
/// [_buildBody] returns a list of all supported source types when no source
@@ -44,6 +51,10 @@ class _AddSourceState extends State<AddSource> {
/// user selected a source type, the functions returns the form for the
/// selected source type.
Widget _buildBody() {
if (_sourceType == FDSourceType.fourchan) {
return AddSourceFourChan(column: widget.column);
}
if (_sourceType == FDSourceType.github) {
return AddSourceGitHub(column: widget.column);
}
@@ -52,6 +63,10 @@ class _AddSourceState extends State<AddSource> {
return AddSourceGoogleNews(column: widget.column);
}
if (_sourceType == FDSourceType.lemmy) {
return AddSourceLemmy(column: widget.column);
}
if (_sourceType == FDSourceType.mastodon) {
return AddSourceMastodon(column: widget.column);
}
@@ -88,10 +103,6 @@ class _AddSourceState extends State<AddSource> {
return AddSourceTumblr(column: widget.column);
}
// if (_sourceType == FDSourceType.x) {
// return AddSourceX(column: widget.column);
// }
if (_sourceType == FDSourceType.youtube) {
return AddSourceYouTube(column: widget.column);
}
@@ -103,14 +114,14 @@ class _AddSourceState extends State<AddSource> {
height: Constants.spacingMiddle,
);
},
itemCount: FDSourceType.values.length - 1,
itemCount: _sourceTypeValues.length,
itemBuilder: (context, index) {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
setState(() {
_sourceType = FDSourceType.values[index];
_sourceType = _sourceTypeValues[index];
});
},
child: Container(
@@ -120,7 +131,7 @@ class _AddSourceState extends State<AddSource> {
/// If we decide later to use a generic color as background
/// the following line can be used:
/// color: Constants.secondary,
color: FDSourceType.values[index].bgColor,
color: _sourceTypeValues[index].bgColor,
borderRadius: BorderRadius.circular(4),
),
child: Column(
@@ -128,7 +139,7 @@ class _AddSourceState extends State<AddSource> {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SourceIcon(
type: FDSourceType.values[index],
type: _sourceTypeValues[index],
icon: null,
size: 48,
),
@@ -136,7 +147,7 @@ class _AddSourceState extends State<AddSource> {
height: Constants.spacingSmall,
),
Text(
FDSourceType.values[index].toLocalizedString(),
_sourceTypeValues[index].toLocalizedString(),
style: TextStyle(
/// Since we are using the brand color as background
/// color, we are using the same color as for the icon
@@ -144,7 +155,7 @@ class _AddSourceState extends State<AddSource> {
/// to use a generic color as background the following
/// line can be used:
/// color: Constants.onSecondary,
color: FDSourceType.values[index].fgColor,
color: _sourceTypeValues[index].fgColor,
),
),
],

View File

@@ -0,0 +1,440 @@
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:provider/provider.dart';
import 'package:feeddeck/models/column.dart';
import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/repositories/app_repository.dart';
import 'package:feeddeck/utils/api_exception.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/utils/openurl.dart';
import 'package:feeddeck/widgets/source/add/add_source_form.dart';
const _helpText = '''
The 4chan source can be used to follow your favorite 4chan boards.
''';
/// The [AddSourceFourChan] widget is used to display the form to add a new
/// 4chan board.
class AddSourceFourChan extends StatefulWidget {
const AddSourceFourChan({
super.key,
required this.column,
});
final FDColumn column;
@override
State<AddSourceFourChan> createState() => _AddSourceFourChanState();
}
class _AddSourceFourChanState extends State<AddSourceFourChan> {
final _formKey = GlobalKey<FormState>();
String _fourChanBoard = 'a';
bool _isLoading = false;
String _error = '';
/// [_addSource] adds a new 4chan board. The user can select a board from the
/// dropdown and we will generate the corresponding URL.
Future<void> _addSource() async {
setState(() {
_isLoading = true;
_error = '';
});
try {
AppRepository app = Provider.of<AppRepository>(context, listen: false);
await app.addSource(
widget.column.id,
FDSourceType.fourchan,
FDSourceOptions(
fourchan: _fourChanBoard,
),
);
setState(() {
_isLoading = false;
_error = '';
});
if (mounted) {
Navigator.of(context).pop();
}
} on ApiException catch (err) {
setState(() {
_isLoading = false;
_error = 'Failed to add source: ${err.message}';
});
} catch (err) {
setState(() {
_isLoading = false;
_error = 'Failed to add source: ${err.toString()}';
});
}
}
@override
Widget build(BuildContext context) {
return AddSourceForm(
onTap: _addSource,
isLoading: _isLoading,
error: _error,
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MarkdownBody(
selectable: true,
data: _helpText,
onTapLink: (text, href, title) {
try {
if (href != null) {
openUrl(href);
}
} catch (_) {}
},
),
const SizedBox(
height: Constants.spacingMiddle,
),
DropdownButton<String>(
value: _fourChanBoard,
isExpanded: true,
underline: Container(height: 1, color: Constants.primary),
onChanged: (String? value) {
setState(() {
_fourChanBoard = value!;
});
},
items: boards.map((FourChanBoard value) {
return DropdownMenuItem(
value: value.id,
child: Text(value.name),
);
}).toList(),
),
],
),
),
);
}
}
/// [FourChanBoard] is the model for a supported 4chan boards.
class FourChanBoard {
String id;
String name;
FourChanBoard({
required this.id,
required this.name,
});
}
/// [boards] is the list of all supported 4chan boards.
final boards = <FourChanBoard>[
FourChanBoard(
id: 'a',
name: 'Anime & Manga',
),
FourChanBoard(
id: 'c',
name: 'Anime/Cute',
),
FourChanBoard(
id: 'w',
name: 'Anime/Wallpapers',
),
FourChanBoard(
id: 'm',
name: 'Mecha',
),
FourChanBoard(
id: 'cgl',
name: 'Cosplay & EGL',
),
FourChanBoard(
id: 'cm',
name: 'Cute/Male',
),
FourChanBoard(
id: 'f',
name: 'Flash',
),
FourChanBoard(
id: 'n',
name: 'Transportation',
),
FourChanBoard(
id: 'jp',
name: 'Otaku Culture',
),
FourChanBoard(
id: 'vt',
name: 'Virtual YouTubers',
),
FourChanBoard(
id: 'v',
name: 'Video Games',
),
FourChanBoard(
id: 'vg',
name: 'Video Game Generals',
),
FourChanBoard(
id: 'vm',
name: 'Video Games/Multiplayer',
),
FourChanBoard(
id: 'vmg',
name: 'Video Games/Mobile',
),
FourChanBoard(
id: 'vp',
name: 'Pokémon',
),
FourChanBoard(
id: 'vr',
name: 'Retro Games',
),
FourChanBoard(
id: 'vrpg',
name: 'Video Games/RPG',
),
FourChanBoard(
id: 'vst',
name: 'Video Games/Strategy',
),
FourChanBoard(
id: 'co',
name: 'Comics & Cartoons',
),
FourChanBoard(
id: 'g',
name: 'Technology',
),
FourChanBoard(
id: 'tv',
name: 'Television & Film',
),
FourChanBoard(
id: 'k',
name: 'Weapons',
),
FourChanBoard(
id: 'o',
name: 'Auto',
),
FourChanBoard(
id: 'an',
name: 'Animals & Nature',
),
FourChanBoard(
id: 'tg',
name: 'Traditional Games',
),
FourChanBoard(
id: 'sp',
name: 'Sports',
),
FourChanBoard(
id: 'xs',
name: 'Extreme Sports',
),
FourChanBoard(
id: 'pw',
name: 'Professional Wrestling',
),
FourChanBoard(
id: 'sci',
name: 'Science & Math',
),
FourChanBoard(
id: 'his',
name: 'History & Humanities',
),
FourChanBoard(
id: 'int',
name: 'International',
),
FourChanBoard(
id: 'out',
name: 'Outdoors',
),
FourChanBoard(
id: 'toy',
name: 'Toys',
),
FourChanBoard(
id: 'i',
name: 'Oekaki',
),
FourChanBoard(
id: 'po',
name: 'Papercraft & Origami',
),
FourChanBoard(
id: 'p',
name: 'Photography',
),
FourChanBoard(
id: 'ck',
name: 'Food & Cooking',
),
FourChanBoard(
id: 'ic',
name: 'Artwork/Critique',
),
FourChanBoard(
id: 'wg',
name: 'Wallpapers/General',
),
FourChanBoard(
id: 'lit',
name: 'Literature',
),
FourChanBoard(
id: 'mu',
name: 'Music',
),
FourChanBoard(
id: 'fa',
name: 'Fashion',
),
FourChanBoard(
id: '3',
name: '3DCG',
),
FourChanBoard(
id: 'gd',
name: 'Graphic Design',
),
FourChanBoard(
id: 'diy',
name: 'Do-It-Yourself',
),
FourChanBoard(
id: 'wsg',
name: 'Worksafe GIF',
),
FourChanBoard(
id: 'qst',
name: 'Quests',
),
FourChanBoard(
id: 'biz',
name: 'Business & Finance',
),
FourChanBoard(
id: 'trv',
name: 'Travel',
),
FourChanBoard(
id: 'fit',
name: 'Fitness',
),
FourChanBoard(
id: 'x',
name: 'Paranormal',
),
FourChanBoard(
id: 'adv',
name: 'Advice',
),
FourChanBoard(
id: 'lgbt',
name: 'LGBT',
),
FourChanBoard(
id: 'mlp',
name: 'Pony',
),
FourChanBoard(
id: 'news',
name: 'Current News',
),
FourChanBoard(
id: 'wsr',
name: 'Worksafe Requests',
),
FourChanBoard(
id: 'vip',
name: 'Very Important Posts',
),
FourChanBoard(
id: 'b',
name: 'Random (NSFW)',
),
FourChanBoard(
id: 'r9k',
name: 'ROBOT9001 (NSFW)',
),
FourChanBoard(
id: 'pol',
name: 'Politically Incorrect (NSFW)',
),
FourChanBoard(
id: 'bant',
name: 'International/Random (NSFW)',
),
FourChanBoard(
id: 'soc',
name: 'Cams & Meetups (NSFW)',
),
FourChanBoard(
id: 's4s',
name: 'Shit 4chan Says (NSFW)',
),
FourChanBoard(
id: 's',
name: 'Sexy Beautiful Women (NSFW)',
),
FourChanBoard(
id: 'hc',
name: 'Hardcore (NSFW)',
),
FourChanBoard(
id: 'hm',
name: 'Handsome Men (NSFW)',
),
FourChanBoard(
id: 'h',
name: 'Hentai (NSFW)',
),
FourChanBoard(
id: 'e',
name: 'Ecchi (NSFW)',
),
FourChanBoard(
id: 'u',
name: 'Yuri (NSFW)',
),
FourChanBoard(
id: 'd',
name: 'Hentai/Alternative (NSFW)',
),
FourChanBoard(
id: 'y',
name: 'Yaoi (NSFW)',
),
FourChanBoard(
id: 't',
name: 'Torrents (NSFW)',
),
FourChanBoard(
id: 'hr',
name: 'High Resolution (NSFW)',
),
FourChanBoard(
id: 'gif',
name: 'Adult GIF (NSFW)',
),
FourChanBoard(
id: 'aco',
name: 'Adult Cartoons (NSFW)',
),
FourChanBoard(
id: 'r',
name: 'Adult Requests (NSFW)',
),
];

View File

@@ -1,21 +1,31 @@
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:provider/provider.dart';
import 'package:feeddeck/models/column.dart';
import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/repositories/app_repository.dart';
import 'package:feeddeck/utils/api_exception.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/utils/openurl.dart';
import 'package:feeddeck/widgets/source/add/add_source_form.dart';
const _helpText = '''
The X (formerly Twitter) source can be used to follow a user on X, e.g. `@rico_berger`.
The Lemmy source can be used to follow your favorite Lemmy communities.
- **Community**: Provide the url of the community you want to follow
(e.g. `https://lemmy.world/c/lemmyworld`).
- **User**: Provide the url of the user you want to follow
(e.g. `https://lemmy.world/u/lwCET`).
- **Lemmy Instance**: Provide the url of an Lemmy instance to follow all posts
of this instance (e.g. `https://lemmy.world`).
''';
/// The [AddSourceX] widget is used to display the form to add a new X
/// (formerly) source.
class AddSourceX extends StatefulWidget {
const AddSourceX({
/// The [AddSourceLemmy] widget is used to display the form to add a new Lemmy
/// source.
class AddSourceLemmy extends StatefulWidget {
const AddSourceLemmy({
super.key,
required this.column,
});
@@ -23,17 +33,17 @@ class AddSourceX extends StatefulWidget {
final FDColumn column;
@override
State<AddSourceX> createState() => _AddSourceXState();
State<AddSourceLemmy> createState() => _AddSourceLemmyState();
}
class _AddSourceXState extends State<AddSourceX> {
class _AddSourceLemmyState extends State<AddSourceLemmy> {
final _formKey = GlobalKey<FormState>();
final _xController = TextEditingController();
final _lemmyController = TextEditingController();
bool _isLoading = false;
String _error = '';
/// [_addSource] adds a new X source where the user can provide the username
/// of a x user.
/// [_addSource] adds a new Lemmy source. The user can provide a Lemmy url,
/// which could be be a community or user or the corresponding RSS feed.
Future<void> _addSource() async {
setState(() {
_isLoading = true;
@@ -41,14 +51,14 @@ class _AddSourceXState extends State<AddSourceX> {
});
try {
// AppRepository app = Provider.of<AppRepository>(context, listen: false);
// await app.addSource(
// widget.column.id,
// FDSourceType.x,
// FDSourceOptions(
// x: _xController.text,
// ),
// );
AppRepository app = Provider.of<AppRepository>(context, listen: false);
await app.addSource(
widget.column.id,
FDSourceType.lemmy,
FDSourceOptions(
lemmy: _lemmyController.text,
),
);
setState(() {
_isLoading = false;
_error = '';
@@ -71,7 +81,7 @@ class _AddSourceXState extends State<AddSourceX> {
@override
void dispose() {
_xController.dispose();
_lemmyController.dispose();
super.dispose();
}
@@ -101,14 +111,14 @@ class _AddSourceXState extends State<AddSourceX> {
height: Constants.spacingMiddle,
),
TextFormField(
controller: _xController,
controller: _lemmyController,
keyboardType: TextInputType.text,
autocorrect: false,
enableSuggestions: true,
maxLines: 1,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Username',
labelText: 'Lemmy Url',
),
onFieldSubmitted: (value) => _addSource(),
),

View File

@@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
@@ -8,6 +9,7 @@ import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/repositories/app_repository.dart';
import 'package:feeddeck/utils/api_exception.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/utils/get_feed.dart';
import 'package:feeddeck/utils/openurl.dart';
import 'package:feeddeck/widgets/source/add/add_source_form.dart';
@@ -52,12 +54,29 @@ class _AddSourceRedditState extends State<AddSourceReddit> {
try {
AppRepository app = Provider.of<AppRepository>(context, listen: false);
/// To avoid getting rate limited by Reddit, we already fetch the feed
/// here and send it to the Supabase edge function within the [data]
/// field.
/// Since this only works for the desktop and mobile clients, we have to
/// check if the user is on the web, so that we can still try to fetch the
/// feed in the edge function.
final feedData = kIsWeb
? null
: await getFeed(
FDSourceType.reddit,
FDSourceOptions(
reddit: _redditController.text,
),
);
await app.addSource(
widget.column.id,
FDSourceType.reddit,
FDSourceOptions(
reddit: _redditController.text,
),
feedData,
);
setState(() {
_isLoading = false;

View File

@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart' as cni;
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:feeddeck/repositories/settings_repository.dart';
@@ -14,7 +13,7 @@ import 'package:feeddeck/repositories/settings_repository.dart';
String getImageUrl(String imageUrl) {
if (imageUrl.startsWith('http://') || imageUrl.startsWith('https://')) {
if (kIsWeb) {
return '${Supabase.instance.client.functionsUrl}/image-proxy-v1?media=${Uri.encodeQueryComponent(imageUrl)}';
return '${SettingsRepository().supabaseUrl}/functions/v1/image-proxy-v1?media=${Uri.encodeQueryComponent(imageUrl)}';
}
return imageUrl;

View File

@@ -50,6 +50,141 @@
</categories>
<releases>
<release version="v1.4.0" date="2024-08-04">
<description>
<p>Added</p>
<ul>
<li>#157: [core] Add Indexes to Improve Query Performance @ricoberger</li>
<li>#145: [mastodon] Add Support for Piped Videos @ricoberger</li>
<li>#144: [reddit] Add Support for Piped Videos @ricoberger</li>
<li>#143: [lemmy] Add Support for Piped Videos @ricoberger</li>
<li>#142: [4chan] Add Support for 4chan @ricoberger</li>
</ul>
<p>Fixed</p>
<ul>
<li>#171: Fix ESLint Dependency Mismatch @ricoberger</li>
<li>#166: Revert "Bump the npm group in /landing with 10 updates" @ricoberger</li>
<li>#148: [core] Fix Blockquote Style @ricoberger</li>
<li>#141: [rss] Fix Image Parsing @ricoberger</li>
<li>#140: [core] Remove referer Check in image-proxy-v1 Function @ricoberger</li>
</ul>
<p>Changed</p>
<ul>
<li>#181: Bump the pub group in /app with 12 updates @dependabot</li>
<li>#180: Bump the npm group in /landing with 8 updates @dependabot</li>
<li>#178: Bump braces from 3.0.2 to 3.0.3 in /landing @dependabot</li>
<li>#174: Bump the npm group in /landing with 8 updates @dependabot</li>
<li>#176: Bump the pub group in /app with 6 updates @dependabot</li>
<li>#175: Bump docker/build-push-action from 5 to 6 in the github-actions group @dependabot</li>
<li>#172: [core] Wrap Actions in SafeArea Widget @ricoberger</li>
<li>#167: Bump the pub group across 1 directory with 17 updates @dependabot</li>
<li>#168: Bump the npm group in /landing with 10 updates @dependabot</li>
<li>#165: Bump the npm group in /landing with 10 updates @dependabot</li>
<li>#163: [nitter] Disable Adding of new Sources @ricoberger</li>
<li>#162: [x] Remove X Source @ricoberger</li>
<li>#161: [core] Update Flutter Version to 3.19.5 @ricoberger</li>
<li>#160: Bump the pub group in /app with 9 updates @dependabot</li>
<li>#159: Bump the github-actions group with 1 update @dependabot</li>
<li>#158: Bump the npm group in /landing with 9 updates @dependabot</li>
<li>#151: Bump the pub group in /app with 3 updates @dependabot</li>
<li>#156: Bump the npm group in /landing with 2 updates @dependabot</li>
<li>#153: Bump the docker group in /supabase/functions/_cmd with 1 update @dependabot</li>
<li>#152: Bump the github-actions group with 1 update @dependabot</li>
<li>#150: Bump the npm-landing group in /landing with 6 updates @dependabot</li>
<li>#149: [core] Upgrade Flutter Version @ricoberger</li>
<li>#115: Bump the npm-email-templates group in /supabase/email-templates with 2 updates @dependabot</li>
<li>#147: [core] Use Upstream just_audio_media_kit Version @ricoberger</li>
<li>#146: [4chan] Improve Options Parsing @ricoberger</li>
</ul>
</description>
<url>https://github.com/feeddeck/feeddeck/releases/tag/v1.2.1</url>
</release>
<release version="v1.3.0" date="2024-02-12">
<description>
<p>Added</p>
<ul>
<li>#136: [nitter] Add Support for Piped Videos @ricoberger</li>
<li>#137: [landing] Add Feature Images for Mobile Screens @ricoberger</li>
<li>#135: [mastodon] Add Support for YouTube Videos @ricoberger</li>
<li>#134: [reddit] Add Support for YouTube Videos @ricoberger</li>
<li>#132: [core] Add Continuous Delivery Workflow for Linux arm64 @ricoberger</li>
<li>#128: [core] Make Log Level Configurable @ricoberger</li>
<li>#125: [core] Add Open Link Action @ricoberger</li>
<li>#118: [core] Add Client Side Scraping of Sources @ricoberger</li>
<li>#105: [core] Refactor Tools and add get-feed Tool @ricoberger</li>
<li>#101: [rss] Show Videos from RSS Feeds @ricoberger</li>
<li>#98: [core] Add Tests for Sources @ricoberger</li>
<li>#97: [core] Add Test Setup for Deno @ricoberger</li>
<li>#94: [lemmy] Add Support for Lemmy @ricoberger</li>
<li>#87: [core] Add Test Setup for Flutter @ricoberger</li>
</ul>
<p>Fixed</p>
<ul>
<li>#133: [core] Fix Password Validations @ricoberger</li>
<li>#124: [core] Add Missing SafeArea Widget @ricoberger</li>
<li>#96: [core] Fix Converting of HTML to Plain Text in Description @ricoberger</li>
<li>#93: [core] Fix Index Reset for Tabs in Small Deck Layout @ricoberger</li>
</ul>
<p>Changed</p>
<ul>
<li>#138: [core] Increase Update Interval for Reddit and Nitter @ricoberger</li>
<li>#131: [core] Update macOS GitHub Action Runners @ricoberger</li>
<li>#130: [core] Forbid Weak Passwords @ricoberger</li>
<li>#129: [rss] Do Not Remove HTML Tags @ricoberger</li>
<li>#127: Bump the docker group in /supabase/functions/_cmd with 1 update @dependabot</li>
<li>#126: Bump the npm-landing group in /landing with 2 updates @dependabot</li>
<li>#121: Bump the pub group in /app with 2 updates @dependabot</li>
<li>#120: Bump the npm-landing group in /landing with 1 update @dependabot</li>
<li>#123: [landing] Add Lemmy Icon @ricoberger</li>
<li>#122: [core] Fix Naming of Files @ricoberger</li>
<li>#119: Bump the pub group in /app with 9 updates @dependabot</li>
<li>#114: Bump the docker group in /supabase/functions/_cmd with 1 update @dependabot</li>
<li>#110: Bump the github-actions group with 4 updates @dependabot</li>
<li>#116: Bump the npm-landing group in /landing with 11 updates @dependabot</li>
<li>#113: [core] Add Assignees to Dependabot Configuration @ricoberger</li>
<li>#112: [core] Add Additional Headers for Web Deployment @ricoberger</li>
<li>#106: Update Flutter to Version 3.16.5 @ricoberger</li>
<li>#104: [core] Replace Deprecated serve Function @ricoberger</li>
<li>#103: [core] Improve Error Handling for Feed Edge Functions @ricoberger</li>
<li>#102: [core] Disable Right Click for Item Actions on Web @ricoberger</li>
<li>#100: [core] Update Deno Modules @ricoberger</li>
<li>#99: [rss] Parse Atom and RDF Feeds from Websites @ricoberger</li>
<li>#95: [core] Improve ItemVideoPlayer Widget @ricoberger</li>
<li>#89: Bump the npm-email-templates group in /supabase/email-templates with 1 update @dependabot</li>
<li>#90: Bump the pub group in /app with 1 update @dependabot</li>
<li>#91: Bump the npm-landing group in /landing with 7 updates @dependabot</li>
<li>#92: Bump the docker group in /supabase/functions/_cmd with 1 update @dependabot</li>
</ul>
</description>
<url>https://github.com/feeddeck/feeddeck/releases/tag/v1.2.1</url>
</release>
<release version="v1.2.1" date="2023-11-30">
<description>
<p>Fixed</p>
<ul>
<li>#84: [podcast] Stop Audio Playback on Windows and Linux @ricoberger</li>
<li>#82: [core] Fix build.gradle File for Android Release @ricoberger</li>
</ul>
<p>Changed</p>
<ul>
<li>#86: [core] Remove Blank Line in Item Preview Description @ricoberger</li>
<li>#85: [core] Add Right Click Support for Item Actions @ricoberger</li>
<li>#83: [medium] Extend Filter Words List @ricoberger</li>
</ul>
</description>
<url>https://github.com/feeddeck/feeddeck/releases/tag/v1.2.1</url>
</release>
<release version="v1.2.0" date="2023-11-26">
<description>
<p>Added</p>
@@ -64,13 +199,13 @@
</ul>
<p>Fixed</p>
<ul>
<li>#75: [core] Fix getMedia Function @ricoberger</li>
<li>#73: [core] Fix Decoding of Special Characters @ricoberger</li>
<li>#64: [github] Fix Icons in Item Preview @ricoberger</li>
<li>#58: [core] Add Missing Divider to Video Quality Selection @ricoberger</li>
<li>#57: [core] Fix Modal Bottom Sheet Size for Images @ricoberger</li>
<li>#55: [github] Fix Notification Links for PRs @ricoberger</li>
<ul>
</ul>
<p>Changed</p>

View File

@@ -6,6 +6,7 @@
#include "generated_plugin_registrant.h"
#include <gtk/gtk_plugin.h>
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
#include <media_kit_video/media_kit_video_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h>
@@ -13,6 +14,9 @@
#include <window_manager/window_manager_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) gtk_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin");
gtk_plugin_register_with_registrar(gtk_registrar);
g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin");
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);

View File

@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
gtk
media_kit_libs_linux
media_kit_video
screen_retriever

View File

@@ -6,9 +6,6 @@ PODS:
- audio_session (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- just_audio (0.0.1):
- FlutterMacOS
- media_kit_libs_macos_video (1.0.4):
@@ -22,12 +19,12 @@ PODS:
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- purchases_flutter (6.4.0):
- purchases_flutter (6.30.2):
- FlutterMacOS
- PurchasesHybridCommon (= 8.0.0)
- PurchasesHybridCommon (8.0.0):
- RevenueCat (= 4.30.5)
- RevenueCat (4.30.5)
- PurchasesHybridCommon (= 11.1.0)
- PurchasesHybridCommon (11.1.0):
- RevenueCat (= 4.43.2)
- RevenueCat (4.43.2)
- screen_brightness_macos (0.1.0):
- FlutterMacOS
- screen_retriever (0.0.1):
@@ -37,9 +34,9 @@ PODS:
- FlutterMacOS
- sign_in_with_apple (0.0.1):
- FlutterMacOS
- sqflite (0.0.2):
- sqflite (0.0.3):
- Flutter
- FlutterMacOS
- FMDB (>= 2.7.5)
- url_launcher_macos (0.0.1):
- FlutterMacOS
- wakelock_plus (0.0.1):
@@ -63,14 +60,13 @@ DEPENDENCIES:
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
- sign_in_with_apple (from `Flutter/ephemeral/.symlinks/plugins/sign_in_with_apple/macos`)
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
- wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
SPEC REPOS:
trunk:
- FMDB
- PurchasesHybridCommon
- RevenueCat
@@ -106,7 +102,7 @@ EXTERNAL SOURCES:
sign_in_with_apple:
:path: Flutter/ephemeral/.symlinks/plugins/sign_in_with_apple/macos
sqflite:
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/darwin
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
wakelock_plus:
@@ -115,29 +111,28 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
SPEC CHECKSUMS:
app_links: 4481ed4d71f384b0c3ae5016f4633aa73d32ff67
app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a
audio_service: b88ff778e0e3915efd4cd1a5ad6f0beef0c950a9
audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
just_audio: 9b67ca7b97c61cfc9784ea23cd8cc55eb226d489
media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82
media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5
media_kit_video: c75b07f14d59706c775778e4dd47dd027de8d1e5
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
purchases_flutter: dd2e2b2d7fda0e64ee50ad426487dfa6dbf1bdd8
PurchasesHybridCommon: 80262c5ffe6621e3cf3812e6103170f6d7fbcb79
RevenueCat: c1e33f4e1f1fd239ba461652f02928e220becc31
package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
purchases_flutter: 3407100959d2aeb636507b2c98970d850dae57ae
PurchasesHybridCommon: 4022d5944cb30ec44ba5159e42aa161fe0e30175
RevenueCat: 3d934653b7e8b09af88fd47e9e84cfaf5d0a89ba
screen_brightness_macos: 2d6d3af2165592d9a55ffcd95b7550970e41ebda
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sign_in_with_apple: a9e97e744e8edc36aefc2723111f652102a7a727
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
PODFILE CHECKSUM: 8d40c19d3cbdb380d870685c3a564c989f1efa52
COCOAPODS: 1.14.2
COCOAPODS: 1.15.2

View File

@@ -208,7 +208,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
33CC10EC2044A3C60003C045 = {

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

File diff suppressed because it is too large Load Diff

View File

@@ -16,10 +16,10 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.2.0+8
version: 1.4.0+11
environment:
sdk: '>=3.1.3 <4.0.0'
sdk: '>=3.3.0 <4.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
@@ -35,41 +35,39 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
cupertino_icons: ^1.0.8
app_links: ^3.4.3
cached_network_image: ^3.2.3
app_links: ^6.1.4
cached_network_image: ^3.3.1
carousel_slider: ^4.2.1
collection: ^1.17.0
flutter_cache_manager: ^3.3.1
flutter_markdown: ^0.6.14
flutter_native_splash: ^2.3.5
crypto: ^3.0.3
flutter_cache_manager: ^3.4.0
flutter_markdown: ^0.7.3
flutter_native_splash: ^2.4.1
html: ^0.15.4
html2md: ^1.2.6
intl: ^0.18.1
just_audio: ^0.9.32
just_audio_background: ^0.0.1-beta.10
# We use our own fork of the "just_audio_media_kit" package, where we
# replaced the "media_kit_libs_windows_audio" with
# "media_kit_libs_windows_video" package, so that we can play video files on
# Windows via the "media_kit" package.
just_audio_media_kit:
git:
url: https://github.com/feeddeck/just_audio_media_kit.git
html2md: ^1.3.2
http: ^1.2.2
intl: ^0.19.0
just_audio: ^0.9.39
just_audio_background: ^0.0.1-beta.13
just_audio_media_kit: ^2.0.5
media_kit: ^1.1.10+1
media_kit_video: ^1.2.4
media_kit_libs_video: ^1.0.4
package_info_plus: ^5.0.1
provider: ^6.0.4
purchases_flutter: ^6.2.0
package_info_plus: ^8.0.1
piped_client: ^0.1.0
provider: ^6.1.2
purchases_flutter: ^6.30.2
rxdart: ^0.27.7
scroll_to_index: ^3.0.1
shared_preferences: ^2.1.0
supabase_flutter: ^1.10.24
timeago: ^3.6.0
url_launcher: ^6.2.1
window_manager: ^0.3.4
youtube_explode_dart: ^2.0.2
shared_preferences: ^2.3.0
sign_in_with_apple: ^6.1.1
supabase_flutter: ^2.5.11
timeago: ^3.7.0
url_launcher: ^6.3.0
window_manager: ^0.3.9
youtube_explode_dart: ^2.2.1
dev_dependencies:
flutter_test:
@@ -80,11 +78,11 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^3.0.1
flutter_lints: ^4.0.0
flutter_launcher_icons: ^0.13.1
import_sorter: ^4.6.0
msix: ^3.16.6
msix: ^3.16.7
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
@@ -171,7 +169,7 @@ msix_config:
publisher_display_name: Rico Berger
identity_name: 26077RicoBerger.FeedDeck
publisher: CN=7740451A-C179-450A-B346-7231CA231332
msix_version: 1.2.0.0
msix_version: 1.4.0.0
logo_path: templates/app-icon/windows.png
languages: en-us
capabilities: internetClient

View File

@@ -313,6 +313,34 @@
"search": [
"pinterest"
]
},
{
"uid": "08d314902e3800cf1b17c4c1226f45d9",
"css": "lemmy",
"code": 59414,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M123.3 176C118.7 176 114.1 176.3 109.5 176.7 72.9 181 39.9 200.5 19.7 234.5-0.1 267.8-5 304.5 5.1 338.5 15.2 372.5 39.7 403.5 75.9 427.9 76.1 428.1 76.3 428.2 76.5 428.4 107.6 447.1 138.7 459.4 171.9 465.9 171 479.7 170.7 493.7 171.6 508.3 174.2 551.7 189.8 591.7 213.1 627L129.5 661.1C125.2 662.8 121.8 666.2 120 670.5 117.3 676.9 118.7 684.4 123.7 689.4 127 692.7 131.4 694.5 136 694.5 138.2 694.5 140.5 694.1 142.6 693.2L234.7 655.6C251.7 675.8 270.9 694.4 291.8 710.5 292.7 711.1 293.5 711.6 294.3 712.3L240.5 783.5C238.4 786.5 237.2 790 237.2 793.7 237.2 803.2 245.1 811 254.6 811 259.9 811 264.9 808.6 268.2 804.4L323 732C352.9 750.9 384.5 765 415.8 774.7 431.8 804.7 463.2 824.1 500 824.1 537.1 824.1 568.5 803.2 584.3 773.8 615.3 764 646.7 750 676.3 731L731.8 804.4C735.1 808.7 740.2 811.3 745.7 811.3 754.3 811.3 761.6 804.9 762.8 796.3 763.5 791.8 762.2 787.2 759.5 783.5L704.9 711.2C705.2 711 705.7 710.8 706 710.5 726.7 694.6 745.8 676.4 762.7 656.6L852.5 693.2C861.3 696.8 871.5 692.5 875.1 683.7 876 681.6 876.4 679.4 876.4 677.1 876.4 670.1 872.1 663.7 865.6 661.1L784.6 628.1C808.7 592.5 825 552.3 828.4 508.5 829.5 493.9 829.4 479.7 828.7 465.7 861.7 459.2 892.6 447 923.5 428.4 923.7 428.2 923.9 428 924.2 427.9 960.3 403.4 984.8 372.5 994.9 338.5 1005 304.5 1000.1 267.8 980.3 234.5 960.1 200.5 927.1 180.9 890.5 176.7 885.9 176.2 881.3 176 876.7 175.9 844 175.3 809.1 185.5 775.5 204.8 750.8 219.1 728.6 241 711.2 264.8 662.5 236.9 599 221.1 521 219.9 514 219.7 506.8 219.7 499.7 219.9 412.9 221.3 343.3 237.8 290.8 267.5 290.7 267.4 290.6 267.1 290.5 266.9 272.9 242.3 250 219.6 224.5 204.9 191 185.5 156 175.4 123.3 176L123.3 176ZM135.2 206.6C158.4 208.4 184 216.5 209.3 231.1 229.5 242.8 250 262.6 265.4 284 258.2 289.3 251.3 295 244.7 301 206.4 336.5 183.8 382.7 175.2 435.7 146.8 429.9 120.3 419.3 92.5 402.7 61.1 381.3 41.9 355.9 34.2 330 26.5 304 29.7 277 45.7 250 61.4 223.7 84.4 210.2 112.9 207 120.3 206.2 127.8 206 135.2 206.7L135.2 206.6ZM864.8 206.6C872.2 206 879.7 206.1 887.1 207 915.6 210.2 938.6 223.7 954.2 250 970.3 277 973.5 304 965.8 330 958.1 355.9 938.9 381.2 907.5 402.6 880 419.1 853.8 429.6 825.8 435.5 817.7 381.5 795.1 334.1 756.4 297.9 750.1 292.1 743.6 286.7 736.7 281.6 752 261.2 771.3 242.3 790.7 231.1 816 216.5 841.6 208.4 864.8 206.6L864.8 206.6ZM500.2 250.2C507 250 513.8 250 520.5 250.2 620.1 251.8 690.7 278.1 735.7 320.1 783.6 364.9 804.1 428.4 798.1 506.2 792.7 577.3 747.3 640.7 687.6 686.4 658.7 708.5 626.7 725.3 594.9 737.6 595 735.7 595.5 733.9 595.5 732 595.6 682.1 556.7 639.7 500 639.7 443.3 639.7 403.2 682 404.5 732.3 404.5 734.4 405.1 736.3 405.2 738.4 372.7 726.2 339.9 709.2 310.3 686.5 251 640.9 206.2 577.7 201.9 506.5 197.2 429.1 217.6 367.3 265.2 323.2 312.9 279.2 389.7 252 500.2 250.2ZM348.5 534.7C323.3 534.7 302.8 555.1 302.8 580.4 302.8 605.7 323.3 626.2 348.5 626.2 373.7 626.2 394.2 605.7 394.2 580.4 394.2 555.1 373.7 534.7 348.5 534.7ZM651.9 535.2C626.9 535.2 606.6 555.4 606.6 580.5 606.6 605.5 626.9 625.8 651.9 625.8 676.9 625.8 697.2 605.5 697.2 580.5 697.2 555.4 676.9 535.2 651.9 535.2ZM500 670.2C542.7 670.2 565.2 696.7 565.1 731.9 565.1 764.6 537.1 793.8 500 793.8 461.8 793.8 435.8 770.4 434.9 731.6 434 696.8 457.3 670.2 500 670.2Z",
"width": 1000
},
"search": [
"lemmy"
]
},
{
"uid": "2efb1796652ef55a1fca1f7a3eda4b86",
"css": "fourchan",
"code": 59415,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M461.3 367.5C461.3 367.5 408.5 45 214.4 45.7 83.6 46.2 32.5 171.8 127.3 200.1 127.3 200.1 14.9 240.6 14.9 312.5 14.9 384.3 193 460 461.3 367.5L461.3 367.5ZM541.7 614.3C541.7 614.3 579.4 939 773.3 947.3 904 952.9 960.9 829.8 867.6 797.1 867.6 797.1 981.7 761.9 985.1 690.2 988.4 618.4 814 534.5 541.7 614.3L541.7 614.3ZM388.7 549.7C388.7 549.7 74.9 641 99.1 833.6 115.3 963.4 246.1 999 262.8 901.5 262.8 901.5 316.6 1008.2 387.9 999.5 459.2 990.8 512.9 804.9 388.7 549.8L388.7 549.7ZM623 447C623 447 945 390.7 942.2 196.6 940.3 65.9 814.1 16.1 786.9 111.2 786.9 111.2 745.2-0.7 673.3 0 601.4 0.8 527.7 179.7 623 447Z",
"width": 1000
},
"search": [
"fourchan"
]
}
]
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 4096 4096" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g id="_4chan" serif:id="4chan" transform="matrix(4.26802,0,0,4.26802,-137.229,-137.223)">
<path d="M474.83,384.842C474.83,384.842 424.165,75.302 237.905,76.022C112.383,76.502 63.359,197.062 154.333,224.174C154.333,224.174 46.448,263.082 46.448,332.019C46.448,400.996 217.353,473.573 474.828,384.801L474.83,384.842ZM552.045,621.726C552.045,621.726 588.193,933.266 774.293,941.263C899.693,946.661 954.355,828.499 864.743,797.15C864.743,797.15 974.308,763.358 977.508,694.503C980.708,625.605 813.361,545.071 552.046,621.727L552.045,621.726ZM405.212,559.744C405.212,559.744 104.028,647.316 127.222,832.176C142.817,956.698 268.377,990.886 284.372,897.356C284.372,897.356 335.955,999.684 404.412,991.366C472.869,983.048 524.374,804.626 405.212,559.746L405.212,559.744ZM630.06,461.098C630.06,461.098 939.04,407.115 936.364,220.856C934.52,95.376 813.44,47.592 787.29,138.843C787.29,138.843 747.302,31.438 678.284,32.155C609.344,32.877 538.569,204.581 630.06,461.098Z" style="fill-rule:nonzero;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 4096 4096" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g id="Lemmy" transform="matrix(170.666,0,0,170.666,-0.0042009,-0.0031286)">
<path d="M2.959,4.223C2.849,4.224 2.738,4.231 2.627,4.242C1.749,4.343 0.958,4.812 0.473,5.628C-0.002,6.428 -0.12,7.309 0.123,8.125C0.365,8.941 0.952,9.683 1.821,10.27C1.826,10.274 1.831,10.278 1.837,10.281C2.583,10.731 3.329,11.025 4.125,11.181C4.105,11.513 4.098,11.85 4.119,12.199C4.182,13.242 4.555,14.2 5.115,15.048L3.109,15.867C3.006,15.908 2.924,15.989 2.881,16.091C2.815,16.246 2.85,16.426 2.969,16.546C3.047,16.624 3.153,16.669 3.263,16.669C3.318,16.669 3.372,16.659 3.422,16.638L5.632,15.734C6.04,16.22 6.501,16.666 7.004,17.052C7.024,17.067 7.045,17.079 7.064,17.095L5.773,18.805C5.722,18.876 5.694,18.961 5.694,19.048C5.694,19.276 5.882,19.464 6.11,19.464C6.237,19.464 6.358,19.406 6.437,19.306L7.751,17.567C8.469,18.022 9.229,18.36 9.98,18.592C10.363,19.313 11.118,19.779 12,19.779C12.891,19.779 13.644,19.278 14.024,18.572C14.768,18.337 15.52,17.999 16.232,17.545L17.564,19.306C17.642,19.41 17.765,19.472 17.896,19.472C18.103,19.472 18.279,19.317 18.308,19.112C18.323,19.003 18.294,18.893 18.228,18.805L16.918,17.07C16.926,17.063 16.936,17.059 16.944,17.052C17.441,16.671 17.899,16.234 18.306,15.758L20.461,16.637C20.672,16.723 20.916,16.62 21.002,16.41C21.023,16.359 21.034,16.305 21.034,16.251C21.034,16.082 20.931,15.93 20.775,15.866L18.831,15.074C19.408,14.22 19.801,13.255 19.882,12.204C19.908,11.854 19.906,11.513 19.89,11.178C20.68,11.021 21.422,10.728 22.163,10.281C22.169,10.277 22.174,10.273 22.18,10.269C23.048,9.682 23.636,8.941 23.878,8.124C24.12,7.308 24.003,6.427 23.528,5.627C23.043,4.811 22.251,4.342 21.373,4.241C21.262,4.229 21.152,4.223 21.041,4.222C20.255,4.207 19.418,4.451 18.612,4.916C18.019,5.258 17.487,5.783 17.069,6.355C15.899,5.685 14.376,5.307 12.505,5.277C12.335,5.274 12.164,5.274 11.994,5.277C9.909,5.311 8.24,5.707 6.98,6.421C6.977,6.418 6.975,6.41 6.972,6.406C6.549,5.815 5.999,5.27 5.388,4.917C4.583,4.452 3.745,4.209 2.96,4.223L2.959,4.223ZM3.245,4.959C3.802,5.001 4.415,5.195 5.024,5.547C5.509,5.827 6,6.302 6.37,6.816C6.197,6.943 6.031,7.079 5.873,7.225C4.953,8.077 4.412,9.185 4.205,10.458C3.524,10.318 2.887,10.064 2.221,9.664C1.466,9.151 1.005,8.541 0.821,7.919C0.636,7.295 0.713,6.648 1.098,6C1.473,5.37 2.026,5.046 2.71,4.967C2.888,4.948 3.067,4.945 3.245,4.96L3.245,4.959ZM20.755,4.959C20.933,4.945 21.112,4.947 21.29,4.967C21.974,5.045 22.527,5.369 22.902,5.999C23.287,6.647 23.364,7.295 23.179,7.919C22.995,8.541 22.534,9.15 21.779,9.663C21.121,10.059 20.491,10.31 19.819,10.452C19.625,9.155 19.082,8.018 18.153,7.15C18.003,7.011 17.846,6.88 17.682,6.758C18.047,6.268 18.511,5.815 18.976,5.546C19.585,5.195 20.198,5.001 20.755,4.959L20.755,4.959ZM12.006,6.004C12.168,6.001 12.331,6.001 12.493,6.004C14.883,6.043 16.578,6.674 17.656,7.682C18.806,8.757 19.298,10.282 19.155,12.149C19.024,13.856 17.935,15.377 16.503,16.473C15.809,17.004 15.041,17.408 14.278,17.702C14.281,17.657 14.292,17.613 14.292,17.567C14.295,16.371 13.36,15.354 12,15.354C10.639,15.354 9.678,16.369 9.708,17.575C9.709,17.626 9.722,17.672 9.726,17.722C8.945,17.429 8.157,17.021 7.448,16.477C6.025,15.382 4.949,13.864 4.845,12.155C4.732,10.298 5.223,8.816 6.366,7.758C7.509,6.7 9.352,6.047 12.006,6.004ZM8.364,12.833C7.759,12.833 7.268,13.323 7.268,13.929C7.268,14.536 7.759,15.028 8.364,15.028C8.969,15.028 9.461,14.536 9.461,13.929C9.461,13.323 8.969,12.833 8.364,12.833ZM15.646,12.844C15.046,12.844 14.559,13.33 14.559,13.931C14.559,14.531 15.046,15.019 15.646,15.019C16.246,15.019 16.733,14.531 16.733,13.931C16.733,13.33 16.246,12.844 15.646,12.844ZM12,16.084C13.024,16.084 13.565,16.722 13.563,17.566C13.562,18.351 12.891,19.051 12,19.051C11.083,19.051 10.46,18.489 10.437,17.558C10.415,16.724 10.976,16.084 12,16.084Z" style="fill-rule:nonzero;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:feeddeck/widgets/item/preview/utils/item_title.dart';
void main() {
testWidgets('Should render title', (tester) async {
await tester.pumpWidget(
const MaterialApp(
home: ItemTitle(itemTitle: 'My Title'),
),
);
expect(find.byType(Container), findsOneWidget);
expect(find.byType(Text), findsOneWidget);
expect(find.text('My Title'), findsOneWidget);
});
testWidgets('Should render empty container when empty string is provided',
(tester) async {
await tester.pumpWidget(
const MaterialApp(
home: ItemTitle(itemTitle: ''),
),
);
expect(find.byType(Container), findsOneWidget);
expect(find.byType(Text), findsNothing);
});
}

4
app/web/_headers Normal file
View File

@@ -0,0 +1,4 @@
/*
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: no-referrer

View File

@@ -1,13 +1,15 @@
import { downloads } from "@/helpers/helpers";
import type { Metadata } from "next";
import type { Metadata, Viewport } from "next";
import { generalMetadata } from "@/helpers/metadata";
import { generalMetadata, generalViewport } from "@/helpers/metadata";
export const metadata: Metadata = {
...generalMetadata,
title: "FeedDeck - Download",
};
export const viewport: Viewport = generalViewport;
export default function Download() {
return (
<main>

View File

@@ -1,9 +1,9 @@
import { ReactNode, useState } from "react";
import type { Metadata } from "next";
import type { Metadata, Viewport } from "next";
import Image from "next/image";
import Link from "next/link";
import { generalMetadata } from "@/helpers/metadata";
import { generalMetadata, generalViewport } from "@/helpers/metadata";
import GetStartedEntry from "@/components/getstartedentry";
export const metadata: Metadata = {
@@ -11,6 +11,8 @@ export const metadata: Metadata = {
title: "FeedDeck - Support",
};
export const viewport: Viewport = generalViewport;
export default function Support() {
return (
<main>

View File

@@ -1,9 +1,9 @@
import { ReactNode } from "react";
import type { Metadata } from "next";
import type { Metadata, Viewport } from "next";
import Image from "next/image";
import Link from "next/link";
import { generalMetadata } from "@/helpers/metadata";
import { generalMetadata, generalViewport } from "@/helpers/metadata";
import GetStartedEntry from "@/components/getstartedentry";
export const metadata: Metadata = {
@@ -11,6 +11,8 @@ export const metadata: Metadata = {
title: "FeedDeck - Support",
};
export const viewport: Viewport = generalViewport;
export default function Support() {
return (
<main>

View File

@@ -1,15 +1,17 @@
import Image from "next/image";
import dynamic from "next/dynamic";
import type { Metadata } from "next";
import type { Metadata, Viewport } from "next";
const Download = dynamic(() => import("@/components/download"), { ssr: false });
import { generalMetadata } from "@/helpers/metadata";
import { generalMetadata, generalViewport } from "@/helpers/metadata";
export const metadata: Metadata = {
...generalMetadata,
title: "FeedDeck - Follow your RSS and Social Media Feeds",
};
export const viewport: Viewport = generalViewport;
export default function Home() {
return (
<main>
@@ -47,6 +49,7 @@ export default function Home() {
src="/hero-1.webp"
width="616"
height="616"
style={{ width: "auto", height: "auto" }}
className="object-cover"
alt="Hero"
loading="eager"
@@ -62,17 +65,17 @@ export default function Home() {
</div>
<div className="flex flex-wrap justify-center gap-20 mt-10">
<FourChan />
<GitHub />
<GoogleNews />
<Lemmy />
<Mastodon />
<Medium />
<Nitter />
<Pinterest />
<Reddit />
<RSS />
<StackOverflow />
<Tumblr />
<X />
<YouTube />
</div>
</div>
@@ -82,33 +85,37 @@ export default function Home() {
<Feature
title="Deck View"
description="View all your RSS and social media feeds in a deck layout."
image="/feature-1.webp"
imageDesktop="/feature-1-desktop.webp"
imageMobile="/feature-1-mobile.webp"
/>
<Feature
title="Details View"
description="View the details of all your RSS and social media items."
image="/feature-2.webp"
imageDesktop="/feature-2-desktop.webp"
imageMobile="/feature-2-mobile.webp"
/>
<Feature
title="YouTube"
description="Follow and view your favorite YouTube channels."
image="/feature-3.webp"
imageDesktop="/feature-3-desktop.webp"
imageMobile="/feature-3-mobile.webp"
/>
<Feature
title="Podcasts"
description="Follow and listen to your favorite podcasts, via the built-in podcast player."
image="/feature-4.webp"
imageDesktop="/feature-4-desktop.webp"
imageMobile="/feature-4-mobile.webp"
/>
<Feature
title="GitHub"
description="View your GitHub notifications and activities of your favorite repositories."
image="/feature-5.webp"
imageDesktop="/feature-5-desktop.webp"
imageMobile="/feature-5-mobile.webp"
/>
<Feature
<FeatureNoBg
title="Available on all Platforms"
description="The same experience on all your devices."
image="/feature-6.webp"
noBg={true}
/>
</div>
@@ -132,11 +139,11 @@ export default function Home() {
}
const Feature = (
{ title, description, image, noBg }: {
{ title, description, imageDesktop, imageMobile }: {
title: string;
description: string;
image: string;
noBg?: boolean;
imageDesktop: string;
imageMobile: string;
},
) => (
<div className="container mx-auto flex flex-col justify-center p-16 items-center text-center">
@@ -146,7 +153,39 @@ const Feature = (
<div className="mb-5 font-light text-gray-400 sm:text-xl">
{description}
</div>
<div className={noBg ? "p-2 rounded-lg" : "p-2 bg-secondary rounded-lg"}>
<div className="p-2 bg-secondary rounded-lg">
<picture>
<source media="(max-width: 640px)" srcSet={imageMobile} />
<source media="(min-width: 640px)" srcSet={imageDesktop} />
<Image
src={imageDesktop}
width={0}
height={0}
style={{ width: "100%", height: "auto" }}
className="object-cover"
alt="Feature"
loading="eager"
/>
</picture>
</div>
</div>
);
const FeatureNoBg = (
{ title, description, image }: {
title: string;
description: string;
image: string;
},
) => (
<div className="container mx-auto flex flex-col justify-center p-16 items-center text-center">
<div className="mb-2 text-4xl tracking-tight font-extrabold">
{title}
</div>
<div className="mb-5 font-light text-gray-400 sm:text-xl">
{description}
</div>
<div className="p-2 rounded-lg">
<Image
src={image}
width={0}
@@ -160,6 +199,24 @@ const Feature = (
</div>
);
const FourChan = () => (
<div className="flex flex-col items-center justify-center">
<svg
width="32px"
height="32px"
viewBox="0 0 4096 4096"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
>
<g id="_4chan" transform="matrix(4.26802,0,0,4.26802,-137.229,-137.223)">
<path d="M474.83,384.842C474.83,384.842 424.165,75.302 237.905,76.022C112.383,76.502 63.359,197.062 154.333,224.174C154.333,224.174 46.448,263.082 46.448,332.019C46.448,400.996 217.353,473.573 474.828,384.801L474.83,384.842ZM552.045,621.726C552.045,621.726 588.193,933.266 774.293,941.263C899.693,946.661 954.355,828.499 864.743,797.15C864.743,797.15 974.308,763.358 977.508,694.503C980.708,625.605 813.361,545.071 552.046,621.727L552.045,621.726ZM405.212,559.744C405.212,559.744 104.028,647.316 127.222,832.176C142.817,956.698 268.377,990.886 284.372,897.356C284.372,897.356 335.955,999.684 404.412,991.366C472.869,983.048 524.374,804.626 405.212,559.746L405.212,559.744ZM630.06,461.098C630.06,461.098 939.04,407.115 936.364,220.856C934.52,95.376 813.44,47.592 787.29,138.843C787.29,138.843 747.302,31.438 678.284,32.155C609.344,32.877 538.569,204.581 630.06,461.098Z" />
</g>
</svg>
<div className="pt-4">4chan</div>
</div>
);
const GitHub = () => (
<div className="flex flex-col items-center justify-center">
<svg
@@ -199,6 +256,24 @@ const GoogleNews = () => (
</div>
);
const Lemmy = () => (
<div className="flex flex-col items-center justify-center">
<svg
width="32px"
height="32px"
viewBox="0 0 4096 4096"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
>
<g id="Lemmy" transform="matrix(170.666,0,0,170.666,-0.0042009,-0.0031286)">
<path d="M2.959,4.223C2.849,4.224 2.738,4.231 2.627,4.242C1.749,4.343 0.958,4.812 0.473,5.628C-0.002,6.428 -0.12,7.309 0.123,8.125C0.365,8.941 0.952,9.683 1.821,10.27C1.826,10.274 1.831,10.278 1.837,10.281C2.583,10.731 3.329,11.025 4.125,11.181C4.105,11.513 4.098,11.85 4.119,12.199C4.182,13.242 4.555,14.2 5.115,15.048L3.109,15.867C3.006,15.908 2.924,15.989 2.881,16.091C2.815,16.246 2.85,16.426 2.969,16.546C3.047,16.624 3.153,16.669 3.263,16.669C3.318,16.669 3.372,16.659 3.422,16.638L5.632,15.734C6.04,16.22 6.501,16.666 7.004,17.052C7.024,17.067 7.045,17.079 7.064,17.095L5.773,18.805C5.722,18.876 5.694,18.961 5.694,19.048C5.694,19.276 5.882,19.464 6.11,19.464C6.237,19.464 6.358,19.406 6.437,19.306L7.751,17.567C8.469,18.022 9.229,18.36 9.98,18.592C10.363,19.313 11.118,19.779 12,19.779C12.891,19.779 13.644,19.278 14.024,18.572C14.768,18.337 15.52,17.999 16.232,17.545L17.564,19.306C17.642,19.41 17.765,19.472 17.896,19.472C18.103,19.472 18.279,19.317 18.308,19.112C18.323,19.003 18.294,18.893 18.228,18.805L16.918,17.07C16.926,17.063 16.936,17.059 16.944,17.052C17.441,16.671 17.899,16.234 18.306,15.758L20.461,16.637C20.672,16.723 20.916,16.62 21.002,16.41C21.023,16.359 21.034,16.305 21.034,16.251C21.034,16.082 20.931,15.93 20.775,15.866L18.831,15.074C19.408,14.22 19.801,13.255 19.882,12.204C19.908,11.854 19.906,11.513 19.89,11.178C20.68,11.021 21.422,10.728 22.163,10.281C22.169,10.277 22.174,10.273 22.18,10.269C23.048,9.682 23.636,8.941 23.878,8.124C24.12,7.308 24.003,6.427 23.528,5.627C23.043,4.811 22.251,4.342 21.373,4.241C21.262,4.229 21.152,4.223 21.041,4.222C20.255,4.207 19.418,4.451 18.612,4.916C18.019,5.258 17.487,5.783 17.069,6.355C15.899,5.685 14.376,5.307 12.505,5.277C12.335,5.274 12.164,5.274 11.994,5.277C9.909,5.311 8.24,5.707 6.98,6.421C6.977,6.418 6.975,6.41 6.972,6.406C6.549,5.815 5.999,5.27 5.388,4.917C4.583,4.452 3.745,4.209 2.96,4.223L2.959,4.223ZM3.245,4.959C3.802,5.001 4.415,5.195 5.024,5.547C5.509,5.827 6,6.302 6.37,6.816C6.197,6.943 6.031,7.079 5.873,7.225C4.953,8.077 4.412,9.185 4.205,10.458C3.524,10.318 2.887,10.064 2.221,9.664C1.466,9.151 1.005,8.541 0.821,7.919C0.636,7.295 0.713,6.648 1.098,6C1.473,5.37 2.026,5.046 2.71,4.967C2.888,4.948 3.067,4.945 3.245,4.96L3.245,4.959ZM20.755,4.959C20.933,4.945 21.112,4.947 21.29,4.967C21.974,5.045 22.527,5.369 22.902,5.999C23.287,6.647 23.364,7.295 23.179,7.919C22.995,8.541 22.534,9.15 21.779,9.663C21.121,10.059 20.491,10.31 19.819,10.452C19.625,9.155 19.082,8.018 18.153,7.15C18.003,7.011 17.846,6.88 17.682,6.758C18.047,6.268 18.511,5.815 18.976,5.546C19.585,5.195 20.198,5.001 20.755,4.959L20.755,4.959ZM12.006,6.004C12.168,6.001 12.331,6.001 12.493,6.004C14.883,6.043 16.578,6.674 17.656,7.682C18.806,8.757 19.298,10.282 19.155,12.149C19.024,13.856 17.935,15.377 16.503,16.473C15.809,17.004 15.041,17.408 14.278,17.702C14.281,17.657 14.292,17.613 14.292,17.567C14.295,16.371 13.36,15.354 12,15.354C10.639,15.354 9.678,16.369 9.708,17.575C9.709,17.626 9.722,17.672 9.726,17.722C8.945,17.429 8.157,17.021 7.448,16.477C6.025,15.382 4.949,13.864 4.845,12.155C4.732,10.298 5.223,8.816 6.366,7.758C7.509,6.7 9.352,6.047 12.006,6.004ZM8.364,12.833C7.759,12.833 7.268,13.323 7.268,13.929C7.268,14.536 7.759,15.028 8.364,15.028C8.969,15.028 9.461,14.536 9.461,13.929C9.461,13.323 8.969,12.833 8.364,12.833ZM15.646,12.844C15.046,12.844 14.559,13.33 14.559,13.931C14.559,14.531 15.046,15.019 15.646,15.019C16.246,15.019 16.733,14.531 16.733,13.931C16.733,13.33 16.246,12.844 15.646,12.844ZM12,16.084C13.024,16.084 13.565,16.722 13.563,17.566C13.562,18.351 12.891,19.051 12,19.051C11.083,19.051 10.46,18.489 10.437,17.558C10.415,16.724 10.976,16.084 12,16.084Z" />
</g>
</svg>
<div className="pt-4">Lemmy</div>
</div>
);
const Mastodon = () => (
<div className="flex flex-col items-center justify-center">
<svg
@@ -235,24 +310,6 @@ const Medium = () => (
</div>
);
const Nitter = () => (
<div className="flex flex-col items-center justify-center">
<svg
width="32px"
height="32px"
viewBox="0 0 4096 4096"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
>
<g id="Nitter" transform="matrix(7.0137,0,0,7.0137,-289.9,-289.9)">
<path d="M98.133,44.8L94.667,48.4L94.667,618.267L98.133,621.867L101.733,625.334L218.267,625.334L221.867,621.867L225.333,618.267L225.333,453.2C225.333,316.4 225.6,288 227.067,288C228.133,288 281.867,362.8 346.667,454.267C411.334,545.867 465.6,621.734 467.2,622.934C469.734,625.067 474.534,625.334 517.467,625.334L564.934,625.334L568.534,621.867L572,618.267L572,48.4L568.534,44.8L564.934,41.333L448.4,41.333L444.8,44.8L441.334,48.4L441.334,213.467C441.334,350.267 441.067,378.667 439.6,378.667C438.534,378.667 384.8,303.867 320,212.267C255.333,120.8 201.067,44.933 199.467,43.733C196.933,41.6 192.133,41.333 149.2,41.333L101.733,41.333L98.133,44.8ZM310,240.933C378.267,337.334 435.867,418.4 438.134,420.8C442,424.934 442.934,425.334 450.267,425.334C457.2,425.334 458.8,424.8 461.867,421.867L465.334,418.267L465.334,65.333L548,65.333L548,601.334L514.4,601.2L480.667,601.2L356.667,425.734C288.533,329.333 230.8,248.267 228.533,245.867C224.667,241.733 223.733,241.333 216.4,241.333C209.467,241.333 207.867,241.867 204.8,244.8L201.333,248.4L201.333,601.334L118.667,601.334L118.667,65.333L152.4,65.467L186,65.467L310,240.933Z" />
</g>
</svg>
<div className="pt-4">Nitter</div>
</div>
);
const Pinterest = () => (
<div className="flex flex-col items-center justify-center">
<svg
@@ -346,24 +403,6 @@ const Tumblr = () => (
</div>
);
const X = () => (
<div className="flex flex-col items-center justify-center">
<svg
width="32px"
height="32px"
viewBox="0 0 4096 4096"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
>
<g id="X" transform="matrix(8.90048,0,0,8.90048,-238.533,-230.522)">
<path d="M389.2,48L459.8,48L305.6,224.2L487,464L345,464L233.7,318.6L106.5,464L35.8,464L200.7,275.5L26.8,48L172.4,48L272.9,180.9L389.2,48ZM364.4,421.8L403.5,421.8L151.1,88L109.1,88L364.4,421.8Z" />
</g>
</svg>
<div className="pt-4">X</div>
</div>
);
const YouTube = () => (
<div className="flex flex-col items-center justify-center">
<svg

View File

@@ -1,13 +1,15 @@
import { ReactNode } from "react";
import type { Metadata } from "next";
import type { Metadata, Viewport } from "next";
import { generalMetadata } from "@/helpers/metadata";
import { generalMetadata, generalViewport } from "@/helpers/metadata";
export const metadata: Metadata = {
...generalMetadata,
title: "FeedDeck - Pricing",
};
export const viewport: Viewport = generalViewport;
export default function Pricing() {
return (
<main>

View File

@@ -1,12 +1,14 @@
import type { Metadata } from "next";
import type { Metadata, Viewport } from "next";
import { generalMetadata } from "@/helpers/metadata";
import { generalMetadata, generalViewport } from "@/helpers/metadata";
export const metadata: Metadata = {
...generalMetadata,
title: "FeedDeck - Privacy Policy",
};
export const viewport: Viewport = generalViewport;
export default function PrivacyPolicy() {
return (
<main>

View File

@@ -1,12 +1,14 @@
import type { Metadata } from "next";
import type { Metadata, Viewport } from "next";
import { generalMetadata } from "@/helpers/metadata";
import { generalMetadata, generalViewport } from "@/helpers/metadata";
export const metadata: Metadata = {
...generalMetadata,
title: "FeedDeck - Support",
};
export const viewport: Viewport = generalViewport;
export default function Support() {
return (
<main>

View File

@@ -1,12 +1,14 @@
import type { Metadata } from "next";
import type { Metadata, Viewport } from "next";
import { generalMetadata } from "@/helpers/metadata";
import { generalMetadata, generalViewport } from "@/helpers/metadata";
export const metadata: Metadata = {
...generalMetadata,
title: "FeedDeck - Terms & Conditions",
};
export const viewport: Viewport = generalViewport;
export default function TermsAndConditions() {
return (
<main>

View File

@@ -1,4 +1,4 @@
import type { Metadata } from "next";
import type { Metadata, Viewport } from "next";
export const generalMetadata: Metadata = {
description: "Follow your RSS and Social Media Feeds",
@@ -7,12 +7,11 @@ export const generalMetadata: Metadata = {
name: "Rico Berger",
},
keywords: ["FeedDeck", "RSS", "Social Media", "Feeds"],
themeColor: "#1f2229",
metadataBase: process.env.NEXT_PUBLIC_METADATA_BASE
? new URL(process.env.NEXT_PUBLIC_METADATA_BASE)
: process.env.NODE_ENV === "development"
? new URL("http://localhost:3000")
: new URL("https://feeddeck.app"),
? new URL("http://localhost:3000")
: new URL("https://feeddeck.app"),
icons: [
{
rel: "apple-touch-icon",
@@ -112,3 +111,7 @@ export const generalMetadata: Metadata = {
"msApplication-PackageFamilyName": "26077RicoBerger.FeedDeck_2w82je6nmmv2c",
},
};
export const generalViewport: Viewport = {
themeColor: "#1f2229",
}

2423
landing/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,18 +9,18 @@
"lint": "next lint"
},
"dependencies": {
"@headlessui/react": "^1.7.15",
"@types/node": "^20.8.10",
"@types/react": "^18.2.33",
"@types/react-dom": "^18.2.14",
"autoprefixer": "^10.4.14",
"eslint": "^8.52.0",
"eslint-config-next": "^14.0.1",
"next": "^14.0.1",
"postcss": "^8.4.24",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tailwindcss": "^3.3.5",
"typescript": "^5.1.3"
"@headlessui/react": "^2.1.2",
"@types/node": "^22.0.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"autoprefixer": "^10.4.19",
"eslint": "^8.57.0",
"eslint-config-next": "^14.2.5",
"next": "^14.2.5",
"postcss": "^8.4.40",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"tailwindcss": "^3.4.7",
"typescript": "^5.5.4"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 575 KiB

After

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

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