diff --git a/.fvmrc b/.fvmrc deleted file mode 100644 index c300356..0000000 --- a/.fvmrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "flutter": "stable" -} \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 1ce0185..0000000 --- a/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# surface - -A new Flutter project. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/assets/translations/en-US.json b/assets/translations/en-US.json index 7c9194a..4772d77 100644 --- a/assets/translations/en-US.json +++ b/assets/translations/en-US.json @@ -190,6 +190,13 @@ "settingsNetworkServerPreset": "Present HyperNet Server", "settingsNetworkServerPresetDescription": "You can choose one of our preset HyperNet server addresses from the list on the right.", "settingsNetworkServerSaved": "Server address saved.", + "settingsPerformance": "Performance", + "settingsImageQuality": "Image Quality", + "settingsImageQualityDescription": "Set the image quality, it will affect the decoding speed of the image.", + "settingsImageQualityLowest": "Lowest", + "settingsImageQualityLow": "Low", + "settingsImageQualityMedium": "Medium", + "settingsImageQualityHigh": "High", "settingsMisc": "Misc", "settingsMiscAbout": "About", "settingsMiscAboutDescription": "View the version information of Solian.", diff --git a/assets/translations/zh-CN.json b/assets/translations/zh-CN.json index 4b486c6..1684cb7 100644 --- a/assets/translations/zh-CN.json +++ b/assets/translations/zh-CN.json @@ -188,6 +188,13 @@ "settingsNetworkServerPreset": "预设的 HyperNet 服务器", "settingsNetworkServerPresetDescription": "你可以在旁边的列表中选择我们提供的预设 HyperNet 服务器地址。", "settingsNetworkServerSaved": "服务器地址已保存。", + "settingsPerformance": "性能", + "settingsImageQuality": "图片预览质量", + "settingsImageQualityDescription": "设置图片预览质量,会影响图片解码速度。", + "settingsImageQualityLowest": "极低", + "settingsImageQualityLow": "低", + "settingsImageQualityMedium": "中", + "settingsImageQualityHigh": "高", "settingsMisc": "杂项", "settingsMiscAbout": "关于", "settingsMiscAboutDescription": "查看 Solian 的版本信息。", diff --git a/assets/translations/zh-HK.json b/assets/translations/zh-HK.json index 66b6b3a..d86dfb1 100644 --- a/assets/translations/zh-HK.json +++ b/assets/translations/zh-HK.json @@ -188,6 +188,13 @@ "settingsNetworkServerPreset": "預設的 HyperNet 服務器", "settingsNetworkServerPresetDescription": "你可以在旁邊的列表中選擇我們提供的預設 HyperNet 服務器地址。", "settingsNetworkServerSaved": "服務器地址已保存。", + "settingsPerformance": "性能", + "settingsImageQuality": "圖片預覽質量", + "settingsImageQualityDescription": "設置圖片預覽質量,會影響圖片解碼速度。", + "settingsImageQualityLowest": "極低", + "settingsImageQualityLow": "低", + "settingsImageQualityMedium": "中", + "settingsImageQualityHigh": "高", "settingsMisc": "雜項", "settingsMiscAbout": "關於", "settingsMiscAboutDescription": "查看 Solian 的版本信息。", @@ -441,5 +448,10 @@ "postImageShareReadMore": "掃描右側 QRCode 查看全文", "postImageShareAds": "來 Solar Network 探索更多有趣帖子", "postShare": "分享", - "postShareImage": "分享帖圖" + "postShareImage": "分享帖圖", + "appInitializing": "正在初始化", + "poweredBy": "由 {} 提供支持", + "shareIntent": "分享", + "shareIntentDescription": "您想對您分享的內容做些什麼?", + "shareIntentPostStory": "發佈動態" } diff --git a/assets/translations/zh-TW.json b/assets/translations/zh-TW.json index 9ce3f6b..7eb24ca 100644 --- a/assets/translations/zh-TW.json +++ b/assets/translations/zh-TW.json @@ -188,6 +188,13 @@ "settingsNetworkServerPreset": "預設的 HyperNet 伺服器", "settingsNetworkServerPresetDescription": "你可以在旁邊的列表中選擇我們提供的預設 HyperNet 伺服器地址。", "settingsNetworkServerSaved": "伺服器地址已儲存。", + "settingsPerformance": "效能", + "settingsImageQuality": "圖片預覽質量", + "settingsImageQualityDescription": "設定圖片預覽質量,會影響圖片解碼速度。", + "settingsImageQualityLowest": "極低", + "settingsImageQualityLow": "低", + "settingsImageQualityMedium": "中", + "settingsImageQualityHigh": "高", "settingsMisc": "雜項", "settingsMiscAbout": "關於", "settingsMiscAboutDescription": "檢視 Solian 的版本資訊。", @@ -441,5 +448,10 @@ "postImageShareReadMore": "掃描右側 QRCode 檢視全文", "postImageShareAds": "來 Solar Network 探索更多有趣帖子", "postShare": "分享", - "postShareImage": "分享帖圖" + "postShareImage": "分享帖圖", + "appInitializing": "正在初始化", + "poweredBy": "由 {} 提供支援", + "shareIntent": "分享", + "shareIntentDescription": "您想對您分享的內容做些什麼?", + "shareIntentPostStory": "釋出動態" } diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 389a36b..73996e1 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -355,7 +355,7 @@ SPEC CHECKSUMS: flutter_native_splash: e8a1e01082d97a8099d973f919f57904c925008a flutter_udid: a2482c67a61b9c806ef59dd82ed8d007f1b7ac04 flutter_webrtc: 1a53bd24f97bcfeff512f13699e721897f261563 - gal: 61e868295d28fe67ffa297fae6dacebf56fd53e1 + gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5 GoogleAppMeasurement: 987769c4ca6b968f2479fbcc9fe3ce34af454b8e GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d diff --git a/lib/main.dart b/lib/main.dart index d86ecf5..b287cd4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,6 +18,7 @@ import 'package:styled_widget/styled_widget.dart'; import 'package:surface/firebase_options.dart'; import 'package:surface/providers/channel.dart'; import 'package:surface/providers/chat_call.dart'; +import 'package:surface/providers/config.dart'; import 'package:surface/providers/link_preview.dart'; import 'package:surface/providers/navigation.dart'; import 'package:surface/providers/notification.dart'; @@ -85,6 +86,8 @@ class SolianApp extends StatelessWidget { assetLoader: JsonAssetLoader(), child: MultiProvider( providers: [ + Provider(create: (ctx) => ConfigProvider()), + // Display layer ChangeNotifierProvider(create: (_) => ThemeProvider()), ChangeNotifierProvider(create: (ctx) => NavigationProvider()), @@ -162,10 +165,11 @@ class _AppSplashScreen extends StatefulWidget { class _AppSplashScreenState extends State<_AppSplashScreen> { bool _isReady = false; - late StreamSubscription _shareIntentSubscription; - Future _initialize() async { try { + final config = context.read(); + await config.initialize(); + if (!mounted) return; final home = context.read(); await home.initialize(); if (!mounted) return; diff --git a/lib/providers/config.dart b/lib/providers/config.dart new file mode 100644 index 0000000..14b8f62 --- /dev/null +++ b/lib/providers/config.dart @@ -0,0 +1,22 @@ +import 'dart:ui'; + +import 'package:shared_preferences/shared_preferences.dart'; + +const Map kImageQualityLevel = { + 'settingsImageQualityLowest': FilterQuality.none, + 'settingsImageQualityLow': FilterQuality.low, + 'settingsImageQualityMedium': FilterQuality.medium, + 'settingsImageQualityHigh': FilterQuality.high, +}; + +class ConfigProvider { + late final SharedPreferences prefs; + + Future initialize() async { + prefs = await SharedPreferences.getInstance(); + } + + FilterQuality get imageQuality { + return kImageQualityLevel.values.elementAtOrNull(prefs.getInt('app_image_quality') ?? 3) ?? FilterQuality.high; + } +} diff --git a/lib/providers/userinfo.dart b/lib/providers/userinfo.dart index 8785435..1039425 100644 --- a/lib/providers/userinfo.dart +++ b/lib/providers/userinfo.dart @@ -1,7 +1,6 @@ import 'dart:developer'; import 'package:flutter/material.dart'; -import 'package:home_widget/home_widget.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:surface/providers/sn_network.dart'; diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index 219a17e..b7920f1 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -1,10 +1,10 @@ import 'dart:io'; +import 'package:collection/collection.dart'; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import 'package:material_symbols_icons/symbols.dart'; @@ -12,6 +12,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:styled_widget/styled_widget.dart'; +import 'package:surface/providers/config.dart'; import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/theme.dart'; import 'package:surface/theme.dart'; @@ -25,7 +26,7 @@ class SettingsScreen extends StatefulWidget { } class _SettingsScreenState extends State { - SharedPreferences? _prefs; + late final SharedPreferences _prefs; String _docBasepath = '/'; final TextEditingController _serverUrlController = TextEditingController(); @@ -39,12 +40,7 @@ class _SettingsScreenState extends State { setState(() {}); } }); - SharedPreferences.getInstance().then((prefs) { - setState(() { - _prefs = prefs; - _serverUrlController.text = prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault; - }); - }); + _prefs = context.read().prefs; } @override @@ -60,6 +56,7 @@ class _SettingsScreenState extends State { return Scaffold( body: SingleChildScrollView( child: Column( + spacing: 16, crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( @@ -78,7 +75,7 @@ class _SettingsScreenState extends State { if (image == null) return; await File(image.path).copy('$_docBasepath/app_background_image'); - _prefs?.setBool('has_background_image', true); + _prefs.setBool('has_background_image', true); setState(() {}); }, @@ -99,29 +96,28 @@ class _SettingsScreenState extends State { trailing: const Icon(Symbols.chevron_right), onTap: () { File('$_docBasepath/app_background_image').deleteSync(); - _prefs?.remove('has_background_image'); + _prefs.remove('has_background_image'); setState(() {}); }, ); }), - if (_prefs != null) - CheckboxListTile( - title: Text('settingsThemeMaterial3').tr(), - subtitle: Text('settingsThemeMaterial3Description').tr(), - contentPadding: const EdgeInsets.only(left: 24, right: 17), - secondary: const Icon(Symbols.new_releases), - value: _prefs!.getBool(kMaterialYouToggleStoreKey) ?? false, - onChanged: (value) { - setState(() { - _prefs!.setBool( - kMaterialYouToggleStoreKey, - value ?? false, - ); - }); - final th = context.watch(); - th.reloadTheme(useMaterial3: value ?? false); - }, - ), + CheckboxListTile( + title: Text('settingsThemeMaterial3').tr(), + subtitle: Text('settingsThemeMaterial3Description').tr(), + contentPadding: const EdgeInsets.only(left: 24, right: 17), + secondary: const Icon(Symbols.new_releases), + value: _prefs.getBool(kMaterialYouToggleStoreKey) ?? false, + onChanged: (value) { + setState(() { + _prefs.setBool( + kMaterialYouToggleStoreKey, + value ?? false, + ); + }); + final th = context.watch(); + th.reloadTheme(useMaterial3: value ?? false); + }, + ), ], ), Column( @@ -139,7 +135,7 @@ class _SettingsScreenState extends State { icon: const Icon(Symbols.save), onPressed: () { sn.setBaseUrl(_serverUrlController.text); - _prefs?.setString( + _prefs.setString( kNetworkServerStoreKey, _serverUrlController.text, ); @@ -182,7 +178,7 @@ class _SettingsScreenState extends State { onChanged: (String? value) { if (value == null) return; _serverUrlController.text = value; - _prefs?.setString(kNetworkServerStoreKey, value); + _prefs.setString(kNetworkServerStoreKey, value); context.showSnackbar('settingsNetworkServerSaved'.tr()); setState(() {}); }, @@ -208,13 +204,56 @@ class _SettingsScreenState extends State { trailing: const Icon(Symbols.chevron_right), onTap: () { _serverUrlController.text = kNetworkServerDefault; - _prefs?.remove(kNetworkServerStoreKey); + _prefs.remove(kNetworkServerStoreKey); context.showSnackbar('settingsNetworkServerSaved'.tr()); setState(() {}); }, ), ], ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('settingsPerformance').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), + ListTile( + title: Text('settingsImageQuality').tr(), + subtitle: Text('settingsImageQualityDescription').tr(), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + leading: const Icon(Symbols.image), + trailing: DropdownButtonHideUnderline( + child: DropdownButton2( + value: kImageQualityLevel.values.elementAtOrNull(_prefs.getInt('app_image_quality') ?? 3) ?? + FilterQuality.high, + isExpanded: true, + items: kImageQualityLevel.entries + .map( + (item) => DropdownMenuItem( + value: item.value, + child: Text(item.key).tr().fontSize(14), + ), + ) + .toList(), + onChanged: (FilterQuality? value) { + if (value == null) return; + _prefs.setInt('app_image_quality', kImageQualityLevel.values.toList().indexOf(value)); + setState(() {}); + }, + buttonStyleData: const ButtonStyleData( + padding: EdgeInsets.symmetric( + horizontal: 16, + vertical: 5, + ), + height: 40, + width: 160, + ), + menuItemStyleData: const MenuItemStyleData( + height: 60, + ), + ), + ), + ), + ], + ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -231,7 +270,7 @@ class _SettingsScreenState extends State { ), ], ), - ].expand((ele) => [ele, const Gap(16)]).toList(), + ], ).padding(vertical: 20), ), ); diff --git a/lib/widgets/post/post_item.dart b/lib/widgets/post/post_item.dart index 5dc98e6..36835b6 100644 --- a/lib/widgets/post/post_item.dart +++ b/lib/widgets/post/post_item.dart @@ -458,6 +458,7 @@ class _PostBottomAction extends StatelessWidget { children: [ if (showReactions || showComments) Row( + spacing: 8, children: [ if (showReactions) InkWell( @@ -523,8 +524,7 @@ class _PostBottomAction extends StatelessWidget { ); }, ), - ].expand((ele) => [ele, const Gap(8)]).toList() - ..removeLast(), + ], ), InkWell( onTap: onShare, diff --git a/lib/widgets/universal_image.dart b/lib/widgets/universal_image.dart index 600776a..9bd8a56 100644 --- a/lib/widgets/universal_image.dart +++ b/lib/widgets/universal_image.dart @@ -4,9 +4,13 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:material_symbols_icons/symbols.dart'; +import 'package:provider/provider.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:flutter_animate/flutter_animate.dart'; + +// Keep this import to make the web image render work import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart'; +import 'package:surface/providers/config.dart'; class UniversalImage extends StatelessWidget { final String url; @@ -15,7 +19,7 @@ class UniversalImage extends StatelessWidget { final bool noProgressIndicator; final bool noErrorWidget; final double? cacheWidth, cacheHeight; - final FilterQuality filterQuality; + final FilterQuality? filterQuality; const UniversalImage( this.url, { @@ -27,7 +31,7 @@ class UniversalImage extends StatelessWidget { this.noErrorWidget = false, this.cacheWidth, this.cacheHeight, - this.filterQuality = FilterQuality.high, + this.filterQuality, }); @override @@ -37,7 +41,7 @@ class UniversalImage extends StatelessWidget { final double? resizeWidth = cacheWidth != null ? (cacheWidth! * devicePixelRatio) : null; return Image( - filterQuality: filterQuality, + filterQuality: filterQuality ?? context.read().imageQuality, image: kIsWeb ? UniversalImage.provider(url) : ResizeImage( diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 910e066..d378ea2 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -289,7 +289,7 @@ SPEC CHECKSUMS: flutter_udid: 6b2b89780c3dfeecf0047bdf93f622d6416b1c07 flutter_webrtc: 53c9e1285ab32dfb58afb1e1471416a877e23d7a FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - gal: 61e868295d28fe67ffa297fae6dacebf56fd53e1 + gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5 GoogleAppMeasurement: 987769c4ca6b968f2479fbcc9fe3ce34af454b8e GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d diff --git a/pubspec.yaml b/pubspec.yaml index 95457a6..8bbcd5f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 2.1.1+32 +version: 2.1.1+33 environment: sdk: ^3.5.4