Merge pull request #2264 from krille-chan/krille/refactor-user-settings

refactor: Use AppSettings enum based configuration everywhere and fix load from json on web
This commit is contained in:
Krille-chan 2025-10-19 20:29:53 +02:00 committed by GitHub
commit 90ec0013f1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 357 additions and 402 deletions

View file

@ -48,9 +48,63 @@ Please visit the website for installation instructions:
# How to build # How to build
Please visit the [Wiki](https://github.com/krille-chan/fluffychat/wiki) for build instructions: 1. To build FluffyChat you need [Flutter](https://flutter.dev) and [Rust](https://www.rust-lang.org/tools/install)
- https://github.com/krille-chan/fluffychat/wiki/How-To-Build 2. Clone the repo:
```
git clone https://github.com/krille-chan/fluffychat.git
cd fluffychat
```
3. Choose your target platform below and enable support for it.
3.1 If you want, enable Googles Firebase Cloud Messaging:
`git apply ./scripts/enable-android-google-services.patch`
4. Debug with: `flutter run`
### Android
* Build with: `flutter build apk`
### iOS / iPadOS
* Have a Mac with Xcode installed, and set up for Xcode-managed app signing
* If you want automatic app installation to connected devices, make sure you have Apple Configurator installed, with the Automation Tools (`cfgutil`) enabled
* Set a few environment variables
* FLUFFYCHAT_NEW_TEAM: the Apple Developer team that your certificates should live under
* FLUFFYCHAT_NEW_GROUP: the group you want App IDs and such to live under (ie: com.example.fluffychat)
* FLUFFYCHAT_INSTALL_IPA: set to `1` if you want the IPA to be deployed to connected devices after building, otherwise unset
* Run `./scripts/build-ios.sh`
### Web
* Build with:
```bash
./scripts/prepare-web.sh # To install Vodozemac
flutter build web --release
```
* Optionally configure by serving a `config.json` at the same path as fluffychat.
An example can be found at `config.sample.json`. All values there are optional.
**Please only the values, you really need**. If you e.g. only want
to change the default homeserver, then only modify the `defaultHomeserver` key.
### Desktop (Linux, Windows, macOS)
* Enable Desktop support in Flutter: https://flutter.dev/desktop
#### Install custom dependencies (Linux)
```bash
sudo apt install libjsoncpp1 libsecret-1-dev libsecret-1-0 librhash0 libwebkit2gtk-4.0-dev
```
* Build with one of these:
```bash
flutter build linux --release
flutter build windows --release
flutter build macos --release
```
# Special thanks # Special thanks

View file

@ -1,10 +1,29 @@
{ {
"application_name": "FluffyChat", "applicationName": "FluffyChat",
"application_welcome_message": null, "defaultHomeserver": "matrix.org",
"default_homeserver": "matrix.org", "privacyUrl": "https://github.com/krille-chan/fluffychat/blob/main/PRIVACY.md",
"web_base_url": "https://fluffychat.im/web", "audioRecordingNumChannels": 1,
"privacy_url": "https://fluffychat.im/en/privacy.html", "audioRecordingAutoGain": true,
"render_html": false, "audioRecordingEchoCancel": false,
"hide_redacted_events": false, "audioRecordingNoiseSuppress": true,
"hide_unknown_events": false "audioRecordingBitRate": 64000,
"audioRecordingSamplingRate": 44100,
"renderHtml": true,
"fontSizeFactor": 1,
"hideRedactedEvents": false,
"hideUnknownEvents": true,
"separateChatTypes": false,
"autoplayImages": true,
"sendTypingNotifications": true,
"sendPublicReadReceipts": true,
"swipeRightToLeftToReply": true,
"sendOnEnter": false,
"showPresences": true,
"displayNavigationRail": false,
"experimentalVoip": false,
"shareKeysWith": "all",
"noEncryptionWarningShown": false,
"displayChatDetailsColumn": false,
"colorSchemeSeedInt": 4283835834,
"enableSoftLogout": false
} }

View file

@ -1,4 +1,3 @@
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/pages/chat/chat_view.dart'; import 'package:fluffychat/pages/chat/chat_view.dart';
import 'package:fluffychat/pages/chat_list/chat_list_body.dart'; import 'package:fluffychat/pages/chat_list/chat_list_body.dart';
import 'package:fluffychat/pages/chat_list/search_title.dart'; import 'package:fluffychat/pages/chat_list/search_title.dart';
@ -25,7 +24,7 @@ void main() {
() async { () async {
// this random dialog popping up is super hard to cover in tests // this random dialog popping up is super hard to cover in tests
SharedPreferences.setMockInitialValues({ SharedPreferences.setMockInitialValues({
SettingKeys.showNoGoogle: false, 'chat.fluffy.show_no_google': false,
}); });
}, },
); );

View file

@ -1,32 +1,25 @@
import 'dart:ui'; import 'dart:ui';
import 'package:matrix/matrix.dart';
abstract class AppConfig { abstract class AppConfig {
static String _applicationName = 'FluffyChat'; // Const and final configuration values (immutable)
static String get applicationName => _applicationName;
static String? _applicationWelcomeMessage;
static String? get applicationWelcomeMessage => _applicationWelcomeMessage;
static String _defaultHomeserver = 'matrix.org';
static String get defaultHomeserver => _defaultHomeserver;
static double fontSizeFactor = 1;
static const Color chatColor = primaryColor;
static Color? colorSchemeSeed = primaryColor;
static const double messageFontSize = 16.0;
static const bool allowOtherHomeservers = true;
static const bool enableRegistration = true;
static const Color primaryColor = Color(0xFF5625BA); static const Color primaryColor = Color(0xFF5625BA);
static const Color primaryColorLight = Color(0xFFCCBDEA); static const Color primaryColorLight = Color(0xFFCCBDEA);
static const Color secondaryColor = Color(0xFF41a2bc); static const Color secondaryColor = Color(0xFF41a2bc);
static String _privacyUrl =
'https://github.com/krille-chan/fluffychat/blob/main/PRIVACY.md';
static const Set<String> defaultReactions = {'👍', '❤️', '😂', '😮', '😢'}; static const Color chatColor = primaryColor;
static const double messageFontSize = 16.0;
static const bool allowOtherHomeservers = true;
static const bool enableRegistration = true;
static const bool hideTypingUsernames = false;
static const String inviteLinkPrefix = 'https://matrix.to/#/';
static const String deepLinkPrefix = 'im.fluffychat://chat/';
static const String schemePrefix = 'matrix:';
static const String pushNotificationsChannelId = 'fluffychat_push';
static const String pushNotificationsAppId = 'chat.fluffy.fluffychat';
static const double borderRadius = 18.0;
static const double columnWidth = 360.0;
static String get privacyUrl => _privacyUrl;
static const String website = 'https://fluffychat.im'; static const String website = 'https://fluffychat.im';
static const String enablePushTutorial = static const String enablePushTutorial =
'https://github.com/krille-chan/fluffychat/wiki/Push-Notifications-without-Google-Services'; 'https://github.com/krille-chan/fluffychat/wiki/Push-Notifications-without-Google-Services';
@ -36,80 +29,25 @@ abstract class AppConfig {
'https://github.com/krille-chan/fluffychat/wiki/How-to-Find-Users-in-FluffyChat'; 'https://github.com/krille-chan/fluffychat/wiki/How-to-Find-Users-in-FluffyChat';
static const String appId = 'im.fluffychat.FluffyChat'; static const String appId = 'im.fluffychat.FluffyChat';
static const String appOpenUrlScheme = 'im.fluffychat'; static const String appOpenUrlScheme = 'im.fluffychat';
static String _webBaseUrl = 'https://fluffychat.im/web';
static String get webBaseUrl => _webBaseUrl;
static const String sourceCodeUrl = static const String sourceCodeUrl =
'https://github.com/krille-chan/fluffychat'; 'https://github.com/krille-chan/fluffychat';
static const String supportUrl = static const String supportUrl =
'https://github.com/krille-chan/fluffychat/issues'; 'https://github.com/krille-chan/fluffychat/issues';
static const String changelogUrl = static const String changelogUrl =
'https://github.com/krille-chan/fluffychat/blob/main/CHANGELOG.md'; 'https://github.com/krille-chan/fluffychat/blob/main/CHANGELOG.md';
static const Set<String> defaultReactions = {'👍', '❤️', '😂', '😮', '😢'};
static final Uri newIssueUrl = Uri( static final Uri newIssueUrl = Uri(
scheme: 'https', scheme: 'https',
host: 'github.com', host: 'github.com',
path: '/krille-chan/fluffychat/issues/new', path: '/krille-chan/fluffychat/issues/new',
); );
static bool renderHtml = true;
static bool hideRedactedEvents = false;
static bool hideUnknownEvents = true;
static bool separateChatTypes = false;
static bool autoplayImages = true;
static bool sendTypingNotifications = true;
static bool sendPublicReadReceipts = true;
static bool swipeRightToLeftToReply = true;
static bool? sendOnEnter;
static bool showPresences = true;
static bool displayNavigationRail = false;
static bool experimentalVoip = false;
static const bool hideTypingUsernames = false;
static const String inviteLinkPrefix = 'https://matrix.to/#/';
static const String deepLinkPrefix = 'im.fluffychat://chat/';
static const String schemePrefix = 'matrix:';
static const String pushNotificationsChannelId = 'fluffychat_push';
static const String pushNotificationsAppId = 'chat.fluffy.fluffychat';
static const double borderRadius = 18.0;
static const double columnWidth = 360.0;
static final Uri homeserverList = Uri( static final Uri homeserverList = Uri(
scheme: 'https', scheme: 'https',
host: 'servers.joinmatrix.org', host: 'servers.joinmatrix.org',
path: 'servers.json', path: 'servers.json',
); );
static void loadFromJson(Map<String, dynamic> json) {
if (json['chat_color'] != null) {
try {
colorSchemeSeed = Color(json['chat_color']);
} catch (e) {
Logs().w(
'Invalid color in config.json! Please make sure to define the color in this format: "0xffdd0000"',
e,
);
}
}
if (json['application_name'] is String) {
_applicationName = json['application_name'];
}
if (json['application_welcome_message'] is String) {
_applicationWelcomeMessage = json['application_welcome_message'];
}
if (json['default_homeserver'] is String) {
_defaultHomeserver = json['default_homeserver'];
}
if (json['privacy_url'] is String) {
_privacyUrl = json['privacy_url'];
}
if (json['web_base_url'] is String) {
_webBaseUrl = json['web_base_url'];
}
if (json['render_html'] is bool) {
renderHtml = json['render_html'];
}
if (json['hide_redacted_events'] is bool) {
hideRedactedEvents = json['hide_redacted_events'];
}
if (json['hide_unknown_events'] is bool) {
hideUnknownEvents = json['hide_unknown_events'];
}
}
} }

View file

@ -1,40 +1,12 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:matrix/matrix_api_lite/utils/logs.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
abstract class SettingKeys { import 'package:fluffychat/utils/platform_infos.dart';
static const String renderHtml = 'chat.fluffy.renderHtml';
static const String hideRedactedEvents = 'chat.fluffy.hideRedactedEvents';
static const String hideUnknownEvents = 'chat.fluffy.hideUnknownEvents';
static const String hideUnimportantStateEvents =
'chat.fluffy.hideUnimportantStateEvents';
static const String separateChatTypes = 'chat.fluffy.separateChatTypes';
static const String sentry = 'sentry';
static const String theme = 'theme';
static const String amoledEnabled = 'amoled_enabled';
static const String codeLanguage = 'code_language';
static const String showNoGoogle = 'chat.fluffy.show_no_google';
static const String fontSizeFactor = 'chat.fluffy.font_size_factor';
static const String showNoPid = 'chat.fluffy.show_no_pid';
static const String databasePassword = 'database-password';
static const String appLockKey = 'chat.fluffy.app_lock';
static const String unifiedPushRegistered =
'chat.fluffy.unifiedpush.registered';
static const String unifiedPushEndpoint = 'chat.fluffy.unifiedpush.endpoint';
static const String ownStatusMessage = 'chat.fluffy.status_msg';
static const String dontAskForBootstrapKey =
'chat.fluffychat.dont_ask_bootstrap';
static const String autoplayImages = 'chat.fluffy.autoplay_images';
static const String sendTypingNotifications =
'chat.fluffy.send_typing_notifications';
static const String sendPublicReadReceipts =
'chat.fluffy.send_public_read_receipts';
static const String sendOnEnter = 'chat.fluffy.send_on_enter';
static const String swipeRightToLeftToReply =
'chat.fluffy.swipeRightToLeftToReply';
static const String experimentalVoip = 'chat.fluffy.experimental_voip';
static const String showPresences = 'chat.fluffy.show_presences';
static const String displayNavigationRail =
'chat.fluffy.display_navigation_rail';
}
enum AppSettings<T> { enum AppSettings<T> {
textMessageMaxLength<int>('textMessageMaxLength', 16384), textMessageMaxLength<int>('textMessageMaxLength', 16384),
@ -44,6 +16,9 @@ enum AppSettings<T> {
audioRecordingNoiseSuppress<bool>('audioRecordingNoiseSuppress', true), audioRecordingNoiseSuppress<bool>('audioRecordingNoiseSuppress', true),
audioRecordingBitRate<int>('audioRecordingBitRate', 64000), audioRecordingBitRate<int>('audioRecordingBitRate', 64000),
audioRecordingSamplingRate<int>('audioRecordingSamplingRate', 44100), audioRecordingSamplingRate<int>('audioRecordingSamplingRate', 44100),
showNoGoogle<bool>('chat.fluffy.show_no_google', false),
unifiedPushRegistered<bool>('chat.fluffy.unifiedpush.registered', false),
unifiedPushEndpoint<String>('chat.fluffy.unifiedpush.endpoint', ''),
pushNotificationsGatewayUrl<String>( pushNotificationsGatewayUrl<String>(
'pushNotificationsGatewayUrl', 'pushNotificationsGatewayUrl',
'https://push.fluffychat.im/_matrix/push/v1/notify', 'https://push.fluffychat.im/_matrix/push/v1/notify',
@ -52,6 +27,19 @@ enum AppSettings<T> {
'pushNotificationsPusherFormat', 'pushNotificationsPusherFormat',
'event_id_only', 'event_id_only',
), ),
renderHtml<bool>('chat.fluffy.renderHtml', true),
fontSizeFactor<double>('chat.fluffy.font_size_factor', 1.0),
hideRedactedEvents<bool>('chat.fluffy.hideRedactedEvents', false),
hideUnknownEvents<bool>('chat.fluffy.hideUnknownEvents', true),
separateChatTypes<bool>('chat.fluffy.separateChatTypes', false),
autoplayImages<bool>('chat.fluffy.autoplay_images', true),
sendTypingNotifications<bool>('chat.fluffy.send_typing_notifications', true),
sendPublicReadReceipts<bool>('chat.fluffy.send_public_read_receipts', true),
swipeRightToLeftToReply<bool>('chat.fluffy.swipeRightToLeftToReply', true),
sendOnEnter<bool>('chat.fluffy.send_on_enter', false),
showPresences<bool>('chat.fluffy.show_presences', true),
displayNavigationRail<bool>('chat.fluffy.display_navigation_rail', false),
experimentalVoip<bool>('chat.fluffy.experimental_voip', false),
shareKeysWith<String>('chat.fluffy.share_keys_with_2', 'all'), shareKeysWith<String>('chat.fluffy.share_keys_with_2', 'all'),
noEncryptionWarningShown<bool>( noEncryptionWarningShown<bool>(
'chat.fluffy.no_encryption_warning_shown', 'chat.fluffy.no_encryption_warning_shown',
@ -61,40 +49,88 @@ enum AppSettings<T> {
'chat.fluffy.display_chat_details_column', 'chat.fluffy.display_chat_details_column',
false, false,
), ),
// AppConfig-mirrored settings
applicationName<String>('chat.fluffy.application_name', 'FluffyChat'),
defaultHomeserver<String>('chat.fluffy.default_homeserver', 'matrix.org'),
privacyUrl<String>(
'chat.fluffy.privacy_url',
'https://github.com/krille-chan/fluffychat/blob/main/PRIVACY.md',
),
// colorSchemeSeed stored as ARGB int
colorSchemeSeedInt<int>(
'chat.fluffy.color_scheme_seed',
0xFF5625BA,
),
enableSoftLogout<bool>('chat.fluffy.enable_soft_logout', false); enableSoftLogout<bool>('chat.fluffy.enable_soft_logout', false);
final String key; final String key;
final T defaultValue; final T defaultValue;
const AppSettings(this.key, this.defaultValue); const AppSettings(this.key, this.defaultValue);
static late final SharedPreferences store;
static Future<SharedPreferences> init({loadWebConfigFile = true}) async {
final store = AppSettings.store = await SharedPreferences.getInstance();
if (store.getBool(AppSettings.sendOnEnter.key) == null) {
await store.setBool(AppSettings.sendOnEnter.key, !PlatformInfos.isMobile);
}
if (kIsWeb && loadWebConfigFile) {
try {
final configJsonString =
utf8.decode((await http.get(Uri.parse('config.json'))).bodyBytes);
final configJson =
json.decode(configJsonString) as Map<String, Object?>;
for (final setting in AppSettings.values) {
if (store.get(setting.key) != null) continue;
final configValue = configJson[setting.key];
if (configValue == null) continue;
if (configValue is bool) {
await store.setBool(setting.key, configValue);
}
if (configValue is String) {
await store.setString(setting.key, configValue);
}
if (configValue is int) {
await store.setInt(setting.key, configValue);
}
if (configValue is double) {
await store.setDouble(setting.key, configValue);
}
}
} on FormatException catch (_) {
Logs().v('[ConfigLoader] config.json not found');
} catch (e) {
Logs().v('[ConfigLoader] config.json not found', e);
}
}
return store;
}
} }
extension AppSettingsBoolExtension on AppSettings<bool> { extension AppSettingsBoolExtension on AppSettings<bool> {
bool getItem(SharedPreferences store) => store.getBool(key) ?? defaultValue; bool get value => AppSettings.store.getBool(key) ?? defaultValue;
Future<void> setItem(SharedPreferences store, bool value) => Future<void> setItem(bool value) => AppSettings.store.setBool(key, value);
store.setBool(key, value);
} }
extension AppSettingsStringExtension on AppSettings<String> { extension AppSettingsStringExtension on AppSettings<String> {
String getItem(SharedPreferences store) => String get value => AppSettings.store.getString(key) ?? defaultValue;
store.getString(key) ?? defaultValue;
Future<void> setItem(SharedPreferences store, String value) => Future<void> setItem(String value) => AppSettings.store.setString(key, value);
store.setString(key, value);
} }
extension AppSettingsIntExtension on AppSettings<int> { extension AppSettingsIntExtension on AppSettings<int> {
int getItem(SharedPreferences store) => store.getInt(key) ?? defaultValue; int get value => AppSettings.store.getInt(key) ?? defaultValue;
Future<void> setItem(SharedPreferences store, int value) => Future<void> setItem(int value) => AppSettings.store.setInt(key, value);
store.setInt(key, value);
} }
extension AppSettingsDoubleExtension on AppSettings<double> { extension AppSettingsDoubleExtension on AppSettings<double> {
double getItem(SharedPreferences store) => double get value => AppSettings.store.getDouble(key) ?? defaultValue;
store.getDouble(key) ?? defaultValue;
Future<void> setItem(SharedPreferences store, double value) => Future<void> setItem(double value) => AppSettings.store.setDouble(key, value);
store.setDouble(key, value);
} }

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'app_config.dart'; import 'app_config.dart';
abstract class FluffyThemes { abstract class FluffyThemes {
@ -45,7 +46,7 @@ abstract class FluffyThemes {
]) { ]) {
final colorScheme = ColorScheme.fromSeed( final colorScheme = ColorScheme.fromSeed(
brightness: brightness, brightness: brightness,
seedColor: seed ?? AppConfig.colorSchemeSeed ?? AppConfig.primaryColor, seedColor: seed ?? Color(AppSettings.colorSchemeSeedInt.value),
); );
final isColumnMode = FluffyThemes.isColumnMode(context); final isColumnMode = FluffyThemes.isColumnMode(context);
return ThemeData( return ThemeData(

View file

@ -6,7 +6,6 @@ import 'package:flutter_vodozemac/flutter_vodozemac.dart' as vod;
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/client_manager.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import 'config/setting_keys.dart'; import 'config/setting_keys.dart';
@ -14,17 +13,17 @@ import 'utils/background_push.dart';
import 'widgets/fluffy_chat_app.dart'; import 'widgets/fluffy_chat_app.dart';
void main() async { void main() async {
Logs().i('Welcome to ${AppConfig.applicationName} <3');
// Our background push shared isolate accesses flutter-internal things very early in the startup proccess // Our background push shared isolate accesses flutter-internal things very early in the startup proccess
// To make sure that the parts of flutter needed are started up already, we need to ensure that the // To make sure that the parts of flutter needed are started up already, we need to ensure that the
// widget bindings are initialized already. // widget bindings are initialized already.
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
final store = await AppSettings.init();
Logs().i('Welcome to ${AppSettings.applicationName.value} <3');
await vod.init(wasmPath: './assets/assets/vodozemac/'); await vod.init(wasmPath: './assets/assets/vodozemac/');
Logs().nativeColors = !PlatformInfos.isIOS; Logs().nativeColors = !PlatformInfos.isIOS;
final store = await SharedPreferences.getInstance();
final clients = await ClientManager.getClients(store: store); final clients = await ClientManager.getClients(store: store);
// If the app starts in detached mode, we assume that it is in // If the app starts in detached mode, we assume that it is in
@ -44,14 +43,14 @@ void main() async {
// To start the flutter engine afterwards we add an custom observer. // To start the flutter engine afterwards we add an custom observer.
WidgetsBinding.instance.addObserver(AppStarter(clients, store)); WidgetsBinding.instance.addObserver(AppStarter(clients, store));
Logs().i( Logs().i(
'${AppConfig.applicationName} started in background-fetch mode. No GUI will be created unless the app is no longer detached.', '${AppSettings.applicationName.value} started in background-fetch mode. No GUI will be created unless the app is no longer detached.',
); );
return; return;
} }
// Started in foreground mode. // Started in foreground mode.
Logs().i( Logs().i(
'${AppConfig.applicationName} started in foreground mode. Rendering GUI...', '${AppSettings.applicationName.value} started in foreground mode. Rendering GUI...',
); );
await startGui(clients, store); await startGui(clients, store);
} }
@ -63,7 +62,7 @@ Future<void> startGui(List<Client> clients, SharedPreferences store) async {
if (PlatformInfos.isMobile) { if (PlatformInfos.isMobile) {
try { try {
pin = pin =
await const FlutterSecureStorage().read(key: SettingKeys.appLockKey); await const FlutterSecureStorage().read(key: 'chat.fluffy.app_lock');
} catch (e, s) { } catch (e, s) {
Logs().d('Unable to read PIN from Secure storage', e, s); Logs().d('Unable to read PIN from Secure storage', e, s);
} }
@ -92,7 +91,7 @@ class AppStarter with WidgetsBindingObserver {
if (state == AppLifecycleState.detached) return; if (state == AppLifecycleState.detached) return;
Logs().i( Logs().i(
'${AppConfig.applicationName} switches from the detached background-fetch mode to ${state.name} mode. Rendering GUI...', '${AppSettings.applicationName.value} switches from the detached background-fetch mode to ${state.name} mode. Rendering GUI...',
); );
// Switching to foreground mode needs to reenable send online sync presence. // Switching to foreground mode needs to reenable send online sync presence.
for (final client in clients) { for (final client in clients) {

View file

@ -13,10 +13,8 @@ import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:universal_html/html.dart' as html; import 'package:universal_html/html.dart' as html;
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
@ -224,7 +222,7 @@ class ChatController extends State<ChatPageWithRoom>
} }
void _loadDraft() async { void _loadDraft() async {
final prefs = await SharedPreferences.getInstance(); final prefs = Matrix.of(context).store;
final draft = prefs.getString('draft_$roomId'); final draft = prefs.getString('draft_$roomId');
if (draft != null && draft.isNotEmpty) { if (draft != null && draft.isNotEmpty) {
sendController.text = draft; sendController.text = draft;
@ -274,7 +272,7 @@ class ChatController extends State<ChatPageWithRoom>
KeyEventResult _customEnterKeyHandling(FocusNode node, KeyEvent evt) { KeyEventResult _customEnterKeyHandling(FocusNode node, KeyEvent evt) {
if (!HardwareKeyboard.instance.isShiftPressed && if (!HardwareKeyboard.instance.isShiftPressed &&
evt.logicalKey.keyLabel == 'Enter' && evt.logicalKey.keyLabel == 'Enter' &&
(AppConfig.sendOnEnter ?? !PlatformInfos.isMobile)) { AppSettings.sendOnEnter.value) {
if (evt is KeyDownEvent) { if (evt is KeyDownEvent) {
send(); send();
} }
@ -325,7 +323,7 @@ class ChatController extends State<ChatPageWithRoom>
WidgetsBinding.instance.addPostFrameCallback(_shareItems); WidgetsBinding.instance.addPostFrameCallback(_shareItems);
super.initState(); super.initState();
_displayChatDetailsColumn = ValueNotifier( _displayChatDetailsColumn = ValueNotifier(
AppSettings.displayChatDetailsColumn.getItem(Matrix.of(context).store), AppSettings.displayChatDetailsColumn.value,
); );
sendingClient = Matrix.of(context).client; sendingClient = Matrix.of(context).client;
@ -365,7 +363,9 @@ class ChatController extends State<ChatPageWithRoom>
var readMarkerEventIndex = readMarkerEventId.isEmpty var readMarkerEventIndex = readMarkerEventId.isEmpty
? -1 ? -1
: timeline!.events : timeline!.events
.filterByVisibleInGui(exceptionEventId: readMarkerEventId) .filterByVisibleInGui(
exceptionEventId: readMarkerEventId,
)
.indexWhere((e) => e.eventId == readMarkerEventId); .indexWhere((e) => e.eventId == readMarkerEventId);
// Read marker is existing but not found in first events. Try a single // Read marker is existing but not found in first events. Try a single
@ -373,7 +373,9 @@ class ChatController extends State<ChatPageWithRoom>
if (readMarkerEventId.isNotEmpty && readMarkerEventIndex == -1) { if (readMarkerEventId.isNotEmpty && readMarkerEventIndex == -1) {
await timeline?.requestHistory(historyCount: _loadHistoryCount); await timeline?.requestHistory(historyCount: _loadHistoryCount);
readMarkerEventIndex = timeline!.events readMarkerEventIndex = timeline!.events
.filterByVisibleInGui(exceptionEventId: readMarkerEventId) .filterByVisibleInGui(
exceptionEventId: readMarkerEventId,
)
.indexWhere((e) => e.eventId == readMarkerEventId); .indexWhere((e) => e.eventId == readMarkerEventId);
} }
@ -492,7 +494,7 @@ class ChatController extends State<ChatPageWithRoom>
_setReadMarkerFuture = timeline _setReadMarkerFuture = timeline
.setReadMarker( .setReadMarker(
eventId: eventId, eventId: eventId,
public: AppConfig.sendPublicReadReceipts, public: AppSettings.sendPublicReadReceipts.value,
) )
.then((_) { .then((_) {
_setReadMarkerFuture = null; _setReadMarkerFuture = null;
@ -542,7 +544,7 @@ class ChatController extends State<ChatPageWithRoom>
Future<void> send() async { Future<void> send() async {
if (sendController.text.trim().isEmpty) return; if (sendController.text.trim().isEmpty) return;
_storeInputTimeoutTimer?.cancel(); _storeInputTimeoutTimer?.cancel();
final prefs = await SharedPreferences.getInstance(); final prefs = Matrix.of(context).store;
prefs.remove('draft_$roomId'); prefs.remove('draft_$roomId');
var parseCommands = true; var parseCommands = true;
@ -960,7 +962,9 @@ class ChatController extends State<ChatPageWithRoom>
final eventIndex = foundEvent == null final eventIndex = foundEvent == null
? -1 ? -1
: timeline!.events : timeline!.events
.filterByVisibleInGui(exceptionEventId: eventId) .filterByVisibleInGui(
exceptionEventId: eventId,
)
.indexOf(foundEvent); .indexOf(foundEvent);
if (eventIndex == -1) { if (eventIndex == -1) {
@ -1203,7 +1207,7 @@ class ChatController extends State<ChatPageWithRoom>
_storeInputTimeoutTimer?.cancel(); _storeInputTimeoutTimer?.cancel();
_storeInputTimeoutTimer = Timer(_storeInputTimeout, () async { _storeInputTimeoutTimer = Timer(_storeInputTimeout, () async {
final prefs = await SharedPreferences.getInstance(); final prefs = Matrix.of(context).store;
await prefs.setString('draft_$roomId', text); await prefs.setString('draft_$roomId', text);
}); });
if (text.endsWith(' ') && Matrix.of(context).hasComplexBundles) { if (text.endsWith(' ') && Matrix.of(context).hasComplexBundles) {
@ -1220,7 +1224,7 @@ class ChatController extends State<ChatPageWithRoom>
} }
} }
} }
if (AppConfig.sendTypingNotifications) { if (AppSettings.sendTypingNotifications.value) {
typingCoolDown?.cancel(); typingCoolDown?.cancel();
typingCoolDown = Timer(const Duration(seconds: 2), () { typingCoolDown = Timer(const Duration(seconds: 2), () {
typingCoolDown = null; typingCoolDown = null;
@ -1307,7 +1311,6 @@ class ChatController extends State<ChatPageWithRoom>
void toggleDisplayChatDetailsColumn() async { void toggleDisplayChatDetailsColumn() async {
await AppSettings.displayChatDetailsColumn.setItem( await AppSettings.displayChatDetailsColumn.setItem(
Matrix.of(context).store,
!_displayChatDetailsColumn.value, !_displayChatDetailsColumn.value,
); );
_displayChatDetailsColumn.value = !_displayChatDetailsColumn.value; _displayChatDetailsColumn.value = !_displayChatDetailsColumn.value;

View file

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:animations/animations.dart'; import 'package:animations/animations.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/recording_input_row.dart'; import 'package:fluffychat/pages/chat/recording_input_row.dart';
import 'package:fluffychat/pages/chat/recording_view_model.dart'; import 'package:fluffychat/pages/chat/recording_view_model.dart';
@ -297,10 +297,11 @@ class ChatInputRow extends StatelessWidget {
maxLines: 8, maxLines: 8,
autofocus: !PlatformInfos.isMobile, autofocus: !PlatformInfos.isMobile,
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
textInputAction: AppConfig.sendOnEnter == true && textInputAction:
PlatformInfos.isMobile AppSettings.sendOnEnter.value == true &&
? TextInputAction.send PlatformInfos.isMobile
: null, ? TextInputAction.send
: null,
onSubmitted: controller.onInputBarSubmitted, onSubmitted: controller.onInputBarSubmitted,
onSubmitImage: controller.sendImageFromClipBoard, onSubmitImage: controller.sendImageFromClipBoard,
focusNode: controller.inputFocus, focusNode: controller.inputFocus,

View file

@ -6,7 +6,7 @@ import 'package:badges/badges.dart';
import 'package:desktop_drop/desktop_drop.dart'; import 'package:desktop_drop/desktop_drop.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/chat.dart';
@ -119,7 +119,7 @@ class ChatView extends StatelessWidget {
]; ];
} else if (!controller.room.isArchived) { } else if (!controller.room.isArchived) {
return [ return [
if (AppConfig.experimentalVoip && if (AppSettings.experimentalVoip.value &&
Matrix.of(context).voipPlugin != null && Matrix.of(context).voipPlugin != null &&
controller.room.isDirectChat) controller.room.isDirectChat)
IconButton( IconButton(

View file

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
class CuteContent extends StatefulWidget { class CuteContent extends StatefulWidget {
@ -21,7 +21,7 @@ class _CuteContentState extends State<CuteContent> {
@override @override
void initState() { void initState() {
if (AppConfig.autoplayImages && !_isOverlayShown) { if (AppSettings.autoplayImages.value && !_isOverlayShown) {
addOverlay(); addOverlay();
} }
super.initState(); super.initState();

View file

@ -4,6 +4,7 @@ import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/pages/image_viewer/image_viewer.dart'; import 'package:fluffychat/pages/image_viewer/image_viewer.dart';
import 'package:fluffychat/utils/file_description.dart'; import 'package:fluffychat/utils/file_description.dart';
import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/utils/url_launcher.dart';
@ -139,14 +140,14 @@ class ImageBubble extends StatelessWidget {
textScaleFactor: MediaQuery.textScalerOf(context).scale(1), textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
style: TextStyle( style: TextStyle(
color: textColor, color: textColor,
fontSize: fontSize: AppSettings.fontSizeFactor.value *
AppConfig.fontSizeFactor * AppConfig.messageFontSize, AppConfig.messageFontSize,
), ),
options: const LinkifyOptions(humanize: false), options: const LinkifyOptions(humanize: false),
linkStyle: TextStyle( linkStyle: TextStyle(
color: linkColor, color: linkColor,
fontSize: fontSize: AppSettings.fontSizeFactor.value *
AppConfig.fontSizeFactor * AppConfig.messageFontSize, AppConfig.messageFontSize,
decoration: TextDecoration.underline, decoration: TextDecoration.underline,
decorationColor: linkColor, decorationColor: linkColor,
), ),

View file

@ -7,6 +7,7 @@ import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:swipe_to_action/swipe_to_action.dart'; import 'package:swipe_to_action/swipe_to_action.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/events/room_creation_state_event.dart'; import 'package:fluffychat/pages/chat/events/room_creation_state_event.dart';
@ -204,7 +205,7 @@ class Message extends StatelessWidget {
child: Icon(Icons.check_outlined), child: Icon(Icons.check_outlined),
), ),
), ),
direction: AppConfig.swipeRightToLeftToReply direction: AppSettings.swipeRightToLeftToReply.value
? SwipeDirection.endToStart ? SwipeDirection.endToStart
: SwipeDirection.startToEnd, : SwipeDirection.startToEnd,
onSwipe: (_) => onSwipe(), onSwipe: (_) => onSwipe(),
@ -243,7 +244,7 @@ class Message extends StatelessWidget {
child: Text( child: Text(
event.originServerTs.localizedTime(context), event.originServerTs.localizedTime(context),
style: TextStyle( style: TextStyle(
fontSize: 12 * AppConfig.fontSizeFactor, fontSize: 12 * AppSettings.fontSizeFactor.value,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: theme.colorScheme.secondary, color: theme.colorScheme.secondary,
), ),
@ -890,7 +891,7 @@ class Message extends StatelessWidget {
child: Text( child: Text(
L10n.of(context).readUpToHere, L10n.of(context).readUpToHere,
style: TextStyle( style: TextStyle(
fontSize: 12 * AppConfig.fontSizeFactor, fontSize: 12 * AppSettings.fontSizeFactor.value,
), ),
), ),
), ),

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/events/video_player.dart'; import 'package:fluffychat/pages/chat/events/video_player.dart';
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
@ -105,7 +106,8 @@ class MessageContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor; final fontSize =
AppConfig.messageFontSize * AppSettings.fontSizeFactor.value;
final buttonTextColor = textColor; final buttonTextColor = textColor;
switch (event.type) { switch (event.type) {
case EventTypes.Message: case EventTypes.Message:
@ -255,7 +257,7 @@ class MessageContent extends StatelessWidget {
}, },
); );
} }
var html = AppConfig.renderHtml && event.isRichMessage var html = AppSettings.renderHtml.value && event.isRichMessage
? event.formattedText ? event.formattedText
: event.body; : event.body;
if (event.messageType == MessageTypes.Emote) { if (event.messageType == MessageTypes.Emote) {
@ -274,14 +276,14 @@ class MessageContent extends StatelessWidget {
html: html, html: html,
textColor: textColor, textColor: textColor,
room: event.room, room: event.room,
fontSize: AppConfig.fontSizeFactor * fontSize: AppSettings.fontSizeFactor.value *
AppConfig.messageFontSize * AppConfig.messageFontSize *
(bigEmotes ? 5 : 1), (bigEmotes ? 5 : 1),
limitHeight: !selected, limitHeight: !selected,
linkStyle: TextStyle( linkStyle: TextStyle(
color: linkColor, color: linkColor,
fontSize: fontSize: AppSettings.fontSizeFactor.value *
AppConfig.fontSizeFactor * AppConfig.messageFontSize, AppConfig.messageFontSize,
decoration: TextDecoration.underline, decoration: TextDecoration.underline,
decorationColor: linkColor, decorationColor: linkColor,
), ),

View file

@ -4,6 +4,7 @@ import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/utils/file_description.dart'; import 'package:fluffychat/utils/file_description.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart';
import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/utils/url_launcher.dart';
@ -92,12 +93,14 @@ class MessageDownloadContent extends StatelessWidget {
textScaleFactor: MediaQuery.textScalerOf(context).scale(1), textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
style: TextStyle( style: TextStyle(
color: textColor, color: textColor,
fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, fontSize: AppSettings.fontSizeFactor.value *
AppConfig.messageFontSize,
), ),
options: const LinkifyOptions(humanize: false), options: const LinkifyOptions(humanize: false),
linkStyle: TextStyle( linkStyle: TextStyle(
color: linkColor, color: linkColor,
fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, fontSize: AppSettings.fontSizeFactor.value *
AppConfig.messageFontSize,
decoration: TextDecoration.underline, decoration: TextDecoration.underline,
decorationColor: linkColor, decorationColor: linkColor,
), ),

View file

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import '../../../config/app_config.dart'; import '../../../config/app_config.dart';
@ -30,7 +31,8 @@ class ReplyContent extends StatelessWidget {
final timeline = this.timeline; final timeline = this.timeline;
final displayEvent = final displayEvent =
timeline != null ? replyEvent.getDisplayEvent(timeline) : replyEvent; timeline != null ? replyEvent.getDisplayEvent(timeline) : replyEvent;
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor; final fontSize =
AppConfig.messageFontSize * AppSettings.fontSizeFactor.value;
final color = theme.brightness == Brightness.dark final color = theme.brightness == Brightness.dark
? theme.colorScheme.onTertiaryContainer ? theme.colorScheme.onTertiaryContainer
: ownMessage : ownMessage

View file

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
@ -68,7 +69,7 @@ class StateMessage extends StatelessWidget {
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: 12 * AppConfig.fontSizeFactor, fontSize: 12 * AppSettings.fontSizeFactor.value,
decoration: event.redacted decoration: event.redacted
? TextDecoration.lineThrough ? TextDecoration.lineThrough
: null, : null,

View file

@ -6,6 +6,7 @@ import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/utils/file_description.dart'; import 'package:fluffychat/utils/file_description.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
@ -136,14 +137,14 @@ class EventVideoPlayer extends StatelessWidget {
textScaleFactor: MediaQuery.textScalerOf(context).scale(1), textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
style: TextStyle( style: TextStyle(
color: textColor, color: textColor,
fontSize: fontSize: AppSettings.fontSizeFactor.value *
AppConfig.fontSizeFactor * AppConfig.messageFontSize, AppConfig.messageFontSize,
), ),
options: const LinkifyOptions(humanize: false), options: const LinkifyOptions(humanize: false),
linkStyle: TextStyle( linkStyle: TextStyle(
color: linkColor, color: linkColor,
fontSize: fontSize: AppSettings.fontSizeFactor.value *
AppConfig.fontSizeFactor * AppConfig.messageFontSize, AppConfig.messageFontSize,
decoration: TextDecoration.underline, decoration: TextDecoration.underline,
decorationColor: linkColor, decorationColor: linkColor,
), ),

View file

@ -415,8 +415,7 @@ class InputBar extends StatelessWidget {
// it sets the types for the callback incorrectly // it sets the types for the callback incorrectly
onSubmitted!(text); onSubmitted!(text);
}, },
maxLength: maxLength: AppSettings.textMessageMaxLength.value,
AppSettings.textMessageMaxLength.getItem(Matrix.of(context).store),
decoration: decoration, decoration: decoration,
onChanged: (text) { onChanged: (text) {
// fix for the library for now // fix for the library for now

View file

@ -15,7 +15,6 @@ import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'events/audio_player.dart'; import 'events/audio_player.dart';
class RecordingViewModel extends StatefulWidget { class RecordingViewModel extends StatefulWidget {
@ -62,8 +61,6 @@ class RecordingViewModelState extends State<RecordingViewModel> {
} }
if (await AudioRecorder().hasPermission() == false) return; if (await AudioRecorder().hasPermission() == false) return;
final store = Matrix.of(context).store;
final audioRecorder = _audioRecorder ??= AudioRecorder(); final audioRecorder = _audioRecorder ??= AudioRecorder();
setState(() {}); setState(() {});
@ -93,12 +90,12 @@ class RecordingViewModelState extends State<RecordingViewModel> {
await audioRecorder.start( await audioRecorder.start(
RecordConfig( RecordConfig(
bitRate: AppSettings.audioRecordingBitRate.getItem(store), bitRate: AppSettings.audioRecordingBitRate.value,
sampleRate: AppSettings.audioRecordingSamplingRate.getItem(store), sampleRate: AppSettings.audioRecordingSamplingRate.value,
numChannels: AppSettings.audioRecordingNumChannels.getItem(store), numChannels: AppSettings.audioRecordingNumChannels.value,
autoGain: AppSettings.audioRecordingAutoGain.getItem(store), autoGain: AppSettings.audioRecordingAutoGain.value,
echoCancel: AppSettings.audioRecordingEchoCancel.getItem(store), echoCancel: AppSettings.audioRecordingEchoCancel.value,
noiseSuppress: AppSettings.audioRecordingNoiseSuppress.getItem(store), noiseSuppress: AppSettings.audioRecordingNoiseSuppress.value,
encoder: codec, encoder: codec,
), ),
path: path ?? '', path: path ?? '',

View file

@ -12,7 +12,6 @@ import 'package:matrix/matrix.dart' as sdk;
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat_list/chat_list_view.dart'; import 'package:fluffychat/pages/chat_list/chat_list_view.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart';
@ -94,9 +93,7 @@ class ChatListController extends State<ChatList>
StreamSubscription? _intentUriStreamSubscription; StreamSubscription? _intentUriStreamSubscription;
ActiveFilter activeFilter = AppConfig.separateChatTypes late ActiveFilter activeFilter;
? ActiveFilter.messages
: ActiveFilter.allChats;
String? _activeSpaceId; String? _activeSpaceId;
String? get activeSpaceId => _activeSpaceId; String? get activeSpaceId => _activeSpaceId;
@ -401,6 +398,9 @@ class ChatListController extends State<ChatList>
@override @override
void initState() { void initState() {
activeFilter = AppSettings.separateChatTypes.value
? ActiveFilter.messages
: ActiveFilter.allChats;
_initReceiveSharingIntent(); _initReceiveSharingIntent();
_activeSpaceId = widget.activeSpace; _activeSpaceId = widget.activeSpace;
@ -713,8 +713,7 @@ class ChatListController extends State<ChatList>
context: context, context: context,
); );
if (result == OkCancelResult.ok) { if (result == OkCancelResult.ok) {
await Matrix.of(context).store.setBool(SettingKeys.showPresences, false); AppSettings.showPresences.setItem(false);
AppConfig.showPresences = false;
setState(() {}); setState(() {});
} }
} }

View file

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_list/chat_list_item.dart'; import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
@ -120,7 +120,8 @@ class ChatListViewBody extends StatelessWidget {
), ),
), ),
], ],
if (!controller.isSearchMode && AppConfig.showPresences) if (!controller.isSearchMode &&
AppSettings.showPresences.value)
GestureDetector( GestureDetector(
onLongPress: () => controller.dismissStatusList(), onLongPress: () => controller.dismissStatusList(),
child: StatusMessageList( child: StatusMessageList(
@ -155,14 +156,14 @@ class ChatListViewBody extends StatelessWidget {
shrinkWrap: true, shrinkWrap: true,
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
children: [ children: [
if (AppConfig.separateChatTypes) if (AppSettings.separateChatTypes.value)
ActiveFilter.messages ActiveFilter.messages
else else
ActiveFilter.allChats, ActiveFilter.allChats,
ActiveFilter.groups, ActiveFilter.groups,
ActiveFilter.unread, ActiveFilter.unread,
if (spaceDelegateCandidates.isNotEmpty && if (spaceDelegateCandidates.isNotEmpty &&
!AppConfig.displayNavigationRail && !AppSettings.displayNavigationRail.value &&
!FluffyThemes.isColumnMode(context)) !FluffyThemes.isColumnMode(context))
ActiveFilter.spaces, ActiveFilter.spaces,
] ]

View file

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart';
@ -32,7 +32,7 @@ class ChatListView extends StatelessWidget {
child: Row( child: Row(
children: [ children: [
if (FluffyThemes.isColumnMode(context) || if (FluffyThemes.isColumnMode(context) ||
AppConfig.displayNavigationRail) ...[ AppSettings.displayNavigationRail.value) ...[
SpacesNavigationRail( SpacesNavigationRail(
activeSpaceId: controller.activeSpaceId, activeSpaceId: controller.activeSpaceId,
onGoToChats: controller.clearActiveSpace, onGoToChats: controller.clearActiveSpace,

View file

@ -11,6 +11,7 @@ import 'package:universal_html/html.dart' as html;
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/homeserver_picker/homeserver_picker_view.dart'; import 'package:fluffychat/pages/homeserver_picker/homeserver_picker_view.dart';
import 'package:fluffychat/utils/file_selector.dart'; import 'package:fluffychat/utils/file_selector.dart';
@ -34,7 +35,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
bool isLoading = false; bool isLoading = false;
final TextEditingController homeserverController = TextEditingController( final TextEditingController homeserverController = TextEditingController(
text: AppConfig.defaultHomeserver, text: AppSettings.defaultHomeserver.value,
); );
String? error; String? error;
@ -211,7 +212,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
case MoreLoginActions.importBackup: case MoreLoginActions.importBackup:
restoreBackup(); restoreBackup();
case MoreLoginActions.privacy: case MoreLoginActions.privacy:
launchUrlString(AppConfig.privacyUrl); launchUrlString(AppSettings.privacyUrl.value);
case MoreLoginActions.about: case MoreLoginActions.about:
PlatformInfos.showDialog(context); PlatformInfos.showDialog(context);
} }

View file

@ -5,6 +5,7 @@ import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
import 'package:fluffychat/widgets/layouts/login_scaffold.dart'; import 'package:fluffychat/widgets/layouts/login_scaffold.dart';
@ -155,7 +156,7 @@ class HomeserverPickerView extends StatelessWidget {
AppConfig.borderRadius, AppConfig.borderRadius,
), ),
), ),
hintText: AppConfig.defaultHomeserver, hintText: AppSettings.defaultHomeserver.value,
hintStyle: TextStyle( hintStyle: TextStyle(
color: theme.colorScheme.surfaceTint, color: theme.colorScheme.surfaceTint,
), ),

View file

@ -4,7 +4,7 @@ import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/fluffy_share.dart';
@ -244,7 +244,7 @@ class SettingsView extends StatelessWidget {
ListTile( ListTile(
leading: const Icon(Icons.privacy_tip_outlined), leading: const Icon(Icons.privacy_tip_outlined),
title: Text(L10n.of(context).privacy), title: Text(L10n.of(context).privacy),
onTap: () => launchUrlString(AppConfig.privacyUrl), onTap: () => launchUrlString(AppSettings.privacyUrl.value),
), ),
ListTile( ListTile(
leading: const Icon(Icons.info_outline_rounded), leading: const Icon(Icons.info_outline_rounded),

View file

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
@ -34,41 +33,29 @@ class SettingsChatView extends StatelessWidget {
SettingsSwitchListTile.adaptive( SettingsSwitchListTile.adaptive(
title: L10n.of(context).formattedMessages, title: L10n.of(context).formattedMessages,
subtitle: L10n.of(context).formattedMessagesDescription, subtitle: L10n.of(context).formattedMessagesDescription,
onChanged: (b) => AppConfig.renderHtml = b, setting: AppSettings.renderHtml,
storeKey: SettingKeys.renderHtml,
defaultValue: AppConfig.renderHtml,
), ),
SettingsSwitchListTile.adaptive( SettingsSwitchListTile.adaptive(
title: L10n.of(context).hideRedactedMessages, title: L10n.of(context).hideRedactedMessages,
subtitle: L10n.of(context).hideRedactedMessagesBody, subtitle: L10n.of(context).hideRedactedMessagesBody,
onChanged: (b) => AppConfig.hideRedactedEvents = b, setting: AppSettings.hideRedactedEvents,
storeKey: SettingKeys.hideRedactedEvents,
defaultValue: AppConfig.hideRedactedEvents,
), ),
SettingsSwitchListTile.adaptive( SettingsSwitchListTile.adaptive(
title: L10n.of(context).hideInvalidOrUnknownMessageFormats, title: L10n.of(context).hideInvalidOrUnknownMessageFormats,
onChanged: (b) => AppConfig.hideUnknownEvents = b, setting: AppSettings.hideUnknownEvents,
storeKey: SettingKeys.hideUnknownEvents,
defaultValue: AppConfig.hideUnknownEvents,
), ),
if (PlatformInfos.isMobile) if (PlatformInfos.isMobile)
SettingsSwitchListTile.adaptive( SettingsSwitchListTile.adaptive(
title: L10n.of(context).autoplayImages, title: L10n.of(context).autoplayImages,
onChanged: (b) => AppConfig.autoplayImages = b, setting: AppSettings.autoplayImages,
storeKey: SettingKeys.autoplayImages,
defaultValue: AppConfig.autoplayImages,
), ),
SettingsSwitchListTile.adaptive( SettingsSwitchListTile.adaptive(
title: L10n.of(context).sendOnEnter, title: L10n.of(context).sendOnEnter,
onChanged: (b) => AppConfig.sendOnEnter = b, setting: AppSettings.sendOnEnter,
storeKey: SettingKeys.sendOnEnter,
defaultValue: AppConfig.sendOnEnter ?? !PlatformInfos.isMobile,
), ),
SettingsSwitchListTile.adaptive( SettingsSwitchListTile.adaptive(
title: L10n.of(context).swipeRightToLeftToReply, title: L10n.of(context).swipeRightToLeftToReply,
onChanged: (b) => AppConfig.swipeRightToLeftToReply = b, setting: AppSettings.swipeRightToLeftToReply,
storeKey: SettingKeys.swipeRightToLeftToReply,
defaultValue: AppConfig.swipeRightToLeftToReply,
), ),
Divider(color: theme.dividerColor), Divider(color: theme.dividerColor),
ListTile( ListTile(
@ -102,12 +89,10 @@ class SettingsChatView extends StatelessWidget {
SettingsSwitchListTile.adaptive( SettingsSwitchListTile.adaptive(
title: L10n.of(context).experimentalVideoCalls, title: L10n.of(context).experimentalVideoCalls,
onChanged: (b) { onChanged: (b) {
AppConfig.experimentalVoip = b;
Matrix.of(context).createVoipPlugin(); Matrix.of(context).createVoipPlugin();
return; return;
}, },
storeKey: SettingKeys.experimentalVoip, setting: AppSettings.experimentalVoip,
defaultValue: AppConfig.experimentalVoip,
), ),
], ],
), ),

View file

@ -112,7 +112,6 @@ class SettingsSecurityController extends State<SettingsSecurity> {
void changeShareKeysWith(ShareKeysWith? shareKeysWith) async { void changeShareKeysWith(ShareKeysWith? shareKeysWith) async {
if (shareKeysWith == null) return; if (shareKeysWith == null) return;
AppSettings.shareKeysWith.setItem( AppSettings.shareKeysWith.setItem(
Matrix.of(context).store,
shareKeysWith.name, shareKeysWith.name,
); );
Matrix.of(context).client.shareKeysWith = shareKeysWith; Matrix.of(context).client.shareKeysWith = shareKeysWith;

View file

@ -63,16 +63,12 @@ class SettingsSecurityView extends StatelessWidget {
title: L10n.of(context).sendTypingNotifications, title: L10n.of(context).sendTypingNotifications,
subtitle: subtitle:
L10n.of(context).sendTypingNotificationsDescription, L10n.of(context).sendTypingNotificationsDescription,
onChanged: (b) => AppConfig.sendTypingNotifications = b, setting: AppSettings.sendTypingNotifications,
storeKey: SettingKeys.sendTypingNotifications,
defaultValue: AppConfig.sendTypingNotifications,
), ),
SettingsSwitchListTile.adaptive( SettingsSwitchListTile.adaptive(
title: L10n.of(context).sendReadReceipts, title: L10n.of(context).sendReadReceipts,
subtitle: L10n.of(context).sendReadReceiptsDescription, subtitle: L10n.of(context).sendReadReceiptsDescription,
onChanged: (b) => AppConfig.sendPublicReadReceipts = b, setting: AppSettings.sendPublicReadReceipts,
storeKey: SettingKeys.sendPublicReadReceipts,
defaultValue: AppConfig.sendPublicReadReceipts,
), ),
ListTile( ListTile(
trailing: const Icon(Icons.chevron_right_outlined), trailing: const Icon(Icons.chevron_right_outlined),

View file

@ -18,7 +18,9 @@ class SettingsStyle extends StatefulWidget {
class SettingsStyleController extends State<SettingsStyle> { class SettingsStyleController extends State<SettingsStyle> {
void setChatColor(Color? color) async { void setChatColor(Color? color) async {
AppConfig.colorSchemeSeed = color; AppSettings.colorSchemeSeedInt.setItem(
color?.toARGB32() ?? AppSettings.colorSchemeSeedInt.defaultValue,
);
ThemeController.of(context).setPrimaryColor(color); ThemeController.of(context).setPrimaryColor(color);
} }
@ -156,11 +158,7 @@ class SettingsStyleController extends State<SettingsStyle> {
} }
void changeFontSizeFactor(double d) { void changeFontSizeFactor(double d) {
setState(() => AppConfig.fontSizeFactor = d); AppSettings.fontSizeFactor.setItem(d);
Matrix.of(context).store.setString(
SettingKeys.fontSizeFactor,
AppConfig.fontSizeFactor.toString(),
);
} }
@override @override

View file

@ -230,7 +230,7 @@ class SettingsStyleView extends StatelessWidget {
style: TextStyle( style: TextStyle(
color: theme.onBubbleColor, color: theme.onBubbleColor,
fontSize: AppConfig.messageFontSize * fontSize: AppConfig.messageFontSize *
AppConfig.fontSizeFactor, AppSettings.fontSizeFactor.value,
), ),
), ),
), ),
@ -263,7 +263,7 @@ class SettingsStyleView extends StatelessWidget {
style: TextStyle( style: TextStyle(
color: theme.colorScheme.onSurface, color: theme.colorScheme.onSurface,
fontSize: AppConfig.messageFontSize * fontSize: AppConfig.messageFontSize *
AppConfig.fontSizeFactor, AppSettings.fontSizeFactor.value,
), ),
), ),
), ),
@ -325,13 +325,15 @@ class SettingsStyleView extends StatelessWidget {
), ),
ListTile( ListTile(
title: Text(L10n.of(context).fontSize), title: Text(L10n.of(context).fontSize),
trailing: Text('× ${AppConfig.fontSizeFactor}'), trailing: Text(
'× ${AppSettings.fontSizeFactor.value}',
),
), ),
Slider.adaptive( Slider.adaptive(
min: 0.5, min: 0.5,
max: 2.5, max: 2.5,
divisions: 20, divisions: 20,
value: AppConfig.fontSizeFactor, value: AppSettings.fontSizeFactor.value,
semanticFormatterCallback: (d) => d.toString(), semanticFormatterCallback: (d) => d.toString(),
onChanged: controller.changeFontSizeFactor, onChanged: controller.changeFontSizeFactor,
), ),
@ -349,21 +351,15 @@ class SettingsStyleView extends StatelessWidget {
), ),
SettingsSwitchListTile.adaptive( SettingsSwitchListTile.adaptive(
title: L10n.of(context).presencesToggle, title: L10n.of(context).presencesToggle,
onChanged: (b) => AppConfig.showPresences = b, setting: AppSettings.showPresences,
storeKey: SettingKeys.showPresences,
defaultValue: AppConfig.showPresences,
), ),
SettingsSwitchListTile.adaptive( SettingsSwitchListTile.adaptive(
title: L10n.of(context).separateChatTypes, title: L10n.of(context).separateChatTypes,
onChanged: (b) => AppConfig.separateChatTypes = b, setting: AppSettings.separateChatTypes,
storeKey: SettingKeys.separateChatTypes,
defaultValue: AppConfig.separateChatTypes,
), ),
SettingsSwitchListTile.adaptive( SettingsSwitchListTile.adaptive(
title: L10n.of(context).displayNavigationRail, title: L10n.of(context).displayNavigationRail,
onChanged: (b) => AppConfig.displayNavigationRail = b, setting: AppSettings.displayNavigationRail,
storeKey: SettingKeys.displayNavigationRail,
defaultValue: AppConfig.displayNavigationRail,
), ),
], ],
), ),

View file

@ -217,8 +217,7 @@ class BackgroundPush {
currentPushers.first.lang == 'en' && currentPushers.first.lang == 'en' &&
currentPushers.first.data.url.toString() == gatewayUrl && currentPushers.first.data.url.toString() == gatewayUrl &&
currentPushers.first.data.format == currentPushers.first.data.format ==
AppSettings.pushNotificationsPusherFormat AppSettings.pushNotificationsPusherFormat.value &&
.getItem(matrix!.store) &&
mapEquals( mapEquals(
currentPushers.single.data.additionalProperties, currentPushers.single.data.additionalProperties,
{"data_message": pusherDataMessageFormat}, {"data_message": pusherDataMessageFormat},
@ -258,8 +257,7 @@ class BackgroundPush {
lang: 'en', lang: 'en',
data: PusherData( data: PusherData(
url: Uri.parse(gatewayUrl!), url: Uri.parse(gatewayUrl!),
format: AppSettings.pushNotificationsPusherFormat format: AppSettings.pushNotificationsPusherFormat.value,
.getItem(matrix!.store),
additionalProperties: {"data_message": pusherDataMessageFormat}, additionalProperties: {"data_message": pusherDataMessageFormat},
), ),
kind: 'http', kind: 'http',
@ -325,7 +323,7 @@ class BackgroundPush {
if (matrix == null) { if (matrix == null) {
return; return;
} }
if ((matrix?.store.getBool(SettingKeys.showNoGoogle) ?? false) == true) { if (!AppSettings.showNoGoogle.value) {
return; return;
} }
await loadLocale(); await loadLocale();
@ -356,8 +354,7 @@ class BackgroundPush {
} }
} }
await setupPusher( await setupPusher(
gatewayUrl: gatewayUrl: AppSettings.pushNotificationsGatewayUrl.value,
AppSettings.pushNotificationsGatewayUrl.getItem(matrix!.store),
token: _fcmToken, token: _fcmToken,
); );
} }
@ -414,18 +411,18 @@ class BackgroundPush {
oldTokens: oldTokens, oldTokens: oldTokens,
useDeviceSpecificAppId: true, useDeviceSpecificAppId: true,
); );
await matrix?.store.setString(SettingKeys.unifiedPushEndpoint, newEndpoint); await AppSettings.unifiedPushEndpoint.setItem(newEndpoint);
await matrix?.store.setBool(SettingKeys.unifiedPushRegistered, true); await AppSettings.unifiedPushRegistered.setItem(true);
} }
Future<void> _upUnregistered(String i) async { Future<void> _upUnregistered(String i) async {
upAction = true; upAction = true;
Logs().i('[Push] Removing UnifiedPush endpoint...'); Logs().i('[Push] Removing UnifiedPush endpoint...');
final oldEndpoint = final oldEndpoint = AppSettings.unifiedPushEndpoint.value;
matrix?.store.getString(SettingKeys.unifiedPushEndpoint); await AppSettings.unifiedPushEndpoint
await matrix?.store.setBool(SettingKeys.unifiedPushRegistered, false); .setItem(AppSettings.unifiedPushEndpoint.defaultValue);
await matrix?.store.remove(SettingKeys.unifiedPushEndpoint); await AppSettings.unifiedPushRegistered.setItem(false);
if (oldEndpoint?.isNotEmpty ?? false) { if (oldEndpoint.isNotEmpty) {
// remove the old pusher // remove the old pusher
await setupPusher( await setupPusher(
oldTokens: {oldEndpoint}, oldTokens: {oldEndpoint},

View file

@ -11,7 +11,6 @@ import 'package:matrix/matrix.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:universal_html/html.dart' as html; import 'package:universal_html/html.dart' as html;
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/custom_http_client.dart'; import 'package:fluffychat/utils/custom_http_client.dart';
@ -101,8 +100,8 @@ abstract class ClientManager {
String clientName, String clientName,
SharedPreferences store, SharedPreferences store,
) async { ) async {
final shareKeysWith = AppSettings.shareKeysWith.getItem(store); final shareKeysWith = AppSettings.shareKeysWith.value;
final enableSoftLogout = AppSettings.enableSoftLogout.getItem(store); final enableSoftLogout = AppSettings.enableSoftLogout.value;
return Client( return Client(
clientName, clientName,
@ -147,7 +146,7 @@ abstract class ClientManager {
await NotificationsClient().notify( await NotificationsClient().notify(
title, title,
body: body, body: body,
appName: AppConfig.applicationName, appName: AppSettings.applicationName.value,
hints: [ hints: [
NotificationHint.soundName('message-new-instant'), NotificationHint.soundName('message-new-instant'),
], ],

View file

@ -5,6 +5,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/client_manager.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
@ -57,13 +58,13 @@ extension InitWithRestoreExtension on Client {
? const FlutterSecureStorage() ? const FlutterSecureStorage()
: null; : null;
await storage?.delete( await storage?.delete(
key: '${AppConfig.applicationName}_session_backup_$clientName', key: '${AppSettings.applicationName.value}_session_backup_$clientName',
); );
} }
Future<void> initWithRestore({void Function()? onMigration}) async { Future<void> initWithRestore({void Function()? onMigration}) async {
final storageKey = final storageKey =
'${AppConfig.applicationName}_session_backup_$clientName'; '${AppSettings.applicationName.value}_session_backup_$clientName';
final storage = PlatformInfos.isMobile || PlatformInfos.isLinux final storage = PlatformInfos.isMobile || PlatformInfos.isLinux
? const FlutterSecureStorage() ? const FlutterSecureStorage()
: null; : null;

View file

@ -1,9 +1,12 @@
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import '../../config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart';
extension VisibleInGuiExtension on List<Event> { extension VisibleInGuiExtension on List<Event> {
List<Event> filterByVisibleInGui({String? exceptionEventId}) => where( List<Event> filterByVisibleInGui({
String? exceptionEventId,
}) =>
where(
(event) => event.isVisibleInGui || event.eventId == exceptionEventId, (event) => event.isVisibleInGui || event.eventId == exceptionEventId,
).toList(); ).toList();
} }
@ -19,9 +22,9 @@ extension IsStateExtension on Event {
// if a reaction has been redacted we also want it to be hidden in the timeline // if a reaction has been redacted we also want it to be hidden in the timeline
!{EventTypes.Reaction, EventTypes.Redaction}.contains(type) && !{EventTypes.Reaction, EventTypes.Redaction}.contains(type) &&
// if we enabled to hide all redacted events, don't show those // if we enabled to hide all redacted events, don't show those
(!AppConfig.hideRedactedEvents || !redacted) && (!AppSettings.hideRedactedEvents.value || !redacted) &&
// if we enabled to hide all unknown events, don't show those // if we enabled to hide all unknown events, don't show those
(!AppConfig.hideUnknownEvents || isEventTypeKnown); (!AppSettings.hideUnknownEvents.value || isEventTypeKnown);
bool get isState => !{ bool get isState => !{
EventTypes.Message, EventTypes.Message,

View file

@ -6,7 +6,6 @@ import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
@ -52,8 +51,7 @@ Future<String?> getDatabaseCipher() async {
} }
void _sendNoEncryptionWarning(Object exception) async { void _sendNoEncryptionWarning(Object exception) async {
final store = await SharedPreferences.getInstance(); final isStored = AppSettings.noEncryptionWarningShown.value;
final isStored = AppSettings.noEncryptionWarningShown.getItem(store);
if (isStored == true) return; if (isStored == true) return;
@ -63,5 +61,5 @@ void _sendNoEncryptionWarning(Object exception) async {
exception.toString(), exception.toString(),
); );
await AppSettings.noEncryptionWarningShown.setItem(store, true); await AppSettings.noEncryptionWarningShown.setItem(true);
} }

View file

@ -6,7 +6,6 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_vodozemac/flutter_vodozemac.dart' as vod; import 'package:flutter_vodozemac/flutter_vodozemac.dart' as vod;
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/client_download_content_extension.dart'; import 'package:fluffychat/utils/client_download_content_extension.dart';
@ -59,7 +58,7 @@ void notificationTapBackground(
await vod.init(); await vod.init();
_vodInitialized = true; _vodInitialized = true;
} }
final store = await SharedPreferences.getInstance(); final store = await AppSettings.init();
final client = (await ClientManager.getClients( final client = (await ClientManager.getClients(
initialize: false, initialize: false,
store: store, store: store,
@ -71,10 +70,6 @@ void notificationTapBackground(
waitUntilLoadCompletedLoaded: false, waitUntilLoadCompletedLoaded: false,
); );
AppConfig.sendPublicReadReceipts =
store.getBool(SettingKeys.sendPublicReadReceipts) ??
AppConfig.sendPublicReadReceipts;
if (!client.isLogged()) { if (!client.isLogged()) {
throw Exception('Notification tab in background but not logged in!'); throw Exception('Notification tab in background but not logged in!');
} }
@ -145,7 +140,7 @@ Future<void> notificationTap(
await room.setReadMarker( await room.setReadMarker(
payload.eventId ?? room.lastEvent!.eventId, payload.eventId ?? room.lastEvent!.eventId,
mRead: payload.eventId ?? room.lastEvent!.eventId, mRead: payload.eventId ?? room.lastEvent!.eventId,
public: AppConfig.sendPublicReadReceipts, public: AppSettings.sendPublicReadReceipts.value,
); );
case FluffyChatNotificationActions.reply: case FluffyChatNotificationActions.reply:
final input = notificationResponse.input; final input = notificationResponse.input;

View file

@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import '../config/app_config.dart'; import '../config/app_config.dart';
@ -36,7 +37,7 @@ abstract class PlatformInfos {
static bool get platformCanRecord => (isMobile || isMacOS); static bool get platformCanRecord => (isMobile || isMacOS);
static String get clientName => static String get clientName =>
'${AppConfig.applicationName} ${isWeb ? 'web' : Platform.operatingSystem}${kReleaseMode ? '' : 'Debug'}'; '${AppSettings.applicationName.value} ${isWeb ? 'web' : Platform.operatingSystem}${kReleaseMode ? '' : 'Debug'}';
static Future<String> getVersion() async { static Future<String> getVersion() async {
var version = kIsWeb ? 'Web' : 'Unknown'; var version = kIsWeb ? 'Web' : 'Unknown';
@ -88,7 +89,7 @@ abstract class PlatformInfos {
height: 64, height: 64,
filterQuality: FilterQuality.medium, filterQuality: FilterQuality.medium,
), ),
applicationName: AppConfig.applicationName, applicationName: AppSettings.applicationName.value,
); );
} }
} }

View file

@ -8,9 +8,9 @@ import 'package:collection/collection.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_shortcuts_new/flutter_shortcuts_new.dart'; import 'package:flutter_shortcuts_new/flutter_shortcuts_new.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/client_download_content_extension.dart'; import 'package:fluffychat/utils/client_download_content_extension.dart';
import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/client_manager.dart';
@ -50,7 +50,7 @@ Future<void> pushHelper(
l10n.incomingMessages, l10n.incomingMessages,
number: notification.counts?.unread, number: notification.counts?.unread,
ticker: l10n.unreadChatsInApp( ticker: l10n.unreadChatsInApp(
AppConfig.applicationName, AppSettings.applicationName.value,
(notification.counts?.unread ?? 0).toString(), (notification.counts?.unread ?? 0).toString(),
), ),
importance: Importance.high, importance: Importance.high,
@ -85,7 +85,7 @@ Future<void> _tryPushHelper(
client ??= (await ClientManager.getClients( client ??= (await ClientManager.getClients(
initialize: false, initialize: false,
store: await SharedPreferences.getInstance(), store: await AppSettings.init(),
)) ))
.first; .first;
final event = await client.getEventByPushNotification( final event = await client.getEventByPushNotification(

View file

@ -4,7 +4,6 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/widgets/lock_screen.dart'; import 'package:fluffychat/widgets/lock_screen.dart';
class AppLockWidget extends StatefulWidget { class AppLockWidget extends StatefulWidget {
@ -65,7 +64,7 @@ class AppLock extends State<AppLockWidget> with WidgetsBindingObserver {
Future<void> changePincode(String? pincode) async { Future<void> changePincode(String? pincode) async {
await const FlutterSecureStorage().write( await const FlutterSecureStorage().write(
key: SettingKeys.appLockKey, key: 'chat.fluffy.app_lock',
value: pincode, value: pincode,
); );
_pincode = pincode; _pincode = pincode;

View file

@ -21,7 +21,7 @@ class _ConfigViewerState extends State<ConfigViewer> {
String initialValue, String initialValue,
) async { ) async {
if (appSetting is AppSettings<bool>) { if (appSetting is AppSettings<bool>) {
await appSetting.setItem(store, !(initialValue == 'true')); await appSetting.setItem(!(initialValue == 'true'));
setState(() {}); setState(() {});
return; return;
} }
@ -35,13 +35,13 @@ class _ConfigViewerState extends State<ConfigViewer> {
if (value == null) return; if (value == null) return;
if (appSetting is AppSettings<String>) { if (appSetting is AppSettings<String>) {
await appSetting.setItem(store, value); await appSetting.setItem(value);
} }
if (appSetting is AppSettings<int>) { if (appSetting is AppSettings<int>) {
await appSetting.setItem(store, int.parse(value)); await appSetting.setItem(int.parse(value));
} }
if (appSetting is AppSettings<double>) { if (appSetting is AppSettings<double>) {
await appSetting.setItem(store, double.parse(value)); await appSetting.setItem(double.parse(value));
} }
setState(() {}); setState(() {});
@ -78,16 +78,16 @@ class _ConfigViewerState extends State<ConfigViewer> {
final appSetting = AppSettings.values[i]; final appSetting = AppSettings.values[i];
var value = ''; var value = '';
if (appSetting is AppSettings<String>) { if (appSetting is AppSettings<String>) {
value = appSetting.getItem(store); value = appSetting.value;
} }
if (appSetting is AppSettings<int>) { if (appSetting is AppSettings<int>) {
value = appSetting.getItem(store).toString(); value = appSetting.value.toString();
} }
if (appSetting is AppSettings<bool>) { if (appSetting is AppSettings<bool>) {
value = appSetting.getItem(store).toString(); value = appSetting.value.toString();
} }
if (appSetting is AppSettings<double>) { if (appSetting is AppSettings<double>) {
value = appSetting.getItem(store).toString(); value = appSetting.value.toString();
} }
return ListTile( return ListTile(
title: Text(appSetting.name), title: Text(appSetting.name),

View file

@ -5,11 +5,11 @@ import 'package:matrix/matrix.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:fluffychat/config/routes.dart'; import 'package:fluffychat/config/routes.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/widgets/app_lock.dart'; import 'package:fluffychat/widgets/app_lock.dart';
import 'package:fluffychat/widgets/theme_builder.dart'; import 'package:fluffychat/widgets/theme_builder.dart';
import '../config/app_config.dart';
import '../utils/custom_scroll_behaviour.dart'; import '../utils/custom_scroll_behaviour.dart';
import 'matrix.dart'; import 'matrix.dart';
@ -43,7 +43,7 @@ class FluffyChatApp extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ThemeBuilder( return ThemeBuilder(
builder: (context, themeMode, primaryColor) => MaterialApp.router( builder: (context, themeMode, primaryColor) => MaterialApp.router(
title: AppConfig.applicationName, title: AppSettings.applicationName.value,
themeMode: themeMode, themeMode: themeMode,
theme: FluffyThemes.buildTheme(context, Brightness.light, primaryColor), theme: FluffyThemes.buildTheme(context, Brightness.light, primaryColor),
darkTheme: darkTheme:

View file

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
@ -107,7 +108,7 @@ class _PrivacyButtons extends StatelessWidget {
), ),
), ),
TextButton( TextButton(
onPressed: () => launchUrlString(AppConfig.privacyUrl), onPressed: () => launchUrlString(AppSettings.privacyUrl.value),
child: Text( child: Text(
L10n.of(context).privacy, L10n.of(context).privacy,
style: shadowTextStyle, style: shadowTextStyle,

View file

@ -9,7 +9,7 @@ import 'package:image/image.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:universal_html/html.dart' as html; import 'package:universal_html/html.dart' as html;
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/utils/client_download_content_extension.dart'; import 'package:fluffychat/utils/client_download_content_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
@ -114,7 +114,7 @@ extension LocalNotificationsExtension on MatrixState {
title, title,
body: body, body: body,
replacesId: linuxNotificationIds[roomId] ?? 0, replacesId: linuxNotificationIds[roomId] ?? 0,
appName: AppConfig.applicationName, appName: AppSettings.applicationName.value,
appIcon: 'fluffychat', appIcon: 'fluffychat',
actions: [ actions: [
NotificationAction( NotificationAction(
@ -139,7 +139,7 @@ extension LocalNotificationsExtension on MatrixState {
event.room.setReadMarker( event.room.setReadMarker(
event.eventId, event.eventId,
mRead: event.eventId, mRead: event.eventId,
public: AppConfig.sendPublicReadReceipts, public: AppSettings.sendPublicReadReceipts.value,
); );
break; break;
case DesktopNotificationActions.openChat: case DesktopNotificationActions.openChat:

View file

@ -6,7 +6,6 @@ import 'package:flutter/material.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:desktop_notifications/desktop_notifications.dart'; import 'package:desktop_notifications/desktop_notifications.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
@ -27,7 +26,6 @@ import 'package:fluffychat/utils/voip_plugin.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
import 'package:fluffychat/widgets/fluffy_chat_app.dart'; import 'package:fluffychat/widgets/fluffy_chat_app.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart';
import '../config/app_config.dart';
import '../config/setting_keys.dart'; import '../config/setting_keys.dart';
import '../pages/key_verification/key_verification_dialog.dart'; import '../pages/key_verification/key_verification_dialog.dart';
import '../utils/account_bundles.dart'; import '../utils/account_bundles.dart';
@ -155,7 +153,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
} }
final candidate = final candidate =
_loginClientCandidate ??= await ClientManager.createClient( _loginClientCandidate ??= await ClientManager.createClient(
'${AppConfig.applicationName}-${DateTime.now().millisecondsSinceEpoch}', '${AppSettings.applicationName.value}-${DateTime.now().millisecondsSinceEpoch}',
store, store,
) )
..onLoginStateChanged ..onLoginStateChanged
@ -221,24 +219,6 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
super.initState(); super.initState();
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
initMatrix(); initMatrix();
if (PlatformInfos.isWeb) {
initConfig().then((_) => initSettings());
} else {
initSettings();
}
}
Future<void> initConfig() async {
try {
final configJsonString =
utf8.decode((await http.get(Uri.parse('config.json'))).bodyBytes);
final configJson = json.decode(configJsonString);
AppConfig.loadFromJson(configJson);
} on FormatException catch (_) {
Logs().v('[ConfigLoader] config.json not found');
} catch (e) {
Logs().v('[ConfigLoader] config.json not found', e);
}
} }
void _registerSubs(String name) { void _registerSubs(String name) {
@ -358,7 +338,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
); );
} }
if (result == OkCancelResult.cancel) { if (result == OkCancelResult.cancel) {
await store.setBool(SettingKeys.showNoGoogle, true); await AppSettings.showNoGoogle.setItem(true);
} }
}, },
); );
@ -368,7 +348,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
} }
void createVoipPlugin() async { void createVoipPlugin() async {
if (store.getBool(SettingKeys.experimentalVoip) == false) { if (AppSettings.experimentalVoip.value) {
voipPlugin = null; voipPlugin = null;
return; return;
} }
@ -390,55 +370,6 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
} }
} }
void initSettings() {
AppConfig.fontSizeFactor =
double.tryParse(store.getString(SettingKeys.fontSizeFactor) ?? '') ??
AppConfig.fontSizeFactor;
AppConfig.renderHtml =
store.getBool(SettingKeys.renderHtml) ?? AppConfig.renderHtml;
AppConfig.swipeRightToLeftToReply =
store.getBool(SettingKeys.swipeRightToLeftToReply) ??
AppConfig.swipeRightToLeftToReply;
AppConfig.hideRedactedEvents =
store.getBool(SettingKeys.hideRedactedEvents) ??
AppConfig.hideRedactedEvents;
AppConfig.hideUnknownEvents =
store.getBool(SettingKeys.hideUnknownEvents) ??
AppConfig.hideUnknownEvents;
AppConfig.separateChatTypes =
store.getBool(SettingKeys.separateChatTypes) ??
AppConfig.separateChatTypes;
AppConfig.autoplayImages =
store.getBool(SettingKeys.autoplayImages) ?? AppConfig.autoplayImages;
AppConfig.sendTypingNotifications =
store.getBool(SettingKeys.sendTypingNotifications) ??
AppConfig.sendTypingNotifications;
AppConfig.sendPublicReadReceipts =
store.getBool(SettingKeys.sendPublicReadReceipts) ??
AppConfig.sendPublicReadReceipts;
AppConfig.sendOnEnter =
store.getBool(SettingKeys.sendOnEnter) ?? AppConfig.sendOnEnter;
AppConfig.experimentalVoip = store.getBool(SettingKeys.experimentalVoip) ??
AppConfig.experimentalVoip;
AppConfig.showPresences =
store.getBool(SettingKeys.showPresences) ?? AppConfig.showPresences;
AppConfig.displayNavigationRail =
store.getBool(SettingKeys.displayNavigationRail) ??
AppConfig.displayNavigationRail;
}
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);

View file

@ -1,18 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'matrix.dart'; import 'package:fluffychat/config/setting_keys.dart';
class SettingsSwitchListTile extends StatefulWidget { class SettingsSwitchListTile extends StatefulWidget {
final bool defaultValue; final AppSettings<bool> setting;
final String storeKey;
final String title; final String title;
final String? subtitle; final String? subtitle;
final Function(bool)? onChanged; final Function(bool)? onChanged;
const SettingsSwitchListTile.adaptive({ const SettingsSwitchListTile.adaptive({
super.key, super.key,
this.defaultValue = false, required this.setting,
required this.storeKey,
required this.title, required this.title,
this.subtitle, this.subtitle,
this.onChanged, this.onChanged,
@ -27,13 +25,12 @@ class SettingsSwitchListTileState extends State<SettingsSwitchListTile> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final subtitle = widget.subtitle; final subtitle = widget.subtitle;
return SwitchListTile.adaptive( return SwitchListTile.adaptive(
value: Matrix.of(context).store.getBool(widget.storeKey) ?? value: widget.setting.value,
widget.defaultValue,
title: Text(widget.title), title: Text(widget.title),
subtitle: subtitle == null ? null : Text(subtitle), subtitle: subtitle == null ? null : Text(subtitle),
onChanged: (bool newValue) async { onChanged: (bool newValue) async {
widget.onChanged?.call(newValue); widget.onChanged?.call(newValue);
await Matrix.of(context).store.setBool(widget.storeKey, newValue); await widget.setting.setItem(newValue);
setState(() {}); setState(() {});
}, },
); );