Compare commits

...

35 Commits

Author SHA1 Message Date
Rico Berger
f147481606 Prepare v1.1.1 Release (#40) 2023-10-22 18:06:51 +02:00
Rico Berger
ebb0b63411 [core] Improve Android App Icons (#48)
This commit improves the Android app icons, by adjusting the size of the
icons.
2023-10-22 17:56:04 +02:00
Rico Berger
8248816577 [core] Clear Cached Items (#47)
The cached items in the "ItemsRepositoryStore" are now cleared when a
user selects an active deck in the settings and when a user signes out
of the app.

This is done to force a reload of the items in a column, when a user
switches between decks. For me this works better because, when I switch
between decks I manually trigger a reload for all columns, which is now
not necessary anymore.
2023-10-22 13:29:58 +02:00
Rico Berger
db454b0ebc [core] Fix Number of Loaded Items per Fetch (#46)
We always loaded 51 items per fetch instead of 50 items, because we used
a range of 0 to 50 which includes the item at position 0 and at position
50. This is now fixed by adjusting the range to 0 to 49, so that exactly
50 items are loaded per fetch request.

This commit also improves the number of loaded items shown in the column
header. If the status of the ItemsRepository is "loaded" we add a small
"+" symbol behind the number to indicat that there are more items, which
could be loaded. If the last page was already fetched (status ==
"loadedLast") then the "+" symbol isn't shown.
2023-10-22 12:42:14 +02:00
Rico Berger
74664612fd [core] Fix ItemsRepository Status (#45)
This commit fixes the status of an ItemsRepository when the repository
is initalized from the ItemsRepositoryStore instead of the "_getItems"
function.

After the repository was initialized from the ItemsRepositoryStore we
always set the status of the repository to "loaded", also when we had
less then 50 items. Because of that we also always displayed the "Load
More" button. This is now fixed by adding the status of the repository
to the ItemsRepositoryStore, so that the "Load More" button is only
shown when there are more items to load.
2023-10-22 12:21:00 +02:00
Rico Berger
f60b8795dc [core] Fix Border Radius for Drawer (#44)
This commit fixes the border radius for the drawer used in the large
deck layout. Until now the drawer used the border radius on all edges.
This is now fixed to only use the border radius on the right side of the
drawer. This is more aligned how the border radius is used in the side
sheet for the item details, where we also only use it on the left side.
2023-10-22 11:50:00 +02:00
Rico Berger
a3ad273b5f [core] Fix Status Bar for iOS (#43)
The status bar wasn't visible on iOS when the application was launched.
This is now fixed so that a user can still see the current time, battery
life, etc. when the application is running.

This commit also updates the Podfile for iOS and macOS, which was not
done within #42, so that some dependencies were missing.
2023-10-22 11:34:24 +02:00
Rico Berger
50f1638805 [podcast] Fix Audio Player for Windows and Linux (#42)
Instead of using the "just_audio_windows" and "just_audio_mpv" packages
for Windows and Linux, we are now using "just_audio_media_kit", so that
the audio player is working reliable on these platforms.

Note: We have to adjust the Flatpak, to include the dependencies which
are required on Linux.
2023-10-20 20:09:15 +02:00
Rico Berger
cf8762560e [core] Add Privacy Policy and Terms & Conditions (#39)
This commit adds links to the privacy policy and terms & conditions to
the distributed apps as it is required for the in-app purchase
submission in the Apple App Store.

This commit also updates the terms & conditions to include a specify
section when FeedDeck is used via the Apple App Store, Google Play Store
or the Windows Store.
2023-10-18 21:19:40 +02:00
Rico Berger
f8262dbe7f [core] Enable In-App Purchases for Android (#38)
This commit enables in-app purchases for Android, so that a user can
also subscribe to FeedDeck Premium on Android.
2023-10-18 21:04:32 +02:00
Rico Berger
37b44da41f [podcast] Fix Duration and Play Button (#37)
This commit fixes two bugs within the Podcast player.

It could happen that the play time and remaining time was not shown
correctly in the player. This mainly occured in Safari and is now fixed
by not using a regular expression to show the time, but instead we have
added a new "_printDuration" function which handles the formatting of
the durations.

Not only, but mainly on Safari it could also happen, that once a Podcast
was started and then paused, the loading spinner was displayed and a
user could not continue with the Podcast. This is now fixed, by removing
the spinner and only show the play, pause or replay button. As loading
indication we are now only using the seek bar.

Last but not least this commit also improves the readability of the
code, for parsing the icon of a Podcast feed.
2023-10-18 19:06:49 +02:00
ricoberger
9693fefa81 Fix Linux Release 2023-10-16 22:14:25 +02:00
ricoberger
9ed9d732f6 Prepare v1.1.0 Release 2023-10-16 21:12:48 +02:00
Rico Berger
ef1b37995f [core] Fix Google Client ID (#35)
Fix the used Google Client ID required for the Android login. The Google
Client ID must also be changed in all the Supabase projects.
2023-10-16 20:37:37 +02:00
Rico Berger
8e1e1c0eca [core] Rework Profile Edge Function (#34)
This commit introduces a new "profile-v2" edge function and deprecates
the "profile-v1" edge function. We decided to provide a new edge
function for all profile related operation to improve the handling of
the different operation the edge function is responsible for.

The main differences to the "profile-v1" functions are:
- Pass the operation id within the request url instead of the body of
  the request.
- Move the operations for accounts (e.g. GitHub) to seperate files, to
  improve the readability of the function.
- Do not require the "sourceType" anymore, instead we are using the
  operation id and request method to determine the operation which
  should be executed
- The request body is now only used for the data of the corresponding
  account and not for any other information like the operation id and
  source information.
2023-10-14 21:20:08 +02:00
dependabot[bot]
4c0a1fd5ad Bump the github-actions group with 5 updates (#33)
Bumps the github-actions group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [actions/checkout](https://github.com/actions/checkout) | `3` | `4` |
| [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) | `2` | `3` |
| [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `2` | `3` |
| [docker/login-action](https://github.com/docker/login-action) | `2` | `3` |
| [docker/build-push-action](https://github.com/docker/build-push-action) | `4` | `5` |


Updates `actions/checkout` from 3 to 4
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

Updates `docker/setup-qemu-action` from 2 to 3
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3)

Updates `docker/setup-buildx-action` from 2 to 3
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3)

Updates `docker/login-action` from 2 to 3
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v2...v3)

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

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- 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>
2023-10-13 18:43:53 +02:00
dependabot[bot]
70c26e9fb1 Bump the docker group in /supabase/functions/_cmd with 1 update (#32)
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-10-13 18:43:16 +02:00
Rico Berger
40ed13cbba [core] Add Dependabot Configuration (#31)
Add a configuration for Dependabot to keep our dependencies up to date.
2023-10-13 18:07:24 +02:00
Rico Berger
2ee64724d0 [core] Update NPM Packages (#30)
Update the npm packages used by the email templates and the landing
page.
2023-10-13 17:46:44 +02:00
Rico Berger
88550940a3 [core] Update Flutter to v3.13.7 (#29)
Update the used Flutter version to 3.13.7 and update all Flutter
packages to their latest version.
2023-10-13 17:04:47 +02:00
Rico Berger
20aea17a55 [core] Fix Title Alignment (#28)
When we used the AppBar widget we did not specify the "centerTitle"
property, so that the title was aligned within the default of the
platform. This means on iOS and macOS the title was centered, but on
Android, Windows and Linux the title was aligned on the left side.

This wasn't intended and we want to have the same style on all
Platforms, so that the title is now centered on all Platforms like it
was already done on iOS and macOS.
2023-10-12 23:22:50 +02:00
Rico Berger
4393e3271e [core] Fix Vertical Scrolling (#27)
As reported in #25 the list of sources in a column can not be scrolled
vertically when there are a lot of sources which can not all be
displayed within the existing space.

This problem only occures when a user has no touch device or trackpad,
e.g. on Linux, Windows or the Web.

To fix this we changed the scroll behaviour in the app to allow
scrolling via drag gestures. For the reported case this means the list
of sources can be dragged to the right or left side to view all the
other sources which are hidden.
2023-10-12 21:31:29 +02:00
Rico Berger
0487dbdcde [core] Add In-App Purchases (#24)
We have to add in-app purchases for the iOS, macOS and Android store, so
that users can also get the premium features of the app without using
Stripe for payments.

The in-app purchases are only enabled when a user uses the app with the
default Supabase environment or with the Supabase environment provided
during build time. If a user uses his own Supabase instance, he will not
be able to upgrade to the premium tier via in-app purchases.

We are using RevenueCat for in-app purchases, which automatically sends
all the events for a user to the "revenuecat-webhooks-v1" edge function.
Depending on the received event we can then upgrade / downgrade the
users profile. To be able to use RevenueCat as an additional provider to
Stripe we also had to add a new "subscriptionProvider" provider column
to the "profiles" table, which stores the information via which provider
a user upgraded his account.
2023-10-12 19:53:11 +02:00
Rico Berger
58e38a0a10 [core] Add Flatpak Files to Build Archive (#23)
Instead of downloading the Flatpak files from GitHub in the
"app.feeddeck.feeddeck.yml" file, we are now adding the files to the
archive we create during the release process.
2023-10-11 18:31:29 +02:00
Rico Berger
e83b367e3f [core] Fix Update of Read State for Large List of Items (#22)
When a user scrolls through a list of items and loads more items via the
"Load More" button and then clicks on the mark all items as read button,
it could happen that the request fails, because Supabase can not handle
the update filter with a large list of item ids.

To avoid this error we are splitting the list of item ids provided in
the "updateReadStates" into chunks of 25 items for the in clause in the
update filter.
2023-10-08 20:24:39 +02:00
Rico Berger
b82b486314 [core] Use Published Date for Item Ingestion (#21)
For the item ingestion logic we are now also looking at the published
date for all entries in a RSS feed. If the published date is older then
the last update date of a source the item will be skipped. This way we
should reduce the number of duplicated items, where an entry was updated
in the RSS feed, so that it had a new id and was written again to the
database and not only updated.
2023-10-08 20:19:15 +02:00
Rico Berger
d292cd00b7 [youtube] Add Missing URL in isYoutubeUrl Function (#20)
In the "isYoutubeUrl" function we are checking if the provided url is
related to YouTube. For that we checked if the url starts with
"https://www.youtube.com/" or "https://m.youtube.com/", but we also have
to check if the url starts with "https://youtube.com/" which is used
when a users shares a link to a channel via the YouTube app.

In this commit we add the missing "https://youtube.com/" option to the
"isYoutubeUrl" function.
2023-10-07 19:19:39 +02:00
Rico Berger
86db47c291 [nitter] Allow Usage of Custom Nitter Instances (#19)
It is now possible to use a custom Nitter instances. This means a user
must not rely on our Nitter instance and can instead use his own
instance. To use a custom Nitter instance a user must provide the full
RSS feed url for the instance.
2023-10-03 21:51:09 +02:00
Rico Berger
181807e090 [rss] Allow RSS Feeds without "published" Field (#18)
If a RSS feed entry did not contain a "published" field, we skipped the
entry and didn't added it to the database. Now it is also possible to
use feeds without a "published" field. If a feed entry does not contain
the field it must contain a "updated" field, which we then use for the
"publishedAt" field in the database. If a feed entry doesn't contain one
of the two fields the entry is still skipped.
2023-10-03 21:38:30 +02:00
Rico Berger
fe24f1ea0d [podcast] Improve Parsing (#17)
This commit improves the parsing of the RSS feeds for a podcast, because
some podcasts were not working as expected:

- The "https://gotime.fm/rss" podcast uses the "itunes:image" field for
  the podcast icon instead of "image", so that we now looking at both
  fields to add the podcast icon.
- The "https://itsallwidgets.com/podcast/feed" podcast didn't contain a
  link in the entries, so that all entries were skipped. For that a
  podcast must not contain a link anymore to be parsed, but most contain
  a media file which can be played within the app.
2023-10-02 19:59:52 +02:00
Rico Berger
f4e96e32fd [core] Add "Get Started" Guide (#16)
This commit adds a "Get Started" guide for the mobile and desktop
version of FeedDeck. The guide describes the basic concepts and settings
of the app to make it easier to get started with the FeedDeck.

This is also used for the review process in the different app stores to
make it easier to get the app approved.
2023-10-02 18:03:05 +02:00
ricoberger
f1c4ce06fb [core] Fix app.feeddeck.feeddeck.yml 2023-10-01 10:16:08 +02:00
Rico Berger
af483e155b [core] Add Flatpak (#15)
Add all files to create a Flatpak which can be distributed via Flathub.

Signed-off-by: ricoberger <mail@ricoberger.de>
2023-09-30 12:02:13 +02:00
ricoberger
b4a2c261b5 [core] Add Issue and Pull Request Template
This commit adds two issue templates and a pull request template. A user
can now select between reporting a bug or requesting a new feature when
opening an issue. The pull request template provides some guidance on
naming the pull request and on the required description.
2023-09-22 20:26:58 +02:00
Rico Berger
4df54be0df [core] Fix Issues with App Submission (#14)
During the review of the submission of the iOS and macOS versions of
FeedDeck we encountered some problems, so that the app was rejected. For
that we had to make the following changes:

- Change the foreground and background color of the "Sign in with Apple"
  button. This means we are now using black as foreground and white as
  background color.
- Add the "Sign in with Apple" capability to the macOS version, to
  handle the sign in within the app as it is done for the iOS version.
- Add description in the "CreateDeck" widget, because it was confusing
  for the review and they were thinking that they had to provide their
  name.
2023-09-22 09:15:03 +02:00
153 changed files with 5905 additions and 2920 deletions

45
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,45 @@
---
name: Bug report
about: Create a report to help us improve
title: '[<SOURCE>]'
labels: bug
assignees: ''
---
<!--
Please add the source type for your bug to the title of the issue, e.g. "[rss] RSS Feed could not be parsed". When
your problem is not related to a source use "[core]", e.g. "[core] Feature xyz is not working"
-->
## Describe the Bug
A clear and concise description of what the bug is.
## Platform
- [ ] Android
- [ ] iOS
- [ ] Linux
- [ ] macOS
- [ ] Windows
- [ ] Web
## To Reproduce
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
## Expected behavior
A clear and concise description of what you expected to happen.
## Screenshots
If applicable, add screenshots to help explain your problem.
## Additional context
Add any other context about the problem here.

View File

@@ -0,0 +1,31 @@
---
name: Feature request
about: Suggest an idea for this project
title: '[<SOURCE>]'
labels: enhancement
assignees: ''
---
<!--
Please add the source type for your bug to the title of the issue, e.g. "[rss] RSS Feed could not be parsed". When
your problem is not related to a source use "core", e.g. "[core] Feature xyz is not working"
-->
## Is your feature request related to a problem? Please describe
A clear and concise description of what the problem is. Ex. I'm always
frustrated when [...]
## Describe the solution you'd like
A clear and concise description of what you want to happen.
## Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've
considered.
## Additional context
Add any other context or screenshots about the feature request here.

11
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,11 @@
<!--
Keep PR title verbose enough and add prefix telling about what source it touches e.g "[rss] Add feature xyz" or if the
the PR is not realated to a source use "[core]", e.g. "[core] Fix xyz".
If you add a breaking change within your PR you should add ":warning:" to the title,
e.g. ":warning: [core] My breaking change"
-->
<!--
Description of what have been changed. Please also reference an issue, when available.
-->

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

47
.github/assets/badge-app-store.svg vendored Normal file
View File

@@ -0,0 +1,47 @@
<?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 120 40" 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>
<g>
<g>
<path d="M110.135,0L9.535,0C9.168,0 8.806,0 8.44,0.002C8.134,0.004 7.83,0.01 7.521,0.015C6.85,0.023 6.18,0.082 5.517,0.191C4.856,0.304 4.215,0.515 3.616,0.818C3.019,1.124 2.472,1.522 1.998,1.997C1.52,2.471 1.122,3.018 0.819,3.618C0.515,4.217 0.305,4.859 0.194,5.521C0.083,6.183 0.023,6.853 0.015,7.523C0.006,7.83 0.005,8.138 0,8.444L0,31.559C0.005,31.869 0.006,32.17 0.015,32.481C0.023,33.151 0.083,33.821 0.194,34.482C0.304,35.145 0.515,35.788 0.819,36.387C1.122,36.985 1.52,37.53 1.998,38.001C2.471,38.478 3.017,38.876 3.616,39.18C4.215,39.484 4.855,39.697 5.517,39.81C6.18,39.919 6.85,39.978 7.521,39.987C7.83,39.994 8.134,39.998 8.44,39.998C8.806,40 9.168,40 9.535,40L110.135,40C110.494,40 110.859,40 111.219,39.998C111.523,39.998 111.836,39.994 112.141,39.987C112.811,39.979 113.479,39.92 114.141,39.81C114.804,39.696 115.448,39.483 116.049,39.18C116.647,38.876 117.194,38.478 117.666,38.001C118.142,37.528 118.541,36.983 118.848,36.387C119.15,35.787 119.358,35.145 119.467,34.482C119.578,33.821 119.64,33.151 119.652,32.481C119.656,32.17 119.656,31.869 119.656,31.559C119.664,31.195 119.664,30.834 119.664,30.465L119.664,9.536C119.664,9.17 119.664,8.807 119.656,8.444C119.656,8.138 119.656,7.83 119.652,7.523C119.64,6.852 119.578,6.183 119.467,5.521C119.358,4.859 119.149,4.217 118.848,3.618C118.23,2.415 117.251,1.436 116.049,0.818C115.448,0.516 114.804,0.304 114.141,0.191C113.48,0.081 112.811,0.022 112.141,0.015C111.836,0.01 111.523,0.004 111.219,0.002C110.859,-0 110.494,-0 110.135,-0L110.135,0Z" style="fill:rgb(166,166,166);fill-rule:nonzero;"/>
<path d="M8.445,39.125C8.14,39.125 7.843,39.121 7.541,39.114C6.914,39.106 6.29,39.052 5.671,38.951C5.095,38.852 4.537,38.667 4.015,38.403C3.498,38.142 3.026,37.798 2.618,37.387C2.204,36.98 1.859,36.508 1.597,35.99C1.333,35.469 1.149,34.91 1.054,34.333C0.951,33.713 0.896,33.086 0.888,32.458C0.881,32.247 0.873,31.545 0.873,31.545L0.873,8.444C0.873,8.444 0.882,7.753 0.888,7.55C0.895,6.922 0.951,6.297 1.053,5.678C1.149,5.099 1.332,4.539 1.597,4.016C1.857,3.498 2.2,3.026 2.612,2.618C3.023,2.206 3.496,1.861 4.014,1.595C4.535,1.332 5.092,1.149 5.667,1.051C6.288,0.95 6.915,0.895 7.543,0.887L8.445,0.875L111.214,0.875L112.127,0.888C112.75,0.895 113.371,0.949 113.985,1.05C114.566,1.149 115.13,1.334 115.656,1.598C116.694,2.133 117.539,2.979 118.071,4.018C118.332,4.538 118.512,5.094 118.606,5.667C118.71,6.291 118.768,6.922 118.78,7.554C118.783,7.837 118.783,8.142 118.783,8.444C118.791,8.819 118.791,9.176 118.791,9.536L118.791,30.465C118.791,30.828 118.791,31.183 118.783,31.54C118.783,31.865 118.783,32.163 118.779,32.47C118.768,33.091 118.71,33.71 118.608,34.323C118.515,34.904 118.333,35.468 118.068,35.993C117.805,36.506 117.462,36.973 117.053,37.379C116.644,37.793 116.172,38.138 115.653,38.401C115.128,38.667 114.566,38.853 113.985,38.951C113.367,39.052 112.742,39.107 112.116,39.114C111.823,39.121 111.517,39.125 111.219,39.125L110.135,39.127L8.445,39.125Z" style="fill-rule:nonzero;"/>
</g>
<g id="_Group_">
<g id="_Group_2_" serif:id="_Group_2">
<g id="_Group_3_" serif:id="_Group_3">
<path id="_Path_" d="M24.769,20.301C24.791,18.606 25.682,17.036 27.125,16.149C26.21,14.842 24.729,14.041 23.134,13.991C21.455,13.815 19.827,14.996 18.971,14.996C18.099,14.996 16.782,14.008 15.363,14.038C13.498,14.098 11.797,15.135 10.89,16.766C8.956,20.114 10.399,25.035 12.251,27.742C13.178,29.067 14.261,30.547 15.679,30.495C17.066,30.437 17.584,29.61 19.258,29.61C20.917,29.61 21.403,30.495 22.849,30.462C24.338,30.437 25.275,29.13 26.17,27.792C26.836,26.848 27.348,25.804 27.688,24.7C25.924,23.954 24.771,22.216 24.769,20.301Z" style="fill:white;fill-rule:nonzero;"/>
<path id="_Path_2_" serif:id="_Path_2" d="M22.037,12.211C22.848,11.237 23.249,9.984 23.152,8.72C21.912,8.85 20.766,9.443 19.944,10.38C19.132,11.304 18.721,12.514 18.8,13.741C20.056,13.754 21.25,13.189 22.037,12.211Z" style="fill:white;fill-rule:nonzero;"/>
</g>
</g>
<g>
<path d="M42.302,27.14L37.569,27.14L36.432,30.496L34.427,30.496L38.911,18.078L40.994,18.078L45.477,30.496L43.438,30.496L42.302,27.14ZM38.059,25.591L41.811,25.591L39.961,20.144L39.91,20.144L38.059,25.591Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M55.16,25.97C55.16,28.783 53.654,30.591 51.381,30.591C50.209,30.652 49.099,30.035 48.533,29.007L48.49,29.007L48.49,33.491L46.631,33.491L46.631,21.442L48.43,21.442L48.43,22.948L48.464,22.948C49.056,21.926 50.166,21.31 51.347,21.348C53.645,21.348 55.16,23.164 55.16,25.97ZM53.25,25.97C53.25,24.137 52.302,22.932 50.857,22.932C49.437,22.932 48.482,24.162 48.482,25.97C48.482,27.794 49.437,29.016 50.857,29.016C52.302,29.016 53.25,27.819 53.25,25.97Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M65.125,25.97C65.125,28.783 63.619,30.591 61.346,30.591C60.174,30.652 59.064,30.035 58.498,29.007L58.455,29.007L58.455,33.491L56.596,33.491L56.596,21.442L58.395,21.442L58.395,22.948L58.429,22.948C59.021,21.926 60.131,21.31 61.312,21.348C63.61,21.348 65.125,23.164 65.125,25.97ZM63.214,25.97C63.214,24.137 62.267,22.932 60.822,22.932C59.402,22.932 58.447,24.162 58.447,25.97C58.447,27.794 59.402,29.016 60.822,29.016C62.267,29.016 63.214,27.819 63.214,25.97L63.214,25.97Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M71.71,27.036C71.848,28.268 73.044,29.076 74.679,29.076C76.246,29.076 77.373,28.268 77.373,27.157C77.373,26.193 76.693,25.616 75.084,25.221L73.474,24.833C71.194,24.282 70.135,23.216 70.135,21.485C70.135,19.343 72.002,17.871 74.654,17.871C77.278,17.871 79.077,19.343 79.137,21.485L77.261,21.485C77.149,20.246 76.125,19.498 74.627,19.498C73.13,19.498 72.106,20.255 72.106,21.356C72.106,22.234 72.76,22.751 74.361,23.146L75.729,23.482C78.277,24.085 79.335,25.108 79.335,26.925C79.335,29.248 77.485,30.703 74.542,30.703C71.788,30.703 69.928,29.282 69.808,27.036L71.71,27.036Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M83.346,19.3L83.346,21.442L85.068,21.442L85.068,22.914L83.346,22.914L83.346,27.905C83.346,28.681 83.691,29.042 84.448,29.042C84.652,29.038 84.856,29.024 85.059,28.999L85.059,30.462C84.719,30.525 84.373,30.554 84.027,30.548C82.194,30.548 81.479,29.859 81.479,28.103L81.479,22.914L80.163,22.914L80.163,21.442L81.479,21.442L81.479,19.3L83.346,19.3Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M86.065,25.97C86.065,23.121 87.743,21.331 90.359,21.331C92.984,21.331 94.654,23.121 94.654,25.97C94.654,28.826 92.993,30.608 90.359,30.608C87.726,30.608 86.065,28.826 86.065,25.97ZM92.76,25.97C92.76,24.016 91.865,22.862 90.359,22.862C88.853,22.862 87.959,24.024 87.959,25.97C87.959,27.932 88.853,29.076 90.359,29.076C91.865,29.076 92.76,27.932 92.76,25.97L92.76,25.97Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M96.186,21.442L97.959,21.442L97.959,22.983L98.002,22.983C98.248,21.995 99.161,21.309 100.179,21.348C100.393,21.347 100.607,21.37 100.816,21.417L100.816,23.155C100.546,23.073 100.264,23.035 99.981,23.043C99.956,23.042 99.93,23.041 99.905,23.041C98.878,23.041 98.032,23.887 98.032,24.914C98.032,24.985 98.036,25.056 98.044,25.126L98.044,30.496L96.186,30.496L96.186,21.442Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M109.384,27.837C109.134,29.48 107.534,30.608 105.486,30.608C102.852,30.608 101.217,28.844 101.217,26.013C101.217,23.173 102.861,21.331 105.408,21.331C107.913,21.331 109.488,23.052 109.488,25.797L109.488,26.434L103.093,26.434L103.093,26.546C103.087,26.615 103.084,26.684 103.084,26.754C103.084,28.047 104.149,29.112 105.442,29.112C105.471,29.112 105.5,29.111 105.529,29.11C106.428,29.195 107.282,28.675 107.62,27.837L109.384,27.837ZM103.102,25.135L107.628,25.135C107.631,25.094 107.632,25.054 107.632,25.014C107.632,23.819 106.649,22.836 105.455,22.836C105.439,22.836 105.423,22.837 105.408,22.837C105.403,22.837 105.399,22.837 105.394,22.837C104.137,22.837 103.102,23.872 103.102,25.129C103.102,25.131 103.102,25.133 103.102,25.135L103.102,25.135Z" style="fill:white;fill-rule:nonzero;"/>
</g>
</g>
</g>
<g id="_Group_4_" serif:id="_Group_4">
<g>
<path d="M37.826,8.731C37.889,8.726 37.952,8.724 38.015,8.724C39.463,8.724 40.655,9.916 40.655,11.364C40.655,11.475 40.648,11.586 40.634,11.696C40.634,13.602 39.604,14.698 37.826,14.698L35.671,14.698L35.671,8.731L37.826,8.731ZM36.598,13.854L37.723,13.854C37.76,13.856 37.797,13.857 37.835,13.857C38.864,13.857 39.71,13.011 39.71,11.981C39.71,11.89 39.704,11.799 39.69,11.708C39.702,11.623 39.708,11.538 39.708,11.452C39.708,10.42 38.859,9.571 37.827,9.571C37.792,9.571 37.757,9.572 37.723,9.574L36.598,9.574L36.598,13.854Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M41.681,12.444C41.674,12.377 41.671,12.309 41.671,12.241C41.671,11.071 42.634,10.108 43.804,10.108C44.974,10.108 45.937,11.071 45.937,12.241C45.937,12.309 45.934,12.377 45.928,12.444C45.934,12.513 45.938,12.582 45.938,12.651C45.938,13.822 44.975,14.785 43.804,14.785C42.634,14.785 41.671,13.822 41.671,12.651C41.671,12.582 41.674,12.513 41.681,12.444ZM45.014,12.444C45.014,11.468 44.575,10.897 43.806,10.897C43.033,10.897 42.599,11.468 42.599,12.444C42.599,13.428 43.033,13.995 43.806,13.995C44.575,13.995 45.014,13.424 45.014,12.444L45.014,12.444Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M51.573,14.698L50.651,14.698L49.721,11.381L49.65,11.381L48.724,14.698L47.811,14.698L46.569,10.195L47.471,10.195L48.277,13.631L48.344,13.631L49.27,10.195L50.122,10.195L51.048,13.631L51.118,13.631L51.921,10.195L52.81,10.195L51.573,14.698Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M53.854,10.195L54.709,10.195L54.709,10.91L54.775,10.91C55.006,10.385 55.547,10.061 56.119,10.108C56.156,10.105 56.192,10.104 56.229,10.104C57.032,10.104 57.694,10.765 57.694,11.568C57.694,11.64 57.688,11.712 57.678,11.783L57.678,14.698L56.789,14.698L56.789,12.006C56.789,11.282 56.475,10.922 55.817,10.922C55.801,10.922 55.785,10.921 55.769,10.921C55.203,10.921 54.736,11.388 54.736,11.954C54.736,11.991 54.738,12.027 54.742,12.063L54.742,14.698L53.854,14.698L53.854,10.195Z" style="fill:white;fill-rule:nonzero;"/>
<rect x="59.094" y="8.437" width="0.889" height="6.261" style="fill:white;fill-rule:nonzero;"/>
<path d="M61.218,12.444C61.211,12.377 61.208,12.309 61.208,12.241C61.208,11.071 62.171,10.108 63.342,10.108C64.512,10.108 65.475,11.071 65.475,12.241C65.475,12.309 65.472,12.377 65.465,12.444C65.472,12.513 65.475,12.582 65.475,12.651C65.475,13.821 64.512,14.785 63.342,14.785C62.171,14.785 61.208,13.821 61.208,12.651C61.208,12.582 61.211,12.513 61.218,12.444ZM64.551,12.444C64.551,11.468 64.112,10.897 63.343,10.897C62.57,10.897 62.136,11.468 62.136,12.444C62.136,13.428 62.57,13.995 63.343,13.995C64.112,13.995 64.551,13.424 64.551,12.444L64.551,12.444Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M66.401,13.424C66.401,12.614 67.004,12.146 68.076,12.08L69.295,12.01L69.295,11.621C69.295,11.146 68.981,10.877 68.374,10.877C67.877,10.877 67.534,11.059 67.435,11.377L66.575,11.377C66.666,10.604 67.393,10.108 68.415,10.108C69.543,10.108 70.18,10.67 70.18,11.621L70.18,14.698L69.325,14.698L69.325,14.065L69.254,14.065C68.964,14.526 68.446,14.797 67.902,14.772C67.855,14.777 67.808,14.779 67.761,14.779C67.017,14.779 66.404,14.169 66.401,13.424ZM69.295,13.04L69.295,12.663L68.196,12.733C67.576,12.775 67.294,12.986 67.294,13.383C67.294,13.788 67.646,14.024 68.129,14.024C68.165,14.028 68.201,14.029 68.236,14.029C68.792,14.029 69.258,13.594 69.295,13.04Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M71.348,12.444C71.348,11.021 72.08,10.12 73.217,10.12C73.791,10.094 74.33,10.402 74.598,10.91L74.665,10.91L74.665,8.437L75.553,8.437L75.553,14.698L74.702,14.698L74.702,13.986L74.631,13.986C74.343,14.49 73.797,14.793 73.217,14.772C72.072,14.772 71.348,13.871 71.348,12.444ZM72.266,12.444C72.266,13.399 72.716,13.974 73.469,13.974C74.218,13.974 74.681,13.391 74.681,12.448C74.681,11.51 74.213,10.918 73.469,10.918C72.721,10.918 72.266,11.497 72.266,12.444L72.266,12.444Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M79.23,12.444C79.224,12.377 79.22,12.309 79.22,12.241C79.22,11.071 80.183,10.108 81.354,10.108C82.524,10.108 83.487,11.071 83.487,12.241C83.487,12.309 83.484,12.377 83.477,12.444C83.484,12.513 83.487,12.582 83.487,12.651C83.487,13.822 82.524,14.785 81.354,14.785C80.183,14.785 79.22,13.822 79.22,12.651C79.22,12.582 79.223,12.513 79.23,12.444ZM82.563,12.444C82.563,11.468 82.125,10.897 81.355,10.897C80.583,10.897 80.148,11.468 80.148,12.444C80.148,13.428 80.583,13.995 81.355,13.995C82.125,13.995 82.563,13.424 82.563,12.444Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M84.669,10.195L85.525,10.195L85.525,10.91L85.591,10.91C85.822,10.385 86.363,10.061 86.935,10.108C86.972,10.105 87.008,10.104 87.045,10.104C87.848,10.104 88.509,10.765 88.509,11.568C88.509,11.64 88.504,11.712 88.494,11.783L88.494,14.698L87.605,14.698L87.605,12.006C87.605,11.282 87.291,10.922 86.633,10.922C86.617,10.922 86.601,10.921 86.585,10.921C86.019,10.921 85.552,11.388 85.552,11.954C85.552,11.991 85.554,12.027 85.558,12.063L85.558,14.698L84.669,14.698L84.669,10.195Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M93.515,9.074L93.515,10.215L94.491,10.215L94.491,10.964L93.515,10.964L93.515,13.279C93.515,13.751 93.709,13.958 94.152,13.958C94.265,13.957 94.378,13.95 94.491,13.937L94.491,14.677C94.331,14.706 94.169,14.721 94.007,14.723C93.019,14.723 92.626,14.375 92.626,13.507L92.626,10.964L91.911,10.964L91.911,10.215L92.626,10.215L92.626,9.074L93.515,9.074Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M95.705,8.437L96.585,8.437L96.585,10.918L96.656,10.918C96.897,10.388 97.448,10.065 98.029,10.112C98.056,10.11 98.082,10.11 98.109,10.11C98.923,10.11 99.593,10.779 99.593,11.593C99.593,11.659 99.588,11.725 99.58,11.791L99.58,14.698L98.69,14.698L98.69,12.01C98.69,11.291 98.355,10.926 97.727,10.926C97.699,10.924 97.67,10.923 97.641,10.923C97.064,10.923 96.589,11.398 96.589,11.975C96.589,12.006 96.591,12.037 96.593,12.068L96.593,14.698L95.705,14.698L95.705,8.437Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M104.761,13.482C104.513,14.328 103.687,14.88 102.81,14.785C102.792,14.785 102.774,14.785 102.756,14.785C101.634,14.785 100.711,13.862 100.711,12.74C100.711,12.647 100.717,12.553 100.73,12.46C100.718,12.369 100.712,12.277 100.712,12.185C100.712,11.045 101.649,10.108 102.788,10.108C102.794,10.108 102.8,10.108 102.806,10.108C104.059,10.108 104.815,10.964 104.815,12.378L104.815,12.688L101.635,12.688L101.635,12.738C101.632,12.771 101.631,12.804 101.631,12.838C101.631,13.491 102.168,14.028 102.821,14.028C102.826,14.028 102.83,14.028 102.834,14.028C103.268,14.08 103.693,13.864 103.906,13.482L104.761,13.482ZM101.635,12.031L103.91,12.031C103.912,12.004 103.913,11.977 103.913,11.95C103.913,11.354 103.422,10.864 102.826,10.864C102.818,10.864 102.81,10.864 102.801,10.864C102.796,10.864 102.792,10.864 102.787,10.864C102.155,10.864 101.635,11.384 101.635,12.016C101.635,12.021 101.635,12.026 101.635,12.031L101.635,12.031Z" style="fill:white;fill-rule:nonzero;"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

27
.github/assets/badge-flathub.svg vendored Normal file
View File

@@ -0,0 +1,27 @@
<?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 300 100" 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;">
<g transform="matrix(1,0,0,1,-348.1,-151.685)">
<path d="M646.159,168.627C646.159,159.796 638.99,152.627 630.159,152.627L365.042,152.627C356.211,152.627 349.042,159.796 349.042,168.627L349.042,234.41C349.042,243.241 356.211,250.41 365.042,250.41L630.159,250.41C638.99,250.41 646.159,243.241 646.159,234.41L646.159,168.627Z" style="stroke:rgb(136,138,133);stroke-width:1.88px;"/>
<g>
<path d="M464.084,188.646C467.784,188.646 469.925,186.351 469.925,182.556C469.925,178.778 467.785,176.501 464.209,176.501L460.153,176.501L460.153,188.646L464.084,188.646ZM461.985,187.045L461.985,178.102L464.096,178.102C466.753,178.102 468.123,179.692 468.123,182.556C468.123,185.432 466.753,187.045 463.983,187.045L461.985,187.045ZM478.644,188.83C481.212,188.83 482.89,186.95 482.89,184.133C482.89,181.299 481.212,179.419 478.644,179.419C476.077,179.419 474.398,181.299 474.398,184.133C474.398,186.95 476.077,188.83 478.644,188.83ZM478.65,187.341C476.972,187.341 476.19,185.877 476.19,184.127C476.19,182.384 476.972,180.901 478.65,180.901C480.317,180.901 481.1,182.384 481.1,184.127C481.1,185.877 480.317,187.341 478.65,187.341ZM489.308,188.646L491.111,188.646L492.961,182.07L493.097,182.07L494.947,188.646L496.757,188.646L499.431,179.537L497.598,179.537L495.825,186.197L495.736,186.197L493.957,179.537L492.125,179.537L490.334,186.227L490.245,186.227L488.46,179.537L486.628,179.537L489.308,188.646ZM505.63,183.238C505.63,181.785 506.519,180.955 507.752,180.955C508.956,180.955 509.685,181.743 509.685,183.065L509.685,188.645L511.458,188.645L511.458,182.852C511.458,180.599 510.219,179.419 508.358,179.419C506.988,179.419 506.092,180.053 505.671,181.019L505.558,181.019L505.558,179.537L503.856,179.537L503.856,188.646L505.629,188.646L505.63,183.238ZM518.35,176.501L516.578,176.501L516.578,188.646L518.351,188.646L518.35,176.501ZM527.319,188.83C529.887,188.83 531.565,186.95 531.565,184.133C531.565,181.299 529.887,179.419 527.319,179.419C524.751,179.419 523.073,181.299 523.073,184.133C523.073,186.95 524.751,188.83 527.319,188.83ZM527.325,187.341C525.647,187.341 524.864,185.877 524.864,184.127C524.864,182.384 525.647,180.901 527.324,180.901C528.991,180.901 529.774,182.384 529.774,184.127C529.774,185.877 528.991,187.341 527.324,187.341L527.325,187.341ZM538.937,188.848C540.444,188.848 541.292,188.083 541.63,187.401L541.7,187.401L541.7,188.646L543.432,188.646L543.432,182.597C543.432,179.947 541.345,179.419 539.898,179.419C538.25,179.419 536.732,180.083 536.138,181.743L537.805,182.123C538.065,181.477 538.73,180.853 539.922,180.853C541.066,180.853 541.653,181.453 541.653,182.485L541.653,182.526C541.653,183.173 540.989,183.161 539.353,183.35C537.627,183.552 535.86,184.003 535.86,186.072C535.86,187.863 537.206,188.848 538.938,188.848L538.937,188.848ZM539.323,187.424C538.321,187.424 537.597,186.974 537.597,186.096C537.597,185.147 538.439,184.809 539.465,184.673C540.04,184.596 541.405,184.442 541.659,184.187L541.659,185.361C541.659,186.44 540.799,187.424 539.323,187.424ZM551.95,188.824C553.61,188.824 554.262,187.81 554.583,187.229L554.731,187.229L554.731,188.646L556.462,188.646L556.462,176.5L554.689,176.5L554.689,181.013L554.583,181.013C554.263,180.45 553.658,179.418 551.962,179.418C549.762,179.418 548.142,181.155 548.142,184.108C548.142,187.056 549.738,188.823 551.95,188.823L551.95,188.824ZM552.341,187.312C550.758,187.312 549.933,185.918 549.933,184.092C549.933,182.283 550.74,180.925 552.341,180.925C553.889,180.925 554.719,182.188 554.719,184.092C554.719,186.007 553.871,187.312 552.341,187.312ZM572.744,188.83C575.312,188.83 576.99,186.95 576.99,184.133C576.99,181.299 575.312,179.419 572.744,179.419C570.176,179.419 568.498,181.299 568.498,184.133C568.498,186.95 570.176,188.83 572.744,188.83ZM572.75,187.341C571.072,187.341 570.289,185.877 570.289,184.127C570.289,182.384 571.072,180.901 572.749,180.901C574.416,180.901 575.199,182.384 575.199,184.127C575.199,185.877 574.416,187.341 572.749,187.341L572.75,187.341ZM583.485,183.238C583.485,181.785 584.375,180.955 585.608,180.955C586.812,180.955 587.541,181.743 587.541,183.065L587.541,188.645L589.314,188.645L589.314,182.852C589.314,180.599 588.074,179.419 586.213,179.419C584.843,179.419 583.947,180.053 583.526,181.019L583.414,181.019L583.414,179.537L581.712,179.537L581.712,188.646L583.485,188.646L583.485,183.238Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g>
<path d="M460.153,222.645L464.983,222.645L464.983,211.32L476.06,211.32L476.06,207.272L464.982,207.272L464.982,200.034L477.232,200.034L477.232,195.986L460.152,195.986L460.153,222.645Z" style="fill:white;fill-rule:nonzero;"/>
<rect x="481.41" y="195.986" width="4.712" height="26.66" style="fill:white;fill-rule:nonzero;"/>
<path d="M496.718,223.049C499.855,223.049 501.73,221.578 502.588,219.899L502.745,219.899L502.745,222.645L507.275,222.645L507.275,209.263C507.275,203.978 502.966,202.39 499.152,202.39C494.948,202.39 491.719,204.265 490.678,207.91L495.078,208.535C495.546,207.168 496.874,205.996 499.178,205.996C501.365,205.996 502.563,207.116 502.563,209.081L502.563,209.159C502.563,210.513 501.143,210.579 497.616,210.956C493.737,211.372 490.027,212.531 490.027,217.035C490.027,220.965 492.904,223.049 496.718,223.049ZM497.942,219.586C495.976,219.586 494.57,218.688 494.57,216.956C494.57,215.147 496.145,214.392 498.254,214.093C499.491,213.923 501.964,213.611 502.576,213.117L502.576,215.473C502.576,217.699 500.779,219.586 497.942,219.586Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M522.01,202.65L518.066,202.65L518.066,197.86L513.354,197.86L513.354,202.65L510.516,202.65L510.516,206.296L513.354,206.296L513.354,217.412C513.328,221.174 516.062,223.022 519.602,222.918C520.943,222.879 521.867,222.619 522.375,222.45L521.581,218.766C521.321,218.831 520.787,218.948 520.201,218.948C519.016,218.948 518.066,218.532 518.066,216.631L518.066,206.296L522.01,206.296L522.01,202.65Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M531.083,210.93C531.083,208.04 532.88,206.374 535.405,206.374C537.878,206.374 539.336,207.949 539.336,210.644L539.336,222.645L544.049,222.645L544.049,209.915C544.049,205.085 541.315,202.39 537.162,202.39C534.09,202.39 532.138,203.783 531.214,206.048L530.979,206.048L530.979,195.986L526.371,195.986L526.371,222.646L531.083,222.646L531.083,210.93Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M561.583,214.236C561.583,217.282 559.409,218.792 557.326,218.792C555.061,218.792 553.551,217.191 553.551,214.652L553.551,202.651L548.839,202.651L548.839,215.381C548.839,220.185 551.572,222.905 555.504,222.905C558.498,222.905 560.606,221.33 561.518,219.091L561.726,219.091L561.726,222.645L566.295,222.645L566.295,202.651L561.583,202.651L561.583,214.236Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M571.346,222.645L575.98,222.645L575.98,219.495L576.253,219.495C576.995,220.953 578.544,222.997 581.981,222.997C586.693,222.997 590.221,219.261 590.221,212.674C590.221,206.009 586.589,202.39 581.968,202.39C578.44,202.39 576.969,204.512 576.253,205.957L576.058,205.957L576.058,195.986L571.346,195.986L571.346,222.645ZM575.966,212.648C575.966,208.768 577.633,206.256 580.666,206.256C583.803,206.256 585.417,208.925 585.417,212.648C585.417,216.397 583.777,219.13 580.666,219.13C577.659,219.13 575.966,216.527 575.966,212.648Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(1.24662,0,0,1.24662,192.661,67.896)">
<circle cx="166.688" cy="95.647" r="9.048" style="fill:white;"/>
<path d="M174.826,111.309C174.826,109.371 173.253,107.798 171.315,107.798L161.925,107.798C159.987,107.798 158.414,109.371 158.414,111.309L158.414,120.699C158.414,122.637 159.987,124.21 161.925,124.21L171.315,124.21C173.253,124.21 174.826,122.637 174.826,120.699L174.826,111.309Z" style="fill:white;"/>
<g transform="matrix(0.925902,0.53457,0.53457,-0.925902,99.8266,110.693)">
<path d="M69.514,58.833L57.486,58.833C56.614,58.833 55.805,58.366 55.369,57.611C54.933,56.856 54.933,55.922 55.369,55.167L56.259,53.625L61.383,44.75C61.819,43.994 62.628,43.527 63.5,43.527C64.372,43.527 65.181,43.994 65.617,44.75L66.507,46.292L71.631,55.167C72.067,55.922 72.067,56.856 71.631,57.611C71.195,58.366 70.386,58.833 69.514,58.833Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<path d="M194.992,116.11C194.992,116.99 194.284,117.697 193.404,117.697L180.704,117.697C179.824,117.697 179.117,116.989 179.117,116.11C179.117,115.23 179.825,114.522 180.704,114.522L193.404,114.522C194.284,114.522 194.992,115.23 194.992,116.11ZM187.054,108.172C187.934,108.172 188.642,108.88 188.642,109.76L188.642,122.46C188.642,123.34 187.934,124.047 187.054,124.047C186.174,124.047 185.467,123.339 185.467,122.46L185.467,109.76C185.467,108.88 186.175,108.172 187.054,108.172Z" style="fill:white;fill-rule:nonzero;"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

25
.github/assets/badge-google-play.svg vendored Normal file
View File

@@ -0,0 +1,25 @@
<?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 563 167" 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-miterlimit:2;">
<g transform="matrix(4.16667,0,0,4.16667,0,-4)">
<g>
<g>
<path d="M130,41L4.999,41C2.249,41 0,38.75 0,36L0,6C0,3.25 2.249,1 4.999,1L130,1C132.75,1 135,3.25 135,6L135,36C135,38.75 132.75,41 130,41Z" style="fill:rgb(3,4,4);fill-rule:nonzero;"/>
<path d="M130,1L4.999,1C2.249,1 0,3.25 0,6L0,36C0,38.75 2.249,41 4.999,41L130,41C132.75,41 135,38.75 135,36L135,6C135,3.25 132.75,1 130,1ZM130,1.8C132.316,1.8 134.2,3.685 134.2,6L134.2,36C134.2,38.316 132.316,40.201 130,40.201L4.999,40.201C2.684,40.201 0.8,38.316 0.8,36L0.8,6C0.8,3.685 2.684,1.8 4.999,1.8L130,1.8Z" style="fill:rgb(167,165,166);fill-rule:nonzero;"/>
<path d="M47.376,10.791L44.468,10.791L44.468,11.512L46.647,11.512C46.588,12.098 46.353,12.559 45.96,12.894C45.566,13.229 45.063,13.397 44.468,13.397C43.814,13.397 43.261,13.171 42.809,12.718C42.365,12.257 42.138,11.688 42.138,11C42.138,10.313 42.365,9.743 42.809,9.282C43.261,8.83 43.814,8.604 44.468,8.604C44.803,8.604 45.122,8.662 45.415,8.788C45.708,8.914 45.943,9.09 46.127,9.316L46.68,8.763C46.429,8.478 46.11,8.26 45.717,8.101C45.323,7.942 44.912,7.866 44.468,7.866C43.596,7.866 42.859,8.168 42.256,8.771C41.652,9.375 41.351,10.12 41.351,11C41.351,11.88 41.652,12.626 42.256,13.229C42.859,13.833 43.596,14.134 44.468,14.134C45.381,14.134 46.11,13.841 46.672,13.246C47.166,12.752 47.418,12.081 47.418,11.243C47.418,11.101 47.401,10.95 47.376,10.791Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M48.524,8L48.524,14L52.027,14L52.027,13.263L49.295,13.263L49.295,11.361L51.758,11.361L51.758,10.64L49.295,10.64L49.295,8.738L52.027,8.738L52.027,8L48.524,8Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M56.953,8.738L56.953,8L52.83,8L52.83,8.738L54.506,8.738L54.506,14L55.277,14L55.277,8.738L56.953,8.738Z" style="fill:white;fill-rule:nonzero;"/>
<rect x="59.937" y="8" width="0.771" height="6" style="fill:white;"/>
<path d="M65.803,8.738L65.803,8L61.68,8L61.68,8.738L63.356,8.738L63.356,14L64.127,14L64.127,8.738L65.803,8.738Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M73.605,8.78C73.01,8.168 72.281,7.866 71.409,7.866C70.538,7.866 69.808,8.168 69.213,8.771C68.618,9.366 68.325,10.112 68.325,11C68.325,11.889 68.618,12.634 69.213,13.229C69.808,13.833 70.538,14.134 71.409,14.134C72.272,14.134 73.01,13.833 73.605,13.229C74.2,12.634 74.493,11.889 74.493,11C74.493,10.12 74.2,9.375 73.605,8.78ZM69.767,9.282C70.211,8.83 70.755,8.604 71.409,8.604C72.063,8.604 72.607,8.83 73.043,9.282C73.487,9.727 73.705,10.305 73.705,11C73.705,11.696 73.487,12.274 73.043,12.718C72.607,13.171 72.063,13.397 71.409,13.397C70.755,13.397 70.211,13.171 69.767,12.718C69.331,12.266 69.113,11.696 69.113,11C69.113,10.305 69.331,9.735 69.767,9.282Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M76.345,10.263L76.312,9.106L76.345,9.106L79.396,14L80.2,14L80.2,8L79.429,8L79.429,11.512L79.463,12.668L79.429,12.668L76.513,8L75.575,8L75.575,14L76.345,14L76.345,10.263Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M47.376,10.791L44.468,10.791L44.468,11.512L46.647,11.512C46.588,12.098 46.353,12.559 45.96,12.894C45.566,13.229 45.063,13.397 44.468,13.397C43.814,13.397 43.261,13.171 42.809,12.718C42.365,12.257 42.138,11.688 42.138,11C42.138,10.313 42.365,9.743 42.809,9.282C43.261,8.83 43.814,8.604 44.468,8.604C44.803,8.604 45.122,8.662 45.415,8.788C45.708,8.914 45.943,9.09 46.127,9.316L46.68,8.763C46.429,8.478 46.11,8.26 45.717,8.101C45.323,7.942 44.912,7.866 44.468,7.866C43.596,7.866 42.859,8.168 42.256,8.771C41.652,9.375 41.351,10.12 41.351,11C41.351,11.88 41.652,12.626 42.256,13.229C42.859,13.833 43.596,14.134 44.468,14.134C45.381,14.134 46.11,13.841 46.672,13.246C47.166,12.752 47.418,12.081 47.418,11.243C47.418,11.101 47.401,10.95 47.376,10.791ZM48.524,8L48.524,14L52.027,14L52.027,13.263L49.295,13.263L49.295,11.361L51.758,11.361L51.758,10.64L49.295,10.64L49.295,8.738L52.027,8.738L52.027,8L48.524,8ZM56.953,8.738L56.953,8L52.831,8L52.831,8.738L54.507,8.738L54.507,14L55.277,14L55.277,8.738L56.953,8.738ZM60.708,8L59.937,8L59.937,14L60.708,14L60.708,8ZM65.803,8.738L65.803,8L61.68,8L61.68,8.738L63.356,8.738L63.356,14L64.127,14L64.127,8.738L65.803,8.738ZM73.605,8.78C73.01,8.168 72.281,7.866 71.409,7.866C70.537,7.866 69.808,8.168 69.213,8.771C68.618,9.366 68.325,10.112 68.325,11C68.325,11.889 68.618,12.634 69.213,13.229C69.808,13.833 70.537,14.134 71.409,14.134C72.272,14.134 73.01,13.833 73.605,13.229C74.2,12.634 74.493,11.889 74.493,11C74.493,10.12 74.2,9.375 73.605,8.78ZM69.767,9.282C70.211,8.83 70.755,8.604 71.409,8.604C72.063,8.604 72.607,8.83 73.043,9.282C73.487,9.727 73.705,10.305 73.705,11C73.705,11.696 73.487,12.274 73.043,12.718C72.607,13.171 72.063,13.397 71.409,13.397C70.755,13.397 70.211,13.171 69.767,12.718C69.331,12.266 69.113,11.696 69.113,11C69.113,10.305 69.331,9.735 69.767,9.282ZM76.346,10.263L76.312,9.106L76.346,9.106L79.396,14L80.2,14L80.2,8L79.429,8L79.429,11.512L79.463,12.668L79.429,12.668L76.513,8L75.575,8L75.575,14L76.346,14L76.346,10.263Z" style="fill:none;stroke:white;stroke-width:0.2px;"/>
<path d="M106.936,31.001L108.802,31.001L108.802,18.499L106.936,18.499L106.936,31.001ZM123.743,23.002L121.604,28.422L121.54,28.422L119.32,23.002L117.31,23.002L120.64,30.578L118.741,34.791L120.687,34.791L125.818,23.002L123.743,23.002ZM113.16,29.581C112.55,29.581 111.697,29.275 111.697,28.519C111.697,27.554 112.759,27.184 113.675,27.184C114.495,27.184 114.882,27.361 115.38,27.602C115.235,28.76 114.238,29.581 113.16,29.581ZM113.386,22.729C112.035,22.729 110.636,23.324 110.057,24.643L111.713,25.334C112.067,24.643 112.726,24.418 113.418,24.418C114.383,24.418 115.364,24.997 115.38,26.026L115.38,26.155C115.042,25.962 114.318,25.672 113.434,25.672C111.648,25.672 109.831,26.653 109.831,28.487C109.831,30.16 111.295,31.237 112.935,31.237C114.189,31.237 114.882,30.674 115.315,30.015L115.38,30.015L115.38,30.98L117.182,30.98L117.182,26.187C117.182,23.967 115.524,22.729 113.386,22.729ZM101.854,24.524L99.2,24.524L99.2,20.239L101.854,20.239C103.249,20.239 104.041,21.394 104.041,22.382C104.041,23.351 103.249,24.524 101.854,24.524ZM101.806,18.499L97.334,18.499L97.334,31.001L99.2,31.001L99.2,26.264L101.806,26.264C103.874,26.264 105.907,24.767 105.907,22.382C105.907,19.997 103.874,18.499 101.806,18.499ZM77.424,29.583C76.135,29.583 75.056,28.503 75.056,27.022C75.056,25.523 76.135,24.428 77.424,24.428C78.697,24.428 79.696,25.523 79.696,27.022C79.696,28.503 78.697,29.583 77.424,29.583ZM79.567,23.703L79.502,23.703C79.083,23.203 78.278,22.752 77.263,22.752C75.136,22.752 73.187,24.621 73.187,27.022C73.187,29.406 75.136,31.258 77.263,31.258C78.278,31.258 79.083,30.807 79.502,30.292L79.567,30.292L79.567,30.904C79.567,32.531 78.697,33.401 77.295,33.401C76.152,33.401 75.443,32.58 75.153,31.887L73.526,32.563C73.993,33.691 75.233,35.077 77.295,35.077C79.487,35.077 81.339,33.788 81.339,30.646L81.339,23.01L79.567,23.01L79.567,23.703ZM82.628,31.001L84.497,31.001L84.497,18.499L82.628,18.499L82.628,31.001ZM87.251,26.876C87.204,25.233 88.525,24.396 89.475,24.396C90.216,24.396 90.844,24.766 91.054,25.297L87.251,26.876ZM93.051,25.459C92.697,24.508 91.618,22.752 89.411,22.752C87.219,22.752 85.399,24.476 85.399,27.005C85.399,29.39 87.204,31.258 89.62,31.258C91.569,31.258 92.697,30.066 93.165,29.374L91.714,28.407C91.231,29.116 90.571,29.583 89.62,29.583C88.669,29.583 87.993,29.148 87.558,28.294L93.245,25.942L93.051,25.459ZM47.743,24.057L47.743,25.861L52.061,25.861C51.932,26.876 51.594,27.617 51.078,28.133C50.45,28.761 49.467,29.454 47.743,29.454C45.085,29.454 43.007,27.312 43.007,24.653C43.007,21.995 45.085,19.852 47.743,19.852C49.177,19.852 50.224,20.416 50.998,21.141L52.27,19.868C51.191,18.837 49.757,18.048 47.743,18.048C44.102,18.048 41.041,21.012 41.041,24.653C41.041,28.294 44.102,31.258 47.743,31.258C49.708,31.258 51.191,30.614 52.351,29.406C53.543,28.213 53.914,26.538 53.914,25.185C53.914,24.766 53.881,24.379 53.817,24.057L47.743,24.057ZM58.822,29.583C57.533,29.583 56.421,28.52 56.421,27.005C56.421,25.475 57.533,24.428 58.822,24.428C60.111,24.428 61.223,25.475 61.223,27.005C61.223,28.52 60.111,29.583 58.822,29.583ZM58.822,22.752C56.47,22.752 54.553,24.541 54.553,27.005C54.553,29.454 56.47,31.258 58.822,31.258C61.174,31.258 63.091,29.454 63.091,27.005C63.091,24.541 61.174,22.752 58.822,22.752ZM68.135,29.583C66.847,29.583 65.735,28.52 65.735,27.005C65.735,25.475 66.847,24.428 68.135,24.428C69.424,24.428 70.536,25.475 70.536,27.005C70.536,28.52 69.424,29.583 68.135,29.583ZM68.135,22.752C65.783,22.752 63.866,24.541 63.866,27.005C63.866,29.454 65.783,31.258 68.135,31.258C70.488,31.258 72.405,29.454 72.405,27.005C72.405,24.541 70.488,22.752 68.135,22.752Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M20.717,20.425L10.07,31.725C10.071,31.727 10.071,31.729 10.072,31.731C10.398,32.959 11.519,33.862 12.849,33.862C13.381,33.862 13.88,33.718 14.308,33.466L14.342,33.446L26.326,26.531L20.717,20.425Z" style="fill:rgb(235,67,53);fill-rule:nonzero;"/>
<path d="M31.488,18.501L31.478,18.494L26.304,15.494L20.475,20.681L26.324,26.529L31.471,23.56C32.373,23.073 32.985,22.122 32.985,21.025C32.985,19.936 32.381,18.989 31.488,18.501Z" style="fill:rgb(250,188,19);fill-rule:nonzero;"/>
<path d="M10.07,10.278C10.006,10.514 9.972,10.761 9.972,11.018L9.972,30.985C9.972,31.242 10.005,31.49 10.07,31.725L21.083,20.714L10.07,10.278Z" style="fill:rgb(84,125,191);fill-rule:nonzero;"/>
<path d="M20.795,21.001L26.306,15.492L14.335,8.552C13.9,8.291 13.393,8.141 12.849,8.141C11.519,8.141 10.397,9.046 10.07,10.275L10.07,10.278L20.795,21.001Z" style="fill:rgb(48,168,81);fill-rule:nonzero;"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

52
.github/assets/badge-mac-app-store.svg vendored Normal file
View File

@@ -0,0 +1,52 @@
<?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 157 40" 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>
<g>
<g>
<path d="M146.571,0L9.535,0C9.168,0 8.806,0 8.44,0.002C8.134,0.004 7.83,0.01 7.521,0.015C6.85,0.023 6.18,0.082 5.517,0.191C4.856,0.304 4.215,0.515 3.616,0.818C3.019,1.124 2.472,1.522 1.998,1.997C1.52,2.471 1.122,3.018 0.819,3.618C0.515,4.217 0.305,4.859 0.194,5.521C0.083,6.183 0.023,6.853 0.015,7.523C0.006,7.83 0.005,8.138 0,8.444L0,31.559C0.005,31.869 0.006,32.17 0.015,32.481C0.023,33.151 0.083,33.821 0.194,34.482C0.304,35.145 0.515,35.788 0.819,36.387C1.122,36.985 1.52,37.53 1.998,38.001C2.471,38.478 3.017,38.876 3.616,39.18C4.215,39.484 4.855,39.697 5.517,39.811C6.18,39.919 6.85,39.978 7.521,39.987C7.83,39.994 8.134,39.998 8.44,39.998C8.806,40 9.168,40 9.535,40L146.571,40C146.931,40 147.296,40 147.655,39.998C147.96,39.998 148.272,39.994 148.577,39.987C149.247,39.979 149.916,39.92 150.577,39.81C151.241,39.696 151.884,39.483 152.485,39.18C153.084,38.876 153.63,38.478 154.103,38.001C154.579,37.528 154.977,36.983 155.284,36.387C155.586,35.787 155.795,35.145 155.903,34.482C156.015,33.821 156.077,33.151 156.089,32.481C156.093,32.17 156.093,31.869 156.093,31.559C156.101,31.195 156.101,30.834 156.101,30.465L156.101,9.536C156.101,9.17 156.101,8.807 156.093,8.444C156.093,8.138 156.093,7.83 156.089,7.523C156.077,6.852 156.015,6.183 155.903,5.521C155.795,4.859 155.586,4.217 155.284,3.618C154.667,2.415 153.688,1.436 152.485,0.818C151.884,0.516 151.241,0.304 150.577,0.191C149.916,0.081 149.247,0.022 148.577,0.015C148.272,0.01 147.96,0.004 147.655,0.002C147.296,-0 146.931,-0 146.571,-0L146.571,0Z" style="fill:rgb(166,166,166);fill-rule:nonzero;"/>
<path d="M8.445,39.125C8.14,39.125 7.843,39.121 7.541,39.114C6.914,39.106 6.29,39.052 5.671,38.951C5.095,38.852 4.537,38.667 4.015,38.403C3.498,38.142 3.026,37.798 2.618,37.387C2.204,36.98 1.859,36.508 1.597,35.99C1.333,35.469 1.149,34.91 1.054,34.333C0.951,33.713 0.896,33.086 0.888,32.458C0.881,32.247 0.873,31.545 0.873,31.545L0.873,8.444C0.873,8.444 0.882,7.753 0.888,7.55C0.895,6.922 0.951,6.297 1.053,5.678C1.149,5.099 1.332,4.539 1.597,4.016C1.857,3.498 2.2,3.026 2.612,2.618C3.023,2.206 3.496,1.861 4.014,1.595C4.535,1.332 5.092,1.149 5.667,1.051C6.288,0.95 6.915,0.895 7.543,0.887L8.445,0.875L147.65,0.875L148.563,0.888C149.186,0.895 149.807,0.949 150.422,1.05C151.003,1.149 151.566,1.334 152.093,1.598C153.13,2.133 153.975,2.979 154.508,4.018C154.768,4.538 154.949,5.094 155.043,5.667C155.147,6.291 155.205,6.922 155.217,7.554C155.22,7.837 155.22,8.142 155.22,8.444C155.228,8.819 155.228,9.176 155.228,9.536L155.228,30.465C155.228,30.828 155.228,31.183 155.22,31.54C155.22,31.865 155.22,32.163 155.216,32.47C155.204,33.091 155.147,33.71 155.045,34.323C154.952,34.904 154.769,35.468 154.505,35.993C154.241,36.506 153.898,36.973 153.489,37.379C153.081,37.793 152.608,38.138 152.09,38.401C151.565,38.667 151.002,38.853 150.422,38.951C149.804,39.052 149.179,39.107 148.553,39.114C148.26,39.121 147.953,39.125 147.655,39.125L146.571,39.127L8.445,39.125Z" style="fill-rule:nonzero;"/>
</g>
<g id="_Group_">
<g id="_Group_2_" serif:id="_Group_2">
<g id="_Group_3_" serif:id="_Group_3">
<g id="_Group_4_" serif:id="_Group_4">
<path id="_Path_" d="M24.769,20.301C24.791,18.606 25.682,17.036 27.125,16.149C26.21,14.842 24.729,14.041 23.134,13.991C21.455,13.815 19.827,14.996 18.971,14.996C18.099,14.996 16.782,14.008 15.363,14.038C13.498,14.098 11.797,15.135 10.89,16.766C8.956,20.114 10.399,25.035 12.251,27.742C13.178,29.067 14.261,30.547 15.679,30.495C17.066,30.437 17.584,29.61 19.258,29.61C20.917,29.61 21.403,30.495 22.849,30.462C24.338,30.437 25.275,29.13 26.17,27.792C26.836,26.848 27.348,25.804 27.688,24.7C25.924,23.954 24.771,22.216 24.769,20.301Z" style="fill:white;fill-rule:nonzero;"/>
<path id="_Path_2_" serif:id="_Path_2" d="M22.037,12.211C22.848,11.237 23.249,9.984 23.152,8.72C21.912,8.85 20.766,9.443 19.944,10.38C19.132,11.304 18.721,12.514 18.8,13.741C20.056,13.754 21.25,13.189 22.037,12.211Z" style="fill:white;fill-rule:nonzero;"/>
</g>
</g>
<g>
<path d="M46.149,30.496L46.149,21.356L46.088,21.356L42.345,30.401L40.917,30.401L37.164,21.356L37.104,21.356L37.104,30.496L35.348,30.496L35.348,18.078L37.578,18.078L41.596,27.88L41.665,27.88L45.675,18.078L47.913,18.078L47.913,30.496L46.149,30.496Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M49.396,27.923C49.396,26.34 50.609,25.384 52.761,25.255L55.24,25.117L55.24,24.429C55.24,23.422 54.577,22.853 53.449,22.853C52.588,22.739 51.768,23.289 51.547,24.128L49.809,24.128C49.861,22.492 51.384,21.331 53.501,21.331C55.661,21.331 57.089,22.51 57.089,24.291L57.089,30.496L55.308,30.496L55.308,29.007L55.265,29.007C54.685,30.034 53.587,30.666 52.408,30.651C52.319,30.66 52.23,30.664 52.14,30.664C50.636,30.664 49.398,29.427 49.396,27.923ZM55.24,27.105L55.24,26.408L53.01,26.546C51.9,26.615 51.272,27.097 51.272,27.871C51.272,28.663 51.926,29.18 52.924,29.18C52.973,29.183 53.022,29.185 53.071,29.185C54.227,29.185 55.191,28.261 55.24,27.105L55.24,27.105Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M64.893,24.558C64.726,23.536 63.791,22.805 62.759,22.889C61.331,22.889 60.384,24.085 60.384,25.97C60.384,27.897 61.339,29.059 62.776,29.059C63.795,29.161 64.729,28.443 64.893,27.433L66.683,27.433C66.453,29.367 64.699,30.787 62.759,30.608C60.177,30.608 58.491,28.844 58.491,25.97C58.491,23.155 60.177,21.331 62.742,21.331C64.696,21.165 66.451,22.608 66.667,24.558L64.893,24.558Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M78.759,27.14L74.026,27.14L72.889,30.496L70.884,30.496L75.368,18.078L77.451,18.078L81.934,30.496L79.895,30.496L78.759,27.14ZM74.516,25.591L78.268,25.591L76.419,20.144L76.367,20.144L74.516,25.591Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M91.617,25.97C91.617,28.783 90.111,30.591 87.838,30.591C86.666,30.652 85.556,30.035 84.99,29.007L84.947,29.007L84.947,33.491L83.088,33.491L83.088,21.442L84.887,21.442L84.887,22.948L84.921,22.948C85.513,21.926 86.623,21.31 87.804,21.348C90.102,21.348 91.617,23.164 91.617,25.97ZM89.707,25.97C89.707,24.137 88.759,22.932 87.314,22.932C85.894,22.932 84.939,24.162 84.939,25.97C84.939,27.794 85.894,29.016 87.314,29.016C88.759,29.016 89.707,27.819 89.707,25.97Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M101.582,25.97C101.582,28.783 100.076,30.591 97.803,30.591C96.631,30.652 95.521,30.035 94.955,29.007L94.912,29.007L94.912,33.491L93.053,33.491L93.053,21.442L94.852,21.442L94.852,22.948L94.886,22.948C95.478,21.926 96.588,21.31 97.769,21.348C100.067,21.348 101.582,23.164 101.582,25.97ZM99.671,25.97C99.671,24.137 98.724,22.932 97.279,22.932C95.859,22.932 94.904,24.162 94.904,25.97C94.904,27.794 95.859,29.016 97.279,29.016C98.724,29.016 99.671,27.819 99.671,25.97L99.671,25.97Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M108.168,27.036C108.305,28.268 109.502,29.076 111.136,29.076C112.703,29.076 113.83,28.268 113.83,27.157C113.83,26.193 113.15,25.616 111.541,25.221L109.931,24.833C107.651,24.282 106.592,23.216 106.592,21.485C106.592,19.343 108.459,17.871 111.111,17.871C113.735,17.871 115.534,19.343 115.594,21.485L113.718,21.485C113.606,20.246 112.582,19.498 111.084,19.498C109.587,19.498 108.563,20.255 108.563,21.356C108.563,22.234 109.217,22.751 110.818,23.146L112.186,23.482C114.734,24.085 115.792,25.108 115.792,26.925C115.792,29.248 113.942,30.703 110.999,30.703C108.245,30.703 106.385,29.282 106.265,27.036L108.168,27.036Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M119.803,19.3L119.803,21.442L121.525,21.442L121.525,22.914L119.803,22.914L119.803,27.905C119.803,28.681 120.148,29.042 120.905,29.042C121.109,29.038 121.313,29.024 121.516,28.999L121.516,30.462C121.176,30.525 120.83,30.554 120.484,30.548C118.651,30.548 117.936,29.859 117.936,28.103L117.936,22.914L116.62,22.914L116.62,21.442L117.936,21.442L117.936,19.3L119.803,19.3Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M122.521,25.97C122.521,23.121 124.199,21.331 126.815,21.331C129.44,21.331 131.11,23.121 131.11,25.97C131.11,28.826 129.449,30.608 126.815,30.608C124.182,30.608 122.521,28.826 122.521,25.97ZM129.216,25.97C129.216,24.016 128.321,22.862 126.815,22.862C125.309,22.862 124.414,24.024 124.414,25.97C124.414,27.932 125.309,29.076 126.815,29.076C128.321,29.076 129.216,27.932 129.216,25.97L129.216,25.97Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M132.643,21.442L134.416,21.442L134.416,22.983L134.459,22.983C134.705,21.995 135.618,21.309 136.636,21.348C136.85,21.347 137.064,21.37 137.273,21.417L137.273,23.155C137.003,23.073 136.721,23.035 136.438,23.043C136.413,23.042 136.387,23.041 136.362,23.041C135.335,23.041 134.489,23.887 134.489,24.914C134.489,24.985 134.493,25.056 134.501,25.126L134.501,30.496L132.643,30.496L132.643,21.442Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M145.84,27.837C145.59,29.48 143.99,30.608 141.942,30.608C139.308,30.608 137.673,28.844 137.673,26.013C137.673,23.173 139.317,21.331 141.864,21.331C144.369,21.331 145.944,23.052 145.944,25.797L145.944,26.434L139.549,26.434L139.549,26.546C139.543,26.615 139.54,26.684 139.54,26.754C139.54,28.047 140.605,29.112 141.898,29.112C141.927,29.112 141.956,29.111 141.985,29.11C142.884,29.195 143.738,28.675 144.076,27.837L145.84,27.837ZM139.558,25.135L144.084,25.135C144.087,25.094 144.088,25.054 144.088,25.014C144.088,23.819 143.105,22.836 141.911,22.836C141.895,22.836 141.879,22.837 141.864,22.837C141.859,22.837 141.855,22.837 141.85,22.837C140.593,22.837 139.558,23.872 139.558,25.129C139.558,25.131 139.558,25.133 139.558,25.135L139.558,25.135Z" style="fill:white;fill-rule:nonzero;"/>
</g>
</g>
</g>
</g>
<g id="_Group_5_" serif:id="_Group_5">
<g>
<path d="M37.826,8.731C37.889,8.726 37.952,8.724 38.015,8.724C39.463,8.724 40.655,9.916 40.655,11.364C40.655,11.475 40.648,11.586 40.634,11.696C40.634,13.602 39.604,14.698 37.826,14.698L35.671,14.698L35.671,8.731L37.826,8.731ZM36.598,13.854L37.723,13.854C37.76,13.856 37.797,13.857 37.835,13.857C38.864,13.857 39.71,13.011 39.71,11.981C39.71,11.89 39.704,11.799 39.69,11.708C39.702,11.623 39.708,11.538 39.708,11.452C39.708,10.42 38.859,9.571 37.827,9.571C37.792,9.571 37.757,9.572 37.723,9.574L36.598,9.574L36.598,13.854Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M41.681,12.444C41.674,12.377 41.671,12.309 41.671,12.241C41.671,11.071 42.634,10.108 43.804,10.108C44.974,10.108 45.937,11.071 45.937,12.241C45.937,12.309 45.934,12.377 45.928,12.444C45.934,12.513 45.938,12.582 45.938,12.651C45.938,13.822 44.975,14.785 43.804,14.785C42.634,14.785 41.671,13.822 41.671,12.651C41.671,12.582 41.674,12.513 41.681,12.444ZM45.014,12.444C45.014,11.468 44.575,10.897 43.806,10.897C43.033,10.897 42.599,11.468 42.599,12.444C42.599,13.428 43.033,13.995 43.806,13.995C44.575,13.995 45.014,13.424 45.014,12.444L45.014,12.444Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M51.573,14.698L50.651,14.698L49.721,11.381L49.65,11.381L48.724,14.698L47.811,14.698L46.569,10.195L47.471,10.195L48.277,13.631L48.344,13.631L49.27,10.195L50.122,10.195L51.048,13.631L51.118,13.631L51.921,10.195L52.81,10.195L51.573,14.698Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M53.854,10.195L54.709,10.195L54.709,10.91L54.775,10.91C55.006,10.385 55.547,10.061 56.119,10.108C56.156,10.105 56.192,10.104 56.229,10.104C57.032,10.104 57.694,10.765 57.694,11.568C57.694,11.64 57.688,11.712 57.678,11.783L57.678,14.698L56.789,14.698L56.789,12.006C56.789,11.282 56.475,10.922 55.817,10.922C55.801,10.922 55.785,10.921 55.769,10.921C55.203,10.921 54.736,11.388 54.736,11.954C54.736,11.991 54.738,12.027 54.742,12.063L54.742,14.698L53.854,14.698L53.854,10.195Z" style="fill:white;fill-rule:nonzero;"/>
<rect x="59.094" y="8.437" width="0.889" height="6.261" style="fill:white;fill-rule:nonzero;"/>
<path d="M61.218,12.444C61.211,12.377 61.208,12.309 61.208,12.241C61.208,11.071 62.171,10.108 63.341,10.108C64.512,10.108 65.475,11.071 65.475,12.241C65.475,12.309 65.471,12.377 65.465,12.444C65.472,12.513 65.475,12.582 65.475,12.651C65.475,13.822 64.512,14.785 63.341,14.785C62.171,14.785 61.208,13.822 61.208,12.651C61.208,12.582 61.211,12.513 61.218,12.444ZM64.551,12.444C64.551,11.468 64.112,10.897 63.343,10.897C62.57,10.897 62.136,11.468 62.136,12.444C62.136,13.428 62.57,13.995 63.343,13.995C64.112,13.995 64.551,13.424 64.551,12.444L64.551,12.444Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M66.4,13.424C66.4,12.614 67.004,12.146 68.075,12.08L69.295,12.01L69.295,11.621C69.295,11.146 68.98,10.877 68.373,10.877C67.877,10.877 67.533,11.059 67.435,11.377L66.574,11.377C66.665,10.604 67.393,10.108 68.414,10.108C69.543,10.108 70.18,10.67 70.18,11.621L70.18,14.698L69.324,14.698L69.324,14.065L69.254,14.065C68.964,14.526 68.446,14.797 67.901,14.772C67.855,14.777 67.808,14.779 67.761,14.779C67.016,14.779 66.403,14.169 66.4,13.424ZM69.295,13.04L69.295,12.663L68.195,12.733C67.575,12.775 67.294,12.986 67.294,13.383C67.294,13.788 67.646,14.024 68.129,14.024C68.164,14.028 68.2,14.029 68.236,14.029C68.791,14.029 69.257,13.594 69.295,13.04Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M71.348,12.444C71.348,11.021 72.079,10.12 73.217,10.12C73.79,10.094 74.33,10.402 74.598,10.91L74.664,10.91L74.664,8.437L75.553,8.437L75.553,14.698L74.701,14.698L74.701,13.986L74.631,13.986C74.343,14.49 73.797,14.793 73.217,14.772C72.071,14.772 71.348,13.871 71.348,12.444ZM72.266,12.444C72.266,13.399 72.716,13.974 73.469,13.974C74.218,13.974 74.681,13.391 74.681,12.448C74.681,11.51 74.213,10.918 73.469,10.918C72.721,10.918 72.266,11.497 72.266,12.444L72.266,12.444Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M79.23,12.444C79.223,12.377 79.22,12.309 79.22,12.241C79.22,11.071 80.183,10.108 81.353,10.108C82.524,10.108 83.487,11.071 83.487,12.241C83.487,12.309 83.484,12.377 83.477,12.444C83.484,12.513 83.487,12.582 83.487,12.651C83.487,13.821 82.524,14.785 81.353,14.785C80.183,14.785 79.219,13.821 79.219,12.651C79.219,12.582 79.223,12.513 79.23,12.444ZM82.563,12.444C82.563,11.468 82.124,10.897 81.355,10.897C80.582,10.897 80.148,11.468 80.148,12.444C80.148,13.428 80.582,13.995 81.355,13.995C82.124,13.995 82.563,13.424 82.563,12.444L82.563,12.444Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M84.669,10.195L85.525,10.195L85.525,10.91L85.591,10.91C85.822,10.385 86.363,10.061 86.935,10.108C86.972,10.105 87.008,10.104 87.045,10.104C87.848,10.104 88.509,10.765 88.509,11.568C88.509,11.64 88.504,11.712 88.494,11.783L88.494,14.698L87.605,14.698L87.605,12.006C87.605,11.282 87.291,10.922 86.633,10.922C86.617,10.922 86.601,10.921 86.585,10.921C86.019,10.921 85.552,11.388 85.552,11.954C85.552,11.991 85.554,12.027 85.558,12.063L85.558,14.698L84.669,14.698L84.669,10.195Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M93.515,9.074L93.515,10.215L94.491,10.215L94.491,10.964L93.515,10.964L93.515,13.279C93.515,13.751 93.709,13.958 94.152,13.958C94.265,13.957 94.378,13.95 94.491,13.937L94.491,14.677C94.331,14.706 94.169,14.721 94.007,14.723C93.019,14.723 92.626,14.375 92.626,13.507L92.626,10.964L91.911,10.964L91.911,10.215L92.626,10.215L92.626,9.074L93.515,9.074Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M95.705,8.437L96.585,8.437L96.585,10.918L96.656,10.918C96.897,10.388 97.448,10.065 98.029,10.112C98.056,10.11 98.082,10.11 98.109,10.11C98.923,10.11 99.593,10.779 99.593,11.593C99.593,11.659 99.588,11.725 99.58,11.791L99.58,14.698L98.69,14.698L98.69,12.01C98.69,11.291 98.355,10.926 97.727,10.926C97.699,10.924 97.67,10.923 97.641,10.923C97.064,10.923 96.589,11.398 96.589,11.975C96.589,12.006 96.591,12.037 96.593,12.068L96.593,14.698L95.705,14.698L95.705,8.437Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M104.761,13.482C104.513,14.328 103.687,14.88 102.81,14.785C102.792,14.785 102.774,14.785 102.756,14.785C101.634,14.785 100.711,13.862 100.711,12.74C100.711,12.647 100.717,12.553 100.73,12.46C100.718,12.369 100.712,12.277 100.712,12.185C100.712,11.045 101.649,10.108 102.788,10.108C102.794,10.108 102.8,10.108 102.806,10.108C104.059,10.108 104.815,10.964 104.815,12.378L104.815,12.688L101.635,12.688L101.635,12.738C101.632,12.771 101.631,12.804 101.631,12.838C101.631,13.491 102.168,14.028 102.821,14.028C102.826,14.028 102.83,14.028 102.834,14.028C103.268,14.08 103.693,13.864 103.906,13.482L104.761,13.482ZM101.635,12.031L103.91,12.031C103.912,12.004 103.913,11.977 103.913,11.95C103.913,11.354 103.422,10.864 102.826,10.864C102.818,10.864 102.81,10.864 102.801,10.864C102.796,10.864 102.792,10.864 102.787,10.864C102.155,10.864 101.635,11.384 101.635,12.016C101.635,12.021 101.635,12.026 101.635,12.031L101.635,12.031Z" style="fill:white;fill-rule:nonzero;"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

23
.github/assets/badge-web.svg vendored Normal file
View File

@@ -0,0 +1,23 @@
<?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 135 40" 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;">
<path d="M130.2,40L4.73,40C2.137,39.995 0.005,37.863 -0,35.27L0,4.73C-0,2.135 2.135,0 4.73,0L130.2,0C132.807,-0 134.962,2.123 135,4.73L135,35.27C134.957,37.875 132.805,39.995 130.2,40Z" style="fill:rgb(166,166,166);fill-rule:nonzero;"/>
<path d="M134,35.27C134,37.371 132.271,39.1 130.17,39.1L4.73,39.1C2.627,39.1 0.895,37.373 0.89,35.27L0.89,4.72C0.895,2.617 2.627,0.89 4.73,0.89L130.2,0.89C132.29,0.906 134,2.63 134,4.72L134,35.27Z" style="fill-rule:nonzero;"/>
<path d="M40.56,25.26L41.82,22.08L45.45,22.08L43.73,17.26L45.88,11.81L52.05,28.19L47.5,28.19L46.5,25.26L40.56,25.26Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M36.68,28.19L43.29,11.81L38.91,11.81L34.39,22.4L31.18,11.81L27.81,11.81L24.36,22.4L21.93,17.57L19.73,24.35L22,28.19L26.31,28.19L29.43,18.7L32.43,28.19L36.68,28.19Z" style="fill:rgb(90,15,200);fill-rule:nonzero;"/>
<path d="M12.7,22.57L15.4,22.57C16.136,22.582 16.871,22.488 17.58,22.29L18.28,20.14L20.23,14.14C20.08,13.902 19.91,13.678 19.72,13.47C18.57,12.302 16.965,11.695 15.33,11.81L8.55,11.81L8.55,28.19L12.7,28.19L12.7,22.57ZM16.27,15.57C16.674,15.994 16.888,16.565 16.86,17.15C16.892,17.723 16.706,18.288 16.34,18.73C15.77,19.23 15.014,19.466 14.26,19.38L12.7,19.38L12.7,15L14.27,15C14.987,14.922 15.706,15.13 16.27,15.58L16.27,15.57Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M61.25,14.45L61.25,8.74L61.91,8.74L61.91,13.85L64.43,13.85L64.43,14.45L61.25,14.45Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M67.94,14.45L67.81,13.84C67.645,14.069 67.43,14.258 67.18,14.39C66.925,14.489 66.653,14.536 66.38,14.53C66.017,14.55 65.66,14.432 65.38,14.2C65.131,13.957 64.999,13.617 65.02,13.27C65.02,12.4 65.71,11.95 67.1,11.91L67.82,11.91L67.82,11.64C67.842,11.376 67.768,11.113 67.61,10.9C67.421,10.725 67.167,10.637 66.91,10.66C66.489,10.68 66.076,10.789 65.7,10.98L65.5,10.49C65.719,10.366 65.955,10.275 66.2,10.22C66.444,10.152 66.697,10.119 66.95,10.12C67.359,10.087 67.766,10.208 68.09,10.46C68.358,10.757 68.492,11.151 68.46,11.55L68.46,14.47L67.94,14.45ZM66.47,14C66.836,14.03 67.198,13.906 67.47,13.66C67.712,13.413 67.839,13.075 67.82,12.73L67.82,12.35L67.17,12.35C66.783,12.339 66.399,12.422 66.05,12.59C65.827,12.738 65.697,12.993 65.71,13.26C65.696,13.459 65.773,13.654 65.92,13.79C66.075,13.918 66.269,13.992 66.47,14Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M70.36,10.17L70.36,13C70.337,13.281 70.423,13.56 70.6,13.78C70.801,13.964 71.068,14.058 71.34,14.04C71.712,14.077 72.081,13.94 72.34,13.67C72.576,13.317 72.685,12.893 72.65,12.47L72.65,10.17L73.3,10.17L73.3,14.45L72.76,14.45L72.67,13.88C72.539,14.092 72.348,14.259 72.12,14.36C71.866,14.476 71.589,14.534 71.31,14.53C70.887,14.564 70.467,14.431 70.14,14.16C69.841,13.85 69.682,13.43 69.7,13L69.7,10.2L70.36,10.17Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M77.59,14.45L77.59,11.68C77.613,11.399 77.527,11.12 77.35,10.9C77.149,10.716 76.882,10.622 76.61,10.64C76.238,10.603 75.869,10.74 75.61,11.01C75.37,11.361 75.26,11.786 75.3,12.21L75.3,14.45L74.65,14.45L74.65,10.17L75.18,10.17L75.28,10.76C75.41,10.545 75.602,10.374 75.83,10.27C76.079,10.148 76.353,10.087 76.63,10.09C77.055,10.059 77.475,10.195 77.8,10.47C78.09,10.795 78.232,11.226 78.19,11.66L78.19,14.45L77.59,14.45Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M81.28,14.53C80.746,14.573 80.219,14.379 79.84,14C79.474,13.543 79.292,12.965 79.33,12.38C79.294,11.782 79.479,11.191 79.85,10.72C80.231,10.314 80.774,10.098 81.33,10.13C81.535,10.134 81.739,10.157 81.94,10.2C82.11,10.23 82.275,10.284 82.43,10.36L82.23,10.91C82.077,10.848 81.92,10.798 81.76,10.76C81.612,10.731 81.461,10.714 81.31,10.71C80.44,10.71 80,11.26 80,12.37C79.974,12.797 80.087,13.221 80.32,13.58C80.56,13.881 80.937,14.04 81.32,14C81.698,13.998 82.072,13.92 82.42,13.77L82.42,14.35C82.061,14.504 81.669,14.566 81.28,14.53Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M86.31,14.45L86.31,11.68C86.333,11.399 86.247,11.12 86.07,10.9C85.869,10.716 85.602,10.622 85.33,10.64C84.958,10.608 84.591,10.744 84.33,11.01C84.093,11.363 83.983,11.786 84.02,12.21L84.02,14.45L83.37,14.45L83.37,8.37L84,8.37L84,10.21C84.013,10.393 84.013,10.577 84,10.76C84.135,10.556 84.321,10.39 84.54,10.28C84.792,10.157 85.07,10.096 85.35,10.1C85.776,10.068 86.199,10.2 86.53,10.47C86.846,10.783 87.017,11.215 87,11.66L87,14.45L86.31,14.45Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M93,14.45L92.87,13.84C92.704,14.073 92.485,14.261 92.23,14.39C91.979,14.49 91.71,14.537 91.44,14.53C91.077,14.552 90.719,14.434 90.44,14.2C90.191,13.957 90.059,13.617 90.08,13.27C90.08,12.4 90.77,11.95 92.15,11.91L92.88,11.91L92.88,11.64C92.909,11.374 92.829,11.107 92.66,10.9C92.476,10.723 92.224,10.635 91.97,10.66C91.548,10.678 91.135,10.787 90.76,10.98L90.56,10.49C90.776,10.368 91.008,10.277 91.25,10.22C91.498,10.152 91.753,10.119 92.01,10.12C92.416,10.084 92.821,10.206 93.14,10.46C93.413,10.754 93.547,11.151 93.51,11.55L93.51,14.47L93,14.45ZM91.54,14C91.889,14.016 92.23,13.894 92.49,13.66C92.73,13.412 92.853,13.074 92.83,12.73L92.83,12.35L92.19,12.35C91.803,12.339 91.419,12.422 91.07,12.59C90.844,12.735 90.713,12.992 90.73,13.26C90.716,13.459 90.793,13.654 90.94,13.79C91.108,13.931 91.321,14.005 91.54,14Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M97.58,13.28C97.596,13.646 97.426,13.996 97.13,14.21C96.757,14.444 96.32,14.556 95.88,14.53C95.424,14.554 94.97,14.461 94.56,14.26L94.56,13.66C94.974,13.876 95.433,13.993 95.9,14C96.171,14.015 96.44,13.956 96.68,13.83C96.858,13.732 96.966,13.543 96.96,13.34C96.957,13.171 96.876,13.012 96.74,12.91C96.477,12.731 96.191,12.59 95.89,12.49C95.581,12.391 95.283,12.26 95,12.1C94.851,12.006 94.724,11.879 94.63,11.73C94.542,11.575 94.497,11.399 94.5,11.22C94.516,10.88 94.705,10.57 95,10.4C95.346,10.173 95.757,10.064 96.17,10.09C96.634,10.094 97.093,10.189 97.52,10.37L97.29,10.9C96.921,10.736 96.524,10.647 96.12,10.64C95.881,10.621 95.642,10.669 95.43,10.78C95.282,10.858 95.189,11.013 95.19,11.18C95.188,11.284 95.219,11.386 95.28,11.47C95.355,11.566 95.451,11.645 95.56,11.7C95.802,11.825 96.052,11.932 96.31,12.02C96.68,12.133 97.021,12.324 97.31,12.58C97.487,12.769 97.584,13.021 97.58,13.28Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M71,28.45L69.23,28.45L67.53,22.45C67.46,22.19 67.37,21.82 67.26,21.33C67.15,20.84 67.08,20.5 67.06,20.33C67.06,20.63 66.94,21 66.84,21.47C66.74,21.94 66.65,22.28 66.58,22.47L64.93,28.4L63.15,28.4L61.86,23.4L60.54,18.4L62.21,18.4L63.64,24.22C63.86,25.16 64.02,25.98 64.12,26.7C64.12,26.31 64.25,25.88 64.35,25.4C64.45,24.92 64.54,24.53 64.62,24.24L66.25,18.4L67.87,18.4L69.53,24.26C69.733,25.084 69.89,25.919 70,26.76C70.105,25.92 70.269,25.088 70.49,24.27L72,18.46L73.66,18.46L71,28.45Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M78.15,28.59C77.134,28.657 76.135,28.294 75.4,27.59C74.701,26.819 74.34,25.799 74.4,24.76C74.341,23.707 74.668,22.669 75.32,21.84C75.965,21.125 76.898,20.735 77.86,20.78C78.74,20.735 79.598,21.066 80.22,21.69C80.83,22.378 81.143,23.282 81.09,24.2L81.09,25L76.09,25C76.077,25.621 76.291,26.225 76.69,26.7C77.11,27.111 77.684,27.325 78.27,27.29C78.69,27.294 79.11,27.251 79.52,27.16C79.947,27.065 80.363,26.924 80.76,26.74L80.76,28.05C80.382,28.231 79.982,28.362 79.57,28.44C79.103,28.538 78.627,28.588 78.15,28.59ZM77.86,22C77.399,21.978 76.95,22.157 76.63,22.49C76.296,22.886 76.102,23.382 76.08,23.9L79.51,23.9C79.537,23.388 79.377,22.883 79.06,22.48C78.75,22.152 78.311,21.976 77.86,22Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M86.78,20.75C87.632,20.705 88.454,21.079 88.98,21.75C89.557,22.595 89.835,23.609 89.77,24.63C89.835,25.656 89.553,26.674 88.97,27.52C88.417,28.176 87.602,28.555 86.745,28.555C85.888,28.555 85.073,28.176 84.52,27.52L84.42,27.52L84.12,28.41L82.92,28.41L82.92,17.82L84.52,17.82L84.52,21.88L84.58,21.88C85.073,21.154 85.903,20.728 86.78,20.75ZM86.36,22.07C85.829,22.023 85.307,22.235 84.96,22.64C84.627,23.219 84.474,23.884 84.52,24.55L84.52,24.66C84.467,25.355 84.62,26.051 84.96,26.66C85.301,27.094 85.84,27.327 86.39,27.28C86.913,27.301 87.409,27.042 87.69,26.6C88.026,25.99 88.179,25.295 88.13,24.6C88.13,22.93 87.54,22.07 86.36,22.07Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M101.65,28.45L100.65,25.68L96.83,25.68L95.83,28.45L94.13,28.45L97.87,18.45L99.65,18.45L103.38,28.45L101.65,28.45ZM100.22,24.27L99.28,21.55C99.21,21.37 99.12,21.08 99,20.69C98.88,20.3 98.8,20.01 98.75,19.83C98.612,20.452 98.431,21.063 98.21,21.66L97.31,24.27L100.22,24.27Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M108.37,28.59C107.509,28.641 106.675,28.267 106.14,27.59L106,27.59C106.07,28.22 106.1,28.59 106.1,28.75L106.1,31.85L104.49,31.85L104.49,20.89L105.79,20.89C105.79,21.03 105.9,21.37 106.02,21.89L106.1,21.89C106.592,21.131 107.456,20.691 108.36,20.74C109.211,20.698 110.032,21.071 110.56,21.74C111.137,22.585 111.415,23.599 111.35,24.62C111.409,25.645 111.128,26.661 110.55,27.51C110.04,28.199 109.227,28.601 108.37,28.59ZM108,22.07C107.471,22.022 106.95,22.23 106.6,22.63C106.264,23.163 106.11,23.792 106.16,24.42L106.16,24.66C106.107,25.355 106.26,26.051 106.6,26.66C106.939,27.097 107.479,27.331 108.03,27.28C108.549,27.298 109.04,27.034 109.31,26.59C109.645,25.997 109.802,25.32 109.76,24.64C109.802,23.97 109.646,23.302 109.31,22.72C109.019,22.288 108.52,22.041 108,22.07Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M117.09,28.59C116.229,28.641 115.395,28.267 114.86,27.59L114.77,27.59C114.83,28.22 114.86,28.59 114.86,28.75L114.86,31.85L113.26,31.85L113.26,20.89L114.55,20.89C114.55,21.03 114.67,21.37 114.78,21.89L114.86,21.89C115.354,21.132 116.217,20.693 117.12,20.74C117.972,20.695 118.794,21.069 119.32,21.74C119.897,22.585 120.175,23.599 120.11,24.62C120.169,25.645 119.888,26.661 119.31,27.51C118.791,28.21 117.961,28.614 117.09,28.59ZM116.7,22.07C116.171,22.022 115.65,22.23 115.3,22.63C114.964,23.163 114.81,23.792 114.86,24.42L114.86,24.66C114.807,25.355 114.96,26.051 115.3,26.66C115.641,27.094 116.18,27.327 116.73,27.28C117.249,27.298 117.74,27.034 118.01,26.59C118.345,25.997 118.502,25.32 118.46,24.64C118.505,23.971 118.352,23.303 118.02,22.72C117.723,22.29 117.222,22.043 116.7,22.07Z" style="fill:white;fill-rule:nonzero;"/>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

31
.github/assets/badge-windows-store.svg vendored Normal file
View File

@@ -0,0 +1,31 @@
<?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 864 312" 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="Layer_2">
<rect x="2.3" y="2.3" width="859.5" height="307.5"/>
<path d="M4.5,4.5L859.5,4.5L859.5,307.5L4.5,307.5L4.5,4.5ZM0,312L864,312L864,0L0,0L0,312Z" style="fill:rgb(210,210,210);fill-rule:nonzero;"/>
</g>
<g id="Layer_1">
<rect x="70.5" y="68.6" width="83.8" height="83.8" style="fill:rgb(242,80,34);"/>
<rect x="163" y="68.6" width="83.8" height="83.8" style="fill:rgb(127,186,0);"/>
<rect x="70.5" y="161" width="83.8" height="83.8" style="fill:rgb(0,164,239);"/>
<rect x="163" y="161" width="83.8" height="83.8" style="fill:rgb(255,185,0);"/>
<path d="M408.3,163C408.3,160.5 409.2,158.5 411,156.8C412.8,155.1 414.9,154.3 417.4,154.3C420,154.3 422.2,155.2 423.9,156.9C425.6,158.6 426.5,160.7 426.5,163C426.5,165.4 425.6,167.5 423.8,169.1C422,170.8 419.9,171.6 417.3,171.6C414.7,171.6 412.6,170.8 410.8,169.1C409.2,167.4 408.3,165.4 408.3,163M424.8,244.8L409.9,244.8L409.9,181.3L424.8,181.3L424.8,244.8Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M470.2,233.9C472.4,233.9 474.9,233.4 477.6,232.4C480.3,231.4 482.7,230 485,228.3L485,242.2C482.6,243.6 479.9,244.6 476.9,245.3C473.9,246 470.5,246.4 466.9,246.4C457.6,246.4 450,243.4 444.1,237.5C438.2,231.6 435.3,224 435.3,214.9C435.3,204.7 438.3,196.3 444.3,189.7C450.3,183.1 458.7,179.8 469.7,179.8C472.5,179.8 475.3,180.2 478.2,180.9C481.1,181.6 483.3,182.5 485,183.4L485,197.7C482.7,196 480.3,194.7 477.9,193.8C475.5,192.9 473,192.4 470.5,192.4C464.6,192.4 459.9,194.3 456.2,198.1C452.6,201.9 450.8,207.1 450.8,213.6C450.8,220 452.5,225 456,228.6C459.5,232.2 464.2,233.9 470.2,233.9" style="fill:white;fill-rule:nonzero;"/>
<path d="M527.5,180.3C528.7,180.3 529.8,180.4 530.7,180.5C531.6,180.7 532.5,180.9 533.1,181.1L533.1,196.2C532.3,195.6 531.2,195.1 529.7,194.6C528.2,194.1 526.4,193.8 524.2,193.8C520.5,193.8 517.4,195.3 514.9,198.4C512.4,201.5 511.1,206.2 511.1,212.7L511.1,244.8L496.2,244.8L496.2,181.3L511.1,181.3L511.1,191.3L511.3,191.3C512.7,187.8 514.7,185.1 517.5,183.2C520.3,181.3 523.6,180.3 527.5,180.3" style="fill:white;fill-rule:nonzero;"/>
<path d="M533.9,214C533.9,203.5 536.9,195.2 542.8,189C548.7,182.9 557,179.8 567.5,179.8C577.4,179.8 585.2,182.8 590.8,188.7C596.4,194.6 599.2,202.6 599.2,212.6C599.2,222.9 596.2,231.1 590.3,237.2C584.4,243.3 576.3,246.3 566.1,246.3C556.3,246.3 548.4,243.4 542.7,237.6C536.8,231.9 533.9,224 533.9,214M549.5,213.5C549.5,220.1 551,225.2 554,228.7C557,232.2 561.3,233.9 566.9,233.9C572.3,233.9 576.5,232.2 579.3,228.7C582.1,225.2 583.6,220 583.6,213.1C583.6,206.3 582.1,201.1 579.2,197.6C576.3,194.1 572.1,192.4 566.8,192.4C561.3,192.4 557.1,194.2 554,197.9C551,201.6 549.5,206.7 549.5,213.5" style="fill:white;fill-rule:nonzero;"/>
<path d="M621.4,198C621.4,200.1 622.1,201.8 623.4,203C624.8,204.2 627.8,205.7 632.4,207.6C638.4,210 642.6,212.7 645,215.7C647.4,218.7 648.6,222.3 648.6,226.5C648.6,232.5 646.3,237.3 641.7,240.9C637.1,244.5 630.9,246.3 623.1,246.3C620.5,246.3 617.6,246 614.4,245.3C611.2,244.7 608.5,243.8 606.3,242.9L606.3,228.2C609,230.1 611.9,231.6 615.1,232.7C618.2,233.8 621.1,234.4 623.6,234.4C627,234.4 629.5,233.9 631.1,233C632.7,232.1 633.5,230.5 633.5,228.2C633.5,226.1 632.7,224.4 631,223C629.3,221.6 626.2,219.9 621.5,218.1C616,215.8 612.1,213.2 609.8,210.3C607.5,207.4 606.3,203.7 606.3,199.3C606.3,193.6 608.6,188.9 613.1,185.2C617.6,181.5 623.5,179.7 630.8,179.7C633,179.7 635.5,179.9 638.3,180.4C641.1,180.9 643.4,181.5 645.2,182.3L645.2,196.5C643.2,195.2 640.9,194.1 638.3,193.1C635.7,192.2 633,191.7 630.5,191.7C627.7,191.7 625.4,192.3 623.9,193.4C622.2,194.5 621.4,196.1 621.4,198" style="fill:white;fill-rule:nonzero;"/>
<path d="M655.2,214C655.2,203.5 658.2,195.2 664.1,189C670,182.9 678.3,179.8 688.8,179.8C698.7,179.8 706.5,182.8 712.1,188.7C717.7,194.6 720.5,202.6 720.5,212.6C720.5,222.9 717.5,231.1 711.6,237.2C705.7,243.3 697.6,246.3 687.4,246.3C677.6,246.3 669.7,243.4 664,237.6C658.1,231.9 655.2,224 655.2,214M670.7,213.5C670.7,220.1 672.2,225.2 675.2,228.7C678.2,232.2 682.5,233.9 688.1,233.9C693.5,233.9 697.7,232.2 700.5,228.7C703.3,225.2 704.8,220 704.8,213.1C704.8,206.3 703.3,201.1 700.4,197.6C697.5,194.1 693.3,192.4 688,192.4C682.5,192.4 678.3,194.2 675.2,197.9C672.2,201.6 670.7,206.7 670.7,213.5" style="fill:white;fill-rule:nonzero;"/>
<path d="M769.8,193.6L769.8,226.2C769.8,233 771.4,238 774.5,241.4C777.6,244.8 782.4,246.4 788.7,246.4C790.8,246.4 793,246.2 795.2,245.7C797.4,245.2 799,244.8 799.9,244.2L799.9,231.8C799,232.4 797.9,232.9 796.7,233.3C795.5,233.7 794.4,233.9 793.6,233.9C790.6,233.9 788.3,233.1 786.9,231.5C785.5,229.9 784.8,227.1 784.8,223.2L784.8,193.5L800,193.5L800,181.3L784.9,181.3L784.9,162.5L769.9,167.1L769.9,181.4L747.6,181.4L747.6,173.7C747.6,169.9 748.4,167 750.1,165C751.8,163 754.2,162.1 757.3,162.1C758.9,162.1 760.3,162.3 761.6,162.7C762.8,163.1 763.7,163.5 764.2,163.8L764.2,150.9C763.1,150.5 761.9,150.3 760.5,150.1C759.1,149.9 757.5,149.8 755.7,149.8C748.9,149.8 743.3,151.9 739,156.2C734.7,160.5 732.5,165.9 732.5,172.6L732.5,181.4L721.9,181.4L721.9,193.6L732.5,193.6L732.5,244.9L747.6,244.9L747.6,193.6L769.8,193.6Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M395.5,156.2L395.5,244.8L380.1,244.8L380.1,175.4L379.9,175.4L352.4,244.8L342.2,244.8L314,175.4L313.8,175.4L313.8,244.8L299.6,244.8L299.6,156.2L321.6,156.2L347.1,221.9L347.5,221.9L374.4,156.2L395.5,156.2Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M340,117.9C334.7,120.9 328.7,122.4 322,122.4C316.9,122.4 312.4,121.3 308.4,119C304.5,116.8 301.4,113.6 299.3,109.5C297.2,105.4 296.1,100.8 296.1,95.7C296.1,90.3 297.3,85.4 299.6,81.2C301.9,77 305.2,73.6 309.5,71.2C313.8,68.8 318.6,67.6 324,67.6C326.7,67.6 329.4,67.9 332.1,68.4C334.8,68.9 337,69.6 338.7,70.4L338.7,78.4C334.7,75.7 329.6,74.4 323.4,74.4C319.7,74.4 316.4,75.3 313.4,77.1C310.4,78.9 308.1,81.4 306.5,84.5C304.9,87.7 304.1,91.3 304.1,95.3C304.1,101.6 305.8,106.6 309.1,110.3C312.4,114 317,115.8 322.8,115.8C326.5,115.8 329.8,115.1 332.6,113.6L332.6,99.9L321.3,99.9L321.3,93.3L340,93.3L340,117.9Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M383.1,104.6L356.9,104.6C357,108.4 358.2,111.3 360.2,113.4C362.3,115.4 365,116.5 368.4,116.5C372.9,116.5 376.8,115.2 380.2,112.6L380.2,119C378.7,120 376.8,120.9 374.5,121.5C372.2,122.1 369.8,122.4 367.4,122.4C361.7,122.4 357.2,120.7 354.1,117.3C351,113.9 349.4,109.1 349.4,102.9C349.4,99.1 350.2,95.6 351.7,92.6C353.2,89.5 355.4,87.1 358.1,85.4C360.8,83.7 363.9,82.8 367.2,82.8C372.1,82.8 376,84.4 378.8,87.6C381.6,90.8 383,95.2 383,100.9L383,104.6L383.1,104.6ZM375.8,98.9C375.8,95.6 375,93.1 373.5,91.4C372,89.7 369.9,88.8 367.1,88.8C364.6,88.8 362.4,89.7 360.5,91.5C358.6,93.3 357.5,95.8 356.9,98.9L375.8,98.9Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M411.5,121.1C410.8,121.5 409.9,121.8 408.6,122C407.4,122.2 406.3,122.3 405.3,122.3C398.3,122.3 394.8,118.4 394.8,110.7L394.8,89.7L388.4,89.7L388.4,83.7L394.8,83.7L394.8,74.7L402.1,72.4L402.1,83.6L411.4,83.6L411.4,89.6L402.1,89.6L402.1,109.4C402.1,111.9 402.5,113.7 403.3,114.7C404.1,115.7 405.5,116.2 407.6,116.2C409,116.2 410.3,115.8 411.4,115L411.4,121.1L411.5,121.1Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M447.7,70.3C447.7,71.5 447.3,72.5 446.4,73.4C445.5,74.3 444.4,74.7 443.1,74.7C441.8,74.7 440.7,74.3 439.8,73.4C438.9,72.6 438.5,71.5 438.5,70.2C438.5,68.9 439,67.8 439.9,67C440.8,66.2 441.9,65.7 443.1,65.7C444.3,65.7 445.4,66.1 446.3,67C447.3,68 447.7,69 447.7,70.3M446.7,121.5L439.4,121.5L439.4,83.7L446.7,83.7L446.7,121.5Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M478.4,121.1C477.7,121.5 476.8,121.8 475.5,122C474.3,122.2 473.2,122.3 472.2,122.3C465.2,122.3 461.7,118.4 461.7,110.7L461.7,89.7L455.3,89.7L455.3,83.7L461.7,83.7L461.7,74.7L469,72.4L469,83.6L478.3,83.6L478.3,89.6L469,89.6L469,109.4C469,111.9 469.4,113.7 470.2,114.7C471,115.7 472.4,116.2 474.5,116.2C475.9,116.2 477.2,115.8 478.3,115L478.3,121.1L478.4,121.1Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M526.2,71.7C525,71.1 523.7,70.7 522.2,70.7C518.1,70.7 516.1,73.2 516.1,78.2L516.1,83.6L524.7,83.6L524.7,89.6L516.1,89.6L516.1,121.4L508.8,121.4L508.8,89.7L502.4,89.7L502.4,83.7L508.8,83.7L508.8,77.9C508.8,73.9 509.9,70.7 512.2,68.3C514.5,65.9 517.6,64.7 521.6,64.7C523.6,64.7 525.1,64.9 526.3,65.4L526.3,71.7L526.2,71.7Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M553.1,91C552.7,90.7 551.9,90.4 550.9,90.1C549.9,89.9 549,89.7 548.3,89.7C545.7,89.7 543.6,90.9 542,93.2C540.4,95.5 539.6,98.5 539.6,102.2L539.6,121.5L532.3,121.5L532.3,83.7L539.6,83.7L539.6,91.3L539.8,91.3C540.6,88.7 541.9,86.7 543.6,85.2C545.3,83.7 547.3,83 549.5,83C551,83 552.2,83.2 553,83.5L553,91L553.1,91Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M594.8,102.4C594.8,108.5 593,113.4 589.5,117C586,120.6 581.3,122.4 575.5,122.4C569.8,122.4 565.2,120.6 561.8,117.1C558.4,113.6 556.7,108.9 556.7,103C556.7,96.7 558.5,91.8 562,88.2C565.5,84.6 570.3,82.8 576.4,82.8C582.1,82.8 586.6,84.5 589.8,88C593.2,91.5 594.8,96.3 594.8,102.4M587.3,102.6C587.3,98.1 586.3,94.7 584.4,92.4C582.4,90.1 579.7,88.9 576.1,88.9C572.5,88.9 569.6,90.1 567.5,92.6C565.4,95 564.4,98.5 564.4,102.9C564.4,107.2 565.4,110.5 567.5,112.9C569.6,115.3 572.4,116.5 576.1,116.5C579.8,116.5 582.6,115.3 584.5,113C586.3,110.5 587.3,107.1 587.3,102.6" style="fill:white;fill-rule:nonzero;"/>
<path d="M660.1,121.5L652.8,121.5L652.8,100C652.8,96.1 652.2,93.3 651,91.5C649.8,89.7 647.7,88.9 644.8,88.9C642.4,88.9 640.3,90 638.7,92.2C637,94.4 636.2,97.1 636.2,100.1L636.2,121.5L628.9,121.5L628.9,99.3C628.9,92.4 626.2,88.9 620.9,88.9C618.4,88.9 616.3,90 614.7,92.1C613.1,94.2 612.3,96.9 612.3,100.2L612.3,121.6L605,121.6L605,83.7L612.3,83.7L612.3,89.6L612.4,89.6C615.1,85 619.1,82.8 624.2,82.8C626.6,82.8 628.8,83.5 630.8,84.8C632.7,86.1 634.1,88 635,90.4C636.5,87.8 638.2,85.9 640.3,84.6C642.4,83.4 644.8,82.7 647.6,82.7C655.9,82.7 660,87.8 660,98.1L660,121.5L660.1,121.5Z" style="fill:white;fill-rule:nonzero;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

46
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,46 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
groups:
github-actions:
patterns:
- "*"
- package-ecosystem: "pub"
directory: "/app"
schedule:
interval: "monthly"
groups:
pub:
patterns:
- "*"
- package-ecosystem: "docker"
directory: "/supabase/functions/_cmd"
schedule:
interval: "monthly"
groups:
docker:
patterns:
- "*"
- package-ecosystem: "npm"
directory: "/landing"
schedule:
interval: "monthly"
groups:
npm-landing:
patterns:
- "*"
- package-ecosystem: "npm"
directory: "/supabase/email-templates"
schedule:
interval: "monthly"
groups:
npm-email-templates:
patterns:
- "*"

View File

@@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -36,13 +36,13 @@ jobs:
fi
- name: Setup QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
@@ -50,7 +50,7 @@ jobs:
- name: Build and Push Docker Image
id: docker_build
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
push: true
context: ./supabase/functions
@@ -69,7 +69,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -92,6 +92,8 @@ jobs:
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-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
supabase functions deploy stripe-create-checkout-session-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
supabase functions deploy stripe-webhooks-v1 --no-verify-jwt --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
@@ -112,6 +114,8 @@ jobs:
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-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
supabase functions deploy stripe-create-checkout-session-v1 --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
supabase functions deploy stripe-webhooks-v1 --no-verify-jwt --project-ref $PROJECT_ID --import-map supabase/functions/import_map.json
@@ -130,7 +134,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -162,7 +166,7 @@ jobs:
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.7.12'
flutter-version: '3.13.7'
channel: 'stable'
cache: true
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'
@@ -200,12 +204,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.7.12'
flutter-version: '3.13.7'
channel: 'stable'
cache: true
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'
@@ -218,7 +222,7 @@ jobs:
- name: Build
run: |
flutter config --enable-macos-desktop
flutter build macos --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 }}
FLUTTER_XCODE_CODE_SIGN_IDENTITY="" FLUTTER_XCODE_CODE_SIGNING_REQUIRED=NO flutter build macos --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: |
@@ -253,18 +257,21 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
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.7.12'
flutter-version: '3.13.7'
channel: 'stable'
cache: true
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'
@@ -281,6 +288,9 @@ jobs:
- name: Package
run: |
cp linux/flatpak/app.feeddeck.feeddeck.desktop build/linux/x64/release/bundle/
cp linux/flatpak/app.feeddeck.feeddeck.metainfo.xml build/linux/x64/release/bundle/
cp linux/flatpak/app.feeddeck.feeddeck.svg build/linux/x64/release/bundle/
cd build
cp -r linux/x64/release/bundle/ feeddeck-linux-x86_64
tar -czf feeddeck-linux-x86_64.tar.gz feeddeck-linux-x86_64
@@ -314,12 +324,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.7.12'
flutter-version: '3.13.7'
channel: 'stable'
cache: true
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'

View File

@@ -37,7 +37,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v3

View File

@@ -57,10 +57,10 @@ check your installed version:
```sh
$ flutter --version
Flutter 3.7.12 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 4d9e56e694 (8 weeks ago) • 2023-04-17 21:47:46 -0400
Engine • revision 1a65d409c7
Tools • Dart 2.19.6 • DevTools 2.20.1
Flutter 3.13.7 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 2f708eb839 (4 days ago) • 2023-10-09 09:58:08 -0500
Engine • revision a794cf2681
Tools • Dart 3.1.3 • DevTools 2.25.0
$ deno --version
@@ -90,9 +90,10 @@ provide the following arguments: `--dart-define SUPABASE_URL=<SUPABASE_URL>`,
"version": "0.2.0",
"configurations": [
{
"name": "FeedDeck (Chrome)",
"request": "launch",
"name": "Local - Chrome",
"type": "dart",
"request": "launch",
"program": "app/lib/main.dart",
"args": [
"-d",
"chrome",
@@ -110,28 +111,10 @@ provide the following arguments: `--dart-define SUPABASE_URL=<SUPABASE_URL>`,
]
},
{
"name": "FeedDeck (Web Server)",
"request": "launch",
"name": "Local - iOS Simulator",
"type": "dart",
"args": [
"-d",
"web-server",
"--web-port",
"3000",
"--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": "FeedDeck (iOS)",
"request": "launch",
"type": "dart",
"program": "app/lib/main.dart",
"args": [
"-d",
"iPhone 14 Pro Max",
@@ -146,9 +129,10 @@ provide the following arguments: `--dart-define SUPABASE_URL=<SUPABASE_URL>`,
]
},
{
"name": "FeedDeck (macOS)",
"request": "launch",
"name": "Local - macOS",
"type": "dart",
"request": "launch",
"program": "app/lib/main.dart",
"args": [
"-d",
"macOS",
@@ -326,6 +310,8 @@ supabase functions deploy delete-user-v1 --project-ref <PROJECT-ID> --import-map
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-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
supabase functions deploy stripe-create-checkout-session-v1 --project-ref <PROJECT-ID> --import-map supabase/functions/import_map.json
supabase functions deploy stripe-webhooks-v1 --no-verify-jwt --project-ref <PROJECT-ID> --import-map supabase/functions/import_map.json
@@ -411,26 +397,45 @@ Android, macOS, Windows and Linux if you do not want to use the official ones.
2. Update the `version` key and the `msix_config.msix_version` key in the
`pubspec.yaml` file.
3. Delete the `build/` and `.dart_tool/` directories via the `flutter clean`
3. Add the new release to the `releases` section in
[`app.feeddeck.feeddeck.metainfo.xml`](app/linux/flatpak/app.feeddeck.feeddeck.metainfo.xml).
4. Delete the `build/` and `.dart_tool/` directories via the `flutter clean`
command.
4. Build the app for Web by running `flutter build web`. The build can be found
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.
5. Build the app for Linux by running `flutter build linux --release`.
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.
6. Build the app for macOS by running `flutter build macos --release`. Open
```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
[github.com/flathub/app.feeddeck.feeddeck](https://github.com/flathub/app.feeddeck.feeddeck)
with the new release.
7. Build the app for macOS by running `flutter build macos --release`. Open
Xcode and select **Product** > **Archive** to create and open the archive.
After that the **Validate App** and **Distribute App** options can be used to
upload the build to
[https://appstoreconnect.apple.com](https://appstoreconnect.apple.com).
7. Build the app for Windows by running `flutter build windows --release`. and
8. Build the app for Windows by running `flutter build windows --release`. and
`flutter pub run msix:create --output-path build --output-name feeddeck`. The
build can be found at `app/build/feeddeck.msix` and must be uploaded to
[https://partner.microsoft.com/en-us/dashboard/products/9NPHPGRRCT5H/overview](https://partner.microsoft.com/en-us/dashboard/products/9NPHPGRRCT5H/overview).
8. Create a file `app/android/key.properties` with the following content:
9. Create a file `app/android/key.properties` with the following content:
```plain
storePassword=
@@ -439,12 +444,12 @@ Android, macOS, Windows and Linux if you do not want to use the official ones.
storeFile=
```
9. Build the app for Android by running `flutter build appbundle`. The build can
be found at `app/build/app/outputs/bundle/release/app-release.aab` and must
be uploaded to
[https://play.google.com/apps/publish](https://play.google.com/apps/publish).
10. Build the app for Android by running `flutter build appbundle`. The build
can be found at `app/build/app/outputs/bundle/release/app-release.aab` and
must be uploaded to
[https://play.google.com/apps/publish](https://play.google.com/apps/publish).
10. Build the app for iOS by running `flutter build ipa`. The build can be found
11. Build the app for iOS by running `flutter build ipa`. The build can be found
at `app/build/ios/archive/Runner.xcarchive` and must be opened in Xcode. In
Xcode the **Validate App** and **Distribute App** options can be used to
upload the build to

View File

@@ -7,9 +7,12 @@ TweetDeck. FeedDeck allows you to follow your favorite feeds in one place on all
platforms.
<p>
<a href="https://feeddeck.app/download" target="_blank"><img src=".github/assets/badge-app-store.png" height="50"></a>
<a href="https://feeddeck.app/download" target="_blank"><img src=".github/assets/badge-google-play.png" height="50"></a>
<a href="https://feeddeck.app/download" target="_self"><img src=".github/assets/badge-desktop.png" height="50"></a>
<a href="https://apps.apple.com/us/app/feeddeck/id6451055362" target="_blank"><img src=".github/assets/badge-app-store.svg" height="50"></a>
<a href="https://play.google.com/store/apps/details?id=app.feeddeck.feeddeck" target="_blank"><img src=".github/assets/badge-google-play.svg" height="50"></a>
<a href="https://app.feeddeck.app" target="_blank"><img src=".github/assets/badge-web.svg" height="50"></a>
<a href="https://apps.apple.com/us/app/feeddeck/id6451055362" target="_blank"><img src=".github/assets/badge-mac-app-store.svg" height="50"></a>
<a href="https://www.microsoft.com/store/apps/9NPHPGRRCT5H" target="_blank"><img src=".github/assets/badge-windows-store.svg" height="50"></a>
<a href="https://flathub.org/en/apps/app.feeddeck.feeddeck" target="_blank"><img src=".github/assets/badge-flathub.svg" height="50"></a>
</p>
<img src=".github/assets/screenshot.png" width="100%" />

View File

@@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.feeddeck.feeddeck">
<application
android:label="feeddeck"
android:label="FeedDeck"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

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

View File

@@ -13,17 +13,25 @@ PODS:
- FMDB/standard (2.7.5)
- just_audio (0.0.1):
- Flutter
- media_kit_native_event_loop (1.0.0):
- Flutter
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- purchases_flutter (6.0.0):
- Flutter
- PurchasesHybridCommon (= 7.0.0)
- PurchasesHybridCommon (7.0.0):
- RevenueCat (= 4.27.0)
- RevenueCat (4.27.0)
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- sign_in_with_apple (0.0.1):
- Flutter
- sqflite (0.0.2):
- sqflite (0.0.3):
- Flutter
- FMDB (>= 2.7.5)
- url_launcher_ios (0.0.1):
@@ -38,9 +46,11 @@ DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- just_audio (from `.symlinks/plugins/just_audio/ios`)
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- purchases_flutter (from `.symlinks/plugins/purchases_flutter/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`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
@@ -49,6 +59,8 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- FMDB
- PurchasesHybridCommon
- RevenueCat
EXTERNAL SOURCES:
app_links:
@@ -63,12 +75,16 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_native_splash/ios"
just_audio:
:path: ".symlinks/plugins/just_audio/ios"
media_kit_native_event_loop:
:path: ".symlinks/plugins/media_kit_native_event_loop/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/ios"
:path: ".symlinks/plugins/path_provider_foundation/darwin"
purchases_flutter:
:path: ".symlinks/plugins/purchases_flutter/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/ios"
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sign_in_with_apple:
:path: ".symlinks/plugins/sign_in_with_apple/ios"
sqflite:
@@ -86,14 +102,18 @@ SPEC CHECKSUMS:
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa
media_kit_native_event_loop: f1ee9f941ec0af371b245969a3e010901c375480
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
purchases_flutter: 549ccfbbaf5e7cd195043c714b69a35e278c00f1
PurchasesHybridCommon: af3b2413f9cb999bc1fdca44770bdaf39dfb89fa
RevenueCat: 84fbe2eb9bbf63e1abf346ccd3ff9ee45d633e3b
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
PODFILE CHECKSUM: 92152382e3e2d8bbf76cfce953216c7c829d94ce
PODFILE CHECKSUM: ec83c31511fbc978a9918c6fda235238118483f5
COCOAPODS: 1.12.1
COCOAPODS: 1.13.0

View File

@@ -10,6 +10,7 @@
11A5CB70E48EE53811F041B1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4F63A76E5CF724D941DA24D /* Pods_Runner.framework */; };
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
55F35B592ABF74D1007331B3 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55F35B582ABF74D1007331B3 /* StoreKit.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
@@ -34,6 +35,7 @@
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
551175C42A39020E00A80299 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
55F35B582ABF74D1007331B3 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
5C1B3914BEEA60B960FC050D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -56,6 +58,7 @@
buildActionMask = 2147483647;
files = (
11A5CB70E48EE53811F041B1 /* Pods_Runner.framework in Frameworks */,
55F35B592ABF74D1007331B3 /* StoreKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -121,6 +124,7 @@
AC99711BA86FAD05300ADB7E /* Frameworks */ = {
isa = PBXGroup;
children = (
55F35B582ABF74D1007331B3 /* StoreKit.framework */,
E4F63A76E5CF724D941DA24D /* Pods_Runner.framework */,
);
name = Frameworks;
@@ -157,7 +161,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@@ -228,6 +232,7 @@
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (

View File

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

View File

@@ -1,73 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>FeedDeck</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>feeddeck</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIStatusBarHidden</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>app.feeddeck.feeddeck</string>
</array>
</dict>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>FeedDeck</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>feeddeck</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>app.feeddeck.feeddeck</string>
</array>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIStatusBarHidden</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>

View File

@@ -1,6 +1,7 @@
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
@@ -65,6 +66,23 @@ void main() async {
runApp(const FeedDeckApp());
}
/// [FeedDeckScrollBehavior] changes the scrolling behavior of the app. This is
/// required to enable scrolling on desktop via drag, which is the only way that
/// a user can scroll vertically with a mouse via drag.
///
/// E.g. this is required for the [ColumnLayoutSources] widget, where we rely on
/// the drag gesture to scroll the list of sources.
class FeedDeckScrollBehavior extends MaterialScrollBehavior {
@override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
PointerDeviceKind.trackpad,
PointerDeviceKind.stylus,
PointerDeviceKind.unknown,
};
}
/// [onGenerateRoute] is used in `onGenerateRoute` and `onGenerateInitialRoutes`
/// of the [MaterialApp] to add handling for some special routes were do not
/// want to render the [Home] widget (e.g. set a new password).
@@ -131,6 +149,7 @@ class FeedDeckApp extends StatelessWidget {
),
canvasColor: Constants.canvasColor,
appBarTheme: const AppBarTheme(
centerTitle: true,
backgroundColor: Constants.appBarBackgroundColor,
scrolledUnderElevation: Constants.scrolledUnderElevation,
elevation: Constants.appBarElevation,
@@ -173,6 +192,7 @@ class FeedDeckApp extends StatelessWidget {
},
),
),
scrollBehavior: FeedDeckScrollBehavior(),
onGenerateInitialRoutes: (initialRoute) =>
[onGenerateRoute(RouteSettings(name: initialRoute))],
onGenerateRoute: (RouteSettings settings) =>

View File

@@ -29,6 +29,40 @@ FDProfileTier getProfileTierFromString(String state) {
return FDProfileTier.free;
}
/// [FDProfileSubscriptionProvider] is a enum value which defines the different
/// subscription providers for a profile. A user can use [stripe] or
/// [revenuecat] to get a premium account.
enum FDProfileSubscriptionProvider {
stripe,
revenuecat,
}
/// [FDProfileSubscriptionProviderExtension] defines all extensions which are
/// available for the [FDProfileSubscriptionProvider] enum type.
extension FDProfileSubscriptionProviderExtension
on FDProfileSubscriptionProvider {
/// [toShortString] returns a short string of the source type which can safely
/// be passed to our database.
String toShortString() {
return toString().split('.').last;
}
}
/// [getSourceTypeFromString] returns the [FDProfileSubscriptionProvider] from
/// his string representation. This is used to parse the JSON value returned by
/// our database into correct enum value in the [FDSource] model.
FDProfileSubscriptionProvider? getProfileSubscriptionProviderFromString(
String state,
) {
for (FDProfileSubscriptionProvider element
in FDProfileSubscriptionProvider.values) {
if (element.toShortString() == state) {
return element;
}
}
return null;
}
/// [FDProfile] is the model for a profile of a user in our app. The following
/// fields are required for a profile:
/// - An [id] to uniquely identify a column in the database
@@ -38,6 +72,7 @@ FDProfileTier getProfileTierFromString(String state) {
class FDProfile {
String id;
FDProfileTier tier;
FDProfileSubscriptionProvider? subscriptionProvider;
bool accountGithub;
int createdAt;
int updatedAt;
@@ -45,6 +80,7 @@ class FDProfile {
FDProfile({
required this.id,
required this.tier,
required this.subscriptionProvider,
required this.accountGithub,
required this.createdAt,
required this.updatedAt,
@@ -54,6 +90,12 @@ class FDProfile {
return FDProfile(
id: data['id'],
tier: getProfileTierFromString(data['tier']),
subscriptionProvider: data.containsKey('subscriptionProvider') &&
data['subscriptionProvider'] != null
? getProfileSubscriptionProviderFromString(
data['subscriptionProvider'],
)
: null,
accountGithub: data['accountGithub'],
createdAt: data['createdAt'],
updatedAt: data['updatedAt'],

View File

@@ -152,7 +152,7 @@ class AppRepository with ChangeNotifier {
.from('decks')
.insert({
'name': name,
'userId': Supabase.instance.client.auth.currentUser!.id
'userId': Supabase.instance.client.auth.currentUser!.id,
})
.select()
.single();

View File

@@ -105,9 +105,9 @@ class ItemsRepository with ChangeNotifier {
Future<void> _init() async {
final state = ItemsRepositoryStore().get(column.identifier());
if (state != null) {
_status = ItemsStatus.loaded;
_items.addAll(state.items);
_status = state.status;
_filters = state.filters;
_items.addAll(state.items);
notifyListeners();
} else {
await _getItems();
@@ -168,7 +168,7 @@ class ItemsRepository with ChangeNotifier {
/// filter to page through all the items.
final data = await filter
.order('publishedAt')
.range(_filters.offsetFilter, _filters.offsetFilter + 50);
.range(_filters.offsetFilter, _filters.offsetFilter + 50 - 1);
/// The returned items are added to the [_items] field and the status is
/// set to [ItemsStatus.loaded] or [ItemsStatus.loadedLast] based on the
@@ -189,7 +189,12 @@ class ItemsRepository with ChangeNotifier {
/// Finally we store the state of the repository, so that we do not have
/// to retrieve the items from the database again when therepository is
/// reinitialized.
ItemsRepositoryStore().set(column.identifier(), _items, _filters);
ItemsRepositoryStore().set(
column.identifier(),
_status,
_filters,
_items,
);
notifyListeners();
} catch (_) {
_status = ItemsStatus.loaded;
@@ -278,7 +283,7 @@ class ItemsRepository with ChangeNotifier {
}
}
ItemsRepositoryStore().set(column.id, _items, _filters);
ItemsRepositoryStore().set(column.id, _status, _filters, _items);
notifyListeners();
} catch (err) {
rethrow;
@@ -288,18 +293,27 @@ class ItemsRepository with ChangeNotifier {
/// [updateReadStates] can be used to mark a list of items provided via their
/// [itemIds] as read / unread. When the [read] value is `true` items are
/// marked as read and when it is `false` as unread.
///
/// We have to split the provided list of [itemIds] into chunks of 25 items,
/// to avoid the request uri to long error from Supabase. We decided to use 25
/// items per chunk, because we think that this is a good tradeoff between the
/// number of requests and the number of items we can update at once.
Future<void> updateReadStates(List<String> itemIds, bool read) async {
try {
await Supabase.instance.client
.from('items')
.update({'isRead': read}).in_('id', itemIds);
for (var i = 0; i < _items.length; i++) {
if (itemIds.contains(_items[i].id)) {
_items[i].isRead = read;
final chunks = itemIds.slices(25).toList();
for (var i = 0; i < chunks.length; i++) {
await Supabase.instance.client
.from('items')
.update({'isRead': read}).in_('id', chunks[i]);
for (var j = 0; j < _items.length; j++) {
if (chunks[i].contains(_items[j].id)) {
_items[j].isRead = read;
}
}
}
ItemsRepositoryStore().set(column.id, _items, _filters);
ItemsRepositoryStore().set(column.id, _status, _filters, _items);
notifyListeners();
} catch (err) {
rethrow;
@@ -321,7 +335,7 @@ class ItemsRepository with ChangeNotifier {
}
}
ItemsRepositoryStore().set(column.id, _items, _filters);
ItemsRepositoryStore().set(column.id, _status, _filters, _items);
notifyListeners();
} catch (err) {
rethrow;
@@ -333,12 +347,14 @@ class ItemsRepository with ChangeNotifier {
/// in the [ItemsRepositoryStore]. The state contains all the loaded [items] and
/// the [filters] set by user to load the items.
class ItemsRepositoryStoreState {
List<FDItem> items;
ItemsStatus status;
ItemsFilters filters;
List<FDItem> items;
ItemsRepositoryStoreState({
required this.items,
required this.status,
required this.filters,
required this.items,
});
}
@@ -353,7 +369,7 @@ class ItemsRepositoryStore {
static final ItemsRepositoryStore _instance =
ItemsRepositoryStore._internal();
final Map<String, ItemsRepositoryStoreState> itemsRepositoryStoreStates = {};
final Map<String, ItemsRepositoryStoreState> _itemsRepositoryStoreStates = {};
factory ItemsRepositoryStore() {
return _instance;
@@ -366,7 +382,7 @@ class ItemsRepositoryStore {
/// returns `null` to indicate that we have to get the items from the
/// database.
ItemsRepositoryStoreState? get(String columnId) {
return itemsRepositoryStoreStates[columnId];
return _itemsRepositoryStoreStates[columnId];
}
/// [set] saves the [items] and [filters] for a [columnId] in the store. This
@@ -375,10 +391,23 @@ class ItemsRepositoryStore {
///
/// The best is to call the [set] function right before we call
/// `notifyListeners` in the repository.
set(String columnId, List<FDItem> items, ItemsFilters filters) {
return itemsRepositoryStoreStates[columnId] = ItemsRepositoryStoreState(
items: items,
set(
String columnId,
ItemsStatus status,
ItemsFilters filters,
List<FDItem> items,
) {
return _itemsRepositoryStoreStates[columnId] = ItemsRepositoryStoreState(
status: status,
filters: filters,
items: items,
);
}
/// [clear] deletes all the stored [_itemsRepositoryStoreStates] from the
/// store. This method can be used to clear the cache, e.g. when a user
/// changes the active deck or signes out.
clear() {
_itemsRepositoryStoreStates.clear();
}
}

View File

@@ -5,7 +5,6 @@ import 'package:flutter/foundation.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:feeddeck/models/profile.dart';
import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/utils/api_exception.dart';
enum FDProfileStatus {
@@ -13,14 +12,21 @@ enum FDProfileStatus {
initialized,
}
/// The [ProfileRepository] is used to fetch and update a users profile data.
/// The users profile contains all the required information for the users
/// subscription status and the users connected accounts.
class ProfileRepository with ChangeNotifier {
FDProfileStatus _status = FDProfileStatus.uninitialized;
FDProfile? _profile;
FDProfileStatus get status => _status;
FDProfileTier get tier => _profile?.tier ?? FDProfileTier.free;
FDProfileSubscriptionProvider? get subscriptionProvider =>
_profile?.subscriptionProvider;
bool get accountGithub => _profile?.accountGithub ?? false;
/// [init] is used to fetch the users profile from the `profile-v2` edge
/// function.
Future<void> init(bool force) async {
if (_status == FDProfileStatus.initialized && force == false) {
return;
@@ -28,7 +34,7 @@ class ProfileRepository with ChangeNotifier {
try {
final result = await Supabase.instance.client.functions.invoke(
'profile-v1',
'profile-v2/getProfile',
method: HttpMethod.get,
);
@@ -45,16 +51,25 @@ class ProfileRepository with ChangeNotifier {
}
}
Future<void> addGithubAccount(String token) async {
/// [setTier] is used to update the users tier. We do not have to make an API
/// call to update the tier in the database, because this is done via Webhooks
/// by the connected payment provider.
///
/// This is only required to reflect the update in the Flutter app.
void setTier(FDProfileTier tier) {
_profile?.tier = tier;
notifyListeners();
}
/// [githubAddAccount] is used to add a GitHub account to the users profile.
/// For this the user must provide an private access token with the required
/// scopes.
Future<void> githubAddAccount(String token) async {
final result = await Supabase.instance.client.functions.invoke(
'profile-v1',
'profile-v2/githubAddAccount',
method: HttpMethod.post,
body: {
'action': 'add-account',
'sourceType': FDSourceType.github.toShortString(),
'options': {
'token': token,
}
'token': token,
},
);
@@ -66,14 +81,12 @@ class ProfileRepository with ChangeNotifier {
notifyListeners();
}
Future<void> deleteGithubAccount() async {
/// [githubDeleteAccount] deletes the users connected GitHub account. For that
/// we delete the GitHub access token from the database.
Future<void> githubDeleteAccount() async {
final result = await Supabase.instance.client.functions.invoke(
'profile-v1',
method: HttpMethod.post,
body: {
'action': 'delete-account',
'sourceType': FDSourceType.github.toShortString(),
},
'profile-v2/githubDeleteAccount',
method: HttpMethod.delete,
);
if (result.status != 200) {

View File

@@ -17,7 +17,19 @@ class SettingsRepository {
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Iml0eWp1Y3BzcmFzYXZyaWVwc2NyIiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTQwMjY0NjIsImV4cCI6MjAwOTYwMjQ2Mn0.IDo7j9Kh8-5kHLtrZtHTvLf8lUkj7jiLynpIXSZbRFs';
String supabaseSiteUrl = 'https://app.feeddeck.app';
String googleClientId =
'420185423235-9ehth1eodl4lt3cdns7kaf2e89eo6rkq.apps.googleusercontent.com';
'296452997087-2o1qasg1gdbe39b1l39dds1doq3t6h2e.apps.googleusercontent.com';
/// By default the [subscriptionEnabled] variable is set to `true`, so that a
/// user can subscribe to FeedDeck Premium. If the variable is set to `false`
/// the user can not subscribe to FeedDeck Premium.
bool subscriptionEnabled = true;
/// The [revenueCatAppStoreKey] and [revenueCatGooglePlayKey] are used for the
/// in-app purchases. The [revenueCatAppStoreKey] is used for the Apple App
/// Store on iOS and macOS. The [revenueCatGooglePlayKey] is used for the
/// Google Play Store on Android.
final String revenueCatAppStoreKey = 'appl_kThbIaMkylDBtCEmsfczvgCBram';
final String revenueCatGooglePlayKey = 'goog_tBFPbLbygjioviXRIlGlmUOKZYA';
factory SettingsRepository() {
return _instance;
@@ -52,11 +64,16 @@ class SettingsRepository {
googleClientIdPrefs != null) {
/// Store the user provided values within the [SettingsRepository] and
/// use them to initialize the Supabase client.
///
/// Also set the [subscriptionEnabled] variabel to `false`, so that the
/// user can not subscribe to FeedDeck Premium.
supabaseUrl = supabaseUrlPrefs;
supabaseAnonKey = supabaseAnonKeyPrefs;
supabaseSiteUrl = supabaseSiteUrlPrefs;
googleClientId = googleClientIdPrefs;
subscriptionEnabled = false;
await Supabase.initialize(
url: supabaseUrlPrefs,
anonKey: supabaseAnonKeyPrefs,

View File

@@ -121,7 +121,7 @@ class _ColumnLayoutHeaderState extends State<ColumnLayoutHeader> {
),
Text(
Characters(
'${items.column.sources.length} ${items.column.sources.length == 1 ? 'Source' : 'Sources'} / ${items.items.length} ${items.items.length == 1 ? 'Item' : 'Items'}',
'${items.column.sources.length} ${items.column.sources.length == 1 ? 'Source' : 'Sources'} / ${items.items.length}${items.status == ItemsStatus.loaded ? '+' : ''} ${items.items.length == 1 ? 'Item' : 'Items'}',
)
.replaceAll(
Characters(''),

View File

@@ -100,7 +100,7 @@ class ColumnLayoutHeaderSettings extends StatelessWidget {
icon: const Icon(Icons.chevron_right),
),
],
)
),
],
);
}

View File

@@ -91,6 +91,7 @@ class _ColumnLayoutHeaderSettingsDeleteColumnState
await Provider.of<AppRepository>(context, listen: false)
.deleteColumn(widget.column.id);
} catch (_) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
duration: Duration(seconds: 10),

View File

@@ -112,6 +112,64 @@ class _CreateDeckState extends State<CreateDeck> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(
bottom: Constants.spacingMiddle,
),
child: RichText(
textAlign: TextAlign.center,
text: const TextSpan(
style: TextStyle(
color: Constants.onSurface,
fontSize: 14.0,
),
children: [
TextSpan(
text: 'We\'re excited to have you onboard at ',
),
TextSpan(
text: 'FeedDeck',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
TextSpan(
text:
'. We hope you enjoy your journey with us. If you have any questions or need assistance, feel free to reach out.',
),
],
),
),
),
Padding(
padding: const EdgeInsets.only(
bottom: Constants.spacingMiddle,
),
child: RichText(
textAlign: TextAlign.center,
text: const TextSpan(
style: TextStyle(
color: Constants.onSurface,
fontSize: 14.0,
),
children: [
TextSpan(
text:
'Now it\'s time to create your first deck. A deck is a collection of columns and sources. Provide the name for your first deck and click on the ',
),
TextSpan(
text: '"Create Deck"',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
TextSpan(
text: ' button.',
),
],
),
),
),
TextFormField(
controller: _nameController,
keyboardType: TextInputType.text,

View File

@@ -195,9 +195,9 @@ class _DeckLayoutLargeState extends State<DeckLayoutLarge> {
/// The drawer is used to display the [CreateColumn] widget, so that a
/// user can add a new column without leaving the screen.
drawer: ClipRRect(
borderRadius: const BorderRadius.vertical(
top: Radius.circular(Constants.spacingMiddle),
bottom: Radius.circular(Constants.spacingMiddle),
borderRadius: const BorderRadius.only(
topRight: Radius.circular(Constants.spacingMiddle),
bottomRight: Radius.circular(Constants.spacingMiddle),
),
child: Drawer(
width: Constants.columnWidth,

View File

@@ -41,7 +41,7 @@ class ItemDetailsMastodon extends StatelessWidget {
.map((item) => item as String)
.toList()
: null,
)
),
],
);
}

View File

@@ -67,15 +67,8 @@ class ItemAudioPlayerControlButtons extends StatelessWidget {
final playerState = snapshot.data;
final processingState = playerState?.processingState;
final playing = playerState?.playing;
if (processingState == ProcessingState.loading ||
processingState == ProcessingState.buffering) {
return Container(
margin: const EdgeInsets.all(Constants.spacingSmall),
width: 48.0,
height: 48.0,
child: const CircularProgressIndicator(),
);
} else if (playing != true) {
if (playing != true) {
return IconButton(
icon: const Icon(Icons.play_arrow),
iconSize: 64.0,

View File

@@ -35,6 +35,16 @@ class _ItemAudioPlayerSeekBarState extends State<ItemAudioPlayerSeekBar> {
/// audio file.
Duration get _remaining => widget.duration - widget.position;
/// [_printDuration] prints the provided [duration] in a human readable format
/// `HH:mm:ss`.
String _printDuration(Duration duration) {
String negativeSign = duration.isNegative ? '-' : '';
String twoDigits(int n) => n.toString().padLeft(2, '0');
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60).abs());
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60).abs());
return '$negativeSign${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds';
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
@@ -110,10 +120,7 @@ class _ItemAudioPlayerSeekBarState extends State<ItemAudioPlayerSeekBar> {
left: Constants.spacingMiddle,
bottom: 0.0,
child: Text(
RegExp(r'((^0*[1-9]\d*:)?\d{2}:\d{2})\.\d+$')
.firstMatch('${widget.position}')
?.group(1) ??
'${widget.position}',
_printDuration(widget.position),
style: Theme.of(context).textTheme.bodySmall,
),
),
@@ -121,10 +128,7 @@ class _ItemAudioPlayerSeekBarState extends State<ItemAudioPlayerSeekBar> {
right: Constants.spacingMiddle,
bottom: 0.0,
child: Text(
RegExp(r'((^0*[1-9]\d*:)?\d{2}:\d{2})\.\d+$')
.firstMatch('$_remaining')
?.group(1) ??
'$_remaining',
_printDuration(_remaining),
style: Theme.of(context).textTheme.bodySmall,
),
),

View File

@@ -81,7 +81,7 @@ class ItemMediaGallery extends StatelessWidget {
),
Expanded(
child: _buildSingleMedia(context, 1),
)
),
],
),
),
@@ -114,7 +114,7 @@ class ItemMediaGallery extends StatelessWidget {
),
Expanded(
child: _buildSingleMedia(context, 2),
)
),
],
),
),

View File

@@ -63,7 +63,7 @@ class ItemMediaGallery extends StatelessWidget {
),
Expanded(
child: _buildSingleMedia(context, itemMedias![1]),
)
),
],
),
),
@@ -96,7 +96,7 @@ class ItemMediaGallery extends StatelessWidget {
),
Expanded(
child: _buildSingleMedia(context, itemMedias![2]),
)
),
],
),
),

View File

@@ -35,7 +35,7 @@ class SettingsAccountsGithub extends StatelessWidget {
await Provider.of<ProfileRepository>(
context,
listen: false,
).deleteGithubAccount();
).githubDeleteAccount();
} catch (_) {}
}
@@ -166,7 +166,7 @@ class _SettingsAccountsGithubAddState extends State<SettingsAccountsGithubAdd> {
await Provider.of<ProfileRepository>(
context,
listen: false,
).addGithubAccount(_tokenController.text);
).githubAddAccount(_tokenController.text);
setState(() {
_isLoading = false;

View File

@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:feeddeck/repositories/app_repository.dart';
import 'package:feeddeck/repositories/items_repository.dart';
import 'package:feeddeck/utils/constants.dart';
/// The [SettingsDecksSelect] widget shows a list of the users decks, when the
@@ -19,8 +20,13 @@ class _SettingsDecksSelectState extends State<SettingsDecksSelect> {
/// [_selectDeck] sets the provided [deckId] as the active deck. The active
/// deck is updated via the [selectDeck] method of the [AppRepository]. When
/// the active deck is updated the user is redirected to the decks view.
///
/// Before the active deck is changed the [ItemsRepositoryStore] is cleared,
/// to trigger a reload of the items once the deck is loaded.
Future<void> _selectDeck(String deckId) async {
try {
ItemsRepositoryStore().clear();
await Provider.of<AppRepository>(context, listen: false)
.selectDeck(deckId);
if (!mounted) return;

View File

@@ -0,0 +1,198 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:feeddeck/models/profile.dart';
import 'package:feeddeck/repositories/profile_repository.dart';
import 'package:feeddeck/repositories/settings_repository.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/utils/fd_icons.dart';
import 'package:feeddeck/widgets/general/logo.dart';
import 'package:feeddeck/widgets/settings/premium/settings_premium_inapp.dart';
import 'package:feeddeck/widgets/settings/premium/settings_premium_inapp_restore.dart';
import 'package:feeddeck/widgets/settings/premium/settings_premium_stripe.dart';
class SettingsPremium extends StatelessWidget {
const SettingsPremium({super.key});
/// [_showPaymentModal] show a modal to subscribe to FeedDeck Premium via
/// Stripe on the web, Linux and Windows. On macOS, Android and iOS the modal
/// to subscribe via in-app purchases is shown.
void _showPaymentModal(BuildContext context) {
if (kIsWeb || Platform.isLinux || Platform.isWindows) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: true,
useSafeArea: true,
backgroundColor: Colors.transparent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(Constants.spacingMiddle),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
constraints: const BoxConstraints(
maxWidth: Constants.centeredFormMaxWidth,
),
builder: (BuildContext context) {
return const SettingsPremiumStripe();
},
);
} else if (Platform.isMacOS || Platform.isAndroid || Platform.isIOS) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: true,
useSafeArea: true,
backgroundColor: Colors.transparent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(Constants.spacingMiddle),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
constraints: const BoxConstraints(
maxWidth: Constants.centeredFormMaxWidth,
),
builder: (BuildContext context) {
return const SettingsPremiumInApp();
},
);
}
}
@override
Widget build(BuildContext context) {
ProfileRepository profile = Provider.of<ProfileRepository>(
context,
listen: true,
);
/// If subscriptions are disabled, because the user uses a custom Supabase
/// instance or if the profile is not initialized yet or if the user is
/// already on the premium tier we do not show the option to subscribe to
/// FeedDeck Premium.
if (!SettingsRepository().subscriptionEnabled) {
return Container();
}
if (profile.status == FDProfileStatus.uninitialized) {
return Container();
}
if (profile.tier != FDProfileTier.free) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Card(
color: Constants.primary,
margin: const EdgeInsets.only(
bottom: Constants.spacingSmall,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.all(
Constants.spacingMiddle,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
Characters('You are using FeedDeck Premium')
.replaceAll(
Characters(''),
Characters('\u{200B}'),
)
.toString(),
maxLines: 1,
style: const TextStyle(
color: Constants.onPrimary,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
const Icon(
FDIcons.feeddeck,
color: Constants.onPrimary,
),
],
),
),
],
),
),
const SizedBox(
height: Constants.spacingMiddle,
),
],
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => _showPaymentModal(context),
child: Card(
color: Constants.primary,
margin: const EdgeInsets.only(
bottom: Constants.spacingSmall,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: EdgeInsets.all(
Constants.spacingMiddle,
),
child: Column(
children: [
Logo(size: 64),
SizedBox(
height: Constants.spacingSmall,
),
Text(
'Subscribe to FeedDeck Premium',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Constants.onPrimary,
),
),
],
),
),
],
),
),
),
),
const SettingsPremiumInAppRestore(),
const SizedBox(
height: Constants.spacingMiddle,
),
],
);
}
}

View File

@@ -0,0 +1,254 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:provider/provider.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'package:supabase_flutter/supabase_flutter.dart' as supabase;
import 'package:feeddeck/models/profile.dart';
import 'package:feeddeck/repositories/profile_repository.dart';
import 'package:feeddeck/repositories/settings_repository.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/utils/fd_icons.dart';
import 'package:feeddeck/widgets/general/elevated_button_progress_indicator.dart';
class SettingsPremiumInApp extends StatefulWidget {
const SettingsPremiumInApp({super.key});
@override
State<SettingsPremiumInApp> createState() => _SettingsPremiumInAppState();
}
class _SettingsPremiumInAppState extends State<SettingsPremiumInApp> {
late Future<Offering?> _futureFetchOfferings;
bool _isLoading = false;
/// [_fetchOfferings] is used to fetch the Stripe checkout session
/// link. For that we have to call the `stripe-create-checkout-session-v1`
/// Supabase edge function. If the link is generated successfully, the
/// function returns the url, which can then be opened by the user.
Future<Offering?> _fetchOfferings() async {
if (Platform.isAndroid) {
await Purchases.configure(
PurchasesConfiguration(
SettingsRepository().revenueCatGooglePlayKey,
)..appUserID = supabase.Supabase.instance.client.auth.currentUser!.id,
);
} else if (Platform.isMacOS || Platform.isIOS) {
await Purchases.configure(
PurchasesConfiguration(
SettingsRepository().revenueCatAppStoreKey,
)..appUserID = supabase.Supabase.instance.client.auth.currentUser!.id,
);
}
Offerings offerings = await Purchases.getOfferings();
if (offerings.current != null) {
return offerings.current;
} else {
return null;
}
}
/// [_purchase] is used to purchase the provided [package]. If the purchase
/// was successful, the user is notified. If the purchase failed, the user is
/// notified as well.
Future<void> _purchase(Package package) async {
try {
setState(() {
_isLoading = true;
});
CustomerInfo customerInfo = await Purchases.purchasePackage(package);
setState(() {
_isLoading = false;
});
if (!customerInfo.entitlements.all.containsKey('FeedDeck Premium')) {
throw Exception('FeedDeck Premium entitlement not found.');
}
if (customerInfo.entitlements.all['FeedDeck Premium']!.isActive) {
if (!mounted) return;
Provider.of<ProfileRepository>(
context,
listen: false,
).setTier(FDProfileTier.premium);
}
if (!mounted) return;
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
duration: Duration(seconds: 10),
backgroundColor: Constants.primary,
showCloseIcon: true,
content: Text(
'FeedDeck Premium was successfully purchased.',
style: TextStyle(color: Constants.onPrimary),
),
),
);
} on PlatformException catch (err) {
final errorCode = PurchasesErrorHelper.getErrorCode(err);
if (errorCode == PurchasesErrorCode.purchaseCancelledError) {
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
} else {
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: const Duration(seconds: 10),
backgroundColor: Constants.error,
showCloseIcon: true,
content: Text(
'In-app purchase failed: ${err.message}',
style: const TextStyle(color: Constants.onError),
),
),
);
}
} catch (err) {
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: const Duration(seconds: 10),
backgroundColor: Constants.error,
showCloseIcon: true,
content: Text(
'In-app purchase failed: ${err.toString()}',
style: const TextStyle(color: Constants.onError),
),
),
);
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
setState(() {
_futureFetchOfferings = _fetchOfferings();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
shape: const Border(
bottom: BorderSide(
color: Constants.dividerColor,
width: 1,
),
),
title: const Text('FeedDeck Premium'),
actions: [
IconButton(
icon: const Icon(
Icons.close,
),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
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: '''
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.
To use FeedDeck after the trial period with up to 1000 sources, you need to
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),
),
),
],
);
},
),
);
}
}

View File

@@ -0,0 +1,202 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'package:supabase_flutter/supabase_flutter.dart' as supabase;
import 'package:feeddeck/models/profile.dart';
import 'package:feeddeck/repositories/profile_repository.dart';
import 'package:feeddeck/repositories/settings_repository.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/widgets/general/elevated_button_progress_indicator.dart';
class SettingsPremiumInAppRestore extends StatefulWidget {
const SettingsPremiumInAppRestore({super.key});
@override
State<SettingsPremiumInAppRestore> createState() =>
_SettingsPremiumInAppRestoreState();
}
class _SettingsPremiumInAppRestoreState
extends State<SettingsPremiumInAppRestore> {
bool _isLoading = false;
Future<void> _restore() async {
try {
setState(() {
_isLoading = true;
});
if (Platform.isAndroid) {
await Purchases.configure(
PurchasesConfiguration(
SettingsRepository().revenueCatGooglePlayKey,
)..appUserID = supabase.Supabase.instance.client.auth.currentUser!.id,
);
} else if (Platform.isMacOS || Platform.isIOS) {
await Purchases.configure(
PurchasesConfiguration(
SettingsRepository().revenueCatAppStoreKey,
)..appUserID = supabase.Supabase.instance.client.auth.currentUser!.id,
);
} else {
return;
}
CustomerInfo customerInfo = await Purchases.restorePurchases();
setState(() {
_isLoading = false;
});
if (!customerInfo.entitlements.all.containsKey('FeedDeck Premium')) {
throw Exception('FeedDeck Premium entitlement not found.');
}
if (customerInfo.entitlements.all['FeedDeck Premium']!.isActive) {
if (!mounted) return;
Provider.of<ProfileRepository>(
context,
listen: false,
).setTier(FDProfileTier.premium);
}
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
duration: Duration(seconds: 10),
backgroundColor: Constants.primary,
showCloseIcon: true,
content: Text(
'FeedDeck Premium was restored.',
style: TextStyle(color: Constants.onPrimary),
),
),
);
} on PlatformException catch (err) {
setState(() {
_isLoading = false;
});
final errorCode = PurchasesErrorHelper.getErrorCode(err);
if (errorCode == PurchasesErrorCode.purchaseCancelledError) {
return;
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: const Duration(seconds: 10),
backgroundColor: Constants.error,
showCloseIcon: true,
content: Text(
'Restore purchase failed: ${err.message}',
style: const TextStyle(color: Constants.onError),
),
),
);
}
} catch (err) {
setState(() {
_isLoading = false;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: const Duration(seconds: 10),
backgroundColor: Constants.error,
showCloseIcon: true,
content: Text(
'Restore purchase failed: ${err.toString()}',
style: const TextStyle(color: Constants.onError),
),
),
);
}
}
/// [buildIcon] return the provided icon or when the [_isLoading] state is
/// `true` is returns a circular progress indicator.
Widget buildIcon() {
if (_isLoading) return const ElevatedButtonProgressIndicator();
return Container();
}
@override
Widget build(BuildContext context) {
ProfileRepository profile = Provider.of<ProfileRepository>(
context,
listen: true,
);
/// We do not display the restore button when subscriptions are disabled,
/// the profile isn't initalized yet or a user is already subscribed to
/// FeedDeck Premium.
if (!SettingsRepository().subscriptionEnabled) {
return Container();
}
if (profile.status == FDProfileStatus.uninitialized) {
return Container();
}
if (profile.tier != FDProfileTier.free) {
return Container();
}
if (!kIsWeb && (Platform.isMacOS || Platform.isAndroid || Platform.isIOS)) {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => _restore(),
child: Card(
color: Constants.secondary,
margin: const EdgeInsets.only(
bottom: Constants.spacingSmall,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.all(
Constants.spacingMiddle,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
Characters('Restore Purchases')
.replaceAll(
Characters(''),
Characters('\u{200B}'),
)
.toString(),
maxLines: 1,
style: const TextStyle(
overflow: TextOverflow.ellipsis,
),
),
],
),
),
buildIcon(),
],
),
),
],
),
),
),
);
}
return Container();
}
}

View File

@@ -1,134 +1,32 @@
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:provider/provider.dart';
import 'package:supabase_flutter/supabase_flutter.dart' as supabase;
import 'package:feeddeck/models/profile.dart';
import 'package:feeddeck/repositories/profile_repository.dart';
import 'package:feeddeck/utils/api_exception.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/utils/fd_icons.dart';
import 'package:feeddeck/utils/openurl.dart';
import 'package:feeddeck/widgets/general/elevated_button_progress_indicator.dart';
import 'package:feeddeck/widgets/general/logo.dart';
const _settingsPaymentBannerText = '''
const _settingsPremiumStripeText = '''
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.
To use FeedDeck after the trial period with up to 1000 sources you need to
To use FeedDeck after the trial period with up to 1000 sources, you need to
upgrade to a premium account. The premium account costs 5 per month and can be
canceled at any time.
''';
class SettingsPaymentBanner extends StatelessWidget {
const SettingsPaymentBanner({super.key});
class SettingsPremiumStripe extends StatefulWidget {
const SettingsPremiumStripe({super.key});
@override
Widget build(BuildContext context) {
ProfileRepository profile = Provider.of<ProfileRepository>(
context,
listen: true,
);
if (profile.status == FDProfileStatus.uninitialized) {
return Container();
}
if (!kIsWeb) {
return Container();
}
if (profile.tier != FDProfileTier.free) {
return Container();
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: true,
useSafeArea: true,
backgroundColor: Colors.transparent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(Constants.spacingMiddle),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
constraints: const BoxConstraints(
maxWidth: Constants.centeredFormMaxWidth,
),
builder: (BuildContext context) {
return const SettingsPaymentBannerModal();
},
);
},
child: Card(
color: Constants.primary,
margin: const EdgeInsets.only(
bottom: Constants.spacingSmall,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.all(
Constants.spacingMiddle,
),
child: Column(
children: const [
Logo(size: 64),
SizedBox(
height: Constants.spacingSmall,
),
Text(
'Subscribe to FeedDeck Premium',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Constants.onPrimary,
),
),
],
),
),
],
),
),
),
),
const SizedBox(
height: Constants.spacingMiddle,
),
],
);
}
State<SettingsPremiumStripe> createState() => _SettingsPremiumStripeState();
}
class SettingsPaymentBannerModal extends StatefulWidget {
const SettingsPaymentBannerModal({super.key});
@override
State<SettingsPaymentBannerModal> createState() =>
_SettingsPaymentBannerModalState();
}
class _SettingsPaymentBannerModalState
extends State<SettingsPaymentBannerModal> {
class _SettingsPremiumStripeState extends State<SettingsPremiumStripe> {
late Future<String> _futureFetchCheckoutSessionLink;
/// [_fetchCheckoutSessionLink] is used to fetch the Stripe checkout session
@@ -177,7 +75,7 @@ class _SettingsPaymentBannerModalState
width: 1,
),
),
title: const Text('Subscribe to FeedDeck Premium'),
title: const Text('FeedDeck Premium'),
actions: [
IconButton(
icon: const Icon(
@@ -203,7 +101,7 @@ class _SettingsPaymentBannerModalState
child: SingleChildScrollView(
child: MarkdownBody(
selectable: true,
data: _settingsPaymentBannerText,
data: _settingsPremiumStripeText,
),
),
),

View File

@@ -25,9 +25,9 @@ class _SettingsProfileState extends State<SettingsProfile> {
Widget build(BuildContext context) {
Provider.of<AppRepository>(context, listen: true);
return Column(
return const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
children: [
Text(
'Profile',
style: TextStyle(

View File

@@ -1,9 +1,11 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:provider/provider.dart';
import 'package:supabase_flutter/supabase_flutter.dart' as supabase;
import 'package:feeddeck/models/profile.dart';
import 'package:feeddeck/repositories/profile_repository.dart';
import 'package:feeddeck/utils/api_exception.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/utils/openurl.dart';
@@ -22,80 +24,86 @@ class SettingsProfileCustomerPortal extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (!kIsWeb) {
return Container();
}
ProfileRepository profile = Provider.of<ProfileRepository>(
context,
listen: true,
);
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: true,
useSafeArea: true,
backgroundColor: Colors.transparent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(Constants.spacingMiddle),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
constraints: const BoxConstraints(
maxWidth: Constants.centeredFormMaxWidth,
),
builder: (BuildContext context) {
return const SettingsProfileCustomerPortalModal();
},
);
},
child: Card(
color: Constants.secondary,
margin: const EdgeInsets.only(
bottom: Constants.spacingSmall,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.all(
Constants.spacingMiddle,
if (profile.tier == FDProfileTier.premium &&
profile.subscriptionProvider == FDProfileSubscriptionProvider.stripe) {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: true,
useSafeArea: true,
backgroundColor: Colors.transparent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(Constants.spacingMiddle),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
Characters('Customer Portal')
.replaceAll(
Characters(''),
Characters('\u{200B}'),
)
.toString(),
maxLines: 1,
style: const TextStyle(
overflow: TextOverflow.ellipsis,
),
clipBehavior: Clip.antiAliasWithSaveLayer,
constraints: const BoxConstraints(
maxWidth: Constants.centeredFormMaxWidth,
),
builder: (BuildContext context) {
return const SettingsProfileCustomerPortalModal();
},
);
},
child: Card(
color: Constants.secondary,
margin: const EdgeInsets.only(
bottom: Constants.spacingSmall,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.all(
Constants.spacingMiddle,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
Characters('Customer Portal')
.replaceAll(
Characters(''),
Characters('\u{200B}'),
)
.toString(),
maxLines: 1,
style: const TextStyle(
overflow: TextOverflow.ellipsis,
),
),
),
],
],
),
),
),
const Icon(Icons.receipt),
],
const Icon(Icons.receipt),
],
),
),
),
],
],
),
),
),
),
);
);
}
return Container();
}
}
@@ -119,9 +127,9 @@ class _SettingsProfileCustomerPortalModalState
/// Supabase edge function. If the link is generated successfully, the
/// function returns the url, which can then be opened by the user.
Future<String> _fetchCustomerPortalLink() async {
final result = await Supabase.instance.client.functions.invoke(
final result = await supabase.Supabase.instance.client.functions.invoke(
'stripe-create-billing-portal-link-v1',
method: HttpMethod.get,
method: supabase.HttpMethod.get,
);
if (result.status != 200) {

View File

@@ -48,6 +48,7 @@ class _SettingsProfileOpenWebAppState extends State<SettingsProfileOpenWebApp> {
setState(() {
_isLoading = false;
});
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
duration: Duration(seconds: 10),

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart' as supabase;
import 'package:feeddeck/repositories/items_repository.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/widgets/general/elevated_button_progress_indicator.dart';
import 'package:feeddeck/widgets/signin/signin.dart';
@@ -20,12 +21,16 @@ class _SettingsProfileSignOutState extends State<SettingsProfileSignOut> {
/// [_signOut] signs out the currently authenticated user and redirects him
/// to the [SignIn] screen. This will sign out the user from all devices.
///
/// Before the user is signed out the [ItemsRepositoryStore] is cleared, to
/// trigger a reload of the items once the user is signed in again.
Future<void> _signOut() async {
setState(() {
_isLoading = true;
});
try {
ItemsRepositoryStore().clear();
await supabase.Supabase.instance.client.auth.signOut();
setState(() {

View File

@@ -6,9 +6,9 @@ import 'package:feeddeck/repositories/profile_repository.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/widgets/settings/accounts/settings_accounts.dart';
import 'package:feeddeck/widgets/settings/decks/settings_decks.dart';
import 'package:feeddeck/widgets/settings/premium/settings_premium.dart';
import 'package:feeddeck/widgets/settings/profile/settings_profile.dart';
import 'package:feeddeck/widgets/settings/settings_info.dart';
import 'package:feeddeck/widgets/settings/settings_payment_banner.dart';
/// The [Settings] widget implements the settings page for the FeedDeck app. The
/// page is used to display all the users information and to provide a way where
@@ -54,11 +54,11 @@ class _SettingsState extends State<Settings> {
padding: const EdgeInsets.all(
Constants.spacingMiddle,
),
child: Column(
child: const Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
SettingsPaymentBanner(),
children: [
SettingsPremium(),
/// Display all decks. Here the user can switch the active
/// deck, he can delete a deck or update the name of a

View File

@@ -1,3 +1,6 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
@@ -157,6 +160,40 @@ class _SettingsInfoState extends State<SettingsInfo> {
} catch (_) {}
},
),
_buildItem(
'Get Started',
const Icon(Icons.help),
() {
try {
if (kIsWeb ||
Platform.isLinux ||
Platform.isMacOS ||
Platform.isWindows) {
openUrl('https://feeddeck.app/get-started/desktop');
} else {
openUrl('https://feeddeck.app/get-started/mobile');
}
} catch (_) {}
},
),
_buildItem(
'Terms & Conditions',
const Icon(Icons.policy),
() {
try {
openUrl('https://feeddeck.app/terms-and-conditions');
} catch (_) {}
},
),
_buildItem(
'Privacy Policy',
const Icon(Icons.security),
() {
try {
openUrl('https://feeddeck.app/privacy-policy');
} catch (_) {}
},
),
],
);
}

View File

@@ -154,12 +154,12 @@ class _SignInState extends State<SignIn> {
/// [_signInWithApple] handles the sign in of a user via his Apple account.
Future<void> _signInWithApple() async {
try {
if (!kIsWeb && Platform.isIOS) {
/// On iOS we are using the `signInWithApple` method of the Supabase
/// client instead of the `signInWithOAuth` method, so that the user is
/// not redirected for the sign in. Since the sign in is completly
/// handled within the app we have to call the `init` method of the
/// [AppRepository] to load the user data from Supabase.
if (!kIsWeb && (Platform.isIOS || Platform.isMacOS)) {
/// On iOS and macOS we are using the `signInWithApple` method of the
/// Supabase client instead of the `signInWithOAuth` method, so that the
/// user is not redirected for the sign in. Since the sign in is
/// completly handled within the app we have to call the `init` method
/// of the [AppRepository] to load the user data from Supabase.
setState(() {
_isLoading = true;
_error = '';
@@ -186,10 +186,9 @@ class _SignInState extends State<SignIn> {
),
(route) => false,
);
} 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 Apple account. Once the sing in
} else if (!kIsWeb && (Platform.isLinux || Platform.isWindows)) {
/// On Linux and Windows we have to use the [DesktopLoginManager] 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.
setState(() {
@@ -349,8 +348,8 @@ class _SignInState extends State<SignIn> {
/// his Apple account.
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xff000000),
foregroundColor: const Color(0xffffffff),
backgroundColor: const Color(0xffffffff),
foregroundColor: const Color(0xff000000),
maximumSize: const Size.fromHeight(
Constants.elevatedButtonSize,
),

View File

@@ -14,8 +14,11 @@ import 'package:feeddeck/widgets/source/add/add_source_form.dart';
const _helpText = '''
The Nitter source can be used to add an RSS feed for Nitter:
- **RSS Feed**: `https://nitter.net/rico_berger/rss` or `https://nitter.net/search/rss?f=tweets&q=FeedDeck`
- **Username**: `@rico_berger`
- **Search Term**: `FeedDeck`
**Note:** We recommend that you use your own Nitter instance to avoid rate limiting.
''';
/// The [AddSourceNitter] widget is used to display the form to add a new Nitter

View File

@@ -87,6 +87,7 @@ class _SourceListItemState extends State<SourceListItem> {
AppRepository app = Provider.of<AppRepository>(context, listen: false);
await app.deleteSource(widget.columnId, widget.source.id);
} catch (_) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
duration: Duration(seconds: 10),

View File

@@ -0,0 +1,9 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=FeedDeck
Comment=Follow your RSS and Social Media Feeds
Categories=Utility
Icon=app.feeddeck.feeddeck
Terminal=false
Exec=feeddeck

View File

@@ -0,0 +1,167 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>app.feeddeck.feeddeck</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>MIT</project_license>
<name>FeedDeck</name>
<summary>Follow your RSS and Social Media Feeds</summary>
<developer_name>Rico Berger</developer_name>
<url type="homepage">https://feeddeck.app</url>
<url type="bugtracker">https://github.com/feeddeck/feeddeck/issues</url>
<url type="contribute">https://github.com/feeddeck/feeddeck</url>
<url type="donation">https://github.com/sponsors/ricoberger</url>
<url type="vcs-browser">https://github.com/feeddeck/feeddeck</url>
<launchable type="desktop-id">app.feeddeck.feeddeck.desktop</launchable>
<provides>
<id>app.feeddeck.feeddeck</id>
</provides>
<content_rating type="oars-1.1">
<content_attribute id="money-purchasing">intense</content_attribute>
</content_rating>
<description>
<p>
FeedDeck is an open source RSS and social media feed reader, inspired by TweetDeck. FeedDeck allows you to follow your favorite feeds in one place on all platforms. FeedDeck is written in Flutter and uses Supabase and Deno as it's backend.
</p>
<ul>
<li>Available for mobile and desktop: FeedDeck provides the same experience for mobile and desktop with nearly 100% code sharing.</li>
<li>RSS and Social Media Feeds: Follow your favorite RSS and social media feeds.</li>
<li>News: Get the latest news from your favorite RSS feeds and Google News.</li>
<li>Social Media: Follow your friends and favorite topics on Medium, Nitter, Reddit, Tumblr and X.</li>
<li>GitHub: Get your GitHub notifications and follow your repository activities.</li>
<li>Podcasts: Follow and listen to your favorite podcasts, via the built-in podcast player.</li>
<li>YouTube: Follow and view your favorite YouTube channels.</li>
</ul>
</description>
<keywords>
<keyword>RSS</keyword>
<keyword>Social Media</keyword>
<keyword>Reader</keyword>
</keywords>
<categories>
<category>System</category>
<category>Utilities</category>
</categories>
<releases>
<release version="v1.1.1" date="2023-10-21">
<description>
<p>Fixed</p>
<ul>
<li>#46: [core] Fix Number of Loaded Items per Fetch @ricoberger</li>
<li>#45: [core] Fix ItemsRepository Status @ricoberger</li>
<li>#44: [core] Fix Border Radius for Drawer @ricoberger</li>
<li>#43: [core] Fix Status Bar for iOS @ricoberger</li>
<li>#42: [podcast] Fix Audio Player for Windows and Linux @ricoberger</li>
<li>#37: [podcast] Fix Duration and Play Button @ricoberger</li>
</ul>
<p>Changed</p>
<ul>
<li>#48: [core] Improve Android App Icons @ricoberger</li>
<li>#47: [core] Clear Cached Items @ricoberger</li>
<li>#39: [core] Add Privacy Policy and Terms & Conditions @ricoberger</li>
<li>#38: [core] Enable In-App Purchases for Android @ricoberger</li>
</ul>
</description>
<url>https://github.com/feeddeck/feeddeck/releases/tag/v1.1.1</url>
</release>
<release version="v1.1.0" date="2023-10-16">
<description>
<p>Added</p>
<ul>
<li>#31: [core] Add Dependabot Configuration @ricoberger</li>
<li>#24: [core] Add In-App Purchases @ricoberger</li>
<li>#16: [core] Add "Get Started" Guide @ricoberger</li>
<li>#15: [core] Add Flatpak @ricoberger</li>
</ul>
<p>Fixed</p>
<ul>
<li>#35: [core] Fix Google Client ID @ricoberger</li>
<li>#28: [core] Fix Title Alignment @ricoberger</li>
<li>#27: [core] Fix Vertical Scrolling @ricoberger</li>
<li>#22: [core] Fix Update of Read State for Large List of Items @ricoberger</li>
<li>#20: [youtube] Add Missing URL in isYoutubeUrl Function @ricoberger</li>
</ul>
<p>Changed</p>
<ul>
<li>#34: [core] Rework Profile Edge Function @ricoberger</li>
<li>#33: Bump the github-actions group with 5 updates @dependabot</li>
<li>#32: Bump the docker group in /supabase/functions/_cmd with 1 update @dependabot</li>
<li>#30: [core] Update NPM Packages @ricoberger</li>
<li>#29: [core] Update Flutter to v3.13.7 @ricoberger</li>
<li>#23: [core] Add Flatpak Files to Build Archive @ricoberger</li>
<li>#21: [core] Use Published Date for Item Ingestion @ricoberger</li>
<li>#19: [nitter] Allow Usage of Custom Nitter Instances @ricoberger</li>
<li>#18: [rss] Allow RSS Feeds without "published" Field @ricoberger</li>
<li>#17: [podcast] Improve Parsing @ricoberger</li>
<li>#14: [core] Fix Issues with App Submission @ricoberger</li>
</ul>
</description>
<url>https://github.com/feeddeck/feeddeck/releases/tag/v1.1.0</url>
</release>
<release version="v1.0.0" date="2023-09-17">
<description>
<p>Added</p>
<ul>
<li>#10: [core] Add Continuous Delivery for Landing Page @ricoberger</li>
<li>#7: [core] Add Continuous Delivery for Supabase @ricoberger</li>
<li>#8: [core] Add Info Section to Settings @ricoberger</li>
<li>#6: [core] Add Continuous Delivery for Desktop Apps @ricoberger</li>
<li>#4: Add GitHub Action to Publish Web Version @ricoberger</li>
<li>#3: [core] Add Continuous Delivery and Release Workflow @ricoberger</li>
</ul>
<p>Fixed</p>
<ul>
<li>#9: [core] Fix Continuous Delivery for Supabase @ricoberger</li>
</ul>
<p>Changed</p>
<ul>
<li>#12: Prepare v1.0.0 Release @ricoberger</li>
<li>#5: [core] Improve Media Handling @ricoberger</li>
<li>#2: [nitter] Add Media Gallery to Details View @ricoberger</li>
<li>#1: [nitter] Add Basic Authentication @ricoberger</li>
</ul>
</description>
<url>https://github.com/feeddeck/feeddeck/releases/tag/v1.0.0</url>
</release>
</releases>
<supports>
<control>pointing</control>
<control>keyboard</control>
<control>touch</control>
<control>tablet</control>
</supports>
<screenshots>
<screenshot type="default">
<image>https://raw.githubusercontent.com/feeddeck/feeddeck/main/app/linux/flatpak/screenshots/1.png</image>
</screenshot>
<screenshot type="default">
<image>https://raw.githubusercontent.com/feeddeck/feeddeck/main/app/linux/flatpak/screenshots/2.png</image>
</screenshot>
<screenshot type="default">
<image>https://raw.githubusercontent.com/feeddeck/feeddeck/main/app/linux/flatpak/screenshots/3.png</image>
</screenshot>
<screenshot type="default">
<image>https://raw.githubusercontent.com/feeddeck/feeddeck/main/app/linux/flatpak/screenshots/4.png</image>
</screenshot>
<screenshot type="default">
<image>https://raw.githubusercontent.com/feeddeck/feeddeck/main/app/linux/flatpak/screenshots/5.png</image>
</screenshot>
</screenshots>
</component>

View File

@@ -0,0 +1,12 @@
<?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="icon-linux">
<g id="bg" transform="matrix(1.77687,0,0,1.77687,-1011.62,-1480.98)">
<circle cx="1721.91" cy="1986.06" r="1152.59" style="fill:rgb(73,211,180);"/>
</g>
<g id="fg" transform="matrix(13.5357,0,0,13.0255,-2722.19,-2672.07)">
<path d="M443.567,272.226C438.383,276.258 434.063,278.274 430.607,278.274C423.695,278.274 416.783,274.146 409.871,265.89L369.263,293.826C362.927,298.434 359.759,304.194 359.759,311.106L359.759,429.186L359.471,429.474C359.471,432.354 358.559,435.234 356.735,438.114C354.911,440.994 352.943,442.434 350.831,442.434L350.255,442.434L350.255,439.554L347.951,442.722C345.071,442.53 337.583,439.17 325.487,432.642L325.487,432.93C313.199,425.826 302.927,422.274 294.671,422.274C286.223,422.274 278.447,425.442 271.343,431.778C268.079,434.658 265.439,438.162 263.423,442.29C261.407,446.418 260.111,451.266 259.535,456.834C263.759,451.842 268.943,449.058 275.087,448.482C276.239,448.482 277.007,448.386 277.391,448.194L277.967,448.194C282.191,448.194 290.351,450.594 302.447,455.394C308.207,457.698 315.215,458.85 323.471,458.85C334.991,458.85 347.471,455.778 360.911,449.634C376.271,442.53 385.871,434.082 389.711,424.29C390.287,422.946 390.575,421.602 390.575,420.258L390.863,370.722L410.735,370.722L410.735,431.778L421.391,431.778L421.391,301.314C425.231,300.354 428.687,298.482 431.759,295.698C434.831,292.914 437.807,289.314 440.687,284.898C443.183,281.058 444.431,278.274 444.431,276.546L444.719,276.258C444.719,275.49 444.911,274.53 445.295,273.378L443.567,272.226ZM356.015,297.282C350.447,298.242 345.503,299.874 341.183,302.178C336.863,304.482 332.975,307.266 329.519,310.53C321.647,317.634 317.711,326.562 317.711,337.314L317.711,338.754L317.999,339.042L317.999,340.194C317.231,340.194 316.655,340.098 316.271,339.906L313.679,339.906C298.127,339.906 290.351,347.874 290.351,363.81C290.351,368.418 291.695,372.498 294.383,376.05C297.071,379.602 300.719,381.378 305.327,381.378L305.327,379.362L305.039,379.074L305.039,377.922L304.751,377.634C304.751,373.218 308.015,371.01 314.543,371.01L316.559,371.01L317.135,371.298L318.287,371.298L318.287,413.634C318.095,414.018 317.951,414.498 317.855,415.074C317.759,415.65 317.615,416.13 317.423,416.514C317.039,417.282 316.703,418.194 316.415,419.25C316.127,420.306 315.887,421.026 315.695,421.41L318.575,421.41L319.151,421.122L319.727,421.122L320.015,420.834C322.703,420.834 326.351,419.202 330.959,415.938C344.399,407.106 351.119,396.93 351.119,385.41L351.119,310.818C351.119,308.706 351.647,306.45 352.703,304.05C353.759,301.65 355.055,300.162 356.591,299.586L356.015,297.282ZM375.023,266.754C370.415,270.978 365.231,273.09 359.471,273.09C351.407,273.09 345.647,272.226 342.191,270.498L341.615,270.498L341.327,270.21C338.063,269.634 335.375,269.106 333.263,268.626C331.151,268.146 329.615,267.81 328.655,267.618L325.775,267.618C320.207,267.042 317.231,266.562 316.847,266.178L314.831,267.042C298.319,267.426 286.799,273.186 280.271,284.322C277.391,289.314 275.951,293.73 275.951,297.57L275.951,300.162C282.671,295.938 286.991,293.826 288.911,293.826L290.063,293.826L290.351,293.538C294.767,293.538 302.063,294.594 312.239,296.706C321.839,298.818 329.039,299.874 333.839,299.874L334.127,300.162L335.279,300.162C344.687,300.162 353.615,297.09 362.063,290.946C371.087,284.418 376.079,276.642 377.039,267.618L375.023,266.754ZM409.583,301.89C409.775,301.89 409.919,301.938 410.015,302.034C410.111,302.13 410.351,302.082 410.735,301.89L410.735,340.482L390.863,340.482L390.863,294.402C397.967,299.394 404.207,301.89 409.583,301.89Z" style="fill:rgb(31,34,41);fill-rule:nonzero;"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 981 KiB

View File

@@ -6,11 +6,15 @@
#include "generated_plugin_registrant.h"
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
#include <window_manager/window_manager_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
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);
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);

View File

@@ -3,12 +3,14 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
media_kit_libs_linux
screen_retriever
url_launcher_linux
window_manager
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
media_kit_native_event_loop
)
set(PLUGIN_BUNDLED_LIBRARIES)

View File

@@ -11,6 +11,7 @@ import audio_session
import just_audio
import package_info_plus
import path_provider_foundation
import purchases_flutter
import screen_retriever
import shared_preferences_foundation
import sign_in_with_apple
@@ -25,6 +26,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
PurchasesFlutterPlugin.register(with: registry.registrar(forPlugin: "PurchasesFlutterPlugin"))
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SignInWithApplePlugin.register(with: registry.registrar(forPlugin: "SignInWithApplePlugin"))

View File

@@ -1,4 +1,4 @@
platform :osx, '10.14'
platform :osx, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@@ -11,11 +11,19 @@ PODS:
- FMDB/standard (2.7.5)
- just_audio (0.0.1):
- FlutterMacOS
- media_kit_native_event_loop (1.0.0):
- FlutterMacOS
- package_info_plus (0.0.1):
- FlutterMacOS
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- purchases_flutter (6.0.0):
- FlutterMacOS
- PurchasesHybridCommon (= 7.0.0)
- PurchasesHybridCommon (7.0.0):
- RevenueCat (= 4.27.0)
- RevenueCat (4.27.0)
- screen_retriever (0.0.1):
- FlutterMacOS
- shared_preferences_foundation (0.0.1):
@@ -37,10 +45,12 @@ DEPENDENCIES:
- audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- just_audio (from `Flutter/ephemeral/.symlinks/plugins/just_audio/macos`)
- media_kit_native_event_loop (from `Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- purchases_flutter (from `Flutter/ephemeral/.symlinks/plugins/purchases_flutter/macos`)
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/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`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
@@ -49,6 +59,8 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- FMDB
- PurchasesHybridCommon
- RevenueCat
EXTERNAL SOURCES:
app_links:
@@ -61,14 +73,18 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral
just_audio:
:path: Flutter/ephemeral/.symlinks/plugins/just_audio/macos
media_kit_native_event_loop:
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos
package_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
path_provider_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
purchases_flutter:
:path: Flutter/ephemeral/.symlinks/plugins/purchases_flutter/macos
screen_retriever:
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
shared_preferences_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
sign_in_with_apple:
:path: Flutter/ephemeral/.symlinks/plugins/sign_in_with_apple/macos
sqflite:
@@ -85,15 +101,19 @@ SPEC CHECKSUMS:
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
just_audio: 9b67ca7b97c61cfc9784ea23cd8cc55eb226d489
media_kit_native_event_loop: d20622d35dd6d06fe71223976bd70a2bcf595dce
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
purchases_flutter: 9aad80bf27960c38fdeafc27ab066cb55615aed5
PurchasesHybridCommon: af3b2413f9cb999bc1fdca44770bdaf39dfb89fa
RevenueCat: 84fbe2eb9bbf63e1abf346ccd3ff9ee45d633e3b
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
sign_in_with_apple: a9e97e744e8edc36aefc2723111f652102a7a727
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
PODFILE CHECKSUM: 8d40c19d3cbdb380d870685c3a564c989f1efa52
COCOAPODS: 1.12.1
COCOAPODS: 1.13.0

View File

@@ -26,6 +26,7 @@
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
553BE2622AC0648A002EA0C0 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 553BE2612AC0648A002EA0C0 /* StoreKit.framework */; };
FBF50439F2C4D0BF615EFED2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27D60858A3CD81E41E2CDBF2 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
@@ -70,6 +71,8 @@
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
46A27EFECA16837D10C377EF /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
553BE2612AC0648A002EA0C0 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
55A996D72AB9F2F200780025 /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
A5EAD2A55E3BD9F0DF253072 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
@@ -81,6 +84,7 @@
buildActionMask = 2147483647;
files = (
FBF50439F2C4D0BF615EFED2 /* Pods_Runner.framework in Frameworks */,
553BE2622AC0648A002EA0C0 /* StoreKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -152,6 +156,7 @@
33FAB671232836740065AC1E /* Runner */ = {
isa = PBXGroup;
children = (
55A996D72AB9F2F200780025 /* RunnerDebug.entitlements */,
33CC10F02044A3C60003C045 /* AppDelegate.swift */,
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
33E51913231747F40026EE4D /* DebugProfile.entitlements */,
@@ -165,6 +170,7 @@
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
553BE2612AC0648A002EA0C0 /* StoreKit.framework */,
27D60858A3CD81E41E2CDBF2 /* Pods_Runner.framework */,
);
name = Frameworks;
@@ -202,7 +208,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
33CC10EC2044A3C60003C045 = {
@@ -419,6 +425,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = 75AP6HWLUD;
@@ -428,6 +435,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
@@ -546,7 +554,8 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = 75AP6HWLUD;
@@ -556,6 +565,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -569,6 +579,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = 75AP6HWLUD;
@@ -578,6 +589,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};

View File

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

View File

@@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>

View File

@@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>

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.0.0+2
version: 1.1.1+7
environment:
sdk: '>=2.19.6 <3.0.0'
sdk: '>=3.1.3 <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
@@ -47,10 +47,10 @@ dependencies:
intl: ^0.18.1
just_audio: ^0.9.32
just_audio_background: ^0.0.1-beta.10
just_audio_windows: ^0.2.0
just_audio_mpv: ^0.1.6
just_audio_media_kit: ^1.0.0
package_info_plus: ^4.1.0
provider: ^6.0.4
purchases_flutter: ^6.0.0
rxdart: ^0.27.7
scroll_to_index: ^3.0.1
shared_preferences: ^2.1.0
@@ -160,7 +160,7 @@ msix_config:
publisher_display_name: Rico Berger
identity_name: 26077RicoBerger.FeedDeck
publisher: CN=7740451A-C179-450A-B346-7231CA231332
msix_version: 1.0.0.0
msix_version: 1.1.1.0
logo_path: templates/app-icon/windows.png
languages: en-us
capabilities: internetClient

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 182 KiB

View File

@@ -0,0 +1,12 @@
<?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="icon-linux">
<g id="bg" transform="matrix(1.77687,0,0,1.77687,-1011.62,-1480.98)">
<circle cx="1721.91" cy="1986.06" r="1152.59" style="fill:rgb(73,211,180);"/>
</g>
<g id="fg" transform="matrix(13.5357,0,0,13.0255,-2722.19,-2672.07)">
<path d="M443.567,272.226C438.383,276.258 434.063,278.274 430.607,278.274C423.695,278.274 416.783,274.146 409.871,265.89L369.263,293.826C362.927,298.434 359.759,304.194 359.759,311.106L359.759,429.186L359.471,429.474C359.471,432.354 358.559,435.234 356.735,438.114C354.911,440.994 352.943,442.434 350.831,442.434L350.255,442.434L350.255,439.554L347.951,442.722C345.071,442.53 337.583,439.17 325.487,432.642L325.487,432.93C313.199,425.826 302.927,422.274 294.671,422.274C286.223,422.274 278.447,425.442 271.343,431.778C268.079,434.658 265.439,438.162 263.423,442.29C261.407,446.418 260.111,451.266 259.535,456.834C263.759,451.842 268.943,449.058 275.087,448.482C276.239,448.482 277.007,448.386 277.391,448.194L277.967,448.194C282.191,448.194 290.351,450.594 302.447,455.394C308.207,457.698 315.215,458.85 323.471,458.85C334.991,458.85 347.471,455.778 360.911,449.634C376.271,442.53 385.871,434.082 389.711,424.29C390.287,422.946 390.575,421.602 390.575,420.258L390.863,370.722L410.735,370.722L410.735,431.778L421.391,431.778L421.391,301.314C425.231,300.354 428.687,298.482 431.759,295.698C434.831,292.914 437.807,289.314 440.687,284.898C443.183,281.058 444.431,278.274 444.431,276.546L444.719,276.258C444.719,275.49 444.911,274.53 445.295,273.378L443.567,272.226ZM356.015,297.282C350.447,298.242 345.503,299.874 341.183,302.178C336.863,304.482 332.975,307.266 329.519,310.53C321.647,317.634 317.711,326.562 317.711,337.314L317.711,338.754L317.999,339.042L317.999,340.194C317.231,340.194 316.655,340.098 316.271,339.906L313.679,339.906C298.127,339.906 290.351,347.874 290.351,363.81C290.351,368.418 291.695,372.498 294.383,376.05C297.071,379.602 300.719,381.378 305.327,381.378L305.327,379.362L305.039,379.074L305.039,377.922L304.751,377.634C304.751,373.218 308.015,371.01 314.543,371.01L316.559,371.01L317.135,371.298L318.287,371.298L318.287,413.634C318.095,414.018 317.951,414.498 317.855,415.074C317.759,415.65 317.615,416.13 317.423,416.514C317.039,417.282 316.703,418.194 316.415,419.25C316.127,420.306 315.887,421.026 315.695,421.41L318.575,421.41L319.151,421.122L319.727,421.122L320.015,420.834C322.703,420.834 326.351,419.202 330.959,415.938C344.399,407.106 351.119,396.93 351.119,385.41L351.119,310.818C351.119,308.706 351.647,306.45 352.703,304.05C353.759,301.65 355.055,300.162 356.591,299.586L356.015,297.282ZM375.023,266.754C370.415,270.978 365.231,273.09 359.471,273.09C351.407,273.09 345.647,272.226 342.191,270.498L341.615,270.498L341.327,270.21C338.063,269.634 335.375,269.106 333.263,268.626C331.151,268.146 329.615,267.81 328.655,267.618L325.775,267.618C320.207,267.042 317.231,266.562 316.847,266.178L314.831,267.042C298.319,267.426 286.799,273.186 280.271,284.322C277.391,289.314 275.951,293.73 275.951,297.57L275.951,300.162C282.671,295.938 286.991,293.826 288.911,293.826L290.063,293.826L290.351,293.538C294.767,293.538 302.063,294.594 312.239,296.706C321.839,298.818 329.039,299.874 333.839,299.874L334.127,300.162L335.279,300.162C344.687,300.162 353.615,297.09 362.063,290.946C371.087,284.418 376.079,276.642 377.039,267.618L375.023,266.754ZM409.583,301.89C409.775,301.89 409.919,301.938 410.015,302.034C410.111,302.13 410.351,302.082 410.735,301.89L410.735,340.482L390.863,340.482L390.863,294.402C397.967,299.394 404.207,301.89 409.583,301.89Z" style="fill:rgb(31,34,41);fill-rule:nonzero;"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

View File

@@ -7,7 +7,7 @@
#include "generated_plugin_registrant.h"
#include <app_links/app_links_plugin_c_api.h>
#include <just_audio_windows/just_audio_windows_plugin.h>
#include <media_kit_libs_windows_audio/media_kit_libs_windows_audio_plugin_c_api.h>
#include <screen_retriever/screen_retriever_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
#include <window_manager/window_manager_plugin.h>
@@ -15,8 +15,8 @@
void RegisterPlugins(flutter::PluginRegistry* registry) {
AppLinksPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
JustAudioWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("JustAudioWindowsPlugin"));
MediaKitLibsWindowsAudioPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MediaKitLibsWindowsAudioPluginCApi"));
ScreenRetrieverPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(

View File

@@ -4,13 +4,14 @@
list(APPEND FLUTTER_PLUGIN_LIST
app_links
just_audio_windows
media_kit_libs_windows_audio
screen_retriever
url_launcher_windows
window_manager
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
media_kit_native_event_loop
)
set(PLUGIN_BUNDLED_LIBRARIES)

View File

@@ -56,7 +56,7 @@ const IOS = () => (
<path d="M318.7,268.7C318.5,232 335.1,204.3 368.7,183.9C349.9,157 321.5,142.2 284,139.3C248.5,136.5 209.7,160 195.5,160C180.5,160 146.1,140.3 119.1,140.3C63.3,141.2 4,184.8 4,273.5C4,299.7 8.8,326.767 18.4,354.7C31.2,391.4 77.4,481.4 125.6,479.9C150.8,479.3 168.6,462 201.4,462C233.2,462 249.7,479.9 277.8,479.9C326.4,479.2 368.2,397.4 380.4,360.6C315.2,329.9 318.7,270.6 318.7,268.7ZM262.1,104.5C289.4,72.1 286.9,42.6 286.1,32C262,33.4 234.1,48.4 218.2,66.9C200.7,86.7 190.4,111.2 192.6,138.8C218.7,140.8 242.5,127.4 262.1,104.5Z" />
</g>
</svg>
<span className="pt-4 text-xl">iOS (Comming Soon)</span>
<span className="pt-4 text-xl">iOS</span>
</div>
</a>
);
@@ -76,7 +76,7 @@ const Android = () => (
<path d="M420.55,301.93C407.384,301.93 396.55,291.096 396.55,277.93C396.55,264.764 407.384,253.93 420.55,253.93C433.716,253.93 444.55,264.764 444.55,277.93C444.55,291.096 433.716,301.93 420.55,301.93M155.45,301.93C142.284,301.93 131.45,291.096 131.45,277.93C131.45,264.764 142.284,253.93 155.45,253.93C168.616,253.93 179.45,264.764 179.45,277.93C179.45,291.096 168.616,301.93 155.45,301.93M429.15,157.45L477.09,74.45C478.196,72.802 478.786,70.862 478.786,68.878C478.786,63.392 474.272,58.878 468.786,58.878C464.987,58.878 461.502,61.043 459.82,64.45L411.28,148.52C332.897,113.365 243.103,113.365 164.72,148.52L116.18,64.45C114.498,61.043 111.013,58.878 107.214,58.878C101.728,58.878 97.214,63.392 97.214,68.878C97.214,70.862 97.804,72.802 98.91,74.45L146.85,157.45C64.53,202.22 8.24,285.55 0,384L576,384C567.76,285.55 511.46,202.22 429.15,157.45" />
</g>
</svg>
<span className="pt-4 text-xl">Android (Comming Soon)</span>
<span className="pt-4 text-xl">Android</span>
</div>
</a>
);
@@ -119,7 +119,7 @@ const MacOS = () => (
<path d="M318.7,268.7C318.5,232 335.1,204.3 368.7,183.9C349.9,157 321.5,142.2 284,139.3C248.5,136.5 209.7,160 195.5,160C180.5,160 146.1,140.3 119.1,140.3C63.3,141.2 4,184.8 4,273.5C4,299.7 8.8,326.767 18.4,354.7C31.2,391.4 77.4,481.4 125.6,479.9C150.8,479.3 168.6,462 201.4,462C233.2,462 249.7,479.9 277.8,479.9C326.4,479.2 368.2,397.4 380.4,360.6C315.2,329.9 318.7,270.6 318.7,268.7ZM262.1,104.5C289.4,72.1 286.9,42.6 286.1,32C262,33.4 234.1,48.4 218.2,66.9C200.7,86.7 190.4,111.2 192.6,138.8C218.7,140.8 242.5,127.4 262.1,104.5Z" />
</g>
</svg>
<span className="pt-4 text-xl">macOS (Comming Soon)</span>
<span className="pt-4 text-xl">macOS</span>
</div>
</a>
);
@@ -139,7 +139,7 @@ const Windows = () => (
<path d="M0,93.7L183.6,68.4L183.6,245.8L0,245.8L0,93.7ZM0,418.3L183.6,443.6L183.6,268.4L0,268.4L0,418.3ZM203.8,446.3L448,480L448,268.4L203.8,268.4L203.8,446.3ZM203.8,65.7L203.8,245.8L448,245.8L448,32L203.8,65.7Z" />
</g>
</svg>
<span className="pt-4 text-xl">Windows (Comming Soon)</span>
<span className="pt-4 text-xl">Windows</span>
</div>
</a>
);
@@ -159,7 +159,7 @@ const Linux = () => (
<path d="M220.8,123.3C221.8,123.8 222.6,125 223.8,125C224.9,125 226.6,124.6 226.7,123.5C226.9,122.1 224.8,121.2 223.5,120.6C221.8,119.9 219.6,119.6 218,120.5C217.6,120.7 217.2,121.2 217.4,121.6C217.7,122.9 219.7,122.7 220.8,123.3ZM198.9,125C200.1,125 200.9,123.8 201.9,123.3C203,122.7 205,122.9 205.4,121.7C205.6,121.3 205.2,120.8 204.8,120.6C203.2,119.7 201,120 199.3,120.7C198,121.3 195.9,122.2 196.1,123.6C196.2,124.6 197.9,125.1 198.9,125ZM420,403.8C416.4,399.8 414.7,392.2 412.8,384.1C411,376 408.9,367.3 402.3,361.7C401,360.6 399.7,359.6 398.3,358.8C397,358 395.6,357.3 394.2,356.8C403.4,329.5 399.8,302.3 390.5,277.7C379.1,247.6 359.2,221.3 344,203.3C326.9,181.8 310.3,161.4 310.6,131.3C311.1,85.4 315.7,0.1 234.8,0C132.4,-0.2 158,103.4 156.9,135.2C155.2,158.6 150.5,177 134.4,199.9C115.5,222.4 88.9,258.7 76.3,296.6C70.3,314.5 67.5,332.7 70.1,349.9C63.6,355.7 58.7,364.6 53.5,370.1C49.3,374.4 43.2,376 36.5,378.4C29.8,380.8 22.5,384.4 18,392.9C15.9,396.8 15.2,401 15.2,405.3C15.2,409.2 15.8,413.2 16.4,417.1C17.6,425.2 18.9,432.8 17.2,437.9C12,452.3 11.3,462.3 15,469.6C18.8,476.9 26.4,480.1 35.1,481.9C52.4,485.5 75.9,484.6 94.4,494.4C114.2,504.8 134.3,508.5 150.3,504.8C161.9,502.2 171.4,495.2 176.2,484.6C188.7,484.5 202.5,479.2 224.5,478C239.4,476.8 258.1,483.3 279.6,482.1C280.2,484.4 281,486.7 282.1,488.8L282.1,488.9C290.4,505.6 305.9,513.2 322.4,511.9C339,510.6 356.5,500.9 370.7,484C384.3,467.6 406.7,460.8 421.6,451.8C429,447.3 435,441.7 435.5,433.5C435.9,425.3 431.1,416.2 420,403.8ZM223.7,87.3C233.5,65.1 257.9,65.5 267.7,86.9C274.2,101.1 271.3,117.8 263.4,127.3C261.8,126.5 257.5,124.7 250.8,122.4C251.9,121.2 253.9,119.7 254.7,117.8C259.5,106 254.5,90.8 245.6,90.5C238.3,90 231.7,101.3 233.8,113.5C229.7,111.5 224.4,110 220.8,109.1C219.8,102.2 220.5,94.5 223.7,87.3ZM183,75.8C193.1,75.8 203.8,90 202.1,109.3C198.6,110.3 195,111.8 191.9,113.9C193.1,105 188.6,93.8 182.3,94.3C173.9,95 172.5,115.5 180.5,122.4C181.5,123.2 182.4,122.2 174.6,127.9C159,113.3 164.1,75.8 183,75.8ZM169.4,136.5C175.6,131.9 183,126.5 183.5,126C188.2,121.6 197,111.8 211.4,111.8C218.5,111.8 227,114.1 237.3,120.7C243.6,124.8 248.6,125.1 259.9,130C268.3,133.5 273.6,139.7 270.4,148.2C267.8,155.3 259.4,162.6 247.7,166.3C236.6,169.9 227.9,182.3 209.5,181.2C205.6,181 202.5,180.2 199.9,179.1C191.9,175.6 187.7,168.7 179.9,164.1C171.3,159.3 166.7,153.7 165.2,148.8C163.8,143.9 165.2,139.8 169.4,136.5ZM172.7,470.5C170,505.6 128.8,504.9 97.4,488.5C67.5,472.7 28.8,482 20.9,466.6C18.5,461.9 18.5,453.9 23.5,440.2L23.5,440C25.9,432.4 24.1,424 22.9,416.1C21.7,408.3 21.1,401.1 23.8,396.1C27.3,389.4 32.3,387 38.6,384.8C48.9,381.1 50.4,381.4 58.2,374.9C63.7,369.2 67.7,362 72.5,356.9C77.6,351.4 82.5,348.8 90.2,350C98.3,351.2 105.3,356.8 112.1,366L131.7,401.6C141.2,421.5 174.8,450 172.7,470.5ZM171.3,444.6C167.2,438 161.7,431 156.9,425C164,425 171.1,422.8 173.6,416.1C175.9,409.9 173.6,401.2 166.2,391.2C152.7,373 127.9,358.7 127.9,358.7C114.4,350.3 106.8,340 103.3,328.8C99.8,317.6 100.3,305.5 103,293.6C108.2,270.7 121.6,248.4 130.2,234.4C132.5,232.7 131,237.6 121.5,255.2C113,271.3 97.1,308.5 118.9,337.6C119.5,316.9 124.4,295.8 132.7,276.1C144.7,248.7 170,201.2 172,163.4C173.1,164.2 176.6,166.6 178.2,167.5C182.8,170.2 186.3,174.2 190.8,177.8C203.2,187.8 219.3,187 233.2,179C239.4,175.5 244.4,171.5 249.1,170C259,166.9 266.9,161.4 271.4,155C279.1,185.4 297.1,229.3 308.6,250.7C314.7,262.1 326.9,286.2 332.2,315.3C335.5,315.2 339.2,315.7 343.1,316.7C356.9,281 331.4,242.5 319.8,231.8C315.1,227.2 314.9,225.2 317.2,225.3C329.8,236.5 346.4,259 352.4,284.3C355.2,295.9 355.7,308 352.8,320C369.2,326.8 388.7,337.9 383.5,354.8C381.3,354.7 380.3,354.8 379.3,354.8C382.5,344.7 375.4,337.2 356.5,328.7C336.9,320.1 320.5,320.1 318.2,341.2C306.1,345.4 299.9,355.9 296.8,368.5C294,379.7 293.2,393.2 292.4,408.4C291.9,416.1 288.8,426.4 285.6,437.4C253.5,460.3 208.9,470.3 171.3,444.6ZM428.7,433.1C427.8,449.9 387.5,453 365.5,479.6C352.3,495.3 336.1,504 321.9,505.1C307.7,506.2 295.4,500.3 288.2,485.8C283.5,474.7 285.8,462.7 289.3,449.5C293,435.3 298.5,420.7 299.2,408.9C300,393.7 300.9,380.4 303.4,370.2C306,359.9 310,353 317.1,349.1C317.4,348.9 317.8,348.8 318.1,348.6C318.9,361.8 325.4,375.2 336.9,378.1C349.5,381.4 367.6,370.6 375.3,361.8C384.3,361.5 391,360.9 397.9,366.9C407.8,375.4 405,397.2 415,408.5C425.6,420.1 429,428 428.7,433.1ZM173.3,148.7C175.3,150.6 178,153.2 181.3,155.8C187.9,161 197.1,166.4 208.6,166.4C220.2,166.4 231.1,160.5 240.4,155.6C245.3,153 251.3,148.6 255.2,145.2C259.1,141.8 261.1,138.9 258.3,138.6C255.5,138.3 255.7,141.2 252.3,143.7C247.9,146.9 242.6,151.1 238.4,153.5C231,157.7 218.9,163.7 208.5,163.7C198.1,163.7 189.8,158.9 183.6,154C180.5,151.5 177.9,149 175.9,147.1C174.4,145.7 174,142.5 171.6,142.2C170.2,142.1 169.8,145.9 173.3,148.7Z" />
</g>
</svg>
<span className="pt-4 text-xl">Linux (Comming Soon)</span>
<span className="pt-4 text-xl">Linux</span>
</div>
</a>
);

View File

@@ -0,0 +1,405 @@
import { ReactNode, useState } from "react";
import type { Metadata } from "next";
import Image from "next/image";
import Link from "next/link";
import { generalMetadata } from "@/helpers/metadata";
import GetStartedEntry from "@/components/getstartedentry";
export const metadata: Metadata = {
...generalMetadata,
title: "FeedDeck - Support",
};
export default function Support() {
return (
<main>
<div className="container p-8 mx-auto">
<div className="max-w-screen-xl mx-auto">
<h1 className="text-4xl font-bold">Get Started - Desktop</h1>
<Link href="/get-started/mobile">Go to mobile version</Link>
<GetStartedEntry
title="Sign In / Sign Up"
images={[
"/get-started/desktop/1-1.webp",
"/get-started/desktop/1-2.webp",
"/get-started/desktop/1-3.webp",
]}
>
<p className="pt-4">
You can sign in to FeedDeck with your{" "}
<span className="font-bold">Apple</span> account,{" "}
<span className="font-bold">Google</span> account or with an{" "}
<span className="font-bold">email address and password</span>.
</p>
<p className="pt-4">
To sign in with an email address and password select{" "}
<span className="font-bold">
&quot;Sign in with FeedDeck&quot;
</span>{" "}
on the start parge. If you do not have an account yet, you can
create one by clicking on{" "}
<span className="font-bold">Sign Up</span> on the sign in page.
</p>
<p className="pt-4">
If you sign up using your email address and password we send you
an verification email to confirm your email address. Once the
verification is done you are able to sign in using the provided
email address and password.
</p>
</GetStartedEntry>
<GetStartedEntry
title="Add a Column"
images={[
"/get-started/desktop/2-1.webp",
"/get-started/desktop/2-2.webp",
]}
>
<p className="pt-4">
To add a new column click on the{" "}
<span className="font-bold">plus icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" />
</svg>
</span>) on the right side in the bottom bar.
</p>
<p className="pt-4">
After you clicked on the plus icon a dialog will open. In this
dialog you have to provide the name of the column and click on the
{" "}
<span className="font-bold">&quot;Create Column&quot;</span>{" "}
button to create the column.
</p>
</GetStartedEntry>
<GetStartedEntry
title="Manage Columns"
images={[
"/get-started/desktop/3-1.webp",
]}
>
<p className="pt-4">
To adjust the name or position of a column or to delete a column
click on the <span className="font-bold">settings icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z" />
</svg>
</span>) on the right side next to the column name.
</p>
<p className="pt-4">
Adjust the name of the column in the input field and click on the
{" "}
<span className="font-bold">save icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M840-680v480q0 33-23.5 56.5T760-120H200q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h480l160 160Zm-80 34L646-760H200v560h560v-446ZM480-240q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35ZM240-560h360v-160H240v160Zm-40-86v446-560 114Z" />
</svg>
</span>) to change the name of the column.
</p>
<p className="pt-4">
To adjust the position of a column use the arrow keys on the left
and right side on the bottom of the column settings.
</p>
<p className="pt-4">
To delete a column click on the{" "}
<span className="font-bold">trash icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z" />
</svg>
</span>) in the middle on the bottom of the column settings.
</p>
</GetStartedEntry>
<GetStartedEntry
title="Add a Source"
images={[
"/get-started/desktop/4-1.webp",
"/get-started/desktop/4-2.webp",
"/get-started/desktop/4-3.webp",
]}
>
<p className="pt-4">
To add a new source click on the{" "}
<span className="font-bold">settings icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z" />
</svg>
</span>) on the right side next to the column name. When the
settings menu is opened click on the{" "}
<span className="font-bold">&quot;Add Source&quot;</span>{" "}
button to add a new source to the column.
</p>
<p className="pt-4">
Select the type of the source you want to add, e.g. RSS, Podcast,
YouTube or Mastodon and provide the required data in the next
step. Once you have provided all the required data click on the
{" "}
<span className="font-bold">&quot;Add Source&quot;</span>{" "}
button to add the source to the column. In the following you find
some examples for some of the sources:
</p>
<div className="pl-8 pt-4 break-all">
<ul className="list-disc">
<li>
<span className="font-bold">RSS:</span>{" "}
https://www.tagesschau.de/xml/rss2/
</li>
<li>
<span className="font-bold">Podcast:</span>{" "}
https://kubernetespodcast.com/feeds/audio.xml
</li>
<li>
<span className="font-bold">Podcast:</span>{" "}
https://www.youtube.com/user/tagesschau
</li>
<li>
<span className="font-bold">Mastodon:</span>{" "}
@ricoberger@hachyderm.io
</li>
</ul>
</div>
</GetStartedEntry>
<GetStartedEntry
title="Manage Sources"
images={[
"/get-started/desktop/5-1.webp",
]}
>
<p className="pt-4">
To manage the sources for a column click on the{" "}
<span className="font-bold">settings icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z" />
</svg>
</span>) on the right side next to the column name.
</p>
<p className="pt-4">
You can delete a source by clicking on the{" "}
<span className="font-bold">trash icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z" />
</svg>
</span>) on the right side of the source name.
</p>
</GetStartedEntry>
<GetStartedEntry
title="Filter Items"
images={[
"/get-started/desktop/6-1.webp",
]}
>
<p className="pt-4">
You can filter items by searching for the title and content of an
item or by filtering by read, unread or bookmarked items. You can
only show items related to a single source.
</p>
<p className="pt-4">
To search for an item with a specifc title or content provide your
search term in the search input field and press{" "}
<span className="font-bold">Enter</span>.
</p>
<p className="pt-4">
To only show read, unread or bookmarked items, click on the{" "}
<span className="font-bold">filter icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M400-240v-80h160v80H400ZM240-440v-80h480v80H240ZM120-640v-80h720v80H120Z" />
</svg>
</span>) next to the search input field and select the
corresponding option.
</p>
<p className="pt-4">
To only show items related to a single source select the source
below the search input field. To show all items click again on the
selected source.
</p>
</GetStartedEntry>
<GetStartedEntry
title="Mark Items as Read and Bookmark Items"
images={[
"/get-started/desktop/7-1.webp",
"/get-started/desktop/7-2.webp",
]}
>
<p className="pt-4">
To mark an item as{" "}
<span className="font-bold">read / unread</span>{" "}
click a bit longer on the item and select{" "}
<span className="font-bold">&quot;Mark as Read&quot;</span> /{" "}
<span className="font-bold">&quot;Mark as Unread&quot;</span>{" "}
from the menu.
</p>
<p className="pt-4">
To bookmark an item click a bit longer on the item and select{" "}
<span className="font-bold">&quot;Add Bookmark&quot;</span>{" "}
from the menu.
</p>
<p className="pt-4">
When you click on an item the details view for the selected item
is opened and the item will be marked as read automatically. You
can then use the icons in the top right corner in the{" "}
<span className="font-bold">details view</span>{" "}
to mark an items as read / unread or to add it to your bookmarks.
</p>
</GetStartedEntry>
<GetStartedEntry
title="Manage Decks"
images={[
"/get-started/desktop/8-1.webp",
"/get-started/desktop/8-2.webp",
]}
>
<p className="pt-4">
To manage your decks click on the{" "}
<span className="font-bold">settings icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z" />
</svg>
</span>) on the right side in the bottom bar. This will open the
settings menu where you can select the deck you want to view and
where you can manage your decks.
</p>
<p className="pt-4">
To add, edit or delete decks click on the{" "}
<span className="font-bold">edit icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M200-200h56l345-345-56-56-345 345v56Zm572-403L602-771l56-56q23-23 56.5-23t56.5 23l56 56q23 23 24 55.5T829-660l-57 57Zm-58 59L290-120H120v-170l424-424 170 170Zm-141-29-28-28 56 56-28-28Z" />
</svg>
</span>) on the right side of the decks section.
</p>
<p className="pt-4">
To add a new deck use the last input field in the decks section.
After you have provided a name click on the{" "}
<span className="font-bold">plus icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" />
</svg>
</span>) to add a new deck.
</p>
<p className="pt-4">
To adjust the name of a deck use the corresponding input field and
click the <span className="font-bold">save icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M840-680v480q0 33-23.5 56.5T760-120H200q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h480l160 160Zm-80 34L646-760H200v560h560v-446ZM480-240q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35ZM240-560h360v-160H240v160Zm-40-86v446-560 114Z" />
</svg>
</span>) to save the changes.
</p>
<p className="pt-4">
To delete a deck click on the{" "}
<span className="font-bold">trash icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z" />
</svg>
</span>) next to the deck name.
</p>
</GetStartedEntry>
</div>
</div>
</main>
);
}
const Entry = (
{ title, images, children }: {
title: string;
images: string[];
children: ReactNode;
},
) => {
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-16">
<div>
<div className="h-full flex items-center">
<div>
<h2 className="text-xl font-bold">{title}</h2>
{children}
</div>
</div>
</div>
<div className="col-span-2">
<div className="h-full flex items-center justify-center">
<div className="p-2 bg-secondary rounded-lg">
<Image
src={images[0]}
width={0}
height={0}
style={{ width: "100%", height: "auto" }}
className="object-cover"
alt="Feature"
loading="eager"
/>
</div>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,412 @@
import { ReactNode } from "react";
import type { Metadata } from "next";
import Image from "next/image";
import Link from "next/link";
import { generalMetadata } from "@/helpers/metadata";
import GetStartedEntry from "@/components/getstartedentry";
export const metadata: Metadata = {
...generalMetadata,
title: "FeedDeck - Support",
};
export default function Support() {
return (
<main>
<div className="container p-8 mx-auto">
<div className="max-w-screen-xl mx-auto">
<h1 className="text-4xl font-bold">Get Started - Mobile</h1>
<Link href="/get-started/desktop">Go to desktop version</Link>
<GetStartedEntry
title="Sign In / Sign Up"
images={[
"/get-started/mobile/1-1.webp",
"/get-started/mobile/1-2.webp",
"/get-started/mobile/1-3.webp",
]}
isMobile={true}
>
<p className="pt-4">
You can sign in to FeedDeck with your{" "}
<span className="font-bold">Apple</span> account,{" "}
<span className="font-bold">Google</span> account or with an{" "}
<span className="font-bold">email address and password</span>.
</p>
<p className="pt-4">
To sign in with an email address and password select{" "}
<span className="font-bold">
&quot;Sign in with FeedDeck&quot;
</span>{" "}
on the start parge. If you do not have an account yet, you can
create one by clicking on{" "}
<span className="font-bold">Sign Up</span> on the sign in page.
</p>
<p className="pt-4">
If you sign up using your email address and password we send you
an verification email to confirm your email address. Once the
verification is done you are able to sign in using the provided
email address and password.
</p>
</GetStartedEntry>
<GetStartedEntry
title="Add a Column"
images={[
"/get-started/mobile/2-1.webp",
"/get-started/mobile/2-2.webp",
]}
isMobile={true}
>
<p className="pt-4">
To add a new column click on the{" "}
<span className="font-bold">plus icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" />
</svg>
</span>) in the sidebar on the left side.
</p>
<p className="pt-4">
After you clicked on the plus icon a dialog will open. In this
dialog you have to provide the name of the column and click on the
{" "}
<span className="font-bold">&quot;Create Column&quot;</span>{" "}
button to create the column.
</p>
</GetStartedEntry>
<GetStartedEntry
title="Manage Columns"
images={[
"/get-started/mobile/3-1.webp",
]}
isMobile={true}
>
<p className="pt-4">
To adjust the name or position of a column or to delete a column
click on the <span className="font-bold">settings icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z" />
</svg>
</span>) on the right side next to the column name.
</p>
<p className="pt-4">
Adjust the name of the column in the input field and click on the
{" "}
<span className="font-bold">save icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M840-680v480q0 33-23.5 56.5T760-120H200q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h480l160 160Zm-80 34L646-760H200v560h560v-446ZM480-240q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35ZM240-560h360v-160H240v160Zm-40-86v446-560 114Z" />
</svg>
</span>) to change the name of the column.
</p>
<p className="pt-4">
To adjust the position of a column use the arrow keys on the left
and right side on the bottom of the column settings.
</p>
<p className="pt-4">
To delete a column click on the{" "}
<span className="font-bold">trash icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z" />
</svg>
</span>) in the middle on the bottom of the column settings.
</p>
</GetStartedEntry>
<GetStartedEntry
title="Add a Source"
images={[
"/get-started/mobile/4-1.webp",
"/get-started/mobile/4-2.webp",
"/get-started/mobile/4-3.webp",
]}
isMobile={true}
>
<p className="pt-4">
To add a new source click on the{" "}
<span className="font-bold">settings icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z" />
</svg>
</span>) on the right side next to the column name. When the
settings menu is opened click on the{" "}
<span className="font-bold">&quot;Add Source&quot;</span>{" "}
button to add a new source to the column.
</p>
<p className="pt-4">
Select the type of the source you want to add, e.g. RSS, Podcast,
YouTube or Mastodon and provide the required data in the next
step. Once you have provided all the required data click on the
{" "}
<span className="font-bold">&quot;Add Source&quot;</span>{" "}
button to add the source to the column. In the following you find
some examples for some of the sources:
</p>
<div className="pl-8 pt-4 break-all">
<ul className="list-disc">
<li>
<span className="font-bold">RSS:</span>{" "}
https://www.tagesschau.de/xml/rss2/
</li>
<li>
<span className="font-bold">Podcast:</span>{" "}
https://kubernetespodcast.com/feeds/audio.xml
</li>
<li>
<span className="font-bold">Podcast:</span>{" "}
https://www.youtube.com/user/tagesschau
</li>
<li>
<span className="font-bold">Mastodon:</span>{" "}
@ricoberger@hachyderm.io
</li>
</ul>
</div>
</GetStartedEntry>
<GetStartedEntry
title="Manage Sources"
images={[
"/get-started/mobile/5-1.webp",
]}
isMobile={true}
>
<p className="pt-4">
To manage the sources for a column click on the{" "}
<span className="font-bold">settings icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z" />
</svg>
</span>) on the right side next to the column name.
</p>
<p className="pt-4">
You can delete a source by clicking on the{" "}
<span className="font-bold">trash icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z" />
</svg>
</span>) on the right side of the source name.
</p>
</GetStartedEntry>
<GetStartedEntry
title="Filter Items"
images={[
"/get-started/mobile/6-1.webp",
]}
isMobile={true}
>
<p className="pt-4">
You can filter items by searching for the title and content of an
item or by filtering by read, unread or bookmarked items. You can
only show items related to a single source.
</p>
<p className="pt-4">
To search for an item with a specifc title or content provide your
search term in the search input field and press{" "}
<span className="font-bold">Enter</span>.
</p>
<p className="pt-4">
To only show read, unread or bookmarked items, click on the{" "}
<span className="font-bold">filter icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M400-240v-80h160v80H400ZM240-440v-80h480v80H240ZM120-640v-80h720v80H120Z" />
</svg>
</span>) next to the search input field and select the
corresponding option.
</p>
<p className="pt-4">
To only show items related to a single source select the source
below the search input field. To show all items click again on the
selected source.
</p>
</GetStartedEntry>
<GetStartedEntry
title="Mark Items as Read and Bookmark Items"
images={[
"/get-started/mobile/7-1.webp",
"/get-started/mobile/7-2.webp",
]}
isMobile={true}
>
<p className="pt-4">
To mark an item as{" "}
<span className="font-bold">read / unread</span>{" "}
click a bit longer on the item and select{" "}
<span className="font-bold">&quot;Mark as Read&quot;</span> /{" "}
<span className="font-bold">&quot;Mark as Unread&quot;</span>{" "}
from the menu.
</p>
<p className="pt-4">
To bookmark an item click a bit longer on the item and select{" "}
<span className="font-bold">&quot;Add Bookmark&quot;</span>{" "}
from the menu.
</p>
<p className="pt-4">
When you click on an item the details view for the selected item
is opened and the item will be marked as read automatically. You
can then use the icons in the top right corner in the{" "}
<span className="font-bold">details view</span>{" "}
to mark an items as read / unread or to add it to your bookmarks.
</p>
</GetStartedEntry>
<GetStartedEntry
title="Manage Decks"
images={[
"/get-started/mobile/8-1.webp",
"/get-started/mobile/8-2.webp",
]}
isMobile={true}
>
<p className="pt-4">
To manage your decks click on the{" "}
<span className="font-bold">settings icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z" />
</svg>
</span>) in the sidebar on the left side. This will open the
settings menu where you can select the deck you want to view and
where you can manage your decks.
</p>
<p className="pt-4">
To add, edit or delete decks click on the{" "}
<span className="font-bold">edit icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M200-200h56l345-345-56-56-345 345v56Zm572-403L602-771l56-56q23-23 56.5-23t56.5 23l56 56q23 23 24 55.5T829-660l-57 57Zm-58 59L290-120H120v-170l424-424 170 170Zm-141-29-28-28 56 56-28-28Z" />
</svg>
</span>) on the right side of the decks section.
</p>
<p className="pt-4">
To add a new deck use the last input field in the decks section.
After you have provided a name click on the{" "}
<span className="font-bold">plus icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" />
</svg>
</span>) to add a new deck.
</p>
<p className="pt-4">
To adjust the name of a deck use the corresponding input field and
click the <span className="font-bold">save icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M840-680v480q0 33-23.5 56.5T760-120H200q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h480l160 160Zm-80 34L646-760H200v560h560v-446ZM480-240q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35ZM240-560h360v-160H240v160Zm-40-86v446-560 114Z" />
</svg>
</span>) to save the changes.
</p>
<p className="pt-4">
To delete a deck click on the{" "}
<span className="font-bold">trash icon</span>{" "}
(<span className="font-bold inline-flex items-baseline">
<svg
className="w-4 h-4 fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
>
<path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z" />
</svg>
</span>) next to the deck name.
</p>
</GetStartedEntry>
</div>
</div>
</main>
);
}
const Entry = (
{ title, image, children }: {
title: string;
image: string;
children: ReactNode;
},
) => (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-16">
<div>
<div className="h-full flex items-center">
<div>
<h2 className="text-xl font-bold">{title}</h2>
{children}
</div>
</div>
</div>
<div className="col-span-2">
<div className="h-full flex items-center justify-center">
<div className="p-2 bg-secondary rounded-lg">
<Image
src={image}
width={0}
height={0}
style={{ width: "100%", height: "auto" }}
className="object-cover"
alt="Feature"
loading="eager"
/>
</div>
</div>
</div>
</div>
);

View File

@@ -65,8 +65,9 @@ export default function Pricing() {
<div className="flex justify-center items-baseline my-8">
<span className="mr-2 text-5xl font-extrabold">5</span>
<span className="text-gray-400">
/month
/ month{" "}
</span>
<span className="text-xl text-gray-400">*</span>
</div>
<ul role="list" className="mb-8 space-y-4 text-left">
@@ -129,6 +130,10 @@ export default function Pricing() {
</div>
</div>
</div>
<div className="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
* The price for a premium account buyed via in-app purchase on iOS,
macOS or Android can be different due to the fees of the app stores.
</div>
</main>
);
}
@@ -142,9 +147,9 @@ const Feature = ({ children }: { children: ReactNode }) => (
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
clipRule="evenodd"
>
</path>
</svg>

View File

@@ -84,23 +84,101 @@ export default function TermsAndConditions() {
</p>
<p className="py-4">
At some point, we may wish to update the app. The app is currently
available on iOS the requirements for the system(and for any
additional systems we decide to extend the availability of the app
to) may change, and you&apos;ll need to download the updates if you
want to keep using the app. Rico Berger does not promise that it
will always update the app so that it is relevant to you and/or
works with the iOS version that you have installed on your device.
However, you promise to always accept updates to the application
when offered to you, We may also wish to stop providing the app, and
may terminate use of it at any time without giving notice of
termination to you. Unless we tell you otherwise, upon any
available on Android, iOS, macOS, Windows and Linux the
requirements for the system (and for any additional systems we
decide to extend the availability of the app to) may change, and
you&apos;ll need to download the updates if you want to keep using
the app. Rico Berger does not promise that it will always update the
app so that it is relevant to you and/or works with the Android,
iOS, macOS, Windows and Linux version that you have installed on
your device. However, you promise to always accept updates to the
application when offered to you, We may also wish to stop providing
the app, and may terminate use of it at any time without giving
notice of termination to you. Unless we tell you otherwise, upon any
termination, (a) the rights and licenses granted to you in these
terms will end; (b) you must stop using the app, and (if needed)
delete it from your device.
</p>
<p className="py-4">
<h2 className="text-xl font-bold">
Changes to This Terms and Conditions
Accessing and Download the Application from an App Store
</h2>
</p>
<p className="py-4">
The following applies to any App accessed through or downloaded from
an App Store:
</p>
<p className="pl-8 py-4">
<ul className="list-disc">
<li>
(a) You acknowledge and agree that (i) the Terms are concluded
between you and Rico Berger only, and not Apple, Google or
Microsoft, and (ii) Rico Berger, not Apple, Google or Microsoft,
is solely responsible for the App and content thereof. Your use
of the App must comply with the applicable App Store&apos;s
terms of service or usage rules.
</li>
<li>
(b) You acknowledge that the App Store has no obligation
whatsoever to furnish any maintenance and support services with
respect to the App.
</li>
<li>
(c) In the event of any failure of the App to conform to any
applicable warranty, you may notify the App Store, and the App
Store will refund the purchase price for the App to you and to
the maximum extent permitted by applicable law, neither Apple,
Google or Microsoft will have any other warranty obligation
whatsoever with respect to the App. As between Rico Berger and
the App Store, any other claims, losses, liabilities, damages,
costs or expenses attributable to any failure to conform to any
warranty will be the sole responsibility of Rico Berger.
</li>
<li>
(d) You and Rico Berger acknowledge that, as between Rico Berger
and the App Store, the App Store is not responsible for
addressing any claims you have or any claims of any third party
relating to the App or your possession and use of the App,
including, but not limited to: (i) product liability claims;
(ii) any claim that the App fails to conform to any applicable
legal or regulatory requirement; and (iii) claims arising under
consumer protection or similar legislation.
</li>
<li>
(e) You and Rico Berger acknowledge that, in the event of any
third-party claim that the App or your possession and use of
that App infringes that third party&apos;s intellectual property
rights, as between Rico Berger and App Store, Rico Berger, not
the App Store, will be solely responsible for the investigation,
defense, settlement and discharge of any such intellectual
property infringement claim to the extent required by the Terms.
</li>
<li>
(f) You and Rico Berger acknowledge and agree that (i) Apple and
Apple&apos;s subsidiaries, are third-party beneficiaries of the
Terms as related to your license of the App from the Apple App
Store, (ii) Google and Google&apos;s subsidiaries, are
third-party beneficiaries of the Terms as related to your
license of the App from the Android market or Google Play App
Stores, and (iii) Microsoft and its subsidiaries are
third-parties beneficiaries of the Terms as related to your
license of the App from the Windows Store; and that, upon your
acceptance of the terms and conditions of the Terms, Apple,
Google, or Microsoft, as applicable, will have the right (and
will be deemed to have accepted the right) to enforce the Terms
as related to your license of the App against you as a
third-party beneficiary thereof.
</li>
<li>
(g) Without limiting any other terms of the Terms, you must
comply with all applicable third-party terms of agreement when
using the App.
</li>
</ul>
</p>
<p className="py-4">
<h2 className="text-xl font-bold">
Changes to this Terms and Conditions
</h2>
</p>
<p className="py-4">
@@ -110,7 +188,7 @@ export default function TermsAndConditions() {
this page.
</p>
<p className="py-4">
These terms and conditions are effective as of 2023-06-22
These terms and conditions are effective as of 2023-10-18
</p>
<p className="py-4">
<h2 className="text-xl font-bold">Contact Us</h2>

View File

@@ -0,0 +1,61 @@
"use client";
import { ReactNode, useState } from "react";
import Image from "next/image";
import Link from "next/link";
export default function GetStartedEntry({ title, images, isMobile, children }: {
title: string;
images: string[];
isMobile?: boolean;
children: ReactNode;
}) {
const [index, setIndex] = useState<number>(0);
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-16">
<div>
<div className="h-full flex items-center">
<div>
<h2 className="text-xl font-bold">{title}</h2>
{children}
</div>
</div>
</div>
<div className="col-span-2">
<div className="h-full flex items-center justify-center">
<div className="relative p-2 bg-secondary rounded-lg">
<Link
href={images[index]}
target="_blank"
rel="noopener noreferrer"
>
<Image
src={images[index]}
width={0}
height={0}
style={isMobile
? { width: "auto", height: "75vh" }
: { width: "100%", height: "auto" }}
className="object-cover"
alt="Image"
loading="eager"
/>
</Link>
<div className="absolute flex space-x-3 -translate-x-1/2 bottom-5 left-1/2">
{images.map((_, index) => (
<button
key={index}
type="button"
className="w-4 h-4 bg-secondary rounded-full"
onClick={() => setIndex(index)}
>
</button>
))}
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -2,12 +2,13 @@ type TPlatform = "ios" | "android";
type TOS = "ios" | "android" | "macos" | "windows" | "linux";
export const downloads = {
"ios": "https://app.feeddeck.app",
"android": "https://app.feeddeck.app",
"ios": "https://apps.apple.com/us/app/feeddeck/id6451055362",
"android":
"https://play.google.com/store/apps/details?id=app.feeddeck.feeddeck",
"web": "https://app.feeddeck.app",
"macos": "https://app.feeddeck.app",
"windows": "https://app.feeddeck.app",
"linux": "https://app.feeddeck.app",
"macos": "https://apps.apple.com/us/app/feeddeck/id6451055362",
"windows": "https://www.microsoft.com/store/apps/9NPHPGRRCT5H",
"linux": "https://flathub.org/en/apps/app.feeddeck.feeddeck",
};
const getPlatform = (): TPlatform | undefined => {

View File

@@ -103,4 +103,12 @@ export const generalMetadata: Metadata = {
url: "/og.png",
},
},
itunes: {
appId: "1494512160",
},
other: {
"google-play-app": "app-id=io.feeddeck.feeddeck",
"msApplication-ID": "26077RicoBerger.FeedDeck",
"msApplication-PackageFamilyName": "26077RicoBerger.FeedDeck_2w82je6nmmv2c",
},
};

1724
landing/package-lock.json generated

File diff suppressed because it is too large Load Diff

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