chore: Lots of bug fixes and navigation improvements

This commit is contained in:
PartyDonut 2025-09-01 20:21:36 +02:00
parent 9bb5e81812
commit 92d5391b93
35 changed files with 513 additions and 455 deletions

View file

@ -18,6 +18,7 @@ import 'package:fladder/providers/user_provider.dart';
import 'package:fladder/providers/views_provider.dart';
import 'package:fladder/routes/auto_router.gr.dart';
import 'package:fladder/screens/dashboard/home_banner_widget.dart';
import 'package:fladder/screens/home_screen.dart';
import 'package:fladder/screens/shared/media/poster_row.dart';
import 'package:fladder/screens/shared/nested_scaffold.dart';
import 'package:fladder/screens/shared/nested_sliver_appbar.dart';
@ -97,7 +98,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
child: PinchPosterZoom(
scaleDifference: (difference) => ref.read(clientSettingsProvider.notifier).addPosterSize(difference),
child: CustomScrollView(
controller: AdaptiveLayout.scrollOf(context),
controller: AdaptiveLayout.scrollOf(context, HomeTabs.dashboard),
physics: const AlwaysScrollableScrollPhysics(),
slivers: [
const DefaultSliverTopBadding(),
@ -196,6 +197,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
_ => SortingOptions.dateAdded,
},
sortOrder: SortingOrder.descending,
recursive: true,
),
),
posters: view.recentlyAdded,

View file

@ -35,7 +35,7 @@ class FolderDetailScreen extends ConsumerWidget {
switch (item) {
case PhotoModel photoModel:
final photoItems = details?.items.whereType<PhotoModel>().toList();
await context.navigateTo(PhotoViewerRoute(
await context.pushRoute(PhotoViewerRoute(
items: photoItems,
selected: photoModel.id,
));

View file

@ -7,6 +7,7 @@ import 'package:fladder/models/library_filter_model.dart';
import 'package:fladder/providers/favourites_provider.dart';
import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/routes/auto_router.gr.dart';
import 'package:fladder/screens/home_screen.dart';
import 'package:fladder/screens/shared/media/poster_row.dart';
import 'package:fladder/screens/shared/nested_scaffold.dart';
import 'package:fladder/screens/shared/nested_sliver_appbar.dart';
@ -35,7 +36,7 @@ class FavouritesScreen extends ConsumerWidget {
scaleDifference: (difference) => ref.read(clientSettingsProvider.notifier).addPosterSize(difference / 2),
child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
controller: AdaptiveLayout.scrollOf(context),
controller: AdaptiveLayout.scrollOf(context, HomeTabs.favorites),
slivers: [
if (AdaptiveLayout.viewSizeOf(context) == ViewSize.phone)
NestedSliverAppBar(

View file

@ -44,7 +44,7 @@ enum HomeTabs {
HomeTabs.dashboard => context.router.navigate(const DashboardRoute()),
HomeTabs.library => context.router.navigate(const LibraryRoute()),
HomeTabs.favorites => context.router.navigate(const FavouritesRoute()),
HomeTabs.sync => context.router.navigate(SyncedRoute()),
HomeTabs.sync => context.router.navigate(const SyncedRoute()),
};
String label(BuildContext context) => switch (this) {
@ -101,7 +101,7 @@ class HomeScreen extends ConsumerWidget {
label: context.localized.navigationSync,
icon: Icon(e.icon),
selectedIcon: Icon(e.selectedIcon),
route: SyncedRoute(),
route: const SyncedRoute(),
action: () => e.navigate(context),
);
}

View file

@ -11,6 +11,7 @@ import 'package:fladder/models/recommended_model.dart';
import 'package:fladder/models/view_model.dart';
import 'package:fladder/providers/library_screen_provider.dart';
import 'package:fladder/routes/auto_router.gr.dart';
import 'package:fladder/screens/home_screen.dart';
import 'package:fladder/screens/metadata/refresh_metadata.dart';
import 'package:fladder/screens/shared/flat_button.dart';
import 'package:fladder/screens/shared/media/poster_row.dart';
@ -62,7 +63,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> with SingleTicker
onRefresh: () => ref.read(libraryScreenProvider.notifier).fetchAllLibraries(),
child: SizedBox.expand(
child: CustomScrollView(
controller: AdaptiveLayout.scrollOf(context),
controller: AdaptiveLayout.scrollOf(context, HomeTabs.library),
physics: const AlwaysScrollableScrollPhysics(),
slivers: [
const DefaultSliverTopBadding(),

View file

@ -8,7 +8,6 @@ import 'package:iconsax_plus/iconsax_plus.dart';
import 'package:fladder/models/boxset_model.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/photos_model.dart';
import 'package:fladder/models/library_filter_model.dart';
import 'package:fladder/models/library_search/library_search_model.dart';
import 'package:fladder/models/library_search/library_search_options.dart';
@ -55,8 +54,7 @@ class LibrarySearchScreen extends ConsumerStatefulWidget {
final SortingOptions? sortingOptions;
final Map<FladderItemType, bool>? types;
final Map<String, bool>? genres;
final bool recursive;
final PhotoModel? photoToView;
final bool? recursive;
const LibrarySearchScreen({
@QueryParam("parentId") this.viewModelId,
@QueryParam("folderId") this.folderId,
@ -65,8 +63,7 @@ class LibrarySearchScreen extends ConsumerStatefulWidget {
@QueryParam("sortOptions") this.sortingOptions,
@QueryParam("itemTypes") this.types,
@QueryParam("genres") this.genres,
@QueryParam("recursive") this.recursive = true,
this.photoToView,
@QueryParam("recursive") this.recursive,
super.key,
});
@ -109,10 +106,6 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
SystemUiMode.edgeToEdge,
overlays: [],
);
if (context.mounted && widget.photoToView != null) {
libraryProvider.viewGallery(context, selected: widget.photoToView);
}
scrollController.addListener(() {
scrollPosition();
});
@ -227,7 +220,7 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
widget.folderId,
widget.viewModelId,
defaultFilter.copyWith(
favourites: widget.favourites ?? defaultFilter.favourites,
favourites: widget.favourites,
sortOrder: widget.sortOrder ?? defaultFilter.sortOrder,
sortingOption: widget.sortingOptions ?? defaultFilter.sortingOption,
types: widget.types ?? {},

View file

@ -59,8 +59,8 @@ class _LibraryFilterChipsState extends ConsumerState<LibraryFilterChips> {
onClear: () => libraryProvider.setTypes(librarySearchResults.filters.types.setAll(false)),
),
ExpressiveButton(
isSelected: favourites,
icon: favourites ? const Icon(IconsaxPlusBold.heart) : null,
isSelected: favourites == true,
icon: favourites == true ? const Icon(IconsaxPlusBold.heart) : null,
label: Text(context.localized.favorites),
onPressed: () {
libraryProvider.toggleFavourite();
@ -68,8 +68,8 @@ class _LibraryFilterChipsState extends ConsumerState<LibraryFilterChips> {
},
),
ExpressiveButton(
isSelected: recursive,
icon: recursive ? const Icon(IconsaxPlusBold.tick_circle) : null,
isSelected: recursive == true,
icon: recursive == true ? const Icon(IconsaxPlusBold.tick_circle) : null,
label: Text(context.localized.recursive),
onPressed: () {
libraryProvider.toggleRecursive();

View file

@ -61,18 +61,23 @@ class ItemInfoScreenState extends ConsumerState<ItemInfoScreen> {
Container(
color: Theme.of(context).colorScheme.surface,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: Row(
mainAxisSize: MainAxisSize.max,
spacing: 6,
spacing: 12,
children: [
Text(
widget.item.name,
style: Theme.of(context).textTheme.titleLarge,
Expanded(
child: Text(
widget.item.name,
softWrap: false,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.titleLarge,
),
),
const Spacer(),
IconButton(
onPressed: () => context.copyToClipboard(info.model.toString()),
icon: const Icon(Icons.copy_all_rounded)),

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:extended_image/extended_image.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -23,6 +24,7 @@ import 'package:fladder/util/throttler.dart';
import 'package:fladder/widgets/full_screen_helpers/full_screen_wrapper.dart';
import 'package:fladder/widgets/shared/elevated_icon.dart';
import 'package:fladder/widgets/shared/progress_floating_button.dart';
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
class PhotoViewerControls extends ConsumerStatefulWidget {
final EdgeInsets padding;
@ -309,13 +311,19 @@ class _PhotoViewerControllsState extends ConsumerState<PhotoViewerControls> with
children: [
ElevatedIconButton(
onPressed: widget.openOptions,
icon: IconsaxPlusLinear.more_2,
icon: IconsaxPlusLinear.more_square,
),
const Spacer(),
ElevatedIconButton(
SelectableIconButton(
onPressed: markAsFavourite,
color: widget.photo.userData.isFavourite ? Colors.red : null,
selected: false,
icon: widget.photo.userData.isFavourite ? IconsaxPlusBold.heart : IconsaxPlusLinear.heart,
backgroundColor: Theme.of(context)
.colorScheme
.onPrimary
.harmonizeWith(Colors.red)
.withValues(alpha: 0.25),
iconColor: widget.photo.userData.isFavourite ? Colors.red : null,
),
ProgressFloatingButton(
controller: timerController,
@ -341,8 +349,11 @@ class _PhotoViewerControllsState extends ConsumerState<PhotoViewerControls> with
);
}
void markAsFavourite() {
ref.read(userProvider.notifier).setAsFavorite(!widget.photo.userData.isFavourite, widget.photo.id);
Future<void> markAsFavourite() async {
final response =
await ref.read(userProvider.notifier).setAsFavorite(!widget.photo.userData.isFavourite, widget.photo.id);
if (response?.isSuccessful == false) return;
widget.onPhotoChanged(widget.photo
.copyWith(userData: widget.photo.userData.copyWith(isFavourite: !widget.photo.userData.isFavourite)));

View file

@ -90,7 +90,13 @@ class _PhotoViewerScreenState extends ConsumerState<PhotoViewerScreen> with Widg
if (context.mounted) {
setState(() {
photos = {...photos, ...newItems}.toList();
if (photos.length == 1 && newItems.contains(photos.first)) {
photos = newItems;
currentPage = photos.indexWhere((value) => value.id == widget.selected).clamp(0, photos.length - 1);
controller.jumpToPage(currentPage);
} else {
photos = {...photos, ...newItems}.toList();
}
loadingItems = false;
});
}
@ -165,14 +171,14 @@ class _PhotoViewerScreenState extends ConsumerState<PhotoViewerScreen> with Widg
? Center(
child: Text(context.localized.noItemsToShow),
)
: buildViewer(),
: buildViewer(context),
),
),
),
);
}
Widget buildViewer() {
Widget buildViewer(BuildContext context) {
final currentPhoto = photos[currentPage];
final imageHash = currentPhoto.images?.primary?.hash;
return Stack(
@ -498,8 +504,8 @@ class _PhotoViewerScreenState extends ConsumerState<PhotoViewerScreen> with Widg
},
);
void markAsFavourite(PhotoModel photo, {bool? value}) {
ref.read(userProvider.notifier).setAsFavorite(value ?? !photo.userData.isFavourite, photo.id);
Future<void> markAsFavourite(PhotoModel photo, {bool? value}) async {
await ref.read(userProvider.notifier).setAsFavorite(value ?? !photo.userData.isFavourite, photo.id);
setState(() {
int index = photos.indexOf(photo);

View file

@ -24,7 +24,7 @@ class ItemLogo extends StatelessWidget {
child: Text(
item.parentBaseModel.name,
textAlign: TextAlign.start,
maxLines: 3,
maxLines: 2,
overflow: TextOverflow.fade,
style: textStyle ??
Theme.of(context).textTheme.headlineLarge?.copyWith(

View file

@ -77,7 +77,7 @@ class _PosterImageState extends ConsumerState<PosterImage> {
}
Future<void> navigateToDetails() async {
await widget.poster.navigateTo(context);
await widget.poster.navigateTo(context, ref: ref);
}
final posterRadius = FladderTheme.smallShape.borderRadius;

View file

@ -42,6 +42,7 @@ class _PosterRowState extends ConsumerState<PosterRow> {
contentPadding: widget.contentPadding,
label: widget.label,
onLabelClick: widget.onLabelClick,
dominantRatio: dominantRatio,
items: widget.posters,
itemBuilder: (context, index) {
final poster = widget.posters[index];

View file

@ -8,6 +8,7 @@ import 'package:iconsax_plus/iconsax_plus.dart';
import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/providers/sync_provider.dart';
import 'package:fladder/routes/auto_router.gr.dart';
import 'package:fladder/screens/home_screen.dart';
import 'package:fladder/screens/shared/nested_scaffold.dart';
import 'package:fladder/screens/shared/nested_sliver_appbar.dart';
import 'package:fladder/screens/syncing/sync_list_item.dart';
@ -20,9 +21,7 @@ import 'package:fladder/widgets/shared/pull_to_refresh.dart';
@RoutePage()
class SyncedScreen extends ConsumerStatefulWidget {
final ScrollController? navigationScrollController;
const SyncedScreen({this.navigationScrollController, super.key});
const SyncedScreen({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() => _SyncedScreenState();
@ -43,7 +42,7 @@ class _SyncedScreenState extends ConsumerState<SyncedScreen> {
scaleDifference: (difference) => ref.read(clientSettingsProvider.notifier).addPosterSize(difference / 2),
child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
controller: widget.navigationScrollController,
controller: AdaptiveLayout.scrollOf(context, HomeTabs.sync),
slivers: [
if (AdaptiveLayout.viewSizeOf(context) == ViewSize.phone)
NestedSliverAppBar(