⚡ Allow setting image preview quality
This commit is contained in:
@ -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<void> _initialize() async {
|
||||
try {
|
||||
final config = context.read<ConfigProvider>();
|
||||
await config.initialize();
|
||||
if (!mounted) return;
|
||||
final home = context.read<HomeWidgetProvider>();
|
||||
await home.initialize();
|
||||
if (!mounted) return;
|
||||
|
22
lib/providers/config.dart
Normal file
22
lib/providers/config.dart
Normal file
@ -0,0 +1,22 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
const Map<String, FilterQuality> kImageQualityLevel = {
|
||||
'settingsImageQualityLowest': FilterQuality.none,
|
||||
'settingsImageQualityLow': FilterQuality.low,
|
||||
'settingsImageQualityMedium': FilterQuality.medium,
|
||||
'settingsImageQualityHigh': FilterQuality.high,
|
||||
};
|
||||
|
||||
class ConfigProvider {
|
||||
late final SharedPreferences prefs;
|
||||
|
||||
Future<void> initialize() async {
|
||||
prefs = await SharedPreferences.getInstance();
|
||||
}
|
||||
|
||||
FilterQuality get imageQuality {
|
||||
return kImageQualityLevel.values.elementAtOrNull(prefs.getInt('app_image_quality') ?? 3) ?? FilterQuality.high;
|
||||
}
|
||||
}
|
@ -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';
|
||||
|
@ -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<SettingsScreen> {
|
||||
SharedPreferences? _prefs;
|
||||
late final SharedPreferences _prefs;
|
||||
String _docBasepath = '/';
|
||||
|
||||
final TextEditingController _serverUrlController = TextEditingController();
|
||||
@ -39,12 +40,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
SharedPreferences.getInstance().then((prefs) {
|
||||
setState(() {
|
||||
_prefs = prefs;
|
||||
_serverUrlController.text = prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
|
||||
});
|
||||
});
|
||||
_prefs = context.read<ConfigProvider>().prefs;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -60,6 +56,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
return Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
@ -78,7 +75,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
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<SettingsScreen> {
|
||||
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<ThemeProvider>();
|
||||
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<ThemeProvider>();
|
||||
th.reloadTheme(useMaterial3: value ?? false);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
@ -139,7 +135,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
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<SettingsScreen> {
|
||||
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<SettingsScreen> {
|
||||
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<FilterQuality>(
|
||||
value: kImageQualityLevel.values.elementAtOrNull(_prefs.getInt('app_image_quality') ?? 3) ??
|
||||
FilterQuality.high,
|
||||
isExpanded: true,
|
||||
items: kImageQualityLevel.entries
|
||||
.map(
|
||||
(item) => DropdownMenuItem<FilterQuality>(
|
||||
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<SettingsScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
].expand((ele) => [ele, const Gap(16)]).toList(),
|
||||
],
|
||||
).padding(vertical: 20),
|
||||
),
|
||||
);
|
||||
|
@ -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,
|
||||
|
@ -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<ConfigProvider>().imageQuality,
|
||||
image: kIsWeb
|
||||
? UniversalImage.provider(url)
|
||||
: ResizeImage(
|
||||
|
Reference in New Issue
Block a user