mirror of
https://github.com/austinried/subtracks.git
synced 2026-02-10 23:02:43 +01:00
active source switching and reactivity
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import 'package:drift/drift.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 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
@@ -23,15 +23,17 @@ class AlbumsGrid extends HookConsumerWidget {
|
||||
final controller = usePagingController<int, Album>(
|
||||
getNextPageKey: (state) =>
|
||||
state.lastPageIsEmpty ? null : state.nextIntPageKey,
|
||||
fetchPage: (pageKey) async {
|
||||
final query = db.albums.select()
|
||||
..where((f) => f.sourceId.equals(sourceId))
|
||||
..limit(kPageSize, offset: (pageKey - 1) * kPageSize);
|
||||
|
||||
return await query.get();
|
||||
},
|
||||
fetchPage: (pageKey) => db.libraryDao.listAlbums(
|
||||
limit: kPageSize,
|
||||
offset: (pageKey - 1) * kPageSize,
|
||||
),
|
||||
);
|
||||
|
||||
useEffect(() {
|
||||
controller.refresh();
|
||||
return;
|
||||
}, [sourceId]);
|
||||
|
||||
return PagingListener(
|
||||
controller: controller,
|
||||
builder: (context, state, fetchNextPage) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:drift/drift.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 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
@@ -25,36 +25,17 @@ class ArtistsList extends HookConsumerWidget {
|
||||
final controller = usePagingController<int, _ArtistItem>(
|
||||
getNextPageKey: (state) =>
|
||||
state.lastPageIsEmpty ? null : state.nextIntPageKey,
|
||||
fetchPage: (pageKey) async {
|
||||
final albumCount = db.albums.id.count();
|
||||
|
||||
final query =
|
||||
db.artists.select().join([
|
||||
leftOuterJoin(
|
||||
db.albums,
|
||||
db.albums.artistId.equalsExp(db.artists.id),
|
||||
),
|
||||
])
|
||||
..addColumns([albumCount])
|
||||
..where(
|
||||
db.artists.sourceId.equals(sourceId) &
|
||||
db.albums.sourceId.equals(sourceId),
|
||||
)
|
||||
..groupBy([db.artists.sourceId, db.artists.id])
|
||||
..orderBy([OrderingTerm.asc(db.artists.name)])
|
||||
..limit(kPageSize, offset: (pageKey - 1) * kPageSize);
|
||||
|
||||
return (await query.get())
|
||||
.map(
|
||||
(row) => (
|
||||
artist: row.readTable(db.artists),
|
||||
albumCount: row.read(albumCount),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
},
|
||||
fetchPage: (pageKey) => db.libraryDao.listArtists(
|
||||
limit: kPageSize,
|
||||
offset: (pageKey - 1) * kPageSize,
|
||||
),
|
||||
);
|
||||
|
||||
useEffect(() {
|
||||
controller.refresh();
|
||||
return;
|
||||
}, [sourceId]);
|
||||
|
||||
return PagingListener(
|
||||
controller: controller,
|
||||
builder: (context, state, fetchNextPage) {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
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 = 16.0;
|
||||
const kHorizontalPadding = 18.0;
|
||||
|
||||
class SettingsScreen extends HookConsumerWidget {
|
||||
const SettingsScreen({super.key});
|
||||
@@ -13,11 +17,14 @@ class SettingsScreen extends HookConsumerWidget {
|
||||
final l = AppLocalizations.of(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: TextH1(l.navigationTabsSettings),
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
const SizedBox(height: 96),
|
||||
// const SizedBox(height: 96),
|
||||
_SectionHeader(l.settingsServersName),
|
||||
// const _Sources(),
|
||||
const _Sources(),
|
||||
// _SectionHeader(l.settingsNetworkName),
|
||||
// const _Network(),
|
||||
// _SectionHeader(l.settingsAboutName),
|
||||
@@ -29,10 +36,10 @@ class SettingsScreen extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
class _Section extends StatelessWidget {
|
||||
final List<Widget> children;
|
||||
|
||||
const _Section({required this.children});
|
||||
|
||||
final List<Widget> children;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
@@ -46,27 +53,22 @@ class _Section extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _SectionHeader extends StatelessWidget {
|
||||
final String title;
|
||||
|
||||
const _SectionHeader(this.title);
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: kHorizontalPadding),
|
||||
child: Text(
|
||||
title,
|
||||
style: theme.textTheme.displaySmall,
|
||||
),
|
||||
child: TextH2(title),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -346,79 +348,66 @@ class _SectionHeader extends StatelessWidget {
|
||||
// }
|
||||
// }
|
||||
|
||||
// class _Sources extends HookConsumerWidget {
|
||||
// const _Sources();
|
||||
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,
|
||||
// ),
|
||||
// );
|
||||
@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);
|
||||
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');
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
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());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ final databaseInitializer = FutureProvider<SubtracksDatabase>((ref) async {
|
||||
.insertOnConflictUpdate(
|
||||
SourcesCompanion.insert(
|
||||
id: Value(1),
|
||||
name: 'test navidrome',
|
||||
isActive: Value(true),
|
||||
name: 'test subsonic',
|
||||
// isActive: Value(true),
|
||||
),
|
||||
);
|
||||
await db
|
||||
@@ -23,9 +23,26 @@ final databaseInitializer = FutureProvider<SubtracksDatabase>((ref) async {
|
||||
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),
|
||||
),
|
||||
);
|
||||
await db
|
||||
.into(db.sources)
|
||||
.insertOnConflictUpdate(
|
||||
SourcesCompanion.insert(
|
||||
id: Value(2),
|
||||
name: 'test navidrome',
|
||||
// isActive: Value(null),
|
||||
),
|
||||
);
|
||||
await db
|
||||
.into(db.subsonicSettings)
|
||||
.insertOnConflictUpdate(
|
||||
SubsonicSettingsCompanion.insert(
|
||||
sourceId: Value(2),
|
||||
address: Uri.parse('http://10.0.2.2:4533'),
|
||||
username: 'admin',
|
||||
password: 'password',
|
||||
useTokenAuth: Value(true),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -10,17 +10,17 @@ final activeSourceInitializer = StreamProvider<(int, SubsonicSource)>((
|
||||
) async* {
|
||||
final db = ref.watch(databaseProvider);
|
||||
|
||||
final activeSource = db.managers.sources
|
||||
.filter((f) => f.isActive.equals(true))
|
||||
.watchSingle();
|
||||
final activeSource = db.sourcesDao.activeSourceId().watchSingle();
|
||||
|
||||
await for (final source in activeSource) {
|
||||
final sourceId = source.read(db.sources.id)!;
|
||||
|
||||
final subsonicSettings = await db.managers.subsonicSettings
|
||||
.filter((f) => f.sourceId.equals(source.id))
|
||||
.filter((f) => f.sourceId.equals(sourceId))
|
||||
.getSingle();
|
||||
|
||||
yield (
|
||||
source.id,
|
||||
sourceId,
|
||||
SubsonicSource(
|
||||
SubsonicClient(
|
||||
http: SubtracksHttpClient(),
|
||||
|
||||
Reference in New Issue
Block a user