Files
tatuchat/lib/config/setting_keys.dart
Welton Moura 762178d9f9
Some checks failed
Main Deploy Workflow / deploy_web (push) Has been cancelled
Main Deploy Workflow / deploy_playstore_internal (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled
feat: adiciona menu Desenvolvedor as configuracoes, nova modal Sobre e icones da marca TatuChat
- Submenu Desenvolvedor em Configuracoes (Sobre o servidor, Registros, Variaveis e ambiente)
- Modal Sobre maior com logo, descricao com link e rodape (codigo-fonte, licenca, fechar)
- Descricao legivel no tema dark
- Drawer com a logo do projeto e links de support/formulario atualizados
- flutter_launcher_icons: regenera icones Android (mipmap + adaptive), Web, iOS, macOS, Windows
2026-09-20 18:16:35 -03:00

269 lines
9.8 KiB
Dart

// SPDX-FileCopyrightText: 2019-Present Christian Kußowski
// SPDX-FileCopyrightText: 2019-Present Contributors to FluffyChat
//
// SPDX-License-Identifier: AGPL-3.0-or-later
import 'dart:convert';
import 'package:async/async.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:managed_configurations/managed_configurations.dart';
import 'package:matrix/matrix_api_lite/utils/logs.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tatchat/utils/platform_infos.dart';
enum AppSettings<T> {
textMessageMaxLength<int>('textMessageMaxLength', 16384),
/// Max lines for unselected HTML/text bubbles; 0 = unlimited (no fade).
messagePreviewMaxLines<int>('chat.fluffy.message_preview_max_lines', 50),
audioRecordingNumChannels<int>('audioRecordingNumChannels', 1),
audioRecordingAutoGain<bool>('audioRecordingAutoGain', true),
audioRecordingEchoCancel<bool>('audioRecordingEchoCancel', false),
audioRecordingNoiseSuppress<bool>('audioRecordingNoiseSuppress', true),
audioRecordingBitRate<int>('audioRecordingBitRate', 64000),
audioRecordingSamplingRate<int>('audioRecordingSamplingRate', 44100),
showNoGoogle<bool>('chat.fluffy.show_no_google', false),
unifiedPushRegistered<bool>('chat.fluffy.unifiedpush.registered', false),
unifiedPushEndpoint<String>('chat.fluffy.unifiedpush.endpoint', ''),
pushNotificationsGatewayUrl<String>(
'pushNotificationsGatewayUrl',
'https://push.tatuchat.weltonmoura.com.br/_matrix/push/v1/notify',
),
pushNotificationsPusherFormat<String>(
'pushNotificationsPusherFormat',
'event_id_only',
),
renderHtml<bool>('chat.fluffy.renderHtml', true),
fontSizeFactor<double>('chat.fluffy.font_size_factor', 1.0),
hideRedactedEvents<bool>('chat.fluffy.hideRedactedEvents', false),
hideUnknownEvents<bool>('chat.fluffy.hideUnknownEvents', true),
autoplayImages<bool>('chat.fluffy.autoplay_images', true),
sendTypingNotifications<bool>('chat.fluffy.send_typing_notifications', true),
sendPublicReadReceipts<bool>('chat.fluffy.send_public_read_receipts', true),
swipeRightToLeftToReply<bool>('chat.fluffy.swipeRightToLeftToReply', true),
sendOnEnter<bool>('chat.fluffy.send_on_enter', false),
displayNavigationRail<bool>('chat.fluffy.display_navigation_rail', false),
shareKeysWith<String>('chat.fluffy.share_keys_with_2', 'all'),
noEncryptionWarningShown<bool>(
'chat.fluffy.no_encryption_warning_shown',
false,
),
displayChatDetailsColumn('chat.fluffy.display_chat_details_column', false),
// AppConfig-mirrored settings
applicationName<String>('chat.fluffy.application_name', 'TatuChat'),
defaultHomeserver<String>(
'chat.fluffy.default_homeserver',
'https://matrix.weltonmoura.com.br',
),
// colorSchemeSeed stored as ARGB int
colorSchemeSeedInt<int>('chat.fluffy.color_scheme_seed', 0xFF00D084),
emojiSuggestionLocale<String>('emoji_suggestion_locale', ''),
enableSoftLogout<bool>('chat.fluffy.enable_soft_logout', true),
enableMatrixNativeOIDC<bool>('chat.fluffy.enable_matrix_native_oidc', true),
presetHomeserver<String>(
'chat.fluffy.preset_homeserver',
'https://matrix.weltonmoura.com.br',
),
welcomeText<String>('chat.fluffy.welcome_text', ''),
website<String>('chat.fluffy.website_url', 'https://tatuchat.weltonmoura.com.br/home'),
logoUrl<String>(
'chat.fluffy.logo_url',
'https://tatuchat.weltonmoura.com.br/assets/assets/logo/mini/logo_mini.png',
),
privacyPolicy<String>(
'chat.fluffy.privacy_policy_url',
'https://tatuchat.weltonmoura.com.br/home/politica-de-privacidade/',
),
tos<String>('chat.fluffy.tos_url', 'https://tatuchat.weltonmoura.com.br/tos'),
sendTimelineEventTimeout<int>('chat.fluffy.send_timeline_event_timeout', 15),
webNotificationSound<bool>('chat.fluffy.web_notification_sound', true),
chatFilter<String>('chat.fluffy.chat_filter', 'allChats'),
hideRoomsInSpaces<bool>('chat.fluffy.hideRoomsInSpaces', false),
showThumbnailsInTimeline<bool>('chat.fluffy.showThumbnailsInTimeline', true),
doubleTapToReact<bool>('chat.fluffy.double_tap_to_react', false),
doubleTapReaction<String>('chat.fluffy.double_tap_reaction', '❤️'),
benchmarksInLogs<bool>('chat.fluffy.benchmarks_in_logs', false),
autoSendErrorReports<bool?>('chat.fluffy.auto_send_eror_reports', null),
knownErrorHashes<List<String>>('chat.fluffy.known_crash_hashes', []),
customLiveKitInstance<String>('chat.fluffy.custom_live_kit_instance', ''),
/// URL of a CORS proxy used as a fallback when importing a mini app from a
/// URL on web, if the target server does not send CORS headers. Should end
/// with `?url=` or `?` (the target URL gets appended). Empty means disabled.
miniAppCorsProxy<String>('chat.fluffy.miniapp_cors_proxy', '');
final String key;
final T _defaultValue;
const AppSettings(this.key, this._defaultValue);
static SharedPreferences get store => _store!;
static SharedPreferences? _store;
static Map<String, Object?>? _platformConfiguration;
T get defaultValue {
final platformConfig = _platformConfiguration?[name];
if (platformConfig is T) return platformConfig;
return _defaultValue;
}
static Future<void> reset({bool loadWebConfigFile = true}) async {
await AppSettings._store!.clear();
await init(loadWebConfigFile: loadWebConfigFile);
}
static Future<SharedPreferences> init({bool loadWebConfigFile = true}) async {
if (AppSettings._store != null) return AppSettings.store;
final store = AppSettings._store = await SharedPreferences.getInstance();
// Migrate wrong datatype for fontSizeFactor
final fontSizeFactorString = Result(
() => store.getString(AppSettings.fontSizeFactor.key),
).asValue?.value;
if (fontSizeFactorString != null) {
Logs().i('Migrate wrong datatype for fontSizeFactor!');
await store.remove(AppSettings.fontSizeFactor.key);
final fontSizeFactor = double.tryParse(fontSizeFactorString);
if (fontSizeFactor != null) {
await store.setDouble(AppSettings.fontSizeFactor.key, fontSizeFactor);
}
}
if (store.getBool(AppSettings.sendOnEnter.key) == null) {
await store.setBool(AppSettings.sendOnEnter.key, !PlatformInfos.isMobile);
}
if (store.getBool(AppSettings.doubleTapToReact.key) == null) {
await store.setBool(
AppSettings.doubleTapToReact.key,
PlatformInfos.isMobile,
);
}
// Load configuration from config.json file or MDM:
if (PlatformInfos.isMobile) {
_platformConfiguration =
await ManagedConfigurations().getManagedConfigurations;
} else if (kIsWeb && loadWebConfigFile) {
try {
final configJsonString = utf8.decode(
(await http.get(Uri.parse('config.json'))).bodyBytes,
);
_platformConfiguration =
json.decode(configJsonString) as Map<String, Object?>;
} on FormatException catch (_) {
Logs().v('[ConfigLoader] config.json not found');
} catch (e) {
Logs().v('[ConfigLoader] config.json not found', e);
}
}
return store;
}
}
extension AppSettingsBoolNExtension on AppSettings<bool?> {
bool? get value {
final value = Result(() => AppSettings.store.getBool(key));
final error = value.asError;
if (error != null) {
Logs().e(
'Unable to fetch $key from storage. Removing entry...',
error.error,
error.stackTrace,
);
}
return value.asValue?.value;
}
Future<void> setItem(bool value) => AppSettings.store.setBool(key, value);
}
extension AppSettingsBoolExtension on AppSettings<bool> {
bool get value {
final value = Result(() => AppSettings.store.getBool(key));
final error = value.asError;
if (error != null) {
Logs().e(
'Unable to fetch $key from storage. Removing entry...',
error.error,
error.stackTrace,
);
}
return value.asValue?.value ?? defaultValue;
}
Future<void> setItem(bool value) => AppSettings.store.setBool(key, value);
}
extension AppSettingsStringExtension on AppSettings<String> {
String get value {
final value = Result(() => AppSettings.store.getString(key));
final error = value.asError;
if (error != null) {
Logs().e(
'Unable to fetch $key from storage. Removing entry...',
error.error,
error.stackTrace,
);
}
return value.asValue?.value ?? defaultValue;
}
Future<void> setItem(String value) => AppSettings.store.setString(key, value);
}
extension AppSettingsIntExtension on AppSettings<int> {
int get value {
final value = Result(() => AppSettings.store.getInt(key));
final error = value.asError;
if (error != null) {
Logs().e(
'Unable to fetch $key from storage. Removing entry...',
error.error,
error.stackTrace,
);
}
return value.asValue?.value ?? defaultValue;
}
Future<void> setItem(int value) => AppSettings.store.setInt(key, value);
}
extension AppSettingsDoubleExtension on AppSettings<double> {
double get value {
final value = Result(() => AppSettings.store.getDouble(key));
final error = value.asError;
if (error != null) {
Logs().e(
'Unable to fetch $key from storage. Removing entry...',
error.error,
error.stackTrace,
);
}
return value.asValue?.value ?? defaultValue;
}
Future<void> setItem(double value) => AppSettings.store.setDouble(key, value);
}
extension AppSettingsStringListExtension on AppSettings<List<String>> {
List<String> get value {
final value = Result(() => AppSettings.store.getStringList(key));
final error = value.asError;
if (error != null) {
Logs().e(
'Unable to fetch $key from storage. Removing entry...',
error.error,
error.stackTrace,
);
}
return value.asValue?.value ?? defaultValue;
}
Future<void> setItem(List<String> value) =>
AppSettings.store.setStringList(key, value);
}