subtracks/lib/app/screens/settings_screen.dart
2025-11-22 11:33:40 +09:00

414 lines
12 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../l10n/generated/app_localizations.dart';
import '../state/database.dart';
import '../state/source.dart';
import '../ui/text.dart';
const kHorizontalPadding = 18.0;
class SettingsScreen extends HookConsumerWidget {
const SettingsScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final l = AppLocalizations.of(context);
return Scaffold(
appBar: AppBar(
title: TextH1(l.navigationTabsSettings),
),
body: ListView(
children: [
// const SizedBox(height: 96),
_SectionHeader(l.settingsServersName),
const _Sources(),
// _SectionHeader(l.settingsNetworkName),
// const _Network(),
// _SectionHeader(l.settingsAboutName),
// _About(),
],
),
);
}
}
class _Section extends StatelessWidget {
const _Section({required this.children});
final List<Widget> children;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
...children,
const SizedBox(height: 32),
],
);
}
}
class _SectionHeader extends StatelessWidget {
const _SectionHeader(this.title);
final String title;
@override
Widget build(BuildContext context) {
return Column(
children: [
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: kHorizontalPadding),
child: TextH2(title),
),
),
],
);
}
}
// class _Network extends StatelessWidget {
// const _Network();
// @override
// Widget build(BuildContext context) {
// return const _Section(
// children: [
// _OfflineMode(),
// _MaxBitrateWifi(),
// _MaxBitrateMobile(),
// _StreamFormat(),
// ],
// );
// }
// }
// class _About extends HookConsumerWidget {
// _About();
// final _homepage = Uri.parse('https://github.com/austinried/subtracks');
// final _donate = Uri.parse('https://ko-fi.com/austinried');
// @override
// Widget build(BuildContext context, WidgetRef ref) {
// final l = AppLocalizations.of(context);
// final pkg = ref.watch(packageInfoProvider).requireValue;
// return _Section(
// children: [
// ListTile(
// title: const Text('subtracks'),
// subtitle: Text(l.settingsAboutVersion(pkg.version)),
// ),
// ListTile(
// title: Text(l.settingsAboutActionsLicenses),
// // trailing: const Icon(Icons.open_in_new_rounded),
// onTap: () {},
// ),
// ListTile(
// title: Text(l.settingsAboutActionsProjectHomepage),
// subtitle: Text(_homepage.toString()),
// trailing: const Icon(Icons.open_in_new_rounded),
// onTap: () => launchUrl(
// _homepage,
// mode: LaunchMode.externalApplication,
// ),
// ),
// ListTile(
// title: Text(l.settingsAboutActionsSupport),
// subtitle: Text(_donate.toString()),
// trailing: const Icon(Icons.open_in_new_rounded),
// onTap: () => launchUrl(
// _donate,
// mode: LaunchMode.externalApplication,
// ),
// ),
// const SizedBox(height: 12),
// const _ShareLogsButton(),
// ],
// );
// }
// }
// class _ShareLogsButton extends StatelessWidget {
// const _ShareLogsButton();
// @override
// Widget build(BuildContext context) {
// final l = AppLocalizations.of(context);
// return Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// OutlinedButton.icon(
// icon: const Icon(Icons.share),
// label: Text(l.settingsAboutShareLogs),
// onPressed: () async {
// final files = await logFiles();
// if (files.isEmpty) return;
// // ignore: use_build_context_synchronously
// final value = await showDialog<String>(
// context: context,
// builder: (context) => MultipleChoiceDialog<String>(
// title: l.settingsAboutChooseLog,
// current: files.first.path,
// options: files
// .map(
// (e) => MultiChoiceOption.string(
// title: path.basename(e.path),
// option: e.path,
// ),
// )
// .toIList(),
// ),
// );
// if (value == null) return;
// Share.shareXFiles(
// [XFile(value, mimeType: 'text/plain')],
// subject:
// 'Logs from subtracks: ${String.fromCharCodes(
// List.generate(8, (_) => Random().nextInt(26) + 65),
// )}',
// );
// },
// ),
// ],
// );
// }
// }
// class _MaxBitrateWifi extends HookConsumerWidget {
// const _MaxBitrateWifi();
// @override
// Widget build(BuildContext context, WidgetRef ref) {
// final bitrate = ref.watch(
// settingsServiceProvider.select(
// (value) => value.app.maxBitrateWifi,
// ),
// );
// final l = AppLocalizations.of(context);
// return _MaxBitrateOption(
// title: l.settingsNetworkOptionsMaxBitrateWifiTitle,
// bitrate: bitrate,
// onChange: (value) {
// ref.read(settingsServiceProvider.notifier).setMaxBitrateWifi(value);
// },
// );
// }
// }
// class _MaxBitrateMobile extends HookConsumerWidget {
// const _MaxBitrateMobile();
// @override
// Widget build(BuildContext context, WidgetRef ref) {
// final bitrate = ref.watch(
// settingsServiceProvider.select(
// (value) => value.app.maxBitrateMobile,
// ),
// );
// final l = AppLocalizations.of(context);
// return _MaxBitrateOption(
// title: l.settingsNetworkOptionsMaxBitrateMobileTitle,
// bitrate: bitrate,
// onChange: (value) {
// ref.read(settingsServiceProvider.notifier).setMaxBitrateMobile(value);
// },
// );
// }
// }
// class _MaxBitrateOption extends HookConsumerWidget {
// final String title;
// final int bitrate;
// final void Function(int value) onChange;
// const _MaxBitrateOption({
// required this.title,
// required this.bitrate,
// required this.onChange,
// });
// static const options = [0, 24, 32, 64, 96, 128, 192, 256, 320];
// String _bitrateText(AppLocalizations l, int bitrate) {
// return bitrate == 0
// ? l.settingsNetworkValuesUnlimitedKbps
// : l.settingsNetworkValuesKbps(bitrate.toString());
// }
// @override
// Widget build(BuildContext context, WidgetRef ref) {
// final l = AppLocalizations.of(context);
// return ListTile(
// title: Text(title),
// subtitle: Text(_bitrateText(l, bitrate)),
// onTap: () async {
// final value = await showDialog<int>(
// context: context,
// builder: (context) => MultipleChoiceDialog<int>(
// title: title,
// current: bitrate,
// options: options
// .map(
// (opt) => MultiChoiceOption.int(
// title: _bitrateText(l, opt),
// option: opt,
// ),
// )
// .toIList(),
// ),
// );
// if (value != null) {
// onChange(value);
// }
// },
// );
// }
// }
// class _StreamFormat extends HookConsumerWidget {
// const _StreamFormat();
// static const options = ['', 'mp3', 'opus', 'ogg'];
// @override
// Widget build(BuildContext context, WidgetRef ref) {
// final streamFormat = ref.watch(
// settingsServiceProvider.select((value) => value.app.streamFormat),
// );
// final l = AppLocalizations.of(context);
// return ListTile(
// title: Text(l.settingsNetworkOptionsStreamFormat),
// subtitle: Text(
// streamFormat ?? l.settingsNetworkOptionsStreamFormatServerDefault,
// ),
// onTap: () async {
// final value = await showDialog<String>(
// context: context,
// builder: (context) => MultipleChoiceDialog<String>(
// title: l.settingsNetworkOptionsStreamFormat,
// current: streamFormat ?? '',
// options: options
// .map(
// (opt) => MultiChoiceOption.string(
// title: opt == ''
// ? l.settingsNetworkOptionsStreamFormatServerDefault
// : opt,
// option: opt,
// ),
// )
// .toIList(),
// ),
// );
// if (value != null) {
// ref
// .read(settingsServiceProvider.notifier)
// .setStreamFormat(value == '' ? null : value);
// }
// },
// );
// }
// }
// class _OfflineMode extends HookConsumerWidget {
// const _OfflineMode();
// @override
// Widget build(BuildContext context, WidgetRef ref) {
// final offline = ref.watch(offlineModeProvider);
// final l = AppLocalizations.of(context);
// return SwitchListTile(
// value: offline,
// title: Text(l.settingsNetworkOptionsOfflineMode),
// subtitle: offline
// ? Text(l.settingsNetworkOptionsOfflineModeOn)
// : Text(l.settingsNetworkOptionsOfflineModeOff),
// onChanged: (value) {
// ref.read(offlineModeProvider.notifier).setMode(value);
// },
// );
// }
// }
class _Sources extends HookConsumerWidget {
const _Sources();
@override
Widget build(BuildContext context, WidgetRef ref) {
final db = ref.watch(databaseProvider);
final activeSourceId = ref.watch(sourceIdProvider);
final sources = useStream(db.sourcesDao.listSources()).data;
final l = AppLocalizations.of(context);
if (sources == null) {
return Container();
}
return _Section(
children: [
RadioGroup<int>(
groupValue: activeSourceId,
onChanged: (value) {
if (value != null) {
db.sourcesDao.setActiveSource(value);
}
},
child: Column(
children: [
for (final (source, settings) in sources)
RadioListTile<int>(
value: source.id,
title: Text(source.name),
subtitle: Text(
settings.address.toString(),
maxLines: 1,
softWrap: false,
overflow: TextOverflow.fade,
),
secondary: IconButton(
icon: const Icon(Icons.edit_rounded),
onPressed: () {
// context.pushRoute(SourceRoute(id: source.id));
},
),
),
],
),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
OutlinedButton.icon(
icon: const Icon(Icons.add_rounded),
label: Text(l.settingsServersActionsAdd),
onPressed: () {
// context.pushRoute(SourceRoute());
},
),
],
),
],
);
}
}