Fedi is open-source client for Pleroma and Mastodon social networks written using Flutter.
Pleroma and Mastodon are parts of Fediverse (decentralized social network). The main idea of Fediverse - nobody owns Fediverse. Anybody can run their server instance and use it to communicate with other people.
So Fedi is an open-source mobile client for social networks and has features similar to Twitter.
Follow us on Fediverse fediapp@fedi.app
- Part 1. Architecture
- Part 2. Code
- Part 3. Build & Config
- Part 4. Used packages
- Part 5. Android Studio Plugins & Feature Plans
- Features
- Coming soon
- Known issues
- Data gathering
- Push notifications
- Localization
- License
- Feedback
- For developers
- Pleroma and Mastodon support
- Separated Pleroma & Mastodon API dart packages which you can use in your applications
- Offline mode. Access to cached data even without network
- Custom emojis. With emoji reactions support on Pleroma
- Customizable home timelines
- Multi-account support
- Push notifications via PushRelayFCM and FCM
- Supports receive and send share intends
- Scheduled and Draft Statuses
- Filters
- Instance details, announcements, trends, activity history, limits
- Day & Night theme
- Bookmarks, Hashtags, Lists, Featured tags, Suggestions
- Messenger-like UI for Conversations(DM) and Pleroma chats
- Customizable real-time notifications & timeline updates via WebSockets and Push Notifications
- Fetch data from Remote instances via Public API. So you can access full data on remote instance if currently logged instance hasn't synchronized all data yet
- A lot of settings options(global or per-instance). For example:
Always show NSFWorAuto-load media content - Threads & Polls
- Special UI for media-only timelines
- Mutes & Blocks
- Editing profile
- Sign up support
- Admin API;
- Support other Fediverse instances: Pixelfed, Misskey, Peertube, GNU Social, Friendica and others;
- Adopt UI for large screens;
- Display timelines from different instances on single Home page(currently you should switch instances to see related data);
- Remember timeline position via Markers API;
- OnBoarding & Tutorial. Popular instances suggestions;
- A lot of minor UX improvements in backlog.
Feel free to open issues if you have suggestions
- See Issues
Fedi doesn't use any special analytics service to track users. However Fedi uses Firebase services for PushNotifications(optional) and CrashReporting(optional).
You can completely remove Firebase via manual building from source.
Fedi gathers crashes and non-fatal errors to make app more stable.
- You can build app from source and remove Crashlytics library via .env config(details below)
- You can disable gathering via settings inside app(option is disabled by default)
Push notifications are implemented via toot-relay-fcm server
PushRelayFCM is Ruby on Rails server which handles web pushes and relays them to FCM.
From 2.5.0 version Fedi uses PushRelayFCM mode without decryption on server-side. So all private data is safe.
PushRelayFCM and Fedi can work in two modes:
- Without server-side decryption (
2.5.0and newer) - relay simple proxy encrypted messages - With server-side decryption (before
2.5.0) -decrypt messages and have access to notification content and useraccess_token. It is not used from2.5.0version, but is still supported in Fedi(see below why you still may want to use it).
(Used in AppStore/GooglePlay versions from 2.5.0)
- Fedi subscribes to
/api/v1/push/subscriptionwithsubscription[endpoint]set to relay server URL - Instances send web push notifications to relay server
- PushRelayFCM doesn't decrypt message
- PushRelayFCM proxies notifications to Fedi app via FCM
- Fedi doesn't decrypt message and use FCM message with encrypted data as simple trigger to load latest notification via REST API (this will be improved in future releases)
- Fedi displays notification
Since PushRelayServer doesn't know private decryption keys, it can't access any private data.
- Doesn't have access to user private data
- Uses rich notifications layouts and actions provided by
awesome_notifications
- Delivery may be delayed. Because PushRelayFCM sends FCM push message without
notification(FCM calls it data message). Readawesome_notificationsandfirebase_messagingdocumentation for details. Fedi uses:mutable_content=>true,:content_available=>true,:priority=>"high",to increase delivery priority
Because it is hard to implement with Flutter. There are no 3rd party Flutter libraries to decrypt ECDH p256v1 by now.
It is possible to decrypt it in Kotlin/Swift and it will be done in the future.
(It is not used in AppStore/GooglePlay versions from 2.5.0)
- Fedi subscribes to
/api/v1/push/subscriptionwithsubscription[endpoint]set to relay server URL - Instances send Web push notifications to relay server
- PushRelayFCM decrypts notifications
- PushRelayFCM relays notifications to Fedi app via FCM
- Fedi displays notification
- PushRelayFCM has access to
title,bodyandaccess_token access_tokenis sensitive data. It is possible to login into your account if someone knowsaccess_token
- Faster push delivery. FCM message(notification type) with
notification.titleandnotification.body, which has higher priority than message withoutnotification.title¬ification.bodyfields. Actually it is more affects iOS, than Android. Readawesome_notificationsandfirebase_messagingdocumentation for details.
- Private data access is main reason why Fedi moved to
Without server-side decryption way - Doesn't use rich notifications layouts and actions provided by
awesome_notifications
App uses flutter_localization API bundle with Flutter SDK.
It uses .arb files located in lib/l10n and generates .dart classes in /lib/generated/ folder.
After you make changes in .arb files you should do additional actions to regenerate Dart classes
- automatically via
flutter intlplugin - manually via
flutter intlpackage
- Help translate Fedi with Weblate
- It is easy to suggest fixes even without registering
- The best option is to create issue for this repository
- Multi-module project structure supported by Melos
- Null-safety support
- Feature-based folder structure
- Prefers composition over inheritance
- Dependency Injection is implemented via
provider - Prefers
StatelessWidgetand async UI update viaStreamBuidlerandBehaviourSubject&StreamControllerin controller classes - Prefers divide
Widgetsin small subWidgetswithconstconstructor(for better performance) if possible - Provides data to nested elements via
provider - Prefers
Repositorypattern. Almost all network data is cached in local SQLite database. UI always displays data from single source. It may be network-only or from database(if data is cached). Doesn't cache and merge data in memory to achieve data consistency - Prefers Effective Dart name and style code conventions
- Prefers long file & classes names like
account_follower_account_cached_list_bloc_impl.dartandAccountFollowerAccountCachedListBloc - It is easy to understand what classes do
- It is easy to navigate in IDE by typing start letters of name
- One class = one file
- Prefers
interfacesforBussines LogicandServices - Simple append
Ito implementation class name.AccountFollowerAccountCachedListBlocis implementation andIAccountFollowerAccountCachedListBlocis interface - Code readability: you can see small list of public methods/fields in interface file instead of exploring long file with implementations
- It is useful to implement extensions for interfaces not for implementations
- It is useful to extend several interfaces in one child to separate logic
- It is useful to create tests and mocks
To build Fedi you need to specify Flutter version in .fvm/fvm_config.json field flutterSdkVersion.
You can achieve this by specifing your system Flutter version by using flutter version $version or using FVM
Fedi uses Flutter Version Management to specify Flutter version to build app.
FVM also helps manage several SDK's versions on local machine
Config is already done, so you just run fvm install in repo folder and configure IDE to use .fvm/flutter_sdk folder instead of system Flutter SDK.
To use flutter version specified in .fvm/fvm_config.json you should prepend fvm like fvm flutter install
More info you can found in FVM documentation
There a lot of useful comand line actions automated by melos commands.
Run fvm flutter pub global run melos run to see all possible melos actions
mooris SQLite ORM. For local data cache & offline mode supporthiveis secure and fast storage for preferencesproviderfor Dependency Injectionflutter_htmlfor content renderingrxdartfor reactive programmingpedantic&dart_code_metricsfor better code analyzing. Seeanalysis_options.yamlfor enabled rulesflutter_cache_managerfor media cachingflutter_intlfor localization via.arbfiles- A lot of UI-related(like
pull_to_refresh) and Platform-dependent(likepermission_handler) libraries flutter_configto config via .env filesfirebase_messagingandawesome_notificationsfor push notifications
You can find full list in pubspec.yaml where each library has comment why it's used
- Fedi uses vector icons compiled in icon font via fluttericon.com
- folder icons_export contains config for fluttericon.com currently used by Fedi
- Fedi have unit-tests for Business Logic and Services classes
- Integration & UI tests are not implemented yet
git clone https://github.com/Big-Fig/Fediverse.app
cd Fediverse.app
Install Flutter version used by this project
fvm install
Link multi-module project dependencies
fvm flutter pub global activate melos
fvm flutter pub global run melos bootstrap
Copy config for prod and dev flavors
cp env_example.env env_prod.env
cp env_example.env env_dev.env
In Example config you can find out how to disable some features like Push notifications.
To enable all features you should change app id, create Firebase project, and edit config file.
- Download all required libraries
fvm flutter pub get
- Run by Flavor
fvm flutter run --flavor dev
or
fvm flutter run --flavor prod
- Specify Flutter SDK path
File->Preferences->Languages & Frameworks->Flutterto<Project_Root>/.fvm/flutter_sdk
- Dart SDK should be configured automatically. But you can check Dart SDK path at (
File->Preferences->Languages & Frameworks->Dart).It should be<Project_Root>/.fvm/flutter_sdk/bin/cache/dart-sdk
- Specify flavor(
prodordev) inRun Configurations
Run->Edit configurations
- Click
Pub getin IDE or runfvm pub getin terminal - Connect device or run emulator
- Run
There are two main flavors.
Implementation details: Build flavors in Flutter (Android and iOS) with different Firebase projects per flavor
Is used for production builds
Is used for development builds. You can use only prod flavor if you don't need special config for development
Main purpose of config files is to exclude sensitive data from source control and quickly enable/disable and config some features like Push Notifications
Build script uses config from project root folder depends on flavor, so to build app you should have next files in root folder
env_prod.env
env_dev.env
Those files are excluded from source control.
You can find all possible config variables(with comments) at env_example.env
fvm flutter pub global run melos run clean
fvm flutter cleanorflutter cleanif you don't use FVM./gradlew cleanin android folderProduct->Cleanin XCodeFile(or Android Studio on Mac)->Invalidate caches & Restartin Android Studio
Sometimes it is also needed to clear iOS pods
fvm flutter pub global run melos run clean:ios
cd ios
rm -rf Pods
rm Podfile.lock
pod install
Sometimes you change package version in pubspec.yaml run pub get but version is not changed
rm pubspec.lock
rm .flutter-plugins
rm .flutter-plugins-dependencies
rm .packages
rm -rf .dart_tool
pub get
Sometimes when you change package version in pubspec.yaml and after pub get version is not changed.
You can check pubspec.lock to see if version is changed.
That may happen when you specify version bounds like >=1.0.0 <2.0.0 or ^1.0.0 which are the same.
See Version constraints in official docs.
rm pubspec.lock
rm .flutter-plugins
rm .flutter-plugins-dependencies
rm .packages
rm -rf .dart_tool
pub get
Fedi specifies explicitly version like 1.0.0 to avoid such issues.
However, that may cause dependencies version conflict
Changing App ID is required if you want to setup own PushRelayFCM server and pushes via your Firebase project for FCM.
It is also useful if you want to have several app versions installed on one device
Unfortunately, it is not possible to use APP_ID from config in all places in Gradle and XCode project files. So in some places ID is hardcoded
So, If you want to change app id from com.fediverse.app for prod and from com.fediverse.app2 for dev you should manually change them (in addition to changing id in .env files)
Actually, you should run Find and Replace com.fediverse.app with your package name on ios and android folders. And rename folders at android/app/src/main/kotlin
However, it may cause strange build errors. So you may need full clean
If you still have errors please explore App ID things in the next docs:
- Build flavors in Flutter (Android and iOS) with different Firebase projects per flavor
flutter_configfirebase_corefirebase_crashlyticsfirebase_messagingreceive_sharing_intent
receive_sharing_intentlib requires to add group.<app_id> to XCode project. Unfortunately, it is not possible due to our internal issues (we've moved app to new iTunes account and can't use old group id). So we usefork of receive_sharing_intentwith custom group ids support.
Fedi uses group fediverse.app for prod and com.fediverse.app2 for dev
Signing config is required to make release builds
Generate key via Tutorial and put it in android/key/key.jks(exclude from source control)
Create android/key.properties(exclude from source control) file with next template.
storePassword=pass1
keyPassword=pass2
keyAlias=keyName
storeFile=../key/key.jks
Follow official tutorial
To use Firebase service you should generate files from your Firebase project page and put them in the project.
Don't forget to enable it in .env file
FIREBASE_ENABLED=false
- Generate
google-services.json
Put google-services.json to folder depends on used flavor
android/app/src/devandroid/app/src/prod
- Generate
GoogleService-Info.plist
Put GoogleService-Info.plist to folder depends on used flavor
ios/config/devios/config/prod
For more details see
- Flutter Firebase documentation
- Build flavors in Flutter (Android and iOS) with different Firebase projects per flavor
To enable Push notifications you should
- change App ID
- create Firebase Project for your App ID,
- generate Firebase config and integrate it in app
- generate FCM server key
- setup own PushRelayFCM server instance and put your FCM server key
App ID and FCM server key(so and PushRelayFCM instance) are connected. It is not possible to use one PushRelayFCM instance with several App IDs and vice versa
On/Off via .env. Firebase Core integration is required
PUSH_FCM_ENABLED=false
Is required if PUSH_FCM_ENABLED=true
PUSH_SUBSCRIPTION_KEYS_P256DH
User agent public key. Base64 encoded string of public key of ECDH key using prime256v1 curve.
PUSH_SUBSCRIPTION_KEYS_AUTH
Auth secret. Base64 encoded string of 16 bytes of random data.
More info in Mastodon docs and PushRelayFCM server docs
PUSH_FCM_RELAY_URL=https://pushrelay.example.com/push/
PUSH_SUBSCRIPTION_KEYS_P256DH=BEpPCn0cfs3P0E0fY-gyOuahx5dW5N8qu
PUSH_SUBSCRIPTION_KEYS_AUTH=T5bhIIyre5TDC
On/Off via .env. Firebase Core integration is required
CRASHLYTICS_ENABLED=false
Used to catch errors on client-side with error description and stackTrace
You should enable Firebase support and change config variable in .env file to enable crash reporting
Uses version from pubspec.yaml
By default Flutter project config it should use version from pubspec.yaml,
However, sometimes it causes strange iOS build errors(version is not changed but should be).
So, Fedi requires a manual increasing version code & name in Runner and Share Extension targets.
XCode project has additional ShareExtension module required by receive_sharing_intent to handle income share events.
It is also important to add Target and ShareExtension to the same group ID.
- Original
assets/server_list.txtis taken from tateisu/SubwayTooter
- mkljczk for PL translation