# Contributing - [Feedback, Issues and Questions](#feedback-issues-and-questions) - [Adding new Features](#adding-new-features) - [Development](#development) - [Working with Flutter](#working-with-flutter) - [Sort all Imports](#sort-all-imports) - [Add a Custom Icon](#add-a-custom-icon) - [Update the Icons and Splash Screen](#update-the-icons-and-splash-screen) - [Run Release Build on a Device](#run-release-build-on-a-device) - [Working with Supabase](#working-with-supabase) - [Working with Deno](#working-with-deno) - [Hosting](#hosting) - [Release](#release) Every contribution to FeedDeck is welcome, whether it is reporting a bug, submitting a fix, proposing new features or becoming a maintainer. To make contributing to FeedDeck as easy as possible you will find more details for the development flow in this documentation. Please note we have a [Code of Conduct](https://github.com/feeddeck/feeddeck/blob/main/CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. ## Feedback, Issues and Questions If you encounter any issue or you have an idea to improve, please: - Search through [existing open and closed GitHub Issues](https://github.com/feeddeck/feeddeck/issues) and [discussions](https://github.com/feeddeck/feeddeck/discussions) for the answer first. If you find a relevant topic, please comment on the issue. - If none of the issues are relevant, please add an issue to [GitHub issues](https://github.com/feeddeck/feeddeck/issues) or start a new [discussions](https://github.com/feeddeck/feeddeck/discussions). Please use the issue templates and provide any relevant information. If you encounter a security vulnerability, please do not open an issue and instead send an email to [admin@feeddeck.app](mailto:admin@feeddeck.app?subject=[GitHub]%20Security%20Vulnerability) or report the security vulnerability via [GitHub](https://github.com/feeddeck/feeddeck/security/advisories). ## Adding new Features When contributing a complex change to the FeedDeck repository, please discuss the change you wish to make within a Github issue with the owners of this repository before making the change. ## Development FeedDeck uses [Flutter](https://flutter.dev), [Supabase](https://supabase.com) and [Deno](https://deno.com), make sure that you have the correct version installed before starting development. You can use the following commands to check your installed version: ```sh $ flutter --version Flutter 3.22.2 • channel stable • https://github.com/flutter/flutter.git Framework • revision 761747bfc5 (3 days ago) • 2024-06-05 22:15:13 +0200 Engine • revision edd8546116 Tools • Dart 3.4.3 • DevTools 2.34.3 $ deno --version deno 1.40.2 (release, aarch64-apple-darwin) v8 12.1.285.6 typescript 5.3.3 ``` ### Working with Flutter To run the app you can use the [`run.sh`](./app/run.sh) script, which will automatically load the `.env` file from the Supabase project and passes the required variables to the `flutter run` command: ```sh ./run.sh --device="chrome" --environment="local" ``` To run the tests the following command can be used: ```sh flutter test ``` To check the test coverage the `--coverage` flag can be added to the command and an HTML report can be generated: ```sh flutter test --coverage # To generate the HTML report lcov is required, which can be installed via Homebrew: brew install lcov genhtml coverage/lcov.info -o coverage/html open coverage/html/index.html ``` #### Sort all Imports To sort all imports in the Dart code in a uniformly way you have to run the `flutter pub run import_sorter:main` command. #### Add a Custom Icon If you have to add a custom icon to the Flutter app we are using [https://www.fluttericon.com](https://www.fluttericon.com). The configuration file for all existing icons can be found at `app/templates/iconfont/config.json`. When you add a new custom icon place the `.svg` file in the `app/templates/iconfont` folder. Please also update the `config.json` file. The content of the generated Dart class should be placed into the `app/lib/utils/fd_icons.dart` file. #### Update the Icons and Splash Screen To update the icon for the app or the splash screens the following two commands can be used: ```sh flutter pub run flutter_launcher_icons:main flutter pub run flutter_native_splash:create ``` The icons can be found in the `app/templates/app-icon` folder. The splash screen icons can be found in the `app/templates/splash-screen` folder. #### Run Release Build on a Device To run the release build on a device for testing, we have to get the Device ID first by running the following command: ```sh $ flutter devices 3 connected devices: Ricos iPad (mobile) • 00008027-0004785E0A31002E • ios • iOS 16.2 20C65 macOS (desktop) • macos • darwin-arm64 • macOS 13.1 22C65 darwin-arm Chrome (web) • chrome • web-javascript • Google Chrome 108.0.5359.124 ``` Then we can use one of the listed devices and execute the following command to build and run the app on this device: ```sh flutter run --release --device-id=00008027-0004785E0A31002E --dart-define SUPABASE_URL= --dart-define SUPABASE_ANON_KEY= --dart-define SUPABASE_SITE_URL= --dart-define GOOGLE_CLIENT_ID= ``` With the above command we can also savely quit the terminal process (by pressing `q`) and continue testing on the device, while it is not connected to our development machine. ### Working with Supabase The Supabase CLI can be installed via Homebrew. For other platforms the install instruction can be found in the [Installing the Supabase CLI](https://supabase.com/docs/guides/cli/getting-started#installing-the-supabase-cli) section in the Supabase documentation: ```sh brew install supabase/tap/supabase ``` After the Supabase CLI is installed we can run Supabase locally by running the following command in the root folder of the repository: ```sh supabase start ``` This can take some time on your first run. Once it is finished the Supabase Studio should be available at [localhost:54323](http://localhost:54323/). After Supabase is running we can reset the local database and apply all the migrations from the `supabase/migrations` directory by running the following command: ```sh supabase db reset ``` We are also using Supabase functions for some parts of the app (e.g. adding a new source). Before we can run the function we have to create a `supabase/.env.local` file. This file contains all the environment variables needed by the functions. All the needed environment variables can be found in the `supabase/.env.example` file. Once the `supabase/.env.local` file is created, we can run the functions with the `supabase functions` command: ```sh supabase functions serve --no-verify-jwt --env-file supabase/.env.local ``` Some other usful commands during development are: - `supabase migration new `: Create a new migration in the `supabase/migrations` folder. - `supabase functions new `: Create a new function in the `supabase/functions` folder. ### Working with Deno While we try to use Supabase for almost everything, we can not use it to fetch the items for sources. For this we are using Deno (because it is already used within the Supabase functions) and run them via Docker. The Deno code can be found in the `supabase/functions/_cmd` folder so we can share the code with the code for the Supabase functions. It contains two components: - `scheduler`: Fetch all sources which must be updated and write them to Redis. - `worker`: Listen to all sources in Redis and fetch all items for the provided sources. To run Redis, the `scheduler` and the `worker` we provide a Docker Compose file. Before we can run it, we have to create `supabase/.env.local` file, which contains all the environment variables for the `scheduler` and `worker`. All the needed environment variables can be found in the `supabase/.env.example` file. Once the `supabase/.env.local` file is created we can run Redis, the `scheduler` and the `worker` via the following command: ```sh cd supabase/functions/_cmd docker-compose up --build ``` To build the Docker image, the following commands can be run: ```sh docker build -f supabase/functions/_cmd/Dockerfile -t ghcr.io/feeddeck/feeddeck:dev supabase/functions # To build the Docker image for another platform use the following: docker buildx build --platform linux/amd64 -f supabase/functions/_cmd/Dockerfile -t ghcr.io/feeddeck/feeddeck:dev supabase/functions # The Docker image can then be used to run the scheduler, worker or tools, e.g. docker run ghcr.io/feeddeck/feeddeck:dev tools get-feed '{"type": "reddit", "options": {"reddit": "/r/kubernetes"}}' ``` To run the tests for our code, the following command can be used: ```sh deno test --allow-env --import-map=supabase/functions/import_map.json supabase/functions ``` To check the test coverage the `--coverage` flag can be added to the command and an HTML report can be generated: ```sh deno test --allow-env --import-map=supabase/functions/import_map.json supabase/functions --coverage=coverage_deno # To generate the HTML report lcov is required, which can be installed via Homebrew: brew install lcov deno coverage coverage_deno --lcov --output=coverage_deno/coverage_deno.lcov genhtml -o coverage_deno/html coverage_deno/coverage_deno.lcov open coverage_deno/html/index.html ``` ## Hosting FeedDeck uses Supabase as backend. For Supabase we can use [Supabase Cloud](https://supabase.com/dashboard) or their [Self-Hosting](https://supabase.com/docs/guides/self-hosting) offer. Once we have a running Supabase instance, we can apply all our database migrations, set the secrets and deploy our functions: ```sh # Link your local development project to a hosted Supabase project supabase link --project-ref # Push all local migrations to a remote database supabase db push # Push all the secrets from the .env file to our remote project and list all secrets afterwards supabase secrets set --env-file supabase/.env supabase secrets list # Deploy all functions supabase functions deploy add-or-update-source-v1 --project-ref --import-map supabase/functions/import_map.json supabase functions deploy add-source-v1 --project-ref --import-map supabase/functions/import_map.json supabase functions deploy delete-user-v1 --project-ref --import-map supabase/functions/import_map.json supabase functions deploy generate-magic-link-v1 --project-ref --import-map supabase/functions/import_map.json supabase functions deploy image-proxy-v1 --no-verify-jwt --project-ref --import-map supabase/functions/import_map.json supabase functions deploy profile-v1 --project-ref --import-map supabase/functions/import_map.json supabase functions deploy profile-v2 --project-ref --import-map supabase/functions/import_map.json supabase functions deploy revenuecat-webhooks-v1 --no-verify-jwt --project-ref --import-map supabase/functions/import_map.json supabase functions deploy stripe-create-billing-portal-link-v1 --project-ref --import-map supabase/functions/import_map.json supabase functions deploy stripe-create-checkout-session-v1 --project-ref --import-map supabase/functions/import_map.json supabase functions deploy stripe-webhooks-v1 --no-verify-jwt --project-ref --import-map supabase/functions/import_map.json ``` Now we have to do some manual steps to finish the setup of our Supabase project: 1. Select the [`settings`](https://supabase.com/dashboard/project/_/editor) table and provide the values for the `supabase_service_role_key` and `supabase_api_url` rows (**Note**: For local development the `supabase_api_url` must be `http://supabase_kong_feeddeck:8000`). These values are needed for the clean up of media files saved in the Supabase storage. The media files are deleted when the corresponding item / source is deleted and via a cron job. 2. Go to [url configuration](https://supabase.com/dashboard/project/_/auth/url-configuration) in the authentication section and set the site url, e.g. `https://app.feeddeck.app`. 3. On the same [page](https://supabase.com/dashboard/project/_/auth/url-configuration) you also have to add the following redirect urls: - ``, e.g. `https://app.feeddeck.app` - `/reset-password`, e.g. `https://app.feeddeck.app/reset-password` - `http://localhost:*/auth` - `app.feeddeck.feeddeck://signin-callback/` 4. Add your [email templates](https://supabase.com/dashboard/project/_/auth/templates). 5. Enable and configure all [auth providers](https://supabase.com/dashboard/project/_/auth/providers): - Enable the email provider - _(Optional)_ Enable the Apple provider (the documentation can be found in the [Login with Apple](https://supabase.com/docs/guides/auth/social-login/auth-apple) section) and provide the following values: - Service ID, e.g. `app.feeddeck.signin` - Service Key - Authorized Client IDs, e.g. `app.feeddeck.feeddeck` - _(Optional)_ Enable the Google provider (the documentation can be found in the [Login with Google](https://supabase.com/docs/guides/auth/social-login/auth-google) section) and provide the following values: - Client ID - Client Secret - Authorized Client IDs, e.g. `app.feeddeck.feeddeck` 6. _(Optional)_ Enable custom SMTP in the [auth section](https://supabase.com/dashboard/project/_/settings/auth) on the settings page. 7. _(Optional)_ Configure Stripe by adding a webhook endpoint. The endpoint url should look as follows: `https://.supabase.co/functions/v1/stripe-webhooks-v1`. Once the endpoint is configured the `customer.subscription.created`, `customer.subscription.deleted` and `checkout.session.completed` event types must be added. Now that we have finished the setup for our Supabase project we have to run the `scheduler`, `worker` and Redis. The `scheduler` and `worker` can be run via the `ghcr.io/feeddeck/feeddeck` Docker image. You can create a similar Docker Compose file as we are providing for the development to run the `scheduler`, `worker` and Redis. The Docker Compose file can be found here: [docker-compose.yaml](supabase/functions/_cmd/docker-compose.yaml). You can use the official clients for mobile clients for iOS and Android and desktop clients for macOS, Windows and Linux with your self hosted instance of FeedDeck. To configure the clients double tap on the FeedDeck logo on the sign in page and provide your **Supabase Url**, **Supabase Anon Key** and **Supabase Site Url**. After you have saved the values restart the app. The web version of FeedDeck must be build by your own. Please have a look at the [release](#release) section to see how to build the web version. In the release section you also find the instructions to build your own native clients for iOS, Android, macOS, Windows and Linux if you do not want to use the official ones. ## Release 1. Ensure that all secrets are updated in the Supabase project: ```sh supabase link --project-ref supabase secrets set --env-file supabase/.env.prod supabase secrets list ``` 2. Update the `version` key and the `msix_config.msix_version` key in the `pubspec.yaml` file. 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. 5. Build the app for Web by running `flutter build web`. The build can be found at `app/build/web` and must be uploaded to your hosting provider. 6. Build the app for Linux by running `flutter build linux --release`. 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). 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). 9. Create a file `app/android/key.properties` with the following content: ```plain storePassword= keyPassword= keyAlias=upload storeFile= ``` 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). 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 [https://appstoreconnect.apple.com](https://appstoreconnect.apple.com).