2024-07-30 18:44:49 +00:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:get/get.dart';
|
2024-09-24 14:10:45 +00:00
|
|
|
import 'package:in_app_review/in_app_review.dart';
|
2024-07-31 14:48:22 +00:00
|
|
|
import 'package:provider/provider.dart';
|
2024-07-30 18:44:49 +00:00
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
2024-09-21 14:44:08 +00:00
|
|
|
import 'package:solian/exceptions/request.dart';
|
2024-07-30 18:44:49 +00:00
|
|
|
import 'package:solian/exts.dart';
|
2024-09-15 07:55:14 +00:00
|
|
|
import 'package:solian/platform.dart';
|
2024-09-21 14:44:08 +00:00
|
|
|
import 'package:solian/providers/auth.dart';
|
2024-09-15 04:25:50 +00:00
|
|
|
import 'package:solian/providers/database/database.dart';
|
2024-07-31 14:48:22 +00:00
|
|
|
import 'package:solian/providers/theme_switcher.dart';
|
2024-08-01 16:29:51 +00:00
|
|
|
import 'package:solian/router.dart';
|
2024-07-30 18:44:49 +00:00
|
|
|
import 'package:solian/theme.dart';
|
2024-09-21 14:44:08 +00:00
|
|
|
import 'package:solian/widgets/reports/abuse_report.dart';
|
2024-07-30 18:44:49 +00:00
|
|
|
|
|
|
|
class SettingScreen extends StatefulWidget {
|
|
|
|
const SettingScreen({super.key});
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<SettingScreen> createState() => _SettingScreenState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _SettingScreenState extends State<SettingScreen> {
|
2024-09-15 07:55:14 +00:00
|
|
|
SharedPreferences? _prefs;
|
2024-07-30 18:44:49 +00:00
|
|
|
|
|
|
|
Widget _buildCaptionHeader(String title) {
|
|
|
|
return Container(
|
|
|
|
width: MediaQuery.of(context).size.width,
|
|
|
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
|
|
|
child: Text(title),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildThemeColorButton(String label, Color color) {
|
|
|
|
return IconButton(
|
|
|
|
icon: Icon(Icons.circle, color: color),
|
|
|
|
tooltip: label,
|
|
|
|
onPressed: () {
|
2024-07-31 14:48:22 +00:00
|
|
|
context.read<ThemeSwitcher>().setTheme(
|
2024-09-13 12:22:10 +00:00
|
|
|
AppTheme.build(
|
2024-07-31 14:48:22 +00:00
|
|
|
Brightness.light,
|
|
|
|
seedColor: color,
|
|
|
|
),
|
2024-09-13 12:22:10 +00:00
|
|
|
AppTheme.build(
|
2024-07-31 14:48:22 +00:00
|
|
|
Brightness.dark,
|
|
|
|
seedColor: color,
|
|
|
|
),
|
|
|
|
);
|
2024-09-15 07:55:14 +00:00
|
|
|
_prefs?.setInt('global_theme_color', color.value);
|
2024-07-31 05:29:26 +00:00
|
|
|
context.clearSnackbar();
|
2024-07-30 18:44:49 +00:00
|
|
|
context.showSnackbar('themeColorApplied'.tr);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-07-31 05:29:26 +00:00
|
|
|
static final List<(String, Color)> _presentTheme = [
|
|
|
|
('themeColorRed', const Color.fromRGBO(154, 98, 91, 1)),
|
|
|
|
('themeColorBlue', const Color.fromRGBO(103, 96, 193, 1)),
|
|
|
|
('themeColorMiku', const Color.fromRGBO(56, 120, 126, 1)),
|
|
|
|
('themeColorKagamine', const Color.fromRGBO(244, 183, 63, 1)),
|
|
|
|
('themeColorLuka', const Color.fromRGBO(243, 174, 218, 1)),
|
|
|
|
];
|
|
|
|
|
2024-07-30 18:44:49 +00:00
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
SharedPreferences.getInstance().then((inst) {
|
|
|
|
_prefs = inst;
|
2024-09-15 07:55:14 +00:00
|
|
|
if (mounted) {
|
|
|
|
setState(() {});
|
|
|
|
}
|
2024-07-30 18:44:49 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Material(
|
|
|
|
color: Theme.of(context).colorScheme.surface,
|
|
|
|
child: ListView(
|
|
|
|
children: [
|
|
|
|
_buildCaptionHeader('themeColor'.tr),
|
|
|
|
SizedBox(
|
|
|
|
height: 56,
|
|
|
|
child: ListView(
|
|
|
|
scrollDirection: Axis.horizontal,
|
2024-07-31 05:29:26 +00:00
|
|
|
children: _presentTheme
|
|
|
|
.map((x) => _buildThemeColorButton(x.$1, x.$2))
|
|
|
|
.toList(),
|
2024-07-30 18:44:49 +00:00
|
|
|
).paddingSymmetric(horizontal: 12, vertical: 8),
|
|
|
|
),
|
2024-09-15 07:55:14 +00:00
|
|
|
_buildCaptionHeader('notification'.tr),
|
|
|
|
Tooltip(
|
|
|
|
message: 'settingsNotificationBgServiceDesc'.tr,
|
|
|
|
child: CheckboxListTile(
|
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 22),
|
|
|
|
secondary: const Icon(Icons.system_security_update_warning),
|
|
|
|
enabled: PlatformInfo.isAndroid,
|
|
|
|
title: Text('settingsNotificationBgService'.tr),
|
|
|
|
subtitle: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Text('holdToSeeDetail'.tr),
|
|
|
|
Text(
|
|
|
|
'needRestartToApply'.tr,
|
|
|
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
|
|
|
)
|
|
|
|
],
|
|
|
|
),
|
|
|
|
value:
|
|
|
|
_prefs?.getBool('service_background_notification') ?? false,
|
|
|
|
onChanged: (value) {
|
|
|
|
_prefs
|
|
|
|
?.setBool('service_background_notification', value ?? false)
|
|
|
|
.then((_) {
|
|
|
|
setState(() {});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
2024-09-17 13:37:20 +00:00
|
|
|
_buildCaptionHeader('update'.tr),
|
|
|
|
CheckboxListTile(
|
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 22),
|
|
|
|
secondary: const Icon(Icons.sync_alt),
|
|
|
|
title: Text('updateCheckStrictly'.tr),
|
|
|
|
subtitle: Text('updateCheckStrictlyDesc'.tr),
|
|
|
|
value: _prefs?.getBool('check_update_strictly') ?? false,
|
|
|
|
onChanged: (value) {
|
|
|
|
_prefs
|
|
|
|
?.setBool('check_update_strictly', value ?? false)
|
|
|
|
.then((_) {
|
|
|
|
setState(() {});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
),
|
2024-09-21 14:44:08 +00:00
|
|
|
Obx(() {
|
|
|
|
final AuthProvider auth = Get.find<AuthProvider>();
|
|
|
|
if (!auth.isAuthorized.value) return const SizedBox.shrink();
|
|
|
|
return Column(
|
|
|
|
children: [
|
|
|
|
_buildCaptionHeader('account'.tr),
|
|
|
|
ListTile(
|
|
|
|
leading: const Icon(Icons.flag),
|
|
|
|
trailing: const Icon(Icons.chevron_right),
|
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 22),
|
|
|
|
title: Text('reportAbuse'.tr),
|
|
|
|
subtitle: Text('reportAbuseDesc'.tr),
|
|
|
|
onTap: () {
|
|
|
|
showDialog(
|
|
|
|
context: context,
|
|
|
|
builder: (context) => const AbuseReportDialog(),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
ListTile(
|
|
|
|
leading: const Icon(Icons.person_remove),
|
|
|
|
trailing: const Icon(Icons.chevron_right),
|
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 22),
|
|
|
|
title: Text('accountDeletion'.tr),
|
|
|
|
subtitle: Text('accountDeletionDesc'.tr),
|
|
|
|
onTap: () {
|
|
|
|
context
|
|
|
|
.showSlideToConfirmDialog(
|
|
|
|
'accountDeletionConfirm'.tr,
|
|
|
|
'accountDeletionConfirmDesc'.trParams({
|
|
|
|
'account': '@${auth.userProfile.value!['name']}',
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
.then((value) async {
|
|
|
|
if (value != true) return;
|
|
|
|
final client = await auth.configureClient('id');
|
|
|
|
final resp = await client.post('/users/me/deletion', {});
|
|
|
|
if (resp.statusCode != 200) {
|
|
|
|
context.showErrorDialog(RequestException(resp));
|
|
|
|
} else {
|
|
|
|
context.showSnackbar('accountDeletionRequested'.tr);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}),
|
2024-10-06 09:31:44 +00:00
|
|
|
_buildCaptionHeader('performance'.tr),
|
|
|
|
CheckboxListTile(
|
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 22),
|
|
|
|
secondary: const Icon(Icons.message),
|
|
|
|
title: Text('animatedMessageList'.tr),
|
|
|
|
subtitle: Text('animatedMessageListDesc'.tr),
|
|
|
|
value: _prefs?.getBool('non_animated_message_list') ?? false,
|
|
|
|
onChanged: (value) {
|
|
|
|
_prefs
|
|
|
|
?.setBool('non_animated_message_list', value ?? false)
|
|
|
|
.then((_) {
|
|
|
|
setState(() {});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
),
|
2024-08-01 16:29:51 +00:00
|
|
|
_buildCaptionHeader('more'.tr),
|
2024-09-13 15:19:33 +00:00
|
|
|
ListTile(
|
2024-09-15 04:25:50 +00:00
|
|
|
leading: const Icon(Icons.delete_sweep),
|
2024-09-13 15:19:33 +00:00
|
|
|
trailing: const Icon(Icons.chevron_right),
|
2024-09-15 04:25:50 +00:00
|
|
|
subtitle: FutureBuilder(
|
|
|
|
future: AppDatabase.getDatabaseSize(),
|
|
|
|
builder: (context, snapshot) {
|
|
|
|
if (!snapshot.hasData) {
|
|
|
|
return Text('localDatabaseSize'.trParams(
|
|
|
|
{'size': 'unknown'.tr},
|
|
|
|
));
|
|
|
|
}
|
|
|
|
return Text('localDatabaseSize'.trParams(
|
|
|
|
{'size': snapshot.data!.formatBytes()},
|
|
|
|
));
|
|
|
|
},
|
|
|
|
),
|
2024-09-13 15:19:33 +00:00
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 22),
|
2024-09-15 04:25:50 +00:00
|
|
|
title: Text('localDatabaseWipe'.tr),
|
2024-09-13 15:19:33 +00:00
|
|
|
onTap: () {
|
2024-09-15 04:25:50 +00:00
|
|
|
AppDatabase.removeDatabase().then((_) {
|
|
|
|
setState(() {});
|
|
|
|
});
|
2024-09-13 15:19:33 +00:00
|
|
|
},
|
|
|
|
),
|
2024-09-24 14:10:45 +00:00
|
|
|
if (PlatformInfo.canRateTheApp)
|
|
|
|
ListTile(
|
|
|
|
leading: const Icon(Icons.star),
|
|
|
|
trailing: const Icon(Icons.chevron_right),
|
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 22),
|
|
|
|
title: Text('rateTheApp'.tr),
|
|
|
|
subtitle: Text('rateTheAppDesc'.tr),
|
|
|
|
onTap: () {
|
|
|
|
final inAppReview = InAppReview.instance;
|
|
|
|
|
|
|
|
inAppReview.openStoreListing(
|
|
|
|
appStoreId: '6499032345',
|
|
|
|
);
|
|
|
|
},
|
|
|
|
),
|
2024-09-13 15:19:33 +00:00
|
|
|
ListTile(
|
2024-09-15 04:25:50 +00:00
|
|
|
leading: const Icon(Icons.info_outline),
|
2024-09-13 15:19:33 +00:00
|
|
|
trailing: const Icon(Icons.chevron_right),
|
|
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 22),
|
2024-09-15 04:25:50 +00:00
|
|
|
title: Text('about'.tr),
|
2024-09-13 15:19:33 +00:00
|
|
|
onTap: () {
|
2024-09-15 04:25:50 +00:00
|
|
|
AppRouter.instance.pushNamed('about');
|
2024-09-13 15:19:33 +00:00
|
|
|
},
|
|
|
|
),
|
2024-07-30 18:44:49 +00:00
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|