the-mews/app/lib/presentation/reader/settings_sheet.dart

193 lines
7.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart' show SignalBuilder;
import '../../application/controllers/reader_settings_controller.dart';
import '../../application/internal_pages.dart';
import '../app.dart';
import 'reader_fonts.dart';
/// Opens the reader settings bottom sheet: fonts, text size, theme, colors,
/// bookmark and subscription for the current page.
void showReaderSettingsSheet(BuildContext context) {
showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
builder: (context) => const SettingsSheet(),
);
}
class SettingsSheet extends StatelessWidget {
const SettingsSheet({super.key});
@override
Widget build(BuildContext context) {
final scope = AppScope.of(context);
final deps = scope.deps;
final strings = scope.strings;
return SignalBuilder(builder: (context) {
final settings = deps.settings;
final library = deps.library;
final page = deps.browser.currentPage.value;
final url = page?.url;
final percent = (settings.fontScale.value * 100).round();
return SafeArea(
child: ListView(
shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(0, 12, 0, 16),
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
strings.t('settings-sheet-title'),
style: Theme.of(context).textTheme.titleMedium,
),
),
// The library pages are internal Mews pages; opening one joins
// the walk history like any other page.
ListTile(
leading: const Icon(Icons.history),
title: Text(strings.t('settings-history')),
onTap: () => _openPage(context, InternalPages.historyUrl),
),
ListTile(
leading: const Icon(Icons.star_outline),
title: Text(strings.t('settings-bookmarks')),
onTap: () => _openPage(context, InternalPages.bookmarksUrl),
),
ListTile(
leading: const Icon(Icons.notifications_outlined),
title: Text(strings.t('settings-subscriptions')),
onTap: () => _openPage(context, InternalPages.subscriptionsUrl),
),
const Divider(),
_heading(context, strings.t('settings-font-heading')),
RadioGroup<String>(
groupValue: settings.font.value,
onChanged: (value) => settings.setFont(value!),
child: Column(
children: [
for (final font in readerFonts)
RadioListTile<String>(
value: font.family,
title: Text(strings.t(font.labelId)),
),
],
),
),
_heading(context, strings.t('settings-size-heading')),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
tooltip: strings.t('settings-size-decrease'),
onPressed: settings.fontScale.value > ReaderSettingsController.minScale
? settings.decreaseSize
: null,
icon: const Icon(Icons.remove),
),
SizedBox(
width: 56,
child: Text(
'$percent%',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium,
),
),
IconButton(
tooltip: strings.t('settings-size-increase'),
onPressed: settings.fontScale.value < ReaderSettingsController.maxScale
? settings.increaseSize
: null,
icon: const Icon(Icons.add),
),
],
),
_heading(context, strings.t('settings-theme-heading')),
RadioGroup<String>(
groupValue: settings.themeMode.value,
onChanged: (value) => settings.setThemeMode(value!),
child: Column(
children: [
for (final (mode, id) in [
('light', 'settings-theme-light'),
('dark', 'settings-theme-dark'),
('system', 'settings-theme-system'),
])
RadioListTile<String>(
value: mode,
title: Text(strings.t(id)),
),
],
),
),
_heading(context, strings.t('settings-variant-heading')),
RadioGroup<String>(
groupValue: settings.variant.value,
onChanged: (value) => settings.setVariant(value!),
child: Column(
children: [
for (final (variant, id) in [
('site', 'settings-variant-site'),
('warm', 'settings-variant-warm'),
('cool', 'settings-variant-cool'),
('green', 'settings-variant-green'),
('mono', 'settings-variant-mono'),
])
RadioListTile<String>(
value: variant,
title: Text(strings.t(id)),
),
],
),
),
const Divider(),
if (url != null && !InternalPages.isInternal(url)) ...[
ListTile(
leading: Icon(library.isBookmarked(url) ? Icons.star : Icons.star_border),
title: Text(
library.isBookmarked(url)
? strings.t('settings-bookmark-remove')
: strings.t('settings-bookmark-add'),
),
onTap: () => library.toggleBookmark(url, page?.title),
),
ListTile(
leading: Icon(library.isSubscribed(url)
? Icons.notifications_active
: Icons.notifications_outlined),
title: Text(
library.isSubscribed(url)
? strings.t('settings-unsubscribe')
: strings.t('settings-subscribe'),
),
subtitle: library.isSubscribed(url)
? Text(strings.t('settings-subscribed-notice'))
: null,
onTap: () => library.toggleSubscription(url, page?.title, page?.entries ?? const []),
),
],
],
),
);
});
}
void _openPage(BuildContext context, String url) {
Navigator.of(context).pop();
AppScope.of(context).deps.browser.open(url);
}
Widget _heading(BuildContext context, String text) => Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 4),
child: Text(
text,
style: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(color: Theme.of(context).colorScheme.primary),
),
);
}