diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f4bfeaf5c..f7db1049a 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -17,7 +17,6 @@ - @@ -161,16 +160,6 @@ android:foregroundServiceType="camera|microphone|mediaProjection"> - - - - - - diff --git a/config.sample.json b/config.sample.json index 03ac42a6a..ad34192a1 100644 --- a/config.sample.json +++ b/config.sample.json @@ -1,10 +1,29 @@ { - "application_name": "PangeaChat", - "application_welcome_message": null, - "default_homeserver": "matrix.pangea.chat", - "web_base_url": "https://web.pangea.chat", - "privacy_url": "https://pangea.chat/privacy", - "render_html": false, - "hide_redacted_events": false, - "hide_unknown_events": false + "applicationName": "PangeaChat", + "defaultHomeserver": "matrix.pangea.chat", + "privacyUrl": "https://pangea.chat/privacy", + "audioRecordingNumChannels": 1, + "audioRecordingAutoGain": true, + "audioRecordingEchoCancel": false, + "audioRecordingNoiseSuppress": true, + "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 } \ No newline at end of file diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart index 4b49aac6b..3a0139365 100644 --- a/integration_test/app_test.dart +++ b/integration_test/app_test.dart @@ -1,4 +1,3 @@ -import 'package:fluffychat/config/setting_keys.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/search_title.dart'; @@ -25,7 +24,7 @@ void main() { () async { // this random dialog popping up is super hard to cover in tests SharedPreferences.setMockInitialValues({ - SettingKeys.showNoGoogle: false, + 'chat.fluffy.show_no_google': false, }); }, ); diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index f566ed862..099555dfb 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -2,97 +2,41 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; abstract class AppConfig { // #Pangea - // static String _applicationName = 'FluffyChat'; - static String _applicationName = 'Pangea Chat'; + static String get defaultHomeserver => Environment.synapseURL; + // Pangea# + // Const and final configuration values (immutable) // #Pangea - - static String get applicationName => _applicationName; - static String? _applicationWelcomeMessage; - - static String? get applicationWelcomeMessage => _applicationWelcomeMessage; - // #Pangea - // static String _defaultHomeserver = 'matrix.org'; - static String get _defaultHomeserver => Environment.synapseURL; + // static const Color primaryColor = Color(0xFF5625BA); + // static const Color primaryColorLight = Color(0xFFCCBDEA); + // static const Color secondaryColor = Color(0xFF41a2bc); + static const Color primaryColor = Color(0xFF8560E0); + static const Color primaryColorLight = Color(0xFFDBC9FF); + static const Color secondaryColor = Color.fromARGB(255, 253, 191, 1); // Pangea# - 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 bool hideTypingUsernames = false; + + static const String inviteLinkPrefix = 'https://matrix.to/#/'; + static const String deepLinkPrefix = 'im.fluffychat://chat/'; + static const String schemePrefix = 'matrix:'; // #Pangea - static const double toolbarMaxHeight = 250.0; - static const double toolbarMinHeight = 150.0; - static const double toolbarMinWidth = 350.0; - static const double toolbarMenuHeight = 50.0; - static const double defaultHeaderHeight = 56.0; - static const double toolbarButtonsHeight = 50.0; - static const double toolbarSpacing = 8.0; - static const double toolbarIconSize = 24.0; - static const double readingAssistanceInputBarHeight = 175.0; - static const double reactionsPickerHeight = 48.0; - static const double chatInputRowOverlayPadding = 8.0; - static const double selectModeInputBarHeight = 0; - // reactionsPickerHeight + (chatInputRowOverlayPadding * 2) + toolbarSpacing; - static const double practiceModeInputBarHeight = - readingAssistanceInputBarHeight + - toolbarButtonsHeight + - (chatInputRowOverlayPadding * 2) + - toolbarSpacing; + // static const String pushNotificationsChannelId = 'fluffychat_push'; + // static const String pushNotificationsAppId = 'chat.fluffy.fluffychat'; + static const String pushNotificationsChannelId = 'pangeachat_push'; + static const String pushNotificationsAppId = 'com.talktolearn.chat'; + // Pangea# + static const double borderRadius = 18.0; + static const double columnWidth = 360.0; - static TextStyle messageTextStyle( - Event? event, - Color textColor, - ) { - final fontSize = messageFontSize * fontSizeFactor; - final bigEmotes = event != null && - event.onlyEmotes && - event.numberEmotes > 0 && - event.numberEmotes <= 3; - - return TextStyle( - color: textColor, - fontSize: bigEmotes ? fontSize * 5 : fontSize, - decoration: - (event?.redacted ?? false) ? TextDecoration.lineThrough : null, - height: 1.3, - ); - } - - // static const Color primaryColor = Color(0xFF5625BA); - // static const Color primaryColorLight = Color(0xFFCCBDEA); - static const Color primaryColor = Color(0xFF8560E0); - static const Color primaryColorLight = Color(0xFFDBC9FF); - // static const Color secondaryColor = Color(0xFF41a2bc); - static const Color secondaryColor = Color.fromARGB(255, 253, 191, 1); - static const Color activeToggleColor = Color(0xFF33D057); - static const Color success = Color(0xFF33D057); - static const Color warning = Color.fromARGB(255, 210, 124, 12); - static const Color gold = Color.fromARGB(255, 253, 191, 1); - static const Color silver = Color.fromARGB(255, 192, 192, 192); - static const Color bronze = Color.fromARGB(255, 205, 127, 50); - static const Color goldLight = Color.fromARGB(255, 254, 223, 73); - - static const Color yellowLight = Color.fromARGB(255, 247, 218, 120); - static const Color yellowDark = Color.fromARGB(255, 253, 191, 1); - - static const Color error = Colors.red; - static const int overlayAnimationDuration = 250; - static const int roomCreationTimeoutSeconds = 15; - // static String _privacyUrl = - // 'https://gitlab.com/famedly/fluffychat/-/blob/main/PRIVACY.md'; - static String _privacyUrl = "https://www.pangeachat.com/privacy"; - //Pangea# - - static const Set defaultReactions = {'👍', '❤️', '😂', '😮', '😢'}; - - static String get privacyUrl => _privacyUrl; // #Pangea // static const String website = 'https://fluffychat.im'; static const String website = "https://pangea.chat/"; @@ -108,9 +52,7 @@ abstract class AppConfig { // static const String appOpenUrlScheme = 'im.fluffychat'; static const String appOpenUrlScheme = 'matrix.pangea.chat'; // Pangea# - static String _webBaseUrl = 'https://fluffychat.im/web'; - static String get webBaseUrl => _webBaseUrl; static const String sourceCodeUrl = 'https://github.com/krille-chan/fluffychat'; // #Pangea @@ -118,126 +60,81 @@ abstract class AppConfig { // 'https://github.com/krille-chan/fluffychat/issues'; // static const String changelogUrl = // 'https://github.com/krille-chan/fluffychat/blob/main/CHANGELOG.md'; + // static const String donationUrl = 'https://ko-fi.com/krille'; static const String supportUrl = 'https://www.pangeachat.com/faqs'; static const String termsOfServiceUrl = 'https://www.pangeachat.com/terms-of-service'; // Pangea# + + static const Set defaultReactions = {'👍', '❤️', '😂', '😮', '😢'}; + static final Uri newIssueUrl = Uri( scheme: 'https', host: 'github.com', path: '/krille-chan/fluffychat/issues/new', ); - // #Pangea - static const bool enableSentry = true; - static const String sentryDns = - 'https://8591d0d863b646feb4f3dda7e5dcab38@o256755.ingest.sentry.io/5243143'; - // Pangea# - static bool renderHtml = true; - // #Pangea - // static bool hideRedactedEvents = false; - static bool hideRedactedEvents = true; - // Pangea# - 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 useActivityImageAsChatBackground = true; - static bool showPresences = true; - // #Pangea - // static bool displayNavigationRail = false; - static bool displayNavigationRail = true; - // Pangea# - 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:'; - // #Pangea - // static const String pushNotificationsChannelId = 'fluffychat_push'; - // static const String pushNotificationsAppId = 'chat.fluffy.fluffychat'; - static const String pushNotificationsChannelId = 'pangeachat_push'; - static const String pushNotificationsAppId = 'com.talktolearn.chat'; - // Pangea# - static const double borderRadius = 18.0; - static const double columnWidth = 360.0; + static final Uri homeserverList = Uri( scheme: 'https', host: 'servers.joinmatrix.org', path: 'servers.json', ); + + static final Uri privacyUrl = Uri( + scheme: 'https', + host: 'github.com', + path: '/krille-chan/fluffychat/blob/main/PRIVACY.md', + ); + // #Pangea - static String googlePlayMangementUrl = - "https://play.google.com/store/account/subscriptions"; - static String googlePlayHistoryUrl = - "https://play.google.com/store/account/orderhistory"; + static String assetsBaseURL = + "https://pangea-chat-client-assets.s3.us-east-1.amazonaws.com"; + static String androidUpdateURL = + "https://play.google.com/store/apps/details?id=com.talktolearn.chat"; + static String iosUpdateURL = "itms-apps://itunes.apple.com/app/id1445118630"; static String googlePlayPaymentMethodUrl = "https://play.google.com/store/paymentmethods"; static String appleMangementUrl = "https://apps.apple.com/account/subscriptions"; - static String stripePerMonth = - "https://buy.stripe.com/test_bIY6ssd8z5Uz8ec8ww"; - static String iosPromoCode = - "https://apps.apple.com/redeem?ctx=offercodes&id=1445118630&code="; - static String androidUpdateURL = - "https://play.google.com/store/apps/details?id=com.talktolearn.chat"; - static String iosUpdateURL = "itms-apps://itunes.apple.com/app/id1445118630"; - - static String windowsTTSDownloadInstructions = - "https://support.microsoft.com/en-us/topic/download-languages-and-voices-for-immersive-reader-read-mode-and-read-aloud-4c83a8d8-7486-42f7-8e46-2b0fdf753130"; - static String androidTTSDownloadInstructions = - "https://support.google.com/accessibility/android/answer/6006983?hl=en"; - static String assetsBaseURL = - "https://pangea-chat-client-assets.s3.us-east-1.amazonaws.com"; - + static String googlePlayMangementUrl = + "https://play.google.com/store/account/subscriptions"; + static String googlePlayHistoryUrl = + "https://play.google.com/store/account/orderhistory"; + static bool useActivityImageAsChatBackground = true; + static const int overlayAnimationDuration = 250; + static double volume = 1.0; + static const Color gold = Color.fromARGB(255, 253, 191, 1); + static const Color goldLight = Color.fromARGB(255, 254, 223, 73); + static const Color success = Color(0xFF33D057); + static const Color error = Colors.red; + static const Color warning = Color.fromARGB(255, 210, 124, 12); + static const Color activeToggleColor = Color(0xFF33D057); + static const Color yellowLight = Color.fromARGB(255, 247, 218, 120); + static const Color yellowDark = Color.fromARGB(255, 253, 191, 1); + static const double toolbarMaxHeight = 250.0; + static const double toolbarMinWidth = 350.0; + static const double toolbarMinHeight = 150.0; + static const double toolbarMenuHeight = 50.0; + static const double readingAssistanceInputBarHeight = 175.0; static String errorSubscriptionId = "pangea_subscription_error"; - static double volume = 1.0; - static bool showedActivityMenu = false; - // Pangea# + static TextStyle messageTextStyle( + Event? event, + Color textColor, + ) { + final fontSize = messageFontSize * AppSettings.fontSizeFactor.value; + final bigEmotes = event != null && + event.onlyEmotes && + event.numberEmotes > 0 && + event.numberEmotes <= 3; - static void loadFromJson(Map 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']; - } - // #Pangea - // if (json['default_homeserver'] is String) { - // _defaultHomeserver = json['default_homeserver']; - // } - // Pangea# - 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) { - // #Pangea - // this is interfering with our PangeaRichText functionality, removing it for now - renderHtml = false; - // renderHtml = json['render_html']; - // Pangea# - } - if (json['hide_redacted_events'] is bool) { - hideRedactedEvents = json['hide_redacted_events']; - } - if (json['hide_unknown_events'] is bool) { - hideUnknownEvents = json['hide_unknown_events']; - } + return TextStyle( + color: textColor, + fontSize: bigEmotes ? fontSize * 5 : fontSize, + decoration: + (event?.redacted ?? false) ? TextDecoration.lineThrough : null, + height: 1.3, + ); } + // Pangea# } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 032bf08e5..ed8c53afa 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -260,6 +260,7 @@ abstract class AppRoutes { // ? TwoColumnLayout( // mainView: ChatList( // activeChat: state.pathParameters['roomid'], + // activeSpace: state.uri.queryParameters['spaceId'], // displayNavigationRail: // state.path?.startsWith('/rooms/settings') != true, // ), @@ -292,7 +293,10 @@ abstract class AppRoutes { // Pangea# : ChatList( activeChat: state.pathParameters['roomid'], - activeSpaceId: state.pathParameters['spaceid'], + // #Pangea + // activeSpace: state.uri.queryParameters['spaceId'], + activeSpace: state.pathParameters['spaceid'], + // Pangea# ), ), routes: [ diff --git a/lib/config/setting_keys.dart b/lib/config/setting_keys.dart index 9d7f5e756..08f4fefc9 100644 --- a/lib/config/setting_keys.dart +++ b/lib/config/setting_keys.dart @@ -1,45 +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'; -abstract class SettingKeys { - 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'; - // #Pangea - static const String volume = 'pangea.volume'; - static const String showedActivityMenu = - 'pangea.showed_activity_menu_tutorial'; - // Pangea# -} +import 'package:fluffychat/utils/platform_infos.dart'; enum AppSettings { textMessageMaxLength('textMessageMaxLength', 16384), @@ -52,6 +19,9 @@ enum AppSettings { // audioRecordingSamplingRate('audioRecordingSamplingRate', 44100), audioRecordingSamplingRate('audioRecordingSamplingRate', 22050), // Pangea# + showNoGoogle('chat.fluffy.show_no_google', false), + unifiedPushRegistered('chat.fluffy.unifiedpush.registered', false), + unifiedPushEndpoint('chat.fluffy.unifiedpush.endpoint', ''), pushNotificationsGatewayUrl( 'pushNotificationsGatewayUrl', // #Pangea @@ -65,6 +35,19 @@ enum AppSettings { // 'event_id_only', // ), // Pangea# + renderHtml('chat.fluffy.renderHtml', true), + fontSizeFactor('chat.fluffy.font_size_factor', 1.0), + hideRedactedEvents('chat.fluffy.hideRedactedEvents', false), + hideUnknownEvents('chat.fluffy.hideUnknownEvents', true), + separateChatTypes('chat.fluffy.separateChatTypes', false), + autoplayImages('chat.fluffy.autoplay_images', true), + sendTypingNotifications('chat.fluffy.send_typing_notifications', true), + sendPublicReadReceipts('chat.fluffy.send_public_read_receipts', true), + swipeRightToLeftToReply('chat.fluffy.swipeRightToLeftToReply', true), + sendOnEnter('chat.fluffy.send_on_enter', false), + showPresences('chat.fluffy.show_presences', true), + displayNavigationRail('chat.fluffy.display_navigation_rail', false), + experimentalVoip('chat.fluffy.experimental_voip', false), shareKeysWith('chat.fluffy.share_keys_with_2', 'all'), noEncryptionWarningShown( 'chat.fluffy.no_encryption_warning_shown', @@ -74,40 +57,95 @@ enum AppSettings { 'chat.fluffy.display_chat_details_column', false, ), + // AppConfig-mirrored settings + // #Pangea + // applicationName('chat.fluffy.application_name', 'FluffyChat'), + // defaultHomeserver('chat.fluffy.default_homeserver', 'matrix.org'), + applicationName('chat.fluffy.application_name', 'Pangea Chat'), + // Pangea# + // colorSchemeSeed stored as ARGB int + colorSchemeSeedInt( + 'chat.fluffy.color_scheme_seed', + // #Pangea + // 0xFF5625BA, + 0xFF8560E0, + // Pangea# + ), + // #Pangea + volume('pangea.value', 1.0), + // Pangea# enableSoftLogout('chat.fluffy.enable_soft_logout', false); final String key; final T defaultValue; const AppSettings(this.key, this.defaultValue); + + static SharedPreferences get store => _store!; + static SharedPreferences? _store; + + static Future init({loadWebConfigFile = true}) async { + if (AppSettings._store != null) return AppSettings.store; + + 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; + for (final setting in AppSettings.values) { + if (store.get(setting.key) != null) continue; + final configValue = configJson[setting.name]; + 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 getItem(SharedPreferences store) => store.getBool(key) ?? defaultValue; + bool get value => AppSettings.store.getBool(key) ?? defaultValue; - Future setItem(SharedPreferences store, bool value) => - store.setBool(key, value); + Future setItem(bool value) => AppSettings.store.setBool(key, value); } extension AppSettingsStringExtension on AppSettings { - String getItem(SharedPreferences store) => - store.getString(key) ?? defaultValue; + String get value => AppSettings.store.getString(key) ?? defaultValue; - Future setItem(SharedPreferences store, String value) => - store.setString(key, value); + Future setItem(String value) => AppSettings.store.setString(key, value); } extension AppSettingsIntExtension on AppSettings { - int getItem(SharedPreferences store) => store.getInt(key) ?? defaultValue; + int get value => AppSettings.store.getInt(key) ?? defaultValue; - Future setItem(SharedPreferences store, int value) => - store.setInt(key, value); + Future setItem(int value) => AppSettings.store.setInt(key, value); } extension AppSettingsDoubleExtension on AppSettings { - double getItem(SharedPreferences store) => - store.getDouble(key) ?? defaultValue; + double get value => AppSettings.store.getDouble(key) ?? defaultValue; - Future setItem(SharedPreferences store, double value) => - store.setDouble(key, value); + Future setItem(double value) => AppSettings.store.setDouble(key, value); } diff --git a/lib/config/themes.dart b/lib/config/themes.dart index 762f1f073..71247db2b 100644 --- a/lib/config/themes.dart +++ b/lib/config/themes.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'app_config.dart'; abstract class FluffyThemes { @@ -49,11 +50,7 @@ abstract class FluffyThemes { ]) { final colorScheme = ColorScheme.fromSeed( brightness: brightness, - seedColor: seed ?? - AppConfig.colorSchemeSeed ?? - Theme.of(context).colorScheme.primary, - // primary: AppConfig.primaryColor, - // secondary: AppConfig.gold, + seedColor: seed ?? Color(AppSettings.colorSchemeSeedInt.value), ); final isColumnMode = FluffyThemes.isColumnMode(context); return ThemeData( diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 2cc4e5ba1..d9340409b 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -3379,6 +3379,39 @@ "longPressToRecordVoiceMessage": "Long press to record voice message.", "pause": "Pause", "resume": "Resume", + "newSubSpace": "New sub space", + "moveToDifferentSpace": "Move to different space", + "moveUp": "Move up", + "moveDown": "Move down", + "removeFromSpaceDescription": "The chat will be removed from the space but still appear in your chat list.", + "countChats": "{chats} chats", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "spaceMemberOf": "Space member of {spaces}", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "Space member of {spaces} can knock", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "donate": "Donate", "ignore": "Block", "ignoredUsers": "Blocked users", "writeAMessageLangCodes": "Type in {l1} or {l2}...", @@ -3947,6 +3980,9 @@ "grammarCopyPOSpropn": "Proper Noun", "grammarCopyPOSnoun": "Noun", "grammarCopyPOSintj": "Interjection", + "grammarCopyPOSidiom": "Idiom", + "grammarCopyPOSphrasalv": "Phrasal Verb", + "grammarCopyPOScompn": "Compound", "grammarCopyPOSx": "Other", "grammarCopyGENDERfem": "Feminine", "grammarCopyPERSON2": "Second Person", diff --git a/lib/main.dart b/lib/main.dart index ac55d22a4..58b72f4d3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,7 +9,6 @@ import 'package:matrix/matrix.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart'; @@ -22,8 +21,6 @@ import 'utils/background_push.dart'; import 'widgets/fluffy_chat_app.dart'; void main() async { - Logs().i('Welcome to ${AppConfig.applicationName} <3'); - // #Pangea try { await dotenv.load(fileName: ".env"); @@ -55,12 +52,14 @@ void main() async { // widget bindings are initialized already. WidgetsFlutterBinding.ensureInitialized(); + final store = await AppSettings.init(); + Logs().i('Welcome to ${AppSettings.applicationName.value} <3'); + // #Pangea // await vod.init(wasmPath: './assets/assets/vodozemac/'); // Pangea# Logs().nativeColors = !PlatformInfos.isIOS; - final store = await SharedPreferences.getInstance(); final clients = await ClientManager.getClients(store: store); // If the app starts in detached mode, we assume that it is in @@ -80,14 +79,14 @@ void main() async { // To start the flutter engine afterwards we add an custom observer. WidgetsBinding.instance.addObserver(AppStarter(clients, store)); 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; } // Started in foreground mode. Logs().i( - '${AppConfig.applicationName} started in foreground mode. Rendering GUI...', + '${AppSettings.applicationName.value} started in foreground mode. Rendering GUI...', ); await startGui(clients, store); } @@ -99,7 +98,7 @@ Future startGui(List clients, SharedPreferences store) async { if (PlatformInfos.isMobile) { try { pin = - await const FlutterSecureStorage().read(key: SettingKeys.appLockKey); + await const FlutterSecureStorage().read(key: 'chat.fluffy.app_lock'); } catch (e, s) { Logs().d('Unable to read PIN from Secure storage', e, s); } @@ -152,7 +151,7 @@ class AppStarter with WidgetsBindingObserver { if (state == AppLifecycleState.detached) return; 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. for (final client in clients) { diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 298df8b0d..69e97a59a 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -15,10 +15,8 @@ import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:shared_preferences/shared_preferences.dart'; 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/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; @@ -341,7 +339,7 @@ class ChatController extends State } void _loadDraft() async { - final prefs = await SharedPreferences.getInstance(); + final prefs = Matrix.of(context).store; final draft = prefs.getString('draft_$roomId'); if (draft != null && draft.isNotEmpty) { // #Pangea @@ -394,7 +392,7 @@ class ChatController extends State KeyEventResult _customEnterKeyHandling(FocusNode node, KeyEvent evt) { if (!HardwareKeyboard.instance.isShiftPressed && evt.logicalKey.keyLabel == 'Enter' && - (AppConfig.sendOnEnter ?? !PlatformInfos.isMobile)) { + AppSettings.sendOnEnter.value) { if (evt is KeyDownEvent) { // #Pangea // send(); @@ -450,7 +448,7 @@ class ChatController extends State WidgetsBinding.instance.addPostFrameCallback(_shareItems); super.initState(); _displayChatDetailsColumn = ValueNotifier( - AppSettings.displayChatDetailsColumn.getItem(Matrix.of(context).store), + AppSettings.displayChatDetailsColumn.value, ); sendingClient = Matrix.of(context).client; @@ -588,7 +586,9 @@ class ChatController extends State var readMarkerEventIndex = readMarkerEventId.isEmpty ? -1 : timeline!.events - .filterByVisibleInGui(exceptionEventId: readMarkerEventId) + .filterByVisibleInGui( + exceptionEventId: readMarkerEventId, + ) .indexWhere((e) => e.eventId == readMarkerEventId); // Read marker is existing but not found in first events. Try a single @@ -596,7 +596,9 @@ class ChatController extends State if (readMarkerEventId.isNotEmpty && readMarkerEventIndex == -1) { await timeline?.requestHistory(historyCount: _loadHistoryCount); readMarkerEventIndex = timeline!.events - .filterByVisibleInGui(exceptionEventId: readMarkerEventId) + .filterByVisibleInGui( + exceptionEventId: readMarkerEventId, + ) .indexWhere((e) => e.eventId == readMarkerEventId); } @@ -772,7 +774,7 @@ class ChatController extends State _setReadMarkerFuture = timeline .setReadMarker( eventId: eventId, - public: AppConfig.sendPublicReadReceipts, + public: AppSettings.sendPublicReadReceipts.value, ) .then((_) { _setReadMarkerFuture = null; @@ -938,7 +940,7 @@ class ChatController extends State // if (sendController.text.trim().isEmpty) return; // Pangea# _storeInputTimeoutTimer?.cancel(); - final prefs = await SharedPreferences.getInstance(); + final prefs = Matrix.of(context).store; prefs.remove('draft_$roomId'); var parseCommands = true; @@ -1564,7 +1566,9 @@ class ChatController extends State final eventIndex = foundEvent == null ? -1 : timeline!.events - .filterByVisibleInGui(exceptionEventId: eventId) + .filterByVisibleInGui( + exceptionEventId: eventId, + ) .indexOf(foundEvent); if (eventIndex == -1) { @@ -1884,7 +1888,7 @@ class ChatController extends State _storeInputTimeoutTimer?.cancel(); _storeInputTimeoutTimer = Timer(_storeInputTimeout, () async { - final prefs = await SharedPreferences.getInstance(); + final prefs = Matrix.of(context).store; await prefs.setString('draft_$roomId', text); }); // #Pangea @@ -1903,7 +1907,7 @@ class ChatController extends State // } // } // Pangea# - if (AppConfig.sendTypingNotifications) { + if (AppSettings.sendTypingNotifications.value) { typingCoolDown?.cancel(); typingCoolDown = Timer(const Duration(seconds: 2), () { typingCoolDown = null; @@ -2392,12 +2396,8 @@ class ChatController extends State inputFocus.unfocus(); activityController.toggleShowDropdown(); - if (!AppConfig.showedActivityMenu) { - AppConfig.showedActivityMenu = true; - Matrix.of(context).store.setBool( - SettingKeys.showedActivityMenu, - AppConfig.showedActivityMenu, - ); + if (!InstructionsEnum.showedActivityMenu.isToggledOff) { + InstructionsEnum.showedActivityMenu.setToggledOff(true); } } @@ -2462,7 +2462,6 @@ class ChatController extends State void toggleDisplayChatDetailsColumn() async { await AppSettings.displayChatDetailsColumn.setItem( - Matrix.of(context).store, !_displayChatDetailsColumn.value, ); _displayChatDetailsColumn.value = !_displayChatDetailsColumn.value; diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index 8bdef7d09..dc375f0cb 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -1,5 +1,9 @@ +// import 'package:flutter/material.dart'; + // import 'package:animations/animations.dart'; -// import 'package:fluffychat/config/app_config.dart'; +// import 'package:matrix/matrix.dart'; + +// import 'package:fluffychat/config/setting_keys.dart'; // import 'package:fluffychat/l10n/l10n.dart'; // import 'package:fluffychat/pages/chat/recording_input_row.dart'; // import 'package:fluffychat/pages/chat/recording_view_model.dart'; @@ -7,9 +11,6 @@ // import 'package:fluffychat/utils/platform_infos.dart'; // import 'package:fluffychat/widgets/avatar.dart'; // import 'package:fluffychat/widgets/matrix.dart'; -// import 'package:flutter/material.dart'; -// import 'package:matrix/matrix.dart'; - // import '../../config/themes.dart'; // import 'chat.dart'; // import 'input_bar.dart'; @@ -296,10 +297,11 @@ // maxLines: 8, // autofocus: !PlatformInfos.isMobile, // keyboardType: TextInputType.multiline, -// textInputAction: AppConfig.sendOnEnter == true && -// PlatformInfos.isMobile -// ? TextInputAction.send -// : null, +// textInputAction: +// AppSettings.sendOnEnter.value == true && +// PlatformInfos.isMobile +// ? TextInputAction.send +// : null, // onSubmitted: controller.onInputBarSubmitted, // onSubmitImage: controller.sendImageFromClipBoard, // focusNode: controller.inputFocus, diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 7d4311624..5ea673739 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -130,7 +130,7 @@ class ChatView extends StatelessWidget { // ]; // } else if (!controller.room.isArchived) { // return [ - // if (AppConfig.experimentalVoip && + // if (AppSettings.experimentalVoip.value && // Matrix.of(context).voipPlugin != null && // controller.room.isDirectChat) // IconButton( diff --git a/lib/pages/chat/encryption_button.dart b/lib/pages/chat/encryption_button.dart index 3bc556b4a..a18e10609 100644 --- a/lib/pages/chat/encryption_button.dart +++ b/lib/pages/chat/encryption_button.dart @@ -21,6 +21,7 @@ class EncryptionButton extends StatelessWidget { .stream .where((s) => s.deviceLists != null), builder: (context, snapshot) { + final shouldBeEncrypted = room.joinRules != JoinRules.public; return FutureBuilder( future: room.encrypted ? room.calcEncryptionHealthState() @@ -46,9 +47,13 @@ class EncryptionButton extends StatelessWidget { ), ), child: Icon( - room.encrypted ? Icons.lock_outlined : Icons.lock_open_outlined, + room.encrypted + ? Icons.lock_outlined + : Icons.no_encryption_outlined, size: 20, - color: theme.colorScheme.onSurface, + color: (shouldBeEncrypted && !room.encrypted) + ? theme.colorScheme.error + : theme.colorScheme.onSurface, ), ), onPressed: () => context.go('/rooms/${room.id}/encryption'), diff --git a/lib/pages/chat/events/cute_events.dart b/lib/pages/chat/events/cute_events.dart index bb675e641..9d43021dc 100644 --- a/lib/pages/chat/events/cute_events.dart +++ b/lib/pages/chat/events/cute_events.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.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'; class CuteContent extends StatefulWidget { @@ -21,7 +21,7 @@ class _CuteContentState extends State { @override void initState() { - if (AppConfig.autoplayImages && !_isOverlayShown) { + if (AppSettings.autoplayImages.value && !_isOverlayShown) { addOverlay(); } super.initState(); diff --git a/lib/pages/chat/events/image_bubble.dart b/lib/pages/chat/events/image_bubble.dart index f056366b1..e93cb9f54 100644 --- a/lib/pages/chat/events/image_bubble.dart +++ b/lib/pages/chat/events/image_bubble.dart @@ -5,6 +5,7 @@ import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:matrix/matrix.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/utils/file_description.dart'; import 'package:fluffychat/utils/url_launcher.dart'; @@ -162,14 +163,14 @@ class ImageBubble extends StatelessWidget { textScaleFactor: MediaQuery.textScalerOf(context).scale(1), style: TextStyle( color: textColor, - fontSize: - AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), options: const LinkifyOptions(humanize: false), linkStyle: TextStyle( color: linkColor, - fontSize: - AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, decoration: TextDecoration.underline, decorationColor: linkColor, ), diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index ff7a0f942..0f240f108 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.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/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/chat.dart'; @@ -308,7 +309,7 @@ class Message extends StatelessWidget { child: Icon(Icons.check_outlined), ), ), - direction: AppConfig.swipeRightToLeftToReply + direction: AppSettings.swipeRightToLeftToReply.value ? SwipeDirection.endToStart : SwipeDirection.startToEnd, onSwipe: (_) => onSwipe(), @@ -350,7 +351,7 @@ class Message extends StatelessWidget { child: Text( event.originServerTs.localizedTime(context), style: TextStyle( - fontSize: 12 * AppConfig.fontSizeFactor, + fontSize: 12 * AppSettings.fontSizeFactor.value, fontWeight: FontWeight.bold, color: theme.colorScheme.secondary, ), @@ -1137,7 +1138,7 @@ class Message extends StatelessWidget { child: Text( L10n.of(context).readUpToHere, style: TextStyle( - fontSize: 12 * AppConfig.fontSizeFactor, + fontSize: 12 * AppSettings.fontSizeFactor.value, ), ), ), diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index bcd3debff..d8d0eb6df 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/events/video_player.dart'; @@ -146,7 +147,8 @@ class MessageContent extends StatelessWidget { @override Widget build(BuildContext context) { - final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor; + final fontSize = + AppConfig.messageFontSize * AppSettings.fontSizeFactor.value; final buttonTextColor = textColor; switch (event.type) { case EventTypes.Message: @@ -306,7 +308,7 @@ class MessageContent extends StatelessWidget { }, ); } - var html = AppConfig.renderHtml && event.isRichMessage + var html = AppSettings.renderHtml.value && event.isRichMessage ? event.formattedText : event.body; if (event.messageType == MessageTypes.Emote) { @@ -325,14 +327,14 @@ class MessageContent extends StatelessWidget { html: html, textColor: textColor, room: event.room, - fontSize: AppConfig.fontSizeFactor * + fontSize: AppSettings.fontSizeFactor.value * AppConfig.messageFontSize * (bigEmotes ? 5 : 1), limitHeight: !selected, linkStyle: TextStyle( color: linkColor, - fontSize: - AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, decoration: TextDecoration.underline, decorationColor: linkColor, ), diff --git a/lib/pages/chat/events/message_download_content.dart b/lib/pages/chat/events/message_download_content.dart index f1b517d14..18b5c0b07 100644 --- a/lib/pages/chat/events/message_download_content.dart +++ b/lib/pages/chat/events/message_download_content.dart @@ -4,6 +4,7 @@ import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:matrix/matrix.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/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/url_launcher.dart'; @@ -92,12 +93,14 @@ class MessageDownloadContent extends StatelessWidget { textScaleFactor: MediaQuery.textScalerOf(context).scale(1), style: TextStyle( color: textColor, - fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), options: const LinkifyOptions(humanize: false), linkStyle: TextStyle( color: linkColor, - fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, decoration: TextDecoration.underline, decorationColor: linkColor, ), diff --git a/lib/pages/chat/events/reply_content.dart b/lib/pages/chat/events/reply_content.dart index 859d59243..e3c7f9484 100644 --- a/lib/pages/chat/events/reply_content.dart +++ b/lib/pages/chat/events/reply_content.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import '../../../config/app_config.dart'; @@ -30,7 +31,8 @@ class ReplyContent extends StatelessWidget { final timeline = this.timeline; final displayEvent = 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 // Pangea# ? ownMessage diff --git a/lib/pages/chat/events/state_message.dart b/lib/pages/chat/events/state_message.dart index b1d0dec8a..eaebc828e 100644 --- a/lib/pages/chat/events/state_message.dart +++ b/lib/pages/chat/events/state_message.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; @@ -79,7 +80,7 @@ class StateMessage extends StatelessWidget { ), textAlign: TextAlign.center, style: TextStyle( - fontSize: 12 * AppConfig.fontSizeFactor, + fontSize: 12 * AppSettings.fontSizeFactor.value, decoration: event.redacted ? TextDecoration.lineThrough : null, diff --git a/lib/pages/chat/events/video_player.dart b/lib/pages/chat/events/video_player.dart index 1940abc33..d78230b1f 100644 --- a/lib/pages/chat/events/video_player.dart +++ b/lib/pages/chat/events/video_player.dart @@ -6,6 +6,7 @@ import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:matrix/matrix.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/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -148,14 +149,14 @@ class EventVideoPlayer extends StatelessWidget { textScaleFactor: MediaQuery.textScalerOf(context).scale(1), style: TextStyle( color: textColor, - fontSize: - AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), options: const LinkifyOptions(humanize: false), linkStyle: TextStyle( color: linkColor, - fontSize: - AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, decoration: TextDecoration.underline, decorationColor: linkColor, ), diff --git a/lib/pages/chat/recording_view_model.dart b/lib/pages/chat/recording_view_model.dart index c65e81246..53ce6b993 100644 --- a/lib/pages/chat/recording_view_model.dart +++ b/lib/pages/chat/recording_view_model.dart @@ -15,7 +15,6 @@ import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; -import 'package:fluffychat/widgets/matrix.dart'; import 'events/audio_player.dart'; // #Pangea @@ -72,8 +71,6 @@ class RecordingViewModelState extends State { } if (await AudioRecorder().hasPermission() == false) return; - final store = Matrix.of(context).store; - final audioRecorder = _audioRecorder ??= AudioRecorder(); setState(() {}); @@ -109,12 +106,12 @@ class RecordingViewModelState extends State { await audioRecorder.start( RecordConfig( - bitRate: AppSettings.audioRecordingBitRate.getItem(store), - sampleRate: AppSettings.audioRecordingSamplingRate.getItem(store), - numChannels: AppSettings.audioRecordingNumChannels.getItem(store), - autoGain: AppSettings.audioRecordingAutoGain.getItem(store), - echoCancel: AppSettings.audioRecordingEchoCancel.getItem(store), - noiseSuppress: AppSettings.audioRecordingNoiseSuppress.getItem(store), + bitRate: AppSettings.audioRecordingBitRate.value, + sampleRate: AppSettings.audioRecordingSamplingRate.value, + numChannels: AppSettings.audioRecordingNumChannels.value, + autoGain: AppSettings.audioRecordingAutoGain.value, + echoCancel: AppSettings.audioRecordingEchoCancel.value, + noiseSuppress: AppSettings.audioRecordingNoiseSuppress.value, encoder: codec, ), path: path ?? '', diff --git a/lib/pages/chat_access_settings/chat_access_settings_controller.dart b/lib/pages/chat_access_settings/chat_access_settings_controller.dart index 9c3a48b86..0c170a1bf 100644 --- a/lib/pages/chat_access_settings/chat_access_settings_controller.dart +++ b/lib/pages/chat_access_settings/chat_access_settings_controller.dart @@ -27,6 +27,16 @@ class ChatAccessSettingsController extends State { bool historyVisibilityLoading = false; bool guestAccessLoading = false; Room get room => Matrix.of(context).client.getRoomById(widget.roomId)!; + Set get knownSpaceParents => { + ...room.client.rooms.where( + (space) => + space.isSpace && + space.spaceChildren.any((child) => child.roomId == room.id), + ), + ...room.spaceParents + .map((parent) => room.client.getRoomById(parent.roomId ?? '')) + .whereType(), + }; String get roomVersion => room @@ -45,11 +55,13 @@ class ChatAccessSettingsController extends State { // Knock is only supported for rooms up from version 7: if (roomVersionInt != null && roomVersionInt <= 6) { joinRules.remove(JoinRules.knock); + joinRules.remove(JoinRules.knockRestricted); } - // Not yet supported in FluffyChat: - joinRules.remove(JoinRules.restricted); - joinRules.remove(JoinRules.knockRestricted); + if (knownSpaceParents.isEmpty) { + joinRules.remove(JoinRules.restricted); + joinRules.remove(JoinRules.knockRestricted); + } // If an unsupported join rule is the current join rule, display it: final currentJoinRule = room.joinRules; @@ -69,7 +81,13 @@ class ChatAccessSettingsController extends State { try { // #Pangea - // await room.setJoinRules(newJoinRules); + // await room.setJoinRules( + // newJoinRules, + // allowConditionRoomId: {JoinRules.restricted, JoinRules.knockRestricted} + // .contains(newJoinRules) + // ? knownSpaceParents.first.id + // : null, + // ); await room.pangeaSetJoinRules( newJoinRules.toString().replaceAll('JoinRules.', ''), ); diff --git a/lib/pages/chat_access_settings/chat_access_settings_page.dart b/lib/pages/chat_access_settings/chat_access_settings_page.dart index 5f679cffb..616d13934 100644 --- a/lib/pages/chat_access_settings/chat_access_settings_page.dart +++ b/lib/pages/chat_access_settings/chat_access_settings_page.dart @@ -88,7 +88,10 @@ class ChatAccessSettingsPageView extends StatelessWidget { enabled: !controller.joinRulesLoading && room.canChangeJoinRules, title: Text( - joinRule.localizedString(L10n.of(context)), + joinRule.localizedString( + L10n.of(context), + controller.knownSpaceParents, + ), ), value: joinRule, ), @@ -280,7 +283,7 @@ class _AliasListTile extends StatelessWidget { } extension JoinRulesDisplayString on JoinRules { - String localizedString(L10n l10n) { + String localizedString(L10n l10n, Set spaceParents) { switch (this) { case JoinRules.public: return l10n.anyoneCanJoin; @@ -291,9 +294,17 @@ extension JoinRulesDisplayString on JoinRules { case JoinRules.private: return l10n.noOneCanJoin; case JoinRules.restricted: - return l10n.restricted; + return l10n.spaceMemberOf( + spaceParents + .map((space) => space.getLocalizedDisplayname(MatrixLocals(l10n))) + .join(', '), + ); case JoinRules.knockRestricted: - return l10n.knockRestricted; + return l10n.spaceMemberOfCanKnock( + spaceParents + .map((space) => space.getLocalizedDisplayname(MatrixLocals(l10n))) + .join(', '), + ); } } } diff --git a/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart b/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart index f6adffd58..754178c25 100644 --- a/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart +++ b/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart @@ -9,6 +9,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat_encryption_settings/chat_encryption_settings.dart'; import 'package:fluffychat/utils/beautify_string_extension.dart'; +import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; class ChatEncryptionSettingsView extends StatelessWidget { @@ -58,7 +59,6 @@ class ChatEncryptionSettingsView extends StatelessWidget { size: 128, color: theme.colorScheme.onInverseSurface, ), - const Divider(), if (room.isDirectChat) Padding( padding: const EdgeInsets.all(16.0), @@ -107,72 +107,73 @@ class ChatEncryptionSettingsView extends StatelessWidget { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: deviceKeys.length, - itemBuilder: (BuildContext context, int i) => - SwitchListTile( - value: !deviceKeys[i].blocked, - activeThumbColor: deviceKeys[i].verified - ? Colors.green - : Colors.orange, - onChanged: (_) => - controller.toggleDeviceKey(deviceKeys[i]), - title: Row( - children: [ - Icon( - deviceKeys[i].verified - ? Icons.verified_outlined - : deviceKeys[i].blocked - ? Icons.block_outlined - : Icons.info_outlined, - color: deviceKeys[i].verified - ? Colors.green - : deviceKeys[i].blocked - ? Colors.red - : Colors.orange, - size: 20, - ), - const SizedBox(width: 4), - Text( - deviceKeys[i].deviceId ?? - L10n.of(context).unknownDevice, - ), - const SizedBox(width: 4), - Flexible( - fit: FlexFit.loose, - child: Material( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, + itemBuilder: (BuildContext context, int i) => Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (i == 0 || + deviceKeys[i].userId != + deviceKeys[i - 1].userId) ...[ + const Divider(), + FutureBuilder( + future: room.client + .getUserProfile(deviceKeys[i].userId), + builder: (context, snapshot) { + final displayname = + snapshot.data?.displayname ?? + deviceKeys[i].userId.localpart ?? + deviceKeys[i].userId; + return ListTile( + leading: Avatar( + name: displayname, + mxContent: snapshot.data?.avatarUrl, ), - side: BorderSide( - color: theme.colorScheme.primary, - ), - ), - color: theme.colorScheme.primaryContainer, - child: Padding( - padding: const EdgeInsets.all(4.0), - child: Text( - deviceKeys[i].userId, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: theme.colorScheme.primary, - fontSize: 12, - fontStyle: FontStyle.italic, - ), - ), - ), - ), + title: Text(displayname), + subtitle: Text(deviceKeys[i].userId), + ); + }, ), ], - ), - subtitle: Text( - deviceKeys[i].ed25519Key?.beautified ?? - L10n.of(context).unknownEncryptionAlgorithm, - style: TextStyle( - fontFamily: 'RobotoMono', - color: theme.colorScheme.secondary, + SwitchListTile( + value: !deviceKeys[i].blocked, + activeThumbColor: deviceKeys[i].verified + ? Colors.green + : Colors.orange, + onChanged: (_) => + controller.toggleDeviceKey(deviceKeys[i]), + title: Row( + children: [ + Text( + deviceKeys[i].verified + ? L10n.of(context).verified + : deviceKeys[i].blocked + ? L10n.of(context).blocked + : L10n.of(context).unverified, + style: TextStyle( + color: deviceKeys[i].verified + ? Colors.green + : deviceKeys[i].blocked + ? Colors.red + : Colors.orange, + ), + ), + const Text(' | ID: '), + Text( + deviceKeys[i].deviceId ?? + L10n.of(context).unknownDevice, + ), + ], + ), + subtitle: Text( + deviceKeys[i].ed25519Key?.beautified ?? + L10n.of(context).unknownEncryptionAlgorithm, + style: TextStyle( + fontFamily: 'RobotoMono', + color: theme.colorScheme.secondary, + fontSize: 11, + ), + ), ), - ), + ], ), ); }, diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index f7c9e16d4..1da716a2d 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -11,7 +11,6 @@ import 'package:matrix/matrix.dart' as sdk; import 'package:matrix/matrix.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/pages/chat_list/chat_list_view.dart'; import 'package:fluffychat/pangea/chat_list/utils/app_version_util.dart'; @@ -83,17 +82,13 @@ extension LocalizedActiveFilter on ActiveFilter { class ChatList extends StatefulWidget { static BuildContext? contextForVoip; final String? activeChat; - // #Pangea - final String? activeSpaceId; - // Pangea# + final String? activeSpace; final bool displayNavigationRail; const ChatList({ super.key, required this.activeChat, - // #Pangea - this.activeSpaceId, - // Pangea# + this.activeSpace, this.displayNavigationRail = false, }); @@ -111,15 +106,10 @@ class ChatListController extends State // StreamSubscription? _intentUriStreamSubscription; // Pangea# - // #Pangea - // ActiveFilter activeFilter = AppConfig.separateChatTypes - // ? ActiveFilter.messages - // : ActiveFilter.allChats; - ActiveFilter activeFilter = ActiveFilter.allChats; - // Pangea# + late ActiveFilter activeFilter; // #Pangea - String? get activeSpaceId => widget.activeSpaceId; + String? get activeSpaceId => widget.activeSpace; // String? _activeSpaceId; // String? get activeSpaceId => _activeSpaceId; @@ -535,7 +525,16 @@ class ChatListController extends State @override void initState() { + // #Pangea + // activeFilter = AppSettings.separateChatTypes.value + // ? ActiveFilter.messages + // : ActiveFilter.allChats; + activeFilter = ActiveFilter.allChats; + // Pangea# _initReceiveSharingIntent(); + // #Pangea + // _activeSpaceId = widget.activeSpace; + // Pangea# scrollController.addListener(_onScroll); _waitForFirstSync(); @@ -993,8 +992,7 @@ class ChatListController extends State context: context, ); if (result == OkCancelResult.ok) { - await Matrix.of(context).store.setBool(SettingKeys.showPresences, false); - AppConfig.showPresences = false; + AppSettings.showPresences.setItem(false); setState(() {}); } } diff --git a/lib/pages/chat_list/chat_list_body.dart b/lib/pages/chat_list/chat_list_body.dart index 705197545..cee769511 100644 --- a/lib/pages/chat_list/chat_list_body.dart +++ b/lib/pages/chat_list/chat_list_body.dart @@ -42,10 +42,7 @@ class ChatListViewBody extends StatelessWidget { // spaceId: activeSpace, // onBack: controller.clearActiveSpace, // onChatTab: (room) => controller.onChatTap(room), - // onChatContext: (room, context) => - // controller.chatContextAction(room, context), // activeChat: controller.activeChat, - // toParentSpace: controller.setActiveSpace, // ); return CourseChats( activeSpace, @@ -108,8 +105,8 @@ class ChatListViewBody extends StatelessWidget { // ), // PublicRoomsHorizontalList(publicRooms: publicRooms), // SearchTitle( - // title: L10n.of(context).publicSpaces, - // icon: const Icon(Icons.workspaces_outlined), + // title: L10n.of(context).publicSpaces, + // icon: const Icon(Icons.workspaces_outlined), // ), // PublicRoomsHorizontalList(publicRooms: publicSpaces), // SearchTitle( @@ -127,27 +124,28 @@ class ChatListViewBody extends StatelessWidget { // curve: FluffyThemes.animationCurve, // child: userSearchResult == null // ? null - // : ListView.builder( - // scrollDirection: Axis.horizontal, - // itemCount: userSearchResult.results.length, - // itemBuilder: (context, i) => _SearchItem( - // title: - // userSearchResult.results[i].displayName ?? - // userSearchResult - // .results[i].userId.localpart ?? - // L10n.of(context).unknownDevice, - // avatar: userSearchResult.results[i].avatarUrl, - // onPressed: () => UserDialog.show( - // context: context, - // profile: userSearchResult.results[i], - // ), - // ), - // ), - // ), - // Pangea# + // : ListView.builder( + // scrollDirection: Axis.horizontal, + // itemCount: userSearchResult.results.length, + // itemBuilder: (context, i) => _SearchItem( + // title: + // userSearchResult.results[i].displayName ?? + // userSearchResult + // .results[i].userId.localpart ?? + // L10n.of(context).unknownDevice, + // avatar: userSearchResult.results[i].avatarUrl, + // onPressed: () => UserDialog.show( + // context: context, + // profile: userSearchResult.results[i], + // ), + // ), + // ), + // ), + // Pangea# ], // #Pangea - // if (!controller.isSearchMode && AppConfig.showPresences) + // if (!controller.isSearchMode && + // AppSettings.showPresences.value) // GestureDetector( // onLongPress: () => controller.dismissStatusList(), // child: StatusMessageList( @@ -184,14 +182,14 @@ class ChatListViewBody extends StatelessWidget { // shrinkWrap: true, // scrollDirection: Axis.horizontal, // children: [ - // if (AppConfig.separateChatTypes) + // if (AppSettings.separateChatTypes.value) // ActiveFilter.messages // else // ActiveFilter.allChats, // ActiveFilter.groups, // ActiveFilter.unread, // if (spaceDelegateCandidates.isNotEmpty && - // !AppConfig.displayNavigationRail && + // !AppSettings.displayNavigationRail.value && // !FluffyThemes.isColumnMode(context)) // ActiveFilter.spaces, // ] @@ -217,24 +215,6 @@ class ChatListViewBody extends StatelessWidget { // title: L10n.of(context).chats, // icon: const Icon(Icons.forum_outlined), // ), - if (controller.isSearchMode && - rooms - .where( - (room) => room - .getLocalizedDisplayname( - MatrixLocals(L10n.of(context)), - ) - .toLowerCase() - .contains(filter), - ) - .isEmpty) - Padding( - padding: const EdgeInsetsGeometry.all(16.0), - child: Text( - L10n.of(context).emptyChatSearch, - textAlign: TextAlign.center, - ), - ), // if (client.prevBatch != null && // rooms.isEmpty && // !controller.isSearchMode) ...[ @@ -268,7 +248,7 @@ class ChatListViewBody extends StatelessWidget { // padding: const EdgeInsets.all(16.0), // child: Text( // client.rooms.isEmpty - // ? L10n.of(context).noChatsFoundHereYet + // ? L10n.of(context).noChatsFoundHere // : L10n.of(context).noMoreChatsFound, // textAlign: TextAlign.center, // style: TextStyle( @@ -280,6 +260,24 @@ class ChatListViewBody extends StatelessWidget { // ], // ), // ], + if (controller.isSearchMode && + rooms + .where( + (room) => room + .getLocalizedDisplayname( + MatrixLocals(L10n.of(context)), + ) + .toLowerCase() + .contains(filter), + ) + .isEmpty) + Padding( + padding: const EdgeInsetsGeometry.all(16.0), + child: Text( + L10n.of(context).emptyChatSearch, + textAlign: TextAlign.center, + ), + ), // Pangea# ], ), diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index 8853e9d7a..346870b00 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -4,6 +4,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pages/chat_list/unread_bubble.dart'; import 'package:fluffychat/pangea/chat_list/utils/get_chat_list_item_subtitle.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; @@ -54,11 +55,6 @@ class ChatListItem extends StatelessWidget { final unread = room.isUnread; final directChatMatrixId = room.directChatMatrixID; final isDirectChat = directChatMatrixId != null; - final unreadBubbleSize = unread || room.hasNewMessages - ? room.notificationCount > 0 - ? 20.0 - : 14.0 - : 0.0; final hasNotifications = room.notificationCount > 0; final backgroundColor = activeChat ? theme.colorScheme.secondaryContainer : null; @@ -248,7 +244,7 @@ class ChatListItem extends StatelessWidget { children: [ if (typingText.isEmpty && ownMessage && - room.lastEvent!.status.isSending) ...[ + room.lastEvent?.status.isSending == true) ...[ const SizedBox( width: 16, height: 16, @@ -272,13 +268,11 @@ class ChatListItem extends StatelessWidget { Expanded( child: room.isSpace && room.membership == Membership.join ? Text( - L10n.of(context).countChatsAndCountParticipants( - // #Pangea - // room.spaceChildren.length, - room.spaceChildCount, - // Pangea# - (room.summary.mJoinedMemberCount ?? 1), - ), + // #Pangea + // L10n.of(context) + // .countChats(room.spaceChildren.length), + L10n.of(context).countChats(room.spaceChildCount), + // Pangea# style: TextStyle(color: theme.colorScheme.outline), ) : typingText.isNotEmpty @@ -367,41 +361,7 @@ class ChatListItem extends StatelessWidget { ), ), const SizedBox(width: 8), - AnimatedContainer( - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - alignment: Alignment.center, - padding: const EdgeInsets.symmetric(horizontal: 7), - height: unreadBubbleSize, - width: !hasNotifications && !unread && !room.hasNewMessages - ? 0 - : (unreadBubbleSize - 9) * - room.notificationCount.toString().length + - 9, - decoration: BoxDecoration( - color: room.highlightCount > 0 - ? theme.colorScheme.error - : hasNotifications || room.markedUnread - ? theme.colorScheme.primary - : theme.colorScheme.primaryContainer, - borderRadius: BorderRadius.circular(7), - ), - child: hasNotifications - ? Text( - room.notificationCount.toString(), - style: TextStyle( - color: room.highlightCount > 0 - ? theme.colorScheme.onError - : hasNotifications - ? theme.colorScheme.onPrimary - : theme.colorScheme.onPrimaryContainer, - fontSize: 13, - fontWeight: FontWeight.w500, - ), - textAlign: TextAlign.center, - ) - : const SizedBox.shrink(), - ), + UnreadBubble(room: room), ], ), onTap: onTap, diff --git a/lib/pages/chat_list/chat_list_view.dart b/lib/pages/chat_list/chat_list_view.dart index 6bf986215..ef756dc45 100644 --- a/lib/pages/chat_list/chat_list_view.dart +++ b/lib/pages/chat_list/chat_list_view.dart @@ -30,7 +30,7 @@ class ChatListView extends StatelessWidget { children: [ // #Pangea // if (FluffyThemes.isColumnMode(context) || - // AppConfig.displayNavigationRail) ...[ + // AppSettings.displayNavigationRail.value) ...[ // SpacesNavigationRail( // activeSpaceId: controller.activeSpaceId, // onGoToChats: controller.clearActiveSpace, diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index 643a6055c..83276dfeb 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -1,9 +1,10 @@ -// #Pangea // import 'package:flutter/material.dart'; // import 'package:go_router/go_router.dart'; // import 'package:matrix/matrix.dart'; +// import 'package:url_launcher/url_launcher_string.dart'; +// import 'package:fluffychat/config/app_config.dart'; // import 'package:fluffychat/config/themes.dart'; // import 'package:fluffychat/l10n/l10n.dart'; // import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; @@ -68,6 +69,17 @@ // ], // ), // ), +// if (Matrix.of(context).backgroundPush?.firebaseEnabled != true) +// PopupMenuItem( +// value: SettingsAction.support, +// child: Row( +// children: [ +// const Icon(Icons.favorite, color: Colors.red), +// const SizedBox(width: 18), +// Text(L10n.of(context).donate), +// ], +// ), +// ), // PopupMenuItem( // value: SettingsAction.settings, // child: Row( @@ -208,6 +220,9 @@ // case SettingsAction.invite: // FluffyShare.shareInviteLink(context); // break; +// case SettingsAction.support: +// launchUrlString(AppConfig.donationUrl); +// break; // case SettingsAction.settings: // context.go('/rooms/settings'); // break; @@ -227,7 +242,7 @@ // newGroup, // setStatus, // invite, +// support, // settings, // archive, // } -// Pangea# diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index 33ea75344..a16220c4e 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; @@ -8,26 +10,35 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pages/chat_list/chat_list_item.dart'; -import 'package:fluffychat/pages/chat_list/search_title.dart'; +import 'package:fluffychat/pages/chat_list/unread_bubble.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/stream_extension.dart'; +import 'package:fluffychat/utils/string_color.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/public_room_dialog.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_modal_action_popup.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; +import 'package:fluffychat/widgets/hover_builder.dart'; import 'package:fluffychat/widgets/matrix.dart'; enum AddRoomType { chat, subspace } +enum SpaceChildAction { edit, moveToSpace, removeFromSpace } + +enum SpaceActions { + settings, + invite, + members, + leave, +} + class SpaceView extends StatefulWidget { final String spaceId; final void Function() onBack; - final void Function(String spaceId) toParentSpace; final void Function(Room room) onChatTab; - final void Function(Room room, BuildContext context) onChatContext; final String? activeChat; const SpaceView({ @@ -35,8 +46,6 @@ class SpaceView extends StatefulWidget { required this.onBack, required this.onChatTab, required this.activeChat, - required this.toParentSpace, - required this.onChatContext, super.key, }); @@ -58,9 +67,28 @@ class _SpaceViewState extends State { } void _loadHierarchy() async { - final room = Matrix.of(context).client.getRoomById(widget.spaceId); + final matrix = Matrix.of(context); + final room = matrix.client.getRoomById(widget.spaceId); if (room == null) return; + final cacheKey = 'spaces_history_cache${room.id}'; + if (_discoveredChildren.isEmpty) { + final cachedChildren = matrix.store.getStringList(cacheKey); + if (cachedChildren != null) { + try { + _discoveredChildren.addAll( + cachedChildren.map( + (jsonString) => + SpaceRoomsChunk$2.fromJson(jsonDecode(jsonString)), + ), + ); + } catch (e, s) { + Logs().e('Unable to json decode spaces hierarchy cache!', e, s); + matrix.store.remove(cacheKey); + } + } + } + setState(() { _isLoading = true; }); @@ -74,16 +102,25 @@ class _SpaceViewState extends State { ); if (!mounted) return; setState(() { + if (_nextBatch == null) _discoveredChildren.clear(); _nextBatch = hierarchy.nextBatch; if (hierarchy.nextBatch == null) { _noMoreRooms = true; } _discoveredChildren.addAll( - hierarchy.rooms - .where((c) => room.client.getRoomById(c.roomId) == null), + hierarchy.rooms.where((room) => room.roomId != widget.spaceId), ); _isLoading = false; }); + + if (_nextBatch == null) { + matrix.store.setStringList( + cacheKey, + _discoveredChildren + .map((child) => jsonEncode(child.toJson())) + .toList(), + ); + } } catch (e, s) { Logs().w('Unable to load hierarchy', e, s); if (!mounted) return; @@ -111,9 +148,7 @@ class _SpaceViewState extends State { ), ); if (mounted && joined == true) { - setState(() { - _discoveredChildren.remove(item); - }); + setState(() {}); } } @@ -129,6 +164,10 @@ class _SpaceViewState extends State { await space?.postLoad(); context.push('/rooms/${widget.spaceId}/invite'); break; + case SpaceActions.members: + await space?.postLoad(); + context.push('/rooms/${widget.spaceId}/details/members'); + break; case SpaceActions.leave: final confirmed = await showOkCancelAlertDialog( context: context, @@ -151,27 +190,11 @@ class _SpaceViewState extends State { } } - void _addChatOrSubspace() async { - final roomType = await showModalActionPopup( - context: context, - title: L10n.of(context).addChatOrSubSpace, - actions: [ - AdaptiveModalAction( - value: AddRoomType.subspace, - label: L10n.of(context).createNewSpace, - ), - AdaptiveModalAction( - value: AddRoomType.chat, - label: L10n.of(context).createGroup, - ), - ], - ); - if (roomType == null) return; - + void _addChatOrSubspace(AddRoomType roomType) async { final names = await showTextInputDialog( context: context, title: roomType == AddRoomType.subspace - ? L10n.of(context).createNewSpace + ? L10n.of(context).newSubSpace : L10n.of(context).createGroup, hintText: roomType == AddRoomType.subspace ? L10n.of(context).spaceName @@ -196,29 +219,169 @@ class _SpaceViewState extends State { late final String roomId; final activeSpace = client.getRoomById(widget.spaceId)!; await activeSpace.postLoad(); + final isPublicSpace = activeSpace.joinRules == JoinRules.public; if (roomType == AddRoomType.subspace) { roomId = await client.createSpace( name: names, - visibility: activeSpace.joinRules == JoinRules.public - ? sdk.Visibility.public - : sdk.Visibility.private, + visibility: + isPublicSpace ? sdk.Visibility.public : sdk.Visibility.private, ); } else { roomId = await client.createGroupChat( + enableEncryption: !isPublicSpace, groupName: names, - preset: activeSpace.joinRules == JoinRules.public + preset: isPublicSpace ? CreateRoomPreset.publicChat : CreateRoomPreset.privateChat, - visibility: activeSpace.joinRules == JoinRules.public - ? sdk.Visibility.public - : sdk.Visibility.private, + visibility: + isPublicSpace ? sdk.Visibility.public : sdk.Visibility.private, + initialState: isPublicSpace + ? null + : [ + StateEvent( + content: { + 'join_rule': 'restricted', + 'allow': [ + { + 'room_id': widget.spaceId, + 'type': 'm.room_membership', + }, + ], + }, + type: EventTypes.RoomJoinRules, + ), + ], ); } await activeSpace.setSpaceChild(roomId); }, ); if (result.error != null) return; + setState(() { + _nextBatch = null; + _discoveredChildren.clear(); + }); + _loadHierarchy(); + } + + void _showSpaceChildEditMenu(BuildContext posContext, String roomId) async { + final overlay = + Overlay.of(posContext).context.findRenderObject() as RenderBox; + + final button = posContext.findRenderObject() as RenderBox; + + final position = RelativeRect.fromRect( + Rect.fromPoints( + button.localToGlobal(const Offset(0, -65), ancestor: overlay), + button.localToGlobal( + button.size.bottomRight(Offset.zero) + const Offset(-50, 0), + ancestor: overlay, + ), + ), + Offset.zero & overlay.size, + ); + + final action = await showMenu( + context: posContext, + position: position, + items: [ + PopupMenuItem( + value: SpaceChildAction.moveToSpace, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.move_down_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).moveToDifferentSpace), + ], + ), + ), + PopupMenuItem( + value: SpaceChildAction.edit, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.edit_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).edit), + ], + ), + ), + PopupMenuItem( + value: SpaceChildAction.removeFromSpace, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.group_remove_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).removeFromSpace), + ], + ), + ), + ], + ); + if (action == null) return; + if (!mounted) return; + final space = Matrix.of(context).client.getRoomById(widget.spaceId); + if (space == null) return; + switch (action) { + case SpaceChildAction.edit: + context.push('/rooms/${widget.spaceId}/details'); + case SpaceChildAction.moveToSpace: + final spacesWithPowerLevels = space.client.rooms + .where( + (room) => + room.isSpace && + room.canChangeStateEvent(EventTypes.SpaceChild) && + room.id != widget.spaceId, + ) + .toList(); + final newSpace = await showModalActionPopup( + context: context, + title: L10n.of(context).space, + actions: spacesWithPowerLevels + .map( + (space) => AdaptiveModalAction( + value: space, + label: space + .getLocalizedDisplayname(MatrixLocals(L10n.of(context))), + ), + ) + .toList(), + ); + if (newSpace == null) return; + final result = await showFutureLoadingDialog( + context: context, + future: () async { + await newSpace.setSpaceChild(newSpace.id); + await space.removeSpaceChild(roomId); + }, + ); + if (result.isError) return; + if (!mounted) return; + _nextBatch = null; + _loadHierarchy(); + return; + + case SpaceChildAction.removeFromSpace: + final consent = await showOkCancelAlertDialog( + context: context, + title: L10n.of(context).removeFromSpace, + message: L10n.of(context).removeFromSpaceDescription, + ); + if (consent != OkCancelResult.ok) return; + if (!mounted) return; + final result = await showFutureLoadingDialog( + context: context, + future: () => space.removeSpaceChild(roomId), + ); + if (result.isError) return; + if (!mounted) return; + _nextBatch = null; + _loadHierarchy(); + return; + } } @override @@ -228,6 +391,11 @@ class _SpaceViewState extends State { final room = Matrix.of(context).client.getRoomById(widget.spaceId); final displayname = room?.getLocalizedDisplayname() ?? L10n.of(context).nothingFound; + const avatarSize = Avatar.defaultSize / 1.5; + final isAdmin = room?.canChangeStateEvent( + EventTypes.SpaceChild, + ) == + true; return Scaffold( appBar: AppBar( leading: FluffyThemes.isColumnMode(context) @@ -242,6 +410,7 @@ class _SpaceViewState extends State { title: ListTile( contentPadding: EdgeInsets.zero, leading: Avatar( + size: avatarSize, mxContent: room?.avatar, name: displayname, border: BorderSide(width: 1, color: theme.dividerColor), @@ -252,18 +421,38 @@ class _SpaceViewState extends State { maxLines: 1, overflow: TextOverflow.ellipsis, ), - subtitle: room == null - ? null - : Text( - L10n.of(context).countChatsAndCountParticipants( - room.spaceChildren.length, - room.summary.mJoinedMemberCount ?? 1, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), ), actions: [ + if (isAdmin) + PopupMenuButton( + icon: const Icon(Icons.add_outlined), + onSelected: _addChatOrSubspace, + tooltip: L10n.of(context).addChatOrSubSpace, + itemBuilder: (context) => [ + PopupMenuItem( + value: AddRoomType.chat, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.group_add_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).newGroup), + ], + ), + ), + PopupMenuItem( + value: AddRoomType.subspace, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.workspaces_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).newSubSpace), + ], + ), + ), + ], + ), PopupMenuButton( useRootNavigator: true, onSelected: _onSpaceAction, @@ -290,6 +479,21 @@ class _SpaceViewState extends State { ], ), ), + PopupMenuItem( + value: SpaceActions.members, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.group_outlined), + const SizedBox(width: 12), + Text( + L10n.of(context).countParticipants( + room?.summary.mJoinedMemberCount ?? 1, + ), + ), + ], + ), + ), PopupMenuItem( value: SpaceActions.leave, child: Row( @@ -305,16 +509,6 @@ class _SpaceViewState extends State { ), ], ), - floatingActionButton: room?.canChangeStateEvent( - EventTypes.SpaceChild, - ) == - true - ? FloatingActionButton.extended( - onPressed: _addChatOrSubspace, - label: Text(L10n.of(context).group), - icon: const Icon(Icons.group_add_outlined), - ) - : null, body: room == null ? const Center( child: Icon( @@ -327,29 +521,11 @@ class _SpaceViewState extends State { .where((s) => s.hasRoomUpdate) .rateLimit(const Duration(seconds: 1)), builder: (context, snapshot) { - final childrenIds = room.spaceChildren - .map((c) => c.roomId) - .whereType() - .toSet(); - - final joinedRooms = room.client.rooms - .where((room) => childrenIds.remove(room.id)) - .toList(); - - final joinedParents = room.spaceParents - .map((parent) { - final roomId = parent.roomId; - if (roomId == null) return null; - return room.client.getRoomById(roomId); - }) - .whereType() - .toList(); final filter = _filterController.text.trim().toLowerCase(); return CustomScrollView( slivers: [ SliverAppBar( floating: true, - toolbarHeight: 72, scrolledUnderElevation: 0, backgroundColor: Colors.transparent, automaticallyImplyLeading: false, @@ -359,11 +535,6 @@ class _SpaceViewState extends State { textInputAction: TextInputAction.search, decoration: InputDecoration( filled: true, - fillColor: theme.colorScheme.secondaryContainer, - border: OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(99), - ), contentPadding: EdgeInsets.zero, hintText: L10n.of(context).search, hintStyle: TextStyle( @@ -382,83 +553,11 @@ class _SpaceViewState extends State { ), ), SliverList.builder( - itemCount: joinedParents.length, + itemCount: _discoveredChildren.length + 1, itemBuilder: (context, i) { - final displayname = - joinedParents[i].getLocalizedDisplayname(); - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 1, - ), - child: Material( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - clipBehavior: Clip.hardEdge, - child: ListTile( - minVerticalPadding: 0, - leading: Icon( - Icons.adaptive.arrow_back_outlined, - size: 16, - ), - title: Row( - children: [ - Avatar( - mxContent: joinedParents[i].avatar, - name: displayname, - size: Avatar.defaultSize / 2, - borderRadius: BorderRadius.circular( - AppConfig.borderRadius / 4, - ), - ), - const SizedBox(width: 8), - Expanded(child: Text(displayname)), - ], - ), - onTap: () => - widget.toParentSpace(joinedParents[i].id), - ), - ), - ); - }, - ), - SliverList.builder( - itemCount: joinedRooms.length, - itemBuilder: (context, i) { - final joinedRoom = joinedRooms[i]; - return ChatListItem( - joinedRoom, - filter: filter, - onTap: () => widget.onChatTab(joinedRoom), - onLongPress: (context) => widget.onChatContext( - joinedRoom, - context, - ), - activeChat: widget.activeChat == joinedRoom.id, - ); - }, - ), - SliverList.builder( - itemCount: _discoveredChildren.length + 2, - itemBuilder: (context, i) { - if (i == 0) { - return SearchTitle( - title: L10n.of(context).discover, - icon: const Icon(Icons.explore_outlined), - ); - } - i--; if (i == _discoveredChildren.length) { if (_noMoreRooms) { - return Padding( - padding: const EdgeInsets.all(12.0), - child: Center( - child: Text( - L10n.of(context).noMoreChatsFound, - style: const TextStyle(fontSize: 13), - ), - ), - ); + return const SizedBox.shrink(); } return Padding( padding: const EdgeInsets.symmetric( @@ -468,11 +567,7 @@ class _SpaceViewState extends State { child: TextButton( onPressed: _isLoading ? null : _loadHierarchy, child: _isLoading - ? LinearProgressIndicator( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - ) + ? const CircularProgressIndicator.adaptive() : Text(L10n.of(context).loadMore), ), ); @@ -484,6 +579,10 @@ class _SpaceViewState extends State { if (!displayname.toLowerCase().contains(filter)) { return const SizedBox.shrink(); } + var joinedRoom = room.client.getRoomById(item.roomId); + if (joinedRoom?.membership == Membership.leave) { + joinedRoom = null; + } return Padding( padding: const EdgeInsets.symmetric( horizontal: 8, @@ -493,51 +592,83 @@ class _SpaceViewState extends State { borderRadius: BorderRadius.circular(AppConfig.borderRadius), clipBehavior: Clip.hardEdge, - child: ListTile( - visualDensity: - const VisualDensity(vertical: -0.5), - contentPadding: - const EdgeInsets.symmetric(horizontal: 8), - onTap: () => _joinChildRoom(item), - leading: Avatar( - mxContent: item.avatarUrl, - name: displayname, - borderRadius: item.roomType == 'm.space' - ? BorderRadius.circular( - AppConfig.borderRadius / 2, - ) + color: joinedRoom != null && + widget.activeChat == joinedRoom.id + ? theme.colorScheme.secondaryContainer + : Colors.transparent, + child: HoverBuilder( + builder: (context, hovered) => ListTile( + visualDensity: + const VisualDensity(vertical: -0.5), + contentPadding: + const EdgeInsets.symmetric(horizontal: 8), + onTap: joinedRoom != null + ? () => widget.onChatTab(joinedRoom!) + : () => _joinChildRoom(item), + onLongPress: isAdmin + ? () => _showSpaceChildEditMenu( + context, + item.roomId, + ) : null, - ), - title: Row( - children: [ - Expanded( - child: Text( - displayname, - maxLines: 1, - overflow: TextOverflow.ellipsis, + leading: hovered && isAdmin + ? SizedBox.square( + dimension: avatarSize, + child: IconButton( + splashRadius: avatarSize, + iconSize: 14, + style: IconButton.styleFrom( + foregroundColor: theme.colorScheme + .onTertiaryContainer, + backgroundColor: theme + .colorScheme.tertiaryContainer, + ), + onPressed: () => + _showSpaceChildEditMenu( + context, + item.roomId, + ), + icon: const Icon(Icons.edit_outlined), + ), + ) + : Avatar( + size: avatarSize, + mxContent: item.avatarUrl, + name: '#', + backgroundColor: + theme.colorScheme.surfaceContainer, + textColor: item.name?.darkColor ?? + theme.colorScheme.onSurface, + border: item.roomType == 'm.space' + ? BorderSide( + color: theme.colorScheme + .surfaceContainerHighest, + ) + : null, + borderRadius: item.roomType == 'm.space' + ? BorderRadius.circular( + AppConfig.borderRadius / 4, + ) + : null, + ), + title: Row( + children: [ + Expanded( + child: Opacity( + opacity: joinedRoom == null ? 0.5 : 1, + child: Text( + displayname, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), ), - ), - Text( - item.numJoinedMembers.toString(), - style: TextStyle( - fontSize: 13, - color: theme.textTheme.bodyMedium!.color, - ), - ), - const SizedBox(width: 4), - const Icon( - Icons.people_outlined, - size: 14, - ), - ], - ), - subtitle: Text( - item.topic ?? - L10n.of(context).countParticipants( - item.numJoinedMembers, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, + if (joinedRoom != null) + UnreadBubble(room: joinedRoom) + else + const Icon(Icons.chevron_right_outlined), + ], + ), ), ), ), @@ -552,9 +683,3 @@ class _SpaceViewState extends State { ); } } - -enum SpaceActions { - settings, - invite, - leave, -} diff --git a/lib/pages/chat_list/unread_bubble.dart b/lib/pages/chat_list/unread_bubble.dart new file mode 100644 index 000000000..964094f97 --- /dev/null +++ b/lib/pages/chat_list/unread_bubble.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; + +import 'package:matrix/matrix.dart'; + +import 'package:fluffychat/config/themes.dart'; + +class UnreadBubble extends StatelessWidget { + final Room room; + const UnreadBubble({required this.room, super.key}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final unread = room.isUnread; + final hasNotifications = room.notificationCount > 0; + final unreadBubbleSize = unread || room.hasNewMessages + ? room.notificationCount > 0 + ? 20.0 + : 14.0 + : 0.0; + return AnimatedContainer( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + alignment: Alignment.center, + padding: const EdgeInsets.symmetric(horizontal: 7), + height: unreadBubbleSize, + width: !hasNotifications && !unread && !room.hasNewMessages + ? 0 + : (unreadBubbleSize - 9) * room.notificationCount.toString().length + + 9, + decoration: BoxDecoration( + color: room.highlightCount > 0 + ? theme.colorScheme.error + : hasNotifications || room.markedUnread + ? theme.colorScheme.primary + : theme.colorScheme.primaryContainer, + borderRadius: BorderRadius.circular(7), + ), + child: hasNotifications + ? Text( + room.notificationCount.toString(), + style: TextStyle( + color: room.highlightCount > 0 + ? theme.colorScheme.onError + : hasNotifications + ? theme.colorScheme.onPrimary + : theme.colorScheme.onPrimaryContainer, + fontSize: 13, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.center, + ) + : const SizedBox.shrink(), + ); + } +} diff --git a/lib/pages/homeserver_picker/homeserver_picker.dart b/lib/pages/homeserver_picker/homeserver_picker.dart index b17a3bc06..1e8d015bf 100644 --- a/lib/pages/homeserver_picker/homeserver_picker.dart +++ b/lib/pages/homeserver_picker/homeserver_picker.dart @@ -8,7 +8,7 @@ import 'package:flutter_web_auth_2/flutter_web_auth_2.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; import 'package:universal_html/html.dart' as html; -import 'package:url_launcher/url_launcher_string.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; @@ -34,7 +34,10 @@ class HomeserverPickerController extends State { bool isLoading = false; final TextEditingController homeserverController = TextEditingController( + // #Pangea + // text: AppSettings.defaultHomeserver.value, text: AppConfig.defaultHomeserver, + // Pangea# ); String? error; @@ -211,7 +214,7 @@ class HomeserverPickerController extends State { case MoreLoginActions.importBackup: restoreBackup(); case MoreLoginActions.privacy: - launchUrlString(AppConfig.privacyUrl); + launchUrl(AppConfig.privacyUrl); case MoreLoginActions.about: PlatformInfos.showDialog(context); } diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index 874e7ebe3..4a7f90bc0 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -148,7 +148,9 @@ class SettingsView extends StatelessWidget { displayname, maxLines: 1, overflow: TextOverflow.ellipsis, - style: const TextStyle(fontSize: 18), + style: const TextStyle( + fontSize: 18, + ), ), ), TextButton.icon( @@ -380,7 +382,7 @@ class SettingsView extends StatelessWidget { // ListTile( // leading: const Icon(Icons.privacy_tip_outlined), // title: Text(L10n.of(context).privacy), - // onTap: () => launchUrlString(AppConfig.privacyUrl), + // onTap: () => launchUrl(AppConfig.privacyUrl), // ), // ListTile( // leading: const Icon(Icons.info_outline_rounded), diff --git a/lib/pages/settings_chat/settings_chat_view.dart b/lib/pages/settings_chat/settings_chat_view.dart index 1b41f51d9..0d635c0fd 100644 --- a/lib/pages/settings_chat/settings_chat_view.dart +++ b/lib/pages/settings_chat/settings_chat_view.dart @@ -4,7 +4,6 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/settings_switch_list_tile.dart'; import 'settings_chat.dart'; @@ -32,46 +31,33 @@ class SettingsChatView extends StatelessWidget { // SettingsSwitchListTile.adaptive( // title: L10n.of(context).formattedMessages, // subtitle: L10n.of(context).formattedMessagesDescription, - // onChanged: (b) => AppConfig.renderHtml = b, - // storeKey: SettingKeys.renderHtml, - // defaultValue: AppConfig.renderHtml, + // setting: AppSettings.renderHtml, // ), // Pangea# SettingsSwitchListTile.adaptive( title: L10n.of(context).hideRedactedMessages, subtitle: L10n.of(context).hideRedactedMessagesBody, - onChanged: (b) => AppConfig.hideRedactedEvents = b, - storeKey: SettingKeys.hideRedactedEvents, - defaultValue: AppConfig.hideRedactedEvents, + setting: AppSettings.hideRedactedEvents, ), SettingsSwitchListTile.adaptive( title: L10n.of(context).hideInvalidOrUnknownMessageFormats, - onChanged: (b) => AppConfig.hideUnknownEvents = b, - storeKey: SettingKeys.hideUnknownEvents, - defaultValue: AppConfig.hideUnknownEvents, + setting: AppSettings.hideUnknownEvents, ), // #Pangea // if (PlatformInfos.isMobile) // SettingsSwitchListTile.adaptive( // title: L10n.of(context).autoplayImages, - // onChanged: (b) => AppConfig.autoplayImages = b, - // storeKey: SettingKeys.autoplayImages, - // defaultValue: AppConfig.autoplayImages, + // setting: AppSettings.autoplayImages, // ), // Pangea# SettingsSwitchListTile.adaptive( title: L10n.of(context).sendOnEnter, - onChanged: (b) => AppConfig.sendOnEnter = b, - storeKey: SettingKeys.sendOnEnter, - defaultValue: AppConfig.sendOnEnter ?? !PlatformInfos.isMobile, + setting: AppSettings.sendOnEnter, ), SettingsSwitchListTile.adaptive( title: L10n.of(context).swipeRightToLeftToReply, - onChanged: (b) => AppConfig.swipeRightToLeftToReply = b, - storeKey: SettingKeys.swipeRightToLeftToReply, - defaultValue: AppConfig.swipeRightToLeftToReply, + setting: AppSettings.swipeRightToLeftToReply, ), - // #Pangea SwitchListTile.adaptive( value: AppConfig.useActivityImageAsChatBackground, @@ -110,12 +96,10 @@ class SettingsChatView extends StatelessWidget { // SettingsSwitchListTile.adaptive( // title: L10n.of(context).experimentalVideoCalls, // onChanged: (b) { - // AppConfig.experimentalVoip = b; // Matrix.of(context).createVoipPlugin(); // return; // }, - // storeKey: SettingKeys.experimentalVoip, - // defaultValue: AppConfig.experimentalVoip, + // setting: AppSettings.experimentalVoip, // ), // Pangea# ], diff --git a/lib/pages/settings_notifications/settings_notifications.dart b/lib/pages/settings_notifications/settings_notifications.dart index b0974940c..162cc881e 100644 --- a/lib/pages/settings_notifications/settings_notifications.dart +++ b/lib/pages/settings_notifications/settings_notifications.dart @@ -201,10 +201,7 @@ class SettingsNotificationsController extends State { void updateVolume(double value) { volumeNotifier.value = value; AppConfig.volume = value; - Matrix.of(context).store.setDouble( - SettingKeys.volume, - value, - ); + AppSettings.volume.setItem(value); } Future requestNotificationPermission() async { diff --git a/lib/pages/settings_security/settings_security.dart b/lib/pages/settings_security/settings_security.dart index e55d4c3c2..c84a144cb 100644 --- a/lib/pages/settings_security/settings_security.dart +++ b/lib/pages/settings_security/settings_security.dart @@ -156,7 +156,6 @@ class SettingsSecurityController extends State { void changeShareKeysWith(ShareKeysWith? shareKeysWith) async { if (shareKeysWith == null) return; AppSettings.shareKeysWith.setItem( - Matrix.of(context).store, shareKeysWith.name, ); Matrix.of(context).client.shareKeysWith = shareKeysWith; diff --git a/lib/pages/settings_security/settings_security_view.dart b/lib/pages/settings_security/settings_security_view.dart index b181c9915..039fd97eb 100644 --- a/lib/pages/settings_security/settings_security_view.dart +++ b/lib/pages/settings_security/settings_security_view.dart @@ -63,16 +63,12 @@ class SettingsSecurityView extends StatelessWidget { title: L10n.of(context).sendTypingNotifications, subtitle: L10n.of(context).sendTypingNotificationsDescription, - onChanged: (b) => AppConfig.sendTypingNotifications = b, - storeKey: SettingKeys.sendTypingNotifications, - defaultValue: AppConfig.sendTypingNotifications, + setting: AppSettings.sendTypingNotifications, ), SettingsSwitchListTile.adaptive( title: L10n.of(context).sendReadReceipts, subtitle: L10n.of(context).sendReadReceiptsDescription, - onChanged: (b) => AppConfig.sendPublicReadReceipts = b, - storeKey: SettingKeys.sendPublicReadReceipts, - defaultValue: AppConfig.sendPublicReadReceipts, + setting: AppSettings.sendPublicReadReceipts, ), ListTile( trailing: const Icon(Icons.chevron_right_outlined), diff --git a/lib/pages/settings_style/settings_style.dart b/lib/pages/settings_style/settings_style.dart index 5a0092a9a..968759ad4 100644 --- a/lib/pages/settings_style/settings_style.dart +++ b/lib/pages/settings_style/settings_style.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/pangea/user/style_settings_repo.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/utils/account_config.dart'; import 'package:fluffychat/utils/file_selector.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; @@ -18,7 +18,9 @@ class SettingsStyle extends StatefulWidget { class SettingsStyleController extends State { void setChatColor(Color? color) async { - AppConfig.colorSchemeSeed = color; + AppSettings.colorSchemeSeedInt.setItem( + color?.toARGB32() ?? AppSettings.colorSchemeSeedInt.defaultValue, + ); ThemeController.of(context).setPrimaryColor(color); } @@ -156,17 +158,7 @@ class SettingsStyleController extends State { } void changeFontSizeFactor(double d) { - setState(() => AppConfig.fontSizeFactor = d); - // #Pangea - // Matrix.of(context).store.setString( - // SettingKeys.fontSizeFactor, - // AppConfig.fontSizeFactor.toString(), - // ); - StyleSettingsRepo.setFontSizeFactor( - Matrix.of(context).client.userID!, - AppConfig.fontSizeFactor, - ); - // Pangea# + AppSettings.fontSizeFactor.setItem(d); } @override diff --git a/lib/pages/settings_style/settings_style_view.dart b/lib/pages/settings_style/settings_style_view.dart index 6bf3b2a79..aa32e6828 100644 --- a/lib/pages/settings_style/settings_style_view.dart +++ b/lib/pages/settings_style/settings_style_view.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/events/state_message.dart'; @@ -228,7 +229,7 @@ class SettingsStyleView extends StatelessWidget { style: TextStyle( color: theme.onBubbleColor, fontSize: AppConfig.messageFontSize * - AppConfig.fontSizeFactor, + AppSettings.fontSizeFactor.value, ), ), ), @@ -261,7 +262,7 @@ class SettingsStyleView extends StatelessWidget { style: TextStyle( color: theme.colorScheme.onSurface, fontSize: AppConfig.messageFontSize * - AppConfig.fontSizeFactor, + AppSettings.fontSizeFactor.value, ), ), ), @@ -323,13 +324,15 @@ class SettingsStyleView extends StatelessWidget { ), ListTile( title: Text(L10n.of(context).fontSize), - trailing: Text('× ${AppConfig.fontSizeFactor}'), + trailing: Text( + '× ${AppSettings.fontSizeFactor.value}', + ), ), Slider.adaptive( min: 0.5, max: 2.5, divisions: 20, - value: AppConfig.fontSizeFactor, + value: AppSettings.fontSizeFactor.value, semanticFormatterCallback: (d) => d.toString(), onChanged: controller.changeFontSizeFactor, ), @@ -348,21 +351,15 @@ class SettingsStyleView extends StatelessWidget { // ), // SettingsSwitchListTile.adaptive( // title: L10n.of(context).presencesToggle, - // onChanged: (b) => AppConfig.showPresences = b, - // storeKey: SettingKeys.showPresences, - // defaultValue: AppConfig.showPresences, + // setting: AppSettings.showPresences, // ), // SettingsSwitchListTile.adaptive( // title: L10n.of(context).separateChatTypes, - // onChanged: (b) => AppConfig.separateChatTypes = b, - // storeKey: SettingKeys.separateChatTypes, - // defaultValue: AppConfig.separateChatTypes, + // setting: AppSettings.separateChatTypes, // ), // SettingsSwitchListTile.adaptive( // title: L10n.of(context).displayNavigationRail, - // onChanged: (b) => AppConfig.displayNavigationRail = b, - // storeKey: SettingKeys.displayNavigationRail, - // defaultValue: AppConfig.displayNavigationRail, + // setting: AppSettings.displayNavigationRail, // ), // Pangea# ], diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart index 582fddb85..fa917769a 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart @@ -1,7 +1,6 @@ import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/instructions/instructions_enum.dart'; @@ -10,7 +9,7 @@ import 'package:fluffychat/widgets/matrix.dart'; extension ActivityMenuLogic on ChatController { bool get shouldShowActivityInstructions { - if (AppConfig.showedActivityMenu || + if (InstructionsEnum.showedActivityMenu.isToggledOff || InstructionsEnum.activityStatsMenu.isToggledOff || MatrixState.pAnyState.isOverlayOpen(RegExp(r"^word-zoom-card-.*$")) || timeline == null || diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_roles_event_widget.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_roles_event_widget.dart index a6e3f3fd9..6a52cfc37 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_roles_event_widget.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_roles_event_widget.dart @@ -4,6 +4,7 @@ import 'package:collection/collection.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/pangea/activity_sessions/activity_role_model.dart'; @@ -72,7 +73,7 @@ class ActivityRolesEvent extends StatelessWidget { maxLines: 2, overflow: TextOverflow.ellipsis, style: TextStyle( - fontSize: 12 * AppConfig.fontSizeFactor, + fontSize: 12 * AppSettings.fontSizeFactor.value, decoration: event.redacted ? TextDecoration.lineThrough : null, ), diff --git a/lib/pangea/analytics_details_popup/lemma_use_example_messages.dart b/lib/pangea/analytics_details_popup/lemma_use_example_messages.dart index 86dbc3364..cb59a93fb 100644 --- a/lib/pangea/analytics_details_popup/lemma_use_example_messages.dart +++ b/lib/pangea/analytics_details_popup/lemma_use_example_messages.dart @@ -6,6 +6,7 @@ import 'package:collection/collection.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart'; import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart'; @@ -118,7 +119,7 @@ class LemmaUseExampleMessages extends StatelessWidget { text: TextSpan( style: TextStyle( color: Theme.of(context).colorScheme.onPrimaryFixed, - fontSize: AppConfig.fontSizeFactor * + fontSize: AppSettings.fontSizeFactor.value * AppConfig.messageFontSize, ), children: example.textSpans, diff --git a/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart b/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart index ea0739580..da4a96c4b 100644 --- a/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart +++ b/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart @@ -7,6 +7,7 @@ import 'package:intl/intl.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/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_downloads/analytics_summary_enum.dart'; @@ -426,7 +427,8 @@ class AnalyticsDownloadDialogState extends State { Text( L10n.of(context).fileType, style: TextStyle( - fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), ), Padding( diff --git a/lib/pangea/analytics_misc/text_loading_shimmer.dart b/lib/pangea/analytics_misc/text_loading_shimmer.dart index f22a87062..1b2232e6a 100644 --- a/lib/pangea/analytics_misc/text_loading_shimmer.dart +++ b/lib/pangea/analytics_misc/text_loading_shimmer.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:shimmer/shimmer.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; class TextLoadingShimmer extends StatelessWidget { final double width; @@ -24,8 +25,8 @@ class TextLoadingShimmer extends StatelessWidget { borderRadius: BorderRadius.circular(4.0), color: Theme.of(context).colorScheme.primary, ), - height: - height ?? (AppConfig.messageFontSize * AppConfig.fontSizeFactor), + height: height ?? + (AppConfig.messageFontSize * AppSettings.fontSizeFactor.value), width: width, ), ); diff --git a/lib/pangea/analytics_practice/analytics_practice_view.dart b/lib/pangea/analytics_practice/analytics_practice_view.dart index 9ada4f9d2..a38c7525f 100644 --- a/lib/pangea/analytics_practice/analytics_practice_view.dart +++ b/lib/pangea/analytics_practice/analytics_practice_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_details_popup/morph_meaning_widget.dart'; @@ -270,7 +271,8 @@ class _ExampleMessageWidget extends StatelessWidget { text: TextSpan( style: TextStyle( color: Theme.of(context).colorScheme.onPrimaryFixed, - fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), children: snapshot.data!, ), @@ -464,8 +466,8 @@ class _ErrorBlankWidgetState extends State<_ErrorBlankWidget> { text: TextSpan( style: TextStyle( color: Theme.of(context).colorScheme.onPrimaryFixed, - fontSize: - AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), children: [ if (trimmedBefore) const TextSpan(text: '…'), @@ -491,7 +493,7 @@ class _ErrorBlankWidgetState extends State<_ErrorBlankWidget> { translation, style: TextStyle( color: Theme.of(context).colorScheme.onPrimaryFixed, - fontSize: AppConfig.fontSizeFactor * + fontSize: AppSettings.fontSizeFactor.value * AppConfig.messageFontSize, fontStyle: FontStyle.italic, ), diff --git a/lib/pangea/bot/utils/bot_style.dart b/lib/pangea/bot/utils/bot_style.dart index 8007660cf..8c344887a 100644 --- a/lib/pangea/bot/utils/bot_style.dart +++ b/lib/pangea/bot/utils/bot_style.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; class BotStyle { @@ -16,7 +17,7 @@ class BotStyle { final TextStyle botStyle = TextStyle( fontWeight: bold ? FontWeight.w700 : null, fontSize: AppConfig.messageFontSize * - AppConfig.fontSizeFactor * + AppSettings.fontSizeFactor.value * (big == true ? 1.2 : 1), fontStyle: italics ? FontStyle.italic : null, color: setColor ? Theme.of(context).colorScheme.primary : null, diff --git a/lib/pangea/chat/widgets/pangea_chat_input_row.dart b/lib/pangea/chat/widgets/pangea_chat_input_row.dart index 4606d39bd..62231dfe7 100644 --- a/lib/pangea/chat/widgets/pangea_chat_input_row.dart +++ b/lib/pangea/chat/widgets/pangea_chat_input_row.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:animations/animations.dart'; -import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/chat.dart'; @@ -201,10 +201,11 @@ class PangeaChatInputRow extends StatelessWidget { maxLines: 8, autofocus: !PlatformInfos.isMobile, keyboardType: TextInputType.multiline, - textInputAction: AppConfig.sendOnEnter == true && - PlatformInfos.isMobile - ? TextInputAction.send - : null, + textInputAction: + AppSettings.sendOnEnter.value == true && + PlatformInfos.isMobile + ? TextInputAction.send + : null, onSubmitted: (_) => controller.onInputBarSubmitted(), onSubmitImage: controller.sendImageFromClipBoard, focusNode: controller.inputFocus, diff --git a/lib/pangea/chat_list/utils/get_chat_list_item_subtitle.dart b/lib/pangea/chat_list/utils/get_chat_list_item_subtitle.dart index 3b86069b6..c4440ac5a 100644 --- a/lib/pangea/chat_list/utils/get_chat_list_item_subtitle.dart +++ b/lib/pangea/chat_list/utils/get_chat_list_item_subtitle.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.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/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/course_chats/open_roles_indicator.dart'; @@ -25,7 +25,9 @@ class ChatListItemSubtitle extends StatelessWidget { !event.redacted && event.type == EventTypes.Message && event.messageType == MessageTypes.Text && - !(AppConfig.renderHtml && !event.redacted && event.isRichMessage); + !(AppSettings.renderHtml.value && + !event.redacted && + event.isRichMessage); } Future _getPangeaMessageEvent( diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index 71a4a5987..0d5fbcdac 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -6,7 +6,6 @@ import 'package:get_storage/get_storage.dart'; import 'package:matrix/matrix.dart'; import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; @@ -67,7 +66,7 @@ class PangeaController { subscriptionController.reinitialize(); StyleSettingsRepo.settings(userID!).then((settings) { - AppConfig.fontSizeFactor = settings.fontSizeFactor; + AppSettings.fontSizeFactor.setItem(settings.fontSizeFactor); AppConfig.useActivityImageAsChatBackground = settings.useActivityImageBackground; }); @@ -136,18 +135,6 @@ class PangeaController { futures.add(GetStorage(key).erase()); } - if (AppConfig.showedActivityMenu) { - futures.add( - SharedPreferences.getInstance().then((prefs) async { - AppConfig.showedActivityMenu = false; - prefs.setBool( - SettingKeys.showedActivityMenu, - AppConfig.showedActivityMenu, - ); - }), - ); - } - await Future.wait(futures); } diff --git a/lib/pangea/instructions/instructions_enum.dart b/lib/pangea/instructions/instructions_enum.dart index a3cecfcdc..10f058054 100644 --- a/lib/pangea/instructions/instructions_enum.dart +++ b/lib/pangea/instructions/instructions_enum.dart @@ -35,6 +35,7 @@ enum InstructionsEnum { dismissSupportChat, shimmerNewToken, shimmerTranslation, + showedActivityMenu, } extension InstructionsEnumExtension on InstructionsEnum { @@ -69,6 +70,7 @@ extension InstructionsEnumExtension on InstructionsEnum { case InstructionsEnum.dismissSupportChat: case InstructionsEnum.shimmerNewToken: case InstructionsEnum.shimmerTranslation: + case InstructionsEnum.showedActivityMenu: ErrorHandler.logError( e: Exception("No title for this instruction"), m: 'InstructionsEnumExtension.title', @@ -133,6 +135,7 @@ extension InstructionsEnumExtension on InstructionsEnum { case InstructionsEnum.dismissSupportChat: case InstructionsEnum.shimmerNewToken: case InstructionsEnum.shimmerTranslation: + case InstructionsEnum.showedActivityMenu: return ""; case InstructionsEnum.disableLanguageTools: return l10n.disableLanguageToolsDesc; diff --git a/lib/pangea/login/pages/login_or_signup_view.dart b/lib/pangea/login/pages/login_or_signup_view.dart index b0b8086c4..d4b078e1e 100644 --- a/lib/pangea/login/pages/login_or_signup_view.dart +++ b/lib/pangea/login/pages/login_or_signup_view.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.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/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/pangea/common/widgets/pangea_logo_svg.dart'; @@ -78,7 +78,7 @@ class LoginOrSignupViewState extends State { forceColor: theme.colorScheme.onSurface, ), Text( - AppConfig.applicationName, + AppSettings.applicationName.value, style: theme.textTheme.headlineSmall ?.copyWith(fontWeight: FontWeight.bold), ), diff --git a/lib/pangea/login/pages/pangea_login_scaffold.dart b/lib/pangea/login/pages/pangea_login_scaffold.dart index af1fc603a..31c32efd4 100644 --- a/lib/pangea/login/pages/pangea_login_scaffold.dart +++ b/lib/pangea/login/pages/pangea_login_scaffold.dart @@ -2,7 +2,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; -import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; @@ -84,7 +84,7 @@ class PangeaLoginScaffold extends StatelessWidget { ), if (showAppName) Text( - AppConfig.applicationName, + AppSettings.applicationName.value, style: Theme.of(context).textTheme.displaySmall, ), const SizedBox(height: 12), diff --git a/lib/pangea/space_analytics/download_space_analytics_dialog.dart b/lib/pangea/space_analytics/download_space_analytics_dialog.dart index 5a783a918..cae1175f3 100644 --- a/lib/pangea/space_analytics/download_space_analytics_dialog.dart +++ b/lib/pangea/space_analytics/download_space_analytics_dialog.dart @@ -6,6 +6,7 @@ import 'package:excel/excel.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/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_downloads/space_analytics_summary_enum.dart'; @@ -289,7 +290,8 @@ class DownloadAnalyticsDialogState extends State { Text( L10n.of(context).fileType, style: TextStyle( - fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), ), Padding( diff --git a/lib/pangea/spaces/space_navigation_column.dart b/lib/pangea/spaces/space_navigation_column.dart index b6d630870..9cac6b7c6 100644 --- a/lib/pangea/spaces/space_navigation_column.dart +++ b/lib/pangea/spaces/space_navigation_column.dart @@ -183,7 +183,7 @@ class _MainView extends StatelessWidget { if (path == null) { return ChatList( activeChat: state.pathParameters['roomid'], - activeSpaceId: state.pathParameters['spaceid'], + activeSpace: state.pathParameters['spaceid'], ); } @@ -220,7 +220,7 @@ class _MainView extends StatelessWidget { return ChatList( activeChat: state.pathParameters['roomid'], - activeSpaceId: state.pathParameters['spaceid'], + activeSpace: state.pathParameters['spaceid'], ); } } diff --git a/lib/pangea/toolbar/layout/message_selection_positioner.dart b/lib/pangea/toolbar/layout/message_selection_positioner.dart index 5d9f2fe16..fc8a90463 100644 --- a/lib/pangea/toolbar/layout/message_selection_positioner.dart +++ b/lib/pangea/toolbar/layout/message_selection_positioner.dart @@ -154,7 +154,7 @@ class MessageSelectionPositionerState extends State widget.event.senderId == widget.event.room.client.userID; bool get showDetails => - AppSettings.displayChatDetailsColumn.getItem(Matrix.of(context).store) && + AppSettings.displayChatDetailsColumn.value && FluffyThemes.isThreeColumnMode(context) && widget.chatController.room.membership == Membership.join; diff --git a/lib/pangea/toolbar/message_practice/message_audio_card.dart b/lib/pangea/toolbar/message_practice/message_audio_card.dart index 6a3661b61..cfc0bc204 100644 --- a/lib/pangea/toolbar/message_practice/message_audio_card.dart +++ b/lib/pangea/toolbar/message_practice/message_audio_card.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/pages/chat/events/audio_player.dart'; import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; @@ -79,8 +80,8 @@ class MessageAudioCardState extends State { senderId: widget.messageEvent.senderId, matrixFile: audioFile, color: Theme.of(context).colorScheme.onPrimaryContainer, - fontSize: - AppConfig.messageFontSize * AppConfig.fontSizeFactor, + fontSize: AppConfig.messageFontSize * + AppSettings.fontSizeFactor.value, linkColor: Theme.of(context).brightness == Brightness.light ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onPrimary, diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index 2bec4ecf2..31fd338ba 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -69,6 +69,7 @@ class BackgroundPush { } final pendingTests = >{}; + bool firebaseEnabled = false; //final firebase = FcmSharedIsolate(); @@ -77,6 +78,7 @@ class BackgroundPush { bool upAction = false; void _init() async { + //firebaseEnabled = true; try { // #Pangea // Handle notifications when app is opened from terminated/background state @@ -346,8 +348,7 @@ class BackgroundPush { currentPushers.first.data.url.toString() == gatewayUrl && currentPushers.first.data.format == // #Pangea - // AppSettings.pushNotificationsPusherFormat - // .getItem(matrix!.store) && + // AppSettings.pushNotificationsPusherFormat.value && null && // Pangea# mapEquals( @@ -393,8 +394,7 @@ class BackgroundPush { data: PusherData( url: Uri.parse(gatewayUrl!), // #Pangea - // format: AppSettings.pushNotificationsPusherFormat - // .getItem(matrix!.store), + // format: AppSettings.pushNotificationsPusherFormat.value, // Pangea# additionalProperties: {"data_message": pusherDataMessageFormat}, ), @@ -468,7 +468,7 @@ class BackgroundPush { if (matrix == null) { return; } - if ((matrix?.store.getBool(SettingKeys.showNoGoogle) ?? false) == true) { + if (!AppSettings.showNoGoogle.value) { return; } await loadLocale(); @@ -499,15 +499,19 @@ class BackgroundPush { } } await setupPusher( - gatewayUrl: - AppSettings.pushNotificationsGatewayUrl.getItem(matrix!.store), + gatewayUrl: AppSettings.pushNotificationsGatewayUrl.value, token: _fcmToken, ); } Future setupUp() async { - await UnifiedPushUi(matrix!.context, ["default"], UPFunctions()) - .registerAppWithDialog(); + await UnifiedPushUi( + context: matrix!.context, + instances: ["default"], + unifiedPushFunctions: UPFunctions(), + showNoDistribDialog: false, + onNoDistribDialogDismissed: () {}, // TODO: Implement me + ).registerAppWithDialog(); } Future _newUpEndpoint(PushEndpoint newPushEndpoint, String i) async { @@ -552,18 +556,18 @@ class BackgroundPush { oldTokens: oldTokens, useDeviceSpecificAppId: true, ); - await matrix?.store.setString(SettingKeys.unifiedPushEndpoint, newEndpoint); - await matrix?.store.setBool(SettingKeys.unifiedPushRegistered, true); + await AppSettings.unifiedPushEndpoint.setItem(newEndpoint); + await AppSettings.unifiedPushRegistered.setItem(true); } Future _upUnregistered(String i) async { upAction = true; Logs().i('[Push] Removing UnifiedPush endpoint...'); - final oldEndpoint = - matrix?.store.getString(SettingKeys.unifiedPushEndpoint); - await matrix?.store.setBool(SettingKeys.unifiedPushRegistered, false); - await matrix?.store.remove(SettingKeys.unifiedPushEndpoint); - if (oldEndpoint?.isNotEmpty ?? false) { + final oldEndpoint = AppSettings.unifiedPushEndpoint.value; + await AppSettings.unifiedPushEndpoint + .setItem(AppSettings.unifiedPushEndpoint.defaultValue); + await AppSettings.unifiedPushRegistered.setItem(false); + if (oldEndpoint.isNotEmpty) { // remove the old pusher await setupPusher( oldTokens: {oldEndpoint}, diff --git a/lib/utils/client_manager.dart b/lib/utils/client_manager.dart index f498ddf8d..f4872c60c 100644 --- a/lib/utils/client_manager.dart +++ b/lib/utils/client_manager.dart @@ -11,7 +11,6 @@ import 'package:matrix/matrix.dart'; import 'package:shared_preferences/shared_preferences.dart'; 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/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; @@ -109,8 +108,8 @@ abstract class ClientManager { String clientName, SharedPreferences store, ) async { - final shareKeysWith = AppSettings.shareKeysWith.getItem(store); - final enableSoftLogout = AppSettings.enableSoftLogout.getItem(store); + final shareKeysWith = AppSettings.shareKeysWith.value; + final enableSoftLogout = AppSettings.enableSoftLogout.value; return Client( clientName, @@ -185,7 +184,7 @@ abstract class ClientManager { await NotificationsClient().notify( title, body: body, - appName: AppConfig.applicationName, + appName: AppSettings.applicationName.value, hints: [ NotificationHint.soundName('message-new-instant'), ], diff --git a/lib/utils/init_with_restore.dart b/lib/utils/init_with_restore.dart index 523b22f9e..feb95e003 100644 --- a/lib/utils/init_with_restore.dart +++ b/lib/utils/init_with_restore.dart @@ -5,6 +5,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.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/utils/client_manager.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -57,13 +58,13 @@ extension InitWithRestoreExtension on Client { ? const FlutterSecureStorage() : null; await storage?.delete( - key: '${AppConfig.applicationName}_session_backup_$clientName', + key: '${AppSettings.applicationName.value}_session_backup_$clientName', ); } Future initWithRestore({void Function()? onMigration}) async { final storageKey = - '${AppConfig.applicationName}_session_backup_$clientName'; + '${AppSettings.applicationName.value}_session_backup_$clientName'; final storage = PlatformInfos.isMobile || PlatformInfos.isLinux ? const FlutterSecureStorage() : null; diff --git a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart index e5f07543b..4df40fc0c 100644 --- a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart +++ b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart @@ -1,13 +1,16 @@ import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/filtered_timeline_extension.dart'; -import '../../config/app_config.dart'; extension VisibleInGuiExtension on List { - List filterByVisibleInGui({String? exceptionEventId}) => where( + List filterByVisibleInGui({ + String? exceptionEventId, + }) => + where( // #Pangea // (event) => event.isVisibleInGui || event.eventId == exceptionEventId, (event) => @@ -28,18 +31,16 @@ extension IsStateExtension on Event { // if a reaction has been redacted we also want it to be hidden in the timeline !{EventTypes.Reaction, EventTypes.Redaction}.contains(type) && // 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 // #Pangea - // (!AppConfig.hideUnknownEvents || isEventTypeKnown); - (!AppConfig.hideUnknownEvents || pangeaIsEventTypeKnown) && - (!isState || importantStateEvents.contains(type)) && + // (!AppSettings.hideUnknownEvents.value || isEventTypeKnown); + (!AppSettings.hideUnknownEvents.value || pangeaIsEventTypeKnown) && content.tryGet(ModelKey.transcription) == null && - // if sending of transcription fails, - // don't show it as a errored audio event in timeline. ((unsigned?['extra_content'] as Map?)?[ModelKey.transcription] == - null); + null) && + (!isState || importantStateEvents.contains(type)); // Pangea# bool get isState => !{ @@ -74,6 +75,7 @@ extension IsStateExtension on Event { PangeaEventTypes.activityRole, ].contains(type); + // we're filtering out some state events that we don't want to render static const Set importantStateEvents = { EventTypes.Encryption, EventTypes.RoomCreate, diff --git a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/cipher.dart b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/cipher.dart index 4c3296c86..70f8d8072 100644 --- a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/cipher.dart +++ b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/cipher.dart @@ -6,7 +6,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:matrix/matrix.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/setting_keys.dart'; @@ -89,8 +88,7 @@ Future getDatabaseCipher() async { } void _sendNoEncryptionWarning(Object exception) async { - final store = await SharedPreferences.getInstance(); - final isStored = AppSettings.noEncryptionWarningShown.getItem(store); + final isStored = AppSettings.noEncryptionWarningShown.value; if (isStored == true) return; @@ -108,5 +106,5 @@ void _sendNoEncryptionWarning(Object exception) async { // ); // Pangea# - await AppSettings.noEncryptionWarningShown.setItem(store, true); + await AppSettings.noEncryptionWarningShown.setItem(true); } diff --git a/lib/utils/notification_background_handler.dart b/lib/utils/notification_background_handler.dart index 531d7e19d..aa8ea17dd 100644 --- a/lib/utils/notification_background_handler.dart +++ b/lib/utils/notification_background_handler.dart @@ -6,7 +6,6 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_vodozemac/flutter_vodozemac.dart' as vod; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/utils/client_download_content_extension.dart'; @@ -59,7 +58,7 @@ void notificationTapBackground( await vod.init(); _vodInitialized = true; } - final store = await SharedPreferences.getInstance(); + final store = await AppSettings.init(); final client = (await ClientManager.getClients( initialize: false, store: store, @@ -71,10 +70,6 @@ void notificationTapBackground( waitUntilLoadCompletedLoaded: false, ); - AppConfig.sendPublicReadReceipts = - store.getBool(SettingKeys.sendPublicReadReceipts) ?? - AppConfig.sendPublicReadReceipts; - if (!client.isLogged()) { throw Exception('Notification tab in background but not logged in!'); } @@ -145,7 +140,7 @@ Future notificationTap( await room.setReadMarker( payload.eventId ?? room.lastEvent!.eventId, mRead: payload.eventId ?? room.lastEvent!.eventId, - public: AppConfig.sendPublicReadReceipts, + public: AppSettings.sendPublicReadReceipts.value, ); case FluffyChatNotificationActions.reply: final input = notificationResponse.input; diff --git a/lib/utils/platform_infos.dart b/lib/utils/platform_infos.dart index 4913c1fe5..e80af37e6 100644 --- a/lib/utils/platform_infos.dart +++ b/lib/utils/platform_infos.dart @@ -8,6 +8,7 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:universal_html/html.dart' as html; import 'package:url_launcher/url_launcher_string.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import '../config/app_config.dart'; @@ -46,7 +47,7 @@ abstract class PlatformInfos { // Pangea# static String get clientName => - '${AppConfig.applicationName} ${isWeb ? 'web' : Platform.operatingSystem}${kReleaseMode ? '' : 'Debug'}'; + '${AppSettings.applicationName.value} ${isWeb ? 'web' : Platform.operatingSystem}${kReleaseMode ? '' : 'Debug'}'; static Future getVersion() async { var version = kIsWeb ? 'Web' : 'Unknown'; @@ -101,7 +102,7 @@ abstract class PlatformInfos { height: 64, filterQuality: FilterQuality.medium, ), - applicationName: AppConfig.applicationName, + applicationName: AppSettings.applicationName.value, ); } diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index 8854eb629..82f49441a 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -8,9 +8,9 @@ import 'package:collection/collection.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_shortcuts_new/flutter_shortcuts_new.dart'; import 'package:matrix/matrix.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/utils/client_download_content_extension.dart'; @@ -60,7 +60,7 @@ Future pushHelper( l10n.incomingMessages, number: notification.counts?.unread, ticker: l10n.unreadChatsInApp( - AppConfig.applicationName, + AppSettings.applicationName.value, (notification.counts?.unread ?? 0).toString(), ), importance: Importance.high, @@ -98,7 +98,7 @@ Future _tryPushHelper( client ??= (await ClientManager.getClients( initialize: false, - store: await SharedPreferences.getInstance(), + store: await AppSettings.init(), )) .first; final event = await client.getEventByPushNotification( @@ -300,25 +300,27 @@ Future _tryPushHelper( importance: Importance.high, priority: Priority.max, groupKey: event.room.spaceParents.firstOrNull?.roomId ?? 'rooms', - actions: [ - AndroidNotificationAction( - FluffyChatNotificationActions.reply.name, - l10n.reply, - inputs: [ - AndroidNotificationActionInput( - label: l10n.writeAMessage, - ), - ], - cancelNotification: false, - allowGeneratedReplies: true, - semanticAction: SemanticAction.reply, - ), - AndroidNotificationAction( - FluffyChatNotificationActions.markAsRead.name, - l10n.markAsRead, - semanticAction: SemanticAction.markAsRead, - ), - ], + actions: event.type == EventTypes.RoomMember + ? null + : [ + AndroidNotificationAction( + FluffyChatNotificationActions.reply.name, + l10n.reply, + inputs: [ + AndroidNotificationActionInput( + label: l10n.writeAMessage, + ), + ], + cancelNotification: false, + allowGeneratedReplies: true, + semanticAction: SemanticAction.reply, + ), + AndroidNotificationAction( + FluffyChatNotificationActions.markAsRead.name, + l10n.markAsRead, + semanticAction: SemanticAction.markAsRead, + ), + ], ); const iOSPlatformChannelSpecifics = DarwinNotificationDetails(); final platformChannelSpecifics = NotificationDetails( diff --git a/lib/widgets/adaptive_dialogs/public_room_dialog.dart b/lib/widgets/adaptive_dialogs/public_room_dialog.dart index 09e125613..6b11d45c9 100644 --- a/lib/widgets/adaptive_dialogs/public_room_dialog.dart +++ b/lib/widgets/adaptive_dialogs/public_room_dialog.dart @@ -30,7 +30,9 @@ class PublicRoomDialog extends StatelessWidget { final result = await showFutureLoadingDialog( context: context, future: () async { - if (chunk != null && client.getRoomById(chunk.roomId) != null) { + if (chunk != null && + client.getRoomById(chunk.roomId) != null && + client.getRoomById(chunk.roomId)?.membership != Membership.leave) { return chunk.roomId; } final roomId = chunk != null && knock @@ -64,6 +66,8 @@ class PublicRoomDialog extends StatelessWidget { if (chunk?.roomType != 'm.space' && !client.getRoomById(result.result!)!.isSpace) { context.go('/rooms/$roomId'); + } else { + context.go('/rooms?spaceId=$roomId'); } return; } diff --git a/lib/widgets/adaptive_dialogs/show_text_input_dialog.dart b/lib/widgets/adaptive_dialogs/show_text_input_dialog.dart index cba2d7247..475e3bd81 100644 --- a/lib/widgets/adaptive_dialogs/show_text_input_dialog.dart +++ b/lib/widgets/adaptive_dialogs/show_text_input_dialog.dart @@ -89,7 +89,6 @@ Future showTextInputDialog({ // Pangea# maxLength: maxLength, keyboardType: keyboardType, - obscureText: obscureText, ); }, ), diff --git a/lib/widgets/app_lock.dart b/lib/widgets/app_lock.dart index d337358d7..c4682d9f4 100644 --- a/lib/widgets/app_lock.dart +++ b/lib/widgets/app_lock.dart @@ -4,7 +4,6 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:matrix/matrix.dart'; import 'package:provider/provider.dart'; -import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/widgets/lock_screen.dart'; class AppLockWidget extends StatefulWidget { @@ -65,7 +64,7 @@ class AppLock extends State with WidgetsBindingObserver { Future changePincode(String? pincode) async { await const FlutterSecureStorage().write( - key: SettingKeys.appLockKey, + key: 'chat.fluffy.app_lock', value: pincode, ); _pincode = pincode; diff --git a/lib/widgets/avatar.dart b/lib/widgets/avatar.dart index 4355f53db..995a15b9a 100644 --- a/lib/widgets/avatar.dart +++ b/lib/widgets/avatar.dart @@ -21,6 +21,8 @@ class Avatar extends StatelessWidget { final BorderRadius? borderRadius; final IconData? icon; final BorderSide? border; + final Color? backgroundColor; + final Color? textColor; // #Pangea final bool useRive; final bool showPresence; @@ -42,6 +44,8 @@ class Avatar extends StatelessWidget { this.borderRadius, this.border, this.icon, + this.backgroundColor, + this.textColor, // #Pangea this.useRive = false, this.showPresence = true, @@ -80,7 +84,6 @@ class Avatar extends StatelessWidget { side: border ?? BorderSide.none, ), clipBehavior: Clip.antiAlias, - // #Pangea // child: MxcImage( child: (userId ?? presenceUserId) == BotName.byEnvironment ? BotFace( @@ -88,6 +91,7 @@ class Avatar extends StatelessWidget { expression: BotExpression.idle, useRive: useRive, ) + // #Pangea : !(mxContent.toString().startsWith('mxc://')) ? ImageByUrl( imageUrl: mxContent, @@ -101,6 +105,7 @@ class Avatar extends StatelessWidget { ), borderRadius: borderRadius, ) + // Pangea# : MxcImage( // Pangea# client: client, @@ -114,7 +119,8 @@ class Avatar extends StatelessWidget { placeholder: (_) => noPic ? Container( decoration: BoxDecoration( - color: name?.lightColorAvatar, + color: + backgroundColor ?? name?.lightColorAvatar, ), alignment: Alignment.center, child: Text( @@ -122,7 +128,7 @@ class Avatar extends StatelessWidget { textAlign: TextAlign.center, style: TextStyle( fontFamily: 'RobotoMono', - color: Colors.white, + color: textColor ?? Colors.white, fontWeight: FontWeight.bold, fontSize: (size / 2.5).roundToDouble(), ), diff --git a/lib/widgets/config_viewer.dart b/lib/widgets/config_viewer.dart index adfb34407..b0537317c 100644 --- a/lib/widgets/config_viewer.dart +++ b/lib/widgets/config_viewer.dart @@ -21,7 +21,7 @@ class _ConfigViewerState extends State { String initialValue, ) async { if (appSetting is AppSettings) { - await appSetting.setItem(store, !(initialValue == 'true')); + await appSetting.setItem(!(initialValue == 'true')); setState(() {}); return; } @@ -35,13 +35,13 @@ class _ConfigViewerState extends State { if (value == null) return; if (appSetting is AppSettings) { - await appSetting.setItem(store, value); + await appSetting.setItem(value); } if (appSetting is AppSettings) { - await appSetting.setItem(store, int.parse(value)); + await appSetting.setItem(int.parse(value)); } if (appSetting is AppSettings) { - await appSetting.setItem(store, double.parse(value)); + await appSetting.setItem(double.parse(value)); } setState(() {}); @@ -78,16 +78,16 @@ class _ConfigViewerState extends State { final appSetting = AppSettings.values[i]; var value = ''; if (appSetting is AppSettings) { - value = appSetting.getItem(store); + value = appSetting.value; } if (appSetting is AppSettings) { - value = appSetting.getItem(store).toString(); + value = appSetting.value.toString(); } if (appSetting is AppSettings) { - value = appSetting.getItem(store).toString(); + value = appSetting.value.toString(); } if (appSetting is AppSettings) { - value = appSetting.getItem(store).toString(); + value = appSetting.value.toString(); } return ListTile( title: Text(appSetting.name), diff --git a/lib/widgets/fluffy_chat_app.dart b/lib/widgets/fluffy_chat_app.dart index aa773d5d5..c89916511 100644 --- a/lib/widgets/fluffy_chat_app.dart +++ b/lib/widgets/fluffy_chat_app.dart @@ -8,13 +8,13 @@ import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/routes.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart'; import 'package:fluffychat/pangea/languages/locale_provider.dart'; import 'package:fluffychat/widgets/app_lock.dart'; import 'package:fluffychat/widgets/theme_builder.dart'; -import '../config/app_config.dart'; import '../utils/custom_scroll_behaviour.dart'; import 'matrix.dart'; @@ -53,7 +53,7 @@ class FluffyChatApp extends StatelessWidget { Widget build(BuildContext context) { return ThemeBuilder( builder: (context, themeMode, primaryColor) => MaterialApp.router( - title: AppConfig.applicationName, + title: AppSettings.applicationName.value, themeMode: themeMode, theme: FluffyThemes.buildTheme(context, Brightness.light, primaryColor), darkTheme: diff --git a/lib/widgets/layouts/login_scaffold.dart b/lib/widgets/layouts/login_scaffold.dart index 963315e5e..9060eace4 100644 --- a/lib/widgets/layouts/login_scaffold.dart +++ b/lib/widgets/layouts/login_scaffold.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:fluffychat/config/app_config.dart'; @@ -132,7 +133,7 @@ class _PrivacyButtons extends StatelessWidget { ), ), TextButton( - onPressed: () => launchUrlString(AppConfig.privacyUrl), + onPressed: () => launchUrl(AppConfig.privacyUrl), child: Text( L10n.of(context).privacy, style: shadowTextStyle, diff --git a/lib/widgets/local_notifications_extension.dart b/lib/widgets/local_notifications_extension.dart index 3036a92d2..87e17f40d 100644 --- a/lib/widgets/local_notifications_extension.dart +++ b/lib/widgets/local_notifications_extension.dart @@ -11,6 +11,7 @@ import 'package:permission_handler/permission_handler.dart'; 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/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/utils/client_download_content_extension.dart'; @@ -122,7 +123,7 @@ extension LocalNotificationsExtension on MatrixState { title, body: body, replacesId: linuxNotificationIds[roomId] ?? 0, - appName: AppConfig.applicationName, + appName: AppSettings.applicationName.value, appIcon: 'fluffychat', actions: [ NotificationAction( @@ -147,7 +148,7 @@ extension LocalNotificationsExtension on MatrixState { event.room.setReadMarker( event.eventId, mRead: event.eventId, - public: AppConfig.sendPublicReadReceipts, + public: AppSettings.sendPublicReadReceipts.value, ); break; case DesktopNotificationActions.openChat: diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 96698dec2..3c07dfdc4 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:app_links/app_links.dart'; import 'package:collection/collection.dart'; import 'package:desktop_notifications/desktop_notifications.dart'; -import 'package:http/http.dart' as http; import 'package:image_picker/image_picker.dart'; import 'package:intl/intl.dart'; import 'package:just_audio/just_audio.dart'; @@ -19,13 +18,13 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:universal_html/html.dart' as html; import 'package:url_launcher/url_launcher_string.dart'; +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_data/analytics_data_service.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/any_state_holder.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/languages/locale_provider.dart'; -import 'package:fluffychat/pangea/user/style_settings_repo.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -34,7 +33,6 @@ import 'package:fluffychat/utils/voip_plugin.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/future_loading_dialog.dart'; -import '../config/app_config.dart'; import '../config/setting_keys.dart'; import '../pages/key_verification/key_verification_dialog.dart'; import '../utils/account_bundles.dart'; @@ -192,7 +190,7 @@ class MatrixState extends State with WidgetsBindingObserver { } final candidate = _loginClientCandidate ??= await ClientManager.createClient( - '${AppConfig.applicationName}-${DateTime.now().millisecondsSinceEpoch}', + '${AppSettings.applicationName.value}-${DateTime.now().millisecondsSinceEpoch}', store, ) ..onLoginStateChanged @@ -283,11 +281,6 @@ class MatrixState extends State with WidgetsBindingObserver { super.initState(); WidgetsBinding.instance.addObserver(this); initMatrix(); - if (PlatformInfos.isWeb) { - initConfig().then((_) => initSettings()); - } else { - initSettings(); - } // #Pangea Sentry.configureScope( (scope) => scope.setUser( @@ -369,19 +362,6 @@ class MatrixState extends State with WidgetsBindingObserver { } // Pangea# - Future 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) { final c = getClientByName(name); if (c == null) { @@ -528,7 +508,7 @@ class MatrixState extends State with WidgetsBindingObserver { ); } if (result == OkCancelResult.cancel) { - await store.setBool(SettingKeys.showNoGoogle, true); + await AppSettings.showNoGoogle.setItem(true); } }, ); @@ -538,7 +518,7 @@ class MatrixState extends State with WidgetsBindingObserver { } void createVoipPlugin() async { - if (store.getBool(SettingKeys.experimentalVoip) == false) { + if (AppSettings.experimentalVoip.value) { voipPlugin = null; return; } @@ -560,72 +540,6 @@ class MatrixState extends State with WidgetsBindingObserver { } } - void initSettings() { - // #Pangea - // AppConfig.fontSizeFactor = - // double.tryParse(store.getString(SettingKeys.fontSizeFactor) ?? '') ?? - // AppConfig.fontSizeFactor; - if (client.isLogged()) { - StyleSettingsRepo.settings(client.userID!).then((settings) { - AppConfig.fontSizeFactor = settings.fontSizeFactor; - AppConfig.useActivityImageAsChatBackground = - settings.useActivityImageBackground; - }); - } - // Pangea# - - 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; - - // #Pangea - AppConfig.volume = store.getDouble(SettingKeys.volume) ?? AppConfig.volume; - - AppConfig.showedActivityMenu = - store.getBool(SettingKeys.showedActivityMenu) ?? - AppConfig.showedActivityMenu; - // Pangea# - } - @override void dispose() { WidgetsBinding.instance.removeObserver(this); diff --git a/lib/widgets/navigation_rail.dart b/lib/widgets/navigation_rail.dart index 3fc977dee..fc2ee8381 100644 --- a/lib/widgets/navigation_rail.dart +++ b/lib/widgets/navigation_rail.dart @@ -73,15 +73,8 @@ class SpacesNavigationRail extends StatelessWidget { .where((s) => s.hasRoomUpdate) .rateLimit(const Duration(seconds: 1)), builder: (context, _) { - final allSpaces = client.rooms.where((room) => room.isSpace); - final rootSpaces = allSpaces - .where( - (space) => !allSpaces.any( - (parentSpace) => parentSpace.spaceChildren - .any((child) => child.roomId == space.id), - ), - ) - .toList(); + final allSpaces = + client.rooms.where((room) => room.isSpace).toList(); // #Pangea // return SizedBox( @@ -99,8 +92,8 @@ class SpacesNavigationRail extends StatelessWidget { child: ListView.builder( scrollDirection: Axis.vertical, // #Pangea - // itemCount: rootSpaces.length + 2, - itemCount: rootSpaces.length + 3, + // itemCount: allSpaces.length + 2, + itemCount: allSpaces.length + 3, // Pangea# itemBuilder: (context, i) { // #Pangea @@ -189,7 +182,7 @@ class SpacesNavigationRail extends StatelessWidget { ); } i--; - if (i == rootSpaces.length) { + if (i == allSpaces.length) { return NaviRailItem( // #Pangea // isSelected: false, @@ -226,9 +219,9 @@ class SpacesNavigationRail extends StatelessWidget { // Pangea# ); } - final space = rootSpaces[i]; + final space = allSpaces[i]; final displayname = - rootSpaces[i].getLocalizedDisplayname( + allSpaces[i].getLocalizedDisplayname( MatrixLocals(L10n.of(context)), ); final spaceChildrenIds = @@ -239,10 +232,10 @@ class SpacesNavigationRail extends StatelessWidget { // #Pangea backgroundColor: Colors.transparent, borderRadius: BorderRadius.circular(0), - // onTap: () => onGoToSpaceId(rootSpaces[i].id), + // onTap: () => onGoToSpaceId(allSpaces[i].id), onTap: () { collapse(); - final room = client.getRoomById(rootSpaces[i].id); + final room = client.getRoomById(allSpaces[i].id); if (room != null) { chatListHandleSpaceTap( context, @@ -250,7 +243,7 @@ class SpacesNavigationRail extends StatelessWidget { ); } else { context.go( - "/rooms/spaces/${rootSpaces[i].id}/details", + "/rooms/spaces/${allSpaces[i].id}/details", ); } }, @@ -259,7 +252,7 @@ class SpacesNavigationRail extends StatelessWidget { spaceChildrenIds.contains(room.id), // #Pangea // icon: Avatar( - // mxContent: rootSpaces[i].avatar, + // mxContent: allSpaces[i].avatar, // name: displayname, // border: BorderSide( // width: 1, @@ -271,7 +264,7 @@ class SpacesNavigationRail extends StatelessWidget { // ), icon: b.Badge( showBadge: - rootSpaces[i].membership == Membership.invite, + allSpaces[i].membership == Membership.invite, badgeStyle: b.BadgeStyle( badgeColor: Theme.of(context).colorScheme.error, elevation: 4, @@ -290,7 +283,7 @@ class SpacesNavigationRail extends StatelessWidget { child: ClipPath( clipper: MapClipper(), child: Avatar( - mxContent: rootSpaces[i].avatar, + mxContent: allSpaces[i].avatar, name: displayname, border: BorderSide( width: 1, diff --git a/lib/widgets/settings_switch_list_tile.dart b/lib/widgets/settings_switch_list_tile.dart index f49b97598..f625f9e64 100644 --- a/lib/widgets/settings_switch_list_tile.dart +++ b/lib/widgets/settings_switch_list_tile.dart @@ -1,18 +1,16 @@ import 'package:flutter/material.dart'; -import 'matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; class SettingsSwitchListTile extends StatefulWidget { - final bool defaultValue; - final String storeKey; + final AppSettings setting; final String title; final String? subtitle; final Function(bool)? onChanged; const SettingsSwitchListTile.adaptive({ super.key, - this.defaultValue = false, - required this.storeKey, + required this.setting, required this.title, this.subtitle, this.onChanged, @@ -27,13 +25,12 @@ class SettingsSwitchListTileState extends State { Widget build(BuildContext context) { final subtitle = widget.subtitle; return SwitchListTile.adaptive( - value: Matrix.of(context).store.getBool(widget.storeKey) ?? - widget.defaultValue, + value: widget.setting.value, title: Text(widget.title), subtitle: subtitle == null ? null : Text(subtitle), onChanged: (bool newValue) async { widget.onChanged?.call(newValue); - await Matrix.of(context).store.setBool(widget.storeKey, newValue); + await widget.setting.setItem(newValue); setState(() {}); }, ); diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 80bbc5087..8507a72ef 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -42,6 +42,8 @@ PODS: - FlutterMacOS - record_macos (1.1.0): - FlutterMacOS + - screen_retriever_macos (0.0.1): + - FlutterMacOS - share_plus (0.0.1): - FlutterMacOS - shared_preferences_foundation (0.0.1): @@ -64,7 +66,12 @@ PODS: - FlutterMacOS - wakelock_plus (0.0.1): - FlutterMacOS + - webcrypto (0.1.1): + - Flutter + - FlutterMacOS - WebRTC-SDK (137.7151.04) + - window_manager (0.5.0): + - FlutterMacOS - window_to_front (0.0.1): - FlutterMacOS @@ -89,6 +96,7 @@ DEPENDENCIES: - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - record_macos (from `Flutter/ephemeral/.symlinks/plugins/record_macos/macos`) + - screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - sqlcipher_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlcipher_flutter_libs/macos`) @@ -96,6 +104,8 @@ DEPENDENCIES: - video_compress (from `Flutter/ephemeral/.symlinks/plugins/video_compress/macos`) - video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`) - wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`) + - webcrypto (from `Flutter/ephemeral/.symlinks/plugins/webcrypto/darwin`) + - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) - window_to_front (from `Flutter/ephemeral/.symlinks/plugins/window_to_front/macos`) SPEC REPOS: @@ -144,6 +154,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin record_macos: :path: Flutter/ephemeral/.symlinks/plugins/record_macos/macos + screen_retriever_macos: + :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos share_plus: :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos shared_preferences_foundation: @@ -158,6 +170,10 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin wakelock_plus: :path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos + webcrypto: + :path: Flutter/ephemeral/.symlinks/plugins/webcrypto/darwin + window_manager: + :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos window_to_front: :path: Flutter/ephemeral/.symlinks/plugins/window_to_front/macos @@ -182,6 +198,7 @@ SPEC CHECKSUMS: package_info_plus: f0052d280d17aa382b932f399edf32507174e870 path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 record_macos: 43194b6c06ca6f8fa132e2acea72b202b92a0f5b + screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 SQLCipher: eb79c64049cb002b4e9fcb30edb7979bf4706dfc @@ -190,7 +207,9 @@ SPEC CHECKSUMS: video_compress: 752b161da855df2492dd1a8fa899743cc8fe9534 video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b wakelock_plus: 917609be14d812ddd9e9528876538b2263aaa03b + webcrypto: a5f5eb3e375cf0a99993e207e97cdcab5c94ce2e WebRTC-SDK: 40d4f5ba05cadff14e4db5614aec402a633f007e + window_manager: b729e31d38fb04905235df9ea896128991cad99e window_to_front: 9e76fd432e36700a197dac86a0011e49c89abe0a PODFILE CHECKSUM: d0975b16fbdecb73b109d8fbc88aa77ffe4c7a8d diff --git a/pubspec.lock b/pubspec.lock index 6aacdbe86..a4e899775 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: "direct main" description: name: animations - sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb + sha256: a8031b276f0a7986ac907195f10ca7cd04ecf2a8a566bd6dbe03018a9b02b427 url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.1.0" ansicolor: dependency: transitive description: @@ -1342,14 +1342,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" - js_interop: - dependency: transitive - description: - name: js_interop - sha256: "7ec859c296958ccea34dc770504bd3ff4ae52fdd9e7eeb2bacc7081ad476a1f5" - url: "https://pub.dev" - source: hosted - version: "0.0.1" json_annotation: dependency: transitive description: @@ -1516,7 +1508,7 @@ packages: path: "/Users/ggurdin/pangea/matrix-dart-sdk" relative: false source: path - version: "2.0.1" + version: "3.0.0" meta: dependency: transitive description: @@ -2558,10 +2550,10 @@ packages: dependency: "direct main" description: name: unifiedpush_ui - sha256: cf86f0214f37debd41f25c0425c8489df85e27f9f8784fed571eb7a86d39ba11 + sha256: "1b36b2aa0bc6b61577e2661c1183bd3442969ecf77b4c78174796d324f66dd1d" url: "https://pub.dev" source: hosted - version: "0.1.0" + version: "0.2.0" universal_html: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 45c4afcfe..82607023c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - animations: ^2.0.11 + animations: ^2.1.0 app_links: ^6.4.1 archive: ^4.0.7 async: ^2.11.0 @@ -91,7 +91,7 @@ dependencies: swipe_to_action: ^0.3.0 tor_detector_web: ^1.1.0 unifiedpush: ^6.2.0 - unifiedpush_ui: ^0.1.0 + unifiedpush_ui: ^0.2.0 universal_html: ^2.2.4 url_launcher: ^6.3.2 video_compress: ^3.1.4