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.
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 NSFW or Auto-load media content
Threads & Polls
Special UI for media-only timelines
Mutes & Blocks
Editing profile
Sign up support
Coming soon
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;
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.
Crash reports via Firebase Crashlytics
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
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.0 and newer) - relay simple proxy encrypted messages
With server-side decryption (before 2.5.0) -decrypt messages and have access to notification content and user access_token. It is not used from 2.5.0 version, but is still supported in Fedi(see below why you still may want to use it).
Without server-side decryption way
(Used in AppStore/GooglePlay versions from 2.5.0)
Fedi subscribes to /api/v1/push/subscription with subscription[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.
Delivery may be delayed. Because PushRelayFCM sends FCM push message without notification (FCM calls it data message). Read awesome_notifications and firebase_messaging documentation for details. Fedi uses :mutable_content=>true,:content_available=>true,:priority=>"high", to increase delivery priority
Why Fedi doesn't decrypt message on client-side?
Because it is hard to implement with Flutter. There are no 3rd party Flutter libraries to decrypt ECDHp256v1 by now.
It is possible to decrypt it in Kotlin/Swift and it will be done in the future.
With server-side decryption way
(It is not used in AppStore/GooglePlay versions from 2.5.0)
Fedi subscribes to /api/v1/push/subscription with subscription[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, body and access_token
access_token is sensitive data. It is possible to login into your account if someone knows access_token
Pros
Faster push delivery. FCM message(notification type) with notification.title and notification.body, which has higher priority than message without notification.title & notification.body fields. Actually it is more affects iOS, than Android. Read awesome_notifications and firebase_messaging documentation for details.
Cons
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
Localization
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
The best option is to create issue for this repository
For developers
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 StatelessWidget and async UI update via StreamBuidler and BehaviourSubject & StreamController in controller classes
Prefers divide Widgets in small sub Widgets with const constructor(for better performance) if possible
Provides data to nested elements via provider
Prefers Repository pattern. 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 long file & classes names like account_follower_account_cached_list_bloc_impl.dart and AccountFollowerAccountCachedListBloc
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 interfaces for Bussines Logic and Services
Simple append I to implementation class name. AccountFollowerAccountCachedListBloc is implementation and IAccountFollowerAccountCachedListBloc is 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
Flutter version & FVM
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
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.
Run
Run from command line
Download all required libraries
fvm flutter pub get
Run by Flavor
fvm flutter run --flavor dev
or
fvm flutter run --flavor prod
Run from IDE
Specify Flutter SDK path
File->Preferences->Languages & Frameworks->Flutter to <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(prod or dev) in Run Configurations
Run->Edit configurations
Click Pub get in IDE or run fvm pub get in terminal
Is used for development builds. You can use only prod flavor if you don't need special config for development
Config
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
If you have strange errors or how to clean project
fvm flutter pub global run melos run clean
fvm flutter clean or flutter clean if you don't use FVM
./gradlew clean in android folder
Product->Clean in XCode
File(or Android Studio on Mac)->Invalidate caches & Restart in 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
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.
Fedi specifies explicitly version like 1.0.0 to avoid such issues.
However, that may cause dependencies version conflict
App ID
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:
receive_sharing_intent lib 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 use fork of receive_sharing_intent with custom group ids support.
Fedi uses group fediverse.app for prod and com.fediverse.app2 for dev
Signing
Android
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.
请发表评论