mirror of
https://github.com/austinried/subtracks.git
synced 2026-02-10 15:02:42 +01:00
migrate l10n, state preloading
This commit is contained in:
@@ -4,11 +4,17 @@ import 'screens/album_screen.dart';
|
||||
import 'screens/artist_screen.dart';
|
||||
import 'screens/library_screen.dart';
|
||||
import 'screens/now_playing_screen.dart';
|
||||
import 'screens/preload_screen.dart';
|
||||
import 'screens/root_shell_screen.dart';
|
||||
import 'screens/settings_screen.dart';
|
||||
|
||||
final router = GoRouter(
|
||||
initialLocation: '/preload',
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/preload',
|
||||
builder: (context, state) => PreloadScreen(),
|
||||
),
|
||||
ShellRoute(
|
||||
builder: (context, state, child) => RootShellScreen(child: child),
|
||||
routes: [
|
||||
|
||||
30
lib/app/screens/preload_screen.dart
Normal file
30
lib/app/screens/preload_screen.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
import '../state/database.dart';
|
||||
import '../state/settings.dart';
|
||||
import '../state/source.dart';
|
||||
|
||||
class PreloadScreen extends HookConsumerWidget {
|
||||
const PreloadScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final initializers = [
|
||||
ref.watch(databaseInitializer),
|
||||
ref.watch(sourceInitializer),
|
||||
ref.watch(packageInfoInitializer),
|
||||
];
|
||||
|
||||
if (initializers.every((v) => v.hasValue)) {
|
||||
Future.delayed(Duration(microseconds: 1)).then((_) {
|
||||
if (context.mounted) {
|
||||
context.replace('/');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,424 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
class SettingsScreen extends StatelessWidget {
|
||||
import '../../l10n/generated/app_localizations.dart';
|
||||
|
||||
const kHorizontalPadding = 16.0;
|
||||
|
||||
class SettingsScreen extends HookConsumerWidget {
|
||||
const SettingsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final l = AppLocalizations.of(context);
|
||||
|
||||
return Scaffold(
|
||||
body: Center(child: Text('Settings!')),
|
||||
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 {
|
||||
final List<Widget> children;
|
||||
|
||||
const _Section({required this.children});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
...children,
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SectionHeader extends StatelessWidget {
|
||||
final String title;
|
||||
|
||||
const _SectionHeader(this.title);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: kHorizontalPadding),
|
||||
child: Text(
|
||||
title,
|
||||
style: theme.textTheme.displaySmall,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 sources = ref.watch(
|
||||
// settingsServiceProvider.select(
|
||||
// (value) => value.sources,
|
||||
// ),
|
||||
// );
|
||||
// final activeSource = ref.watch(
|
||||
// settingsServiceProvider.select(
|
||||
// (value) => value.activeSource,
|
||||
// ),
|
||||
// );
|
||||
|
||||
// final l = AppLocalizations.of(context);
|
||||
|
||||
// return _Section(
|
||||
// children: [
|
||||
// for (var source in sources)
|
||||
// RadioListTile<int>(
|
||||
// value: source.id,
|
||||
// groupValue: activeSource?.id,
|
||||
// onChanged: (value) {
|
||||
// ref
|
||||
// .read(settingsServiceProvider.notifier)
|
||||
// .setActiveSource(source.id);
|
||||
// },
|
||||
// title: Text(source.name),
|
||||
// subtitle: Text(
|
||||
// source.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());
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// // TODO: remove
|
||||
// if (kDebugMode)
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// OutlinedButton.icon(
|
||||
// icon: const Icon(Icons.add_rounded),
|
||||
// label: const Text('Add TEST'),
|
||||
// onPressed: () {
|
||||
// ref
|
||||
// .read(settingsServiceProvider.notifier)
|
||||
// .addTestSource('TEST');
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,7 +1,37 @@
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
import '../../database/database.dart';
|
||||
|
||||
final databaseProvider = Provider<SubtracksDatabase>((ref) {
|
||||
return SubtracksDatabase();
|
||||
final databaseInitializer = FutureProvider<SubtracksDatabase>((ref) async {
|
||||
final db = SubtracksDatabase();
|
||||
|
||||
await db
|
||||
.into(db.sources)
|
||||
.insertOnConflictUpdate(
|
||||
SourcesCompanion.insert(
|
||||
id: Value(1),
|
||||
name: 'test navidrome',
|
||||
),
|
||||
);
|
||||
await db
|
||||
.into(db.subsonicSettings)
|
||||
.insertOnConflictUpdate(
|
||||
SubsonicSettingsCompanion.insert(
|
||||
sourceId: Value(1),
|
||||
address: Uri.parse('http://demo.subsonic.org'),
|
||||
username: 'guest1',
|
||||
password: 'guest',
|
||||
// address: Uri.parse('http://10.0.2.2:4533'),
|
||||
// username: 'admin',
|
||||
// password: 'password',
|
||||
useTokenAuth: Value(true),
|
||||
),
|
||||
);
|
||||
|
||||
return db;
|
||||
});
|
||||
|
||||
final databaseProvider = Provider<SubtracksDatabase>((ref) {
|
||||
return ref.watch(databaseInitializer).requireValue;
|
||||
});
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
final sourceIdProvider = Provider<int>((ref) {
|
||||
return 1;
|
||||
});
|
||||
|
||||
final packageInfoInitializer = FutureProvider<PackageInfo>((ref) {
|
||||
return PackageInfo.fromPlatform();
|
||||
});
|
||||
|
||||
final packageInfoProvider = Provider<PackageInfo>((ref) {
|
||||
return ref.watch(packageInfoInitializer).requireValue;
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ import '../../util/http.dart';
|
||||
import 'database.dart';
|
||||
import 'settings.dart';
|
||||
|
||||
final _sourceProvider = FutureProvider<MusicSource>((ref) async {
|
||||
final sourceInitializer = FutureProvider<MusicSource>((ref) async {
|
||||
final db = ref.watch(databaseProvider);
|
||||
final sourceId = ref.watch(sourceIdProvider);
|
||||
|
||||
@@ -34,6 +34,5 @@ final _sourceProvider = FutureProvider<MusicSource>((ref) async {
|
||||
});
|
||||
|
||||
final sourceProvider = Provider<MusicSource>((ref) {
|
||||
final source = ref.watch(_sourceProvider);
|
||||
return source.requireValue;
|
||||
return ref.watch(sourceInitializer).value!;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user