mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 09:09:29 +01:00
416 lines
12 KiB
Dart
416 lines
12 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
|
|
import '../../l10n/generated/app_localizations.dart';
|
|
import '../state/database.dart';
|
|
import '../state/source.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);
|
|
final textTheme = TextTheme.of(context);
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(l.navigationTabsSettings, style: textTheme.headlineLarge),
|
|
),
|
|
body: ListView(
|
|
children: [
|
|
_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,
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class _SectionHeader extends StatelessWidget {
|
|
const _SectionHeader(
|
|
this.title,
|
|
);
|
|
|
|
final String title;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final text = TextTheme.of(context);
|
|
|
|
return Padding(
|
|
padding: EdgeInsetsGeometry.directional(
|
|
start: kHorizontalPadding,
|
|
end: kHorizontalPadding,
|
|
top: 32,
|
|
bottom: 8,
|
|
),
|
|
child: Text(title, style: text.headlineMedium),
|
|
);
|
|
}
|
|
}
|
|
|
|
// 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, :subsonicSetting) in sources)
|
|
RadioListTile<int>(
|
|
value: source.id,
|
|
title: Text(source.name),
|
|
subtitle: Text(
|
|
subsonicSetting.address.toString(),
|
|
maxLines: 1,
|
|
softWrap: false,
|
|
overflow: TextOverflow.fade,
|
|
),
|
|
secondary: IconButton(
|
|
icon: const Icon(Icons.edit_rounded),
|
|
onPressed: () {
|
|
context.push('/sources/${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.push('/sources/add');
|
|
},
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|