fixed init location

This commit is contained in:
Schnitzel5 2025-06-28 00:48:09 +02:00
parent 1bfb6fd0c1
commit 79da1d2678
5 changed files with 292 additions and 270 deletions

View file

@ -0,0 +1,148 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/track.dart';
import 'package:mangayomi/models/track_search.dart';
import 'package:mangayomi/modules/tracker_library/tracker_item_card.dart';
import 'package:mangayomi/modules/widgets/bottom_text_widget.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/constant.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
class TrackerLibraryImageCard extends ConsumerStatefulWidget {
final TrackSearch track;
final ItemType itemType;
const TrackerLibraryImageCard({
super.key,
required this.track,
required this.itemType,
});
@override
ConsumerState<TrackerLibraryImageCard> createState() =>
_TrackerLibraryImageCardState();
}
class _TrackerLibraryImageCardState
extends ConsumerState<TrackerLibraryImageCard>
with AutomaticKeepAliveClientMixin<TrackerLibraryImageCard> {
@override
Widget build(BuildContext context) {
super.build(context);
final trackData = widget.track;
return GestureDetector(
onTap: () => _showCard(context),
child: StreamBuilder(
stream: isar.tracks
.filter()
.mangaIdIsNotNull()
.mediaIdEqualTo(trackData.mediaId)
.itemTypeEqualTo(widget.itemType)
.watch(fireImmediately: true),
builder: (context, snapshot) {
final hasData = snapshot.hasData && snapshot.data!.isNotEmpty;
return Padding(
padding: const EdgeInsets.only(left: 10),
child: Stack(
children: [
SizedBox(
width: 110,
child: Column(
children: [
Builder(
builder: (context) {
return ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Stack(
children: [
cachedNetworkImage(
imageUrl: toImgUrl(trackData.coverUrl ?? ""),
width: 110,
height: 150,
fit: BoxFit.cover,
),
Positioned(
top: 0,
right: 0,
child: Text.rich(
TextSpan(
style: TextStyle(
background: Paint()
..color = Theme.of(context)
.scaffoldBackgroundColor
.withValues(alpha: 0.75)
..strokeWidth = 20.0
..strokeJoin = StrokeJoin.round
..style = PaintingStyle.stroke,
),
children: [
WidgetSpan(
child: Icon(
Icons.star,
color: context.primaryColor,
),
),
TextSpan(
text: " ${trackData.score ?? "?"}",
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
],
),
),
),
],
),
);
},
),
BottomTextWidget(
fontSize: 12.0,
text: trackData.title!,
isLoading: true,
textColor: Theme.of(context).textTheme.bodyLarge!.color,
isComfortableGrid: true,
),
],
),
),
Container(
width: 110,
height: 150,
color: hasData ? Colors.black.withValues(alpha: 0.7) : null,
),
if (hasData)
Positioned(
top: 0,
left: 0,
child: Padding(
padding: const EdgeInsets.all(4),
child: Icon(
Icons.collections_bookmark,
color: context.primaryColor,
),
),
),
],
),
);
},
),
);
}
void _showCard(BuildContext context) {
showDialog(
context: context,
builder: (context) =>
TrackerItemCard(track: widget.track, itemType: widget.itemType),
);
}
@override
bool get wantKeepAlive => true;
}

View file

@ -1,7 +1,6 @@
// ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_qjs/quickjs/ffi.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@ -15,12 +14,10 @@ import 'package:mangayomi/models/track_preference.dart';
import 'package:mangayomi/models/track_search.dart';
import 'package:mangayomi/modules/library/widgets/search_text_form_field.dart';
import 'package:mangayomi/modules/manga/detail/providers/track_state_providers.dart';
import 'package:mangayomi/modules/tracker_library/tracker_item_card.dart';
import 'package:mangayomi/modules/widgets/bottom_text_widget.dart';
import 'package:mangayomi/modules/tracker_library/tracker_library_section.dart';
import 'package:mangayomi/modules/tracker_library/tracker_section_screen.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/constant.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
enum TrackerProviders {
@ -35,22 +32,6 @@ enum TrackerProviders {
final String name;
}
class TrackLibrarySection {
String name;
Future<List<TrackSearch>?> Function() func;
ItemType itemType;
int syncId;
bool isSearch;
TrackLibrarySection({
required this.name,
required this.func,
required this.syncId,
this.itemType = ItemType.manga,
this.isSearch = false,
});
}
class TrackerLibraryScreen extends ConsumerStatefulWidget {
final String? presetInput;
const TrackerLibraryScreen({required this.presetInput, super.key});
@ -589,251 +570,3 @@ class _TrackerLibraryScreenState extends ConsumerState<TrackerLibraryScreen> {
);
}
}
class TrackerSectionScreen extends StatefulWidget {
final TrackLibrarySection section;
const TrackerSectionScreen({super.key, required this.section});
@override
State<TrackerSectionScreen> createState() => _TrackerSectionScreenState();
}
class _TrackerSectionScreenState extends State<TrackerSectionScreen> {
String _errorMessage = "";
bool _isLoading = true;
List<TrackSearch> _tracks = [];
@override
void initState() {
super.initState();
_fetchData();
}
@override
void didUpdateWidget(covariant TrackerSectionScreen oldWidget) {
super.didUpdateWidget(oldWidget);
_fetchData();
}
@override
Widget build(BuildContext context) {
final l10n = l10nLocalizations(context)!;
return Scaffold(
body: SizedBox(
height: 260,
child: Column(
children: [
ListTile(dense: true, title: Text(widget.section.name)),
Flexible(
child: _isLoading
? const Center(child: CircularProgressIndicator())
: Builder(
builder: (context) {
if (_errorMessage.isNotEmpty) {
return Center(child: Text(_errorMessage));
}
if (_tracks.isNotEmpty) {
return SuperListView.builder(
extentPrecalculationPolicy:
SuperPrecalculationPolicy(),
scrollDirection: Axis.horizontal,
itemCount: _tracks.length,
itemBuilder: (context, index) {
return TrackerLibraryImageCard(
track: _tracks[index],
itemType: widget.section.itemType,
);
},
);
}
return Center(child: Text(l10n.no_result));
},
),
),
],
),
),
);
}
_fetchData() async {
final box = await Hive.openBox("tracker_library");
final key =
"${widget.section.syncId}-${widget.section.itemType.name}-${widget.section.name}";
if (!widget.section.isSearch && box.containsKey(key)) {
final temp = box.get(key);
if (temp is List<TrackSearch>) {
_errorMessage = "";
_tracks = temp;
if (mounted) {
setState(() {
_isLoading = false;
});
}
return;
}
}
try {
_errorMessage = "";
_tracks = await widget.section.func() ?? [];
box.put(key, _tracks);
if (mounted) {
setState(() {
_isLoading = false;
});
}
} catch (e) {
if (mounted) {
setState(() {
_errorMessage = e.toString();
_isLoading = false;
});
}
}
}
}
class TrackerLibraryImageCard extends ConsumerStatefulWidget {
final TrackSearch track;
final ItemType itemType;
const TrackerLibraryImageCard({
super.key,
required this.track,
required this.itemType,
});
@override
ConsumerState<TrackerLibraryImageCard> createState() =>
_TrackerLibraryImageCardState();
}
class _TrackerLibraryImageCardState
extends ConsumerState<TrackerLibraryImageCard>
with AutomaticKeepAliveClientMixin<TrackerLibraryImageCard> {
@override
Widget build(BuildContext context) {
super.build(context);
final trackData = widget.track;
return GestureDetector(
onTap: () => _showCard(context),
child: StreamBuilder(
stream: isar.tracks
.filter()
.mangaIdIsNotNull()
.mediaIdEqualTo(trackData.mediaId)
.itemTypeEqualTo(widget.itemType)
.watch(fireImmediately: true),
builder: (context, snapshot) {
final hasData = snapshot.hasData && snapshot.data!.isNotEmpty;
return Padding(
padding: const EdgeInsets.only(left: 10),
child: Stack(
children: [
SizedBox(
width: 110,
child: Column(
children: [
Builder(
builder: (context) {
return ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Stack(
children: [
cachedNetworkImage(
imageUrl: toImgUrl(trackData.coverUrl ?? ""),
width: 110,
height: 150,
fit: BoxFit.cover,
),
Positioned(
top: 0,
right: 0,
child: Text.rich(
TextSpan(
style: TextStyle(
background: Paint()
..color = Theme.of(context)
.scaffoldBackgroundColor
.withValues(alpha: 0.75)
..strokeWidth = 20.0
..strokeJoin = StrokeJoin.round
..style = PaintingStyle.stroke,
),
children: [
WidgetSpan(
child: Icon(
Icons.star,
color: context.primaryColor,
),
),
TextSpan(
text: " ${trackData.score ?? "?"}",
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
],
),
),
),
],
),
);
},
),
BottomTextWidget(
fontSize: 12.0,
text: trackData.title!,
isLoading: true,
textColor: Theme.of(context).textTheme.bodyLarge!.color,
isComfortableGrid: true,
),
],
),
),
Container(
width: 110,
height: 150,
color: hasData ? Colors.black.withValues(alpha: 0.7) : null,
),
if (hasData)
Positioned(
top: 0,
left: 0,
child: Padding(
padding: const EdgeInsets.all(4),
child: Icon(
Icons.collections_bookmark,
color: context.primaryColor,
),
),
),
],
),
);
},
),
);
}
void _showCard(BuildContext context) {
showDialog(
context: context,
builder: (context) =>
TrackerItemCard(track: widget.track, itemType: widget.itemType),
);
}
@override
bool get wantKeepAlive => true;
}
class SuperPrecalculationPolicy extends ExtentPrecalculationPolicy {
@override
bool shouldPrecalculateExtents(ExtentPrecalculationContext context) {
return context.numberOfItems < 100;
}
}

View file

@ -0,0 +1,18 @@
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/track_search.dart';
class TrackLibrarySection {
String name;
Future<List<TrackSearch>?> Function() func;
ItemType itemType;
int syncId;
bool isSearch;
TrackLibrarySection({
required this.name,
required this.func,
required this.syncId,
this.itemType = ItemType.manga,
this.isSearch = false,
});
}

View file

@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import 'package:hive_flutter/adapters.dart';
import 'package:mangayomi/models/track_search.dart';
import 'package:mangayomi/modules/tracker_library/tracker_library_card.dart';
import 'package:mangayomi/modules/tracker_library/tracker_library_section.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
class TrackerSectionScreen extends StatefulWidget {
final TrackLibrarySection section;
const TrackerSectionScreen({super.key, required this.section});
@override
State<TrackerSectionScreen> createState() => _TrackerSectionScreenState();
}
class _TrackerSectionScreenState extends State<TrackerSectionScreen> {
String _errorMessage = "";
bool _isLoading = true;
List<TrackSearch> _tracks = [];
@override
void initState() {
super.initState();
_fetchData();
}
@override
void didUpdateWidget(covariant TrackerSectionScreen oldWidget) {
super.didUpdateWidget(oldWidget);
_fetchData();
}
@override
Widget build(BuildContext context) {
final l10n = l10nLocalizations(context)!;
return Scaffold(
body: SizedBox(
height: 260,
child: Column(
children: [
ListTile(dense: true, title: Text(widget.section.name)),
Flexible(
child: _isLoading
? const Center(child: CircularProgressIndicator())
: Builder(
builder: (context) {
if (_errorMessage.isNotEmpty) {
return Center(child: Text(_errorMessage));
}
if (_tracks.isNotEmpty) {
return SuperListView.builder(
extentPrecalculationPolicy:
SuperPrecalculationPolicy(),
scrollDirection: Axis.horizontal,
itemCount: _tracks.length,
itemBuilder: (context, index) {
return TrackerLibraryImageCard(
track: _tracks[index],
itemType: widget.section.itemType,
);
},
);
}
return Center(child: Text(l10n.no_result));
},
),
),
],
),
),
);
}
_fetchData() async {
final box = await Hive.openBox("tracker_library");
final key =
"${widget.section.syncId}-${widget.section.itemType.name}-${widget.section.name}";
if (!widget.section.isSearch && box.containsKey(key)) {
final temp = box.get(key);
if (temp is List<TrackSearch>) {
_errorMessage = "";
_tracks = temp;
if (mounted) {
setState(() {
_isLoading = false;
});
}
return;
}
}
try {
_errorMessage = "";
_tracks = await widget.section.func() ?? [];
box.put(key, _tracks);
if (mounted) {
setState(() {
_isLoading = false;
});
}
} catch (e) {
if (mounted) {
setState(() {
_errorMessage = e.toString();
_isLoading = false;
});
}
}
}
}
class SuperPrecalculationPolicy extends ExtentPrecalculationPolicy {
@override
bool shouldPrecalculateExtents(ExtentPrecalculationContext context) {
return context.numberOfItems < 100;
}
}

View file

@ -55,7 +55,11 @@ final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
@riverpod
GoRouter router(Ref ref) {
final router = RouterNotifier();
final initLocation = ref.watch(navigationOrderStateProvider).first;
final hiddenItems = ref.watch(hideItemsStateProvider);
final initLocation = ref
.watch(navigationOrderStateProvider)
.where((e) => !hiddenItems.contains(e))
.first;
return GoRouter(
observers: [BotToastNavigatorObserver()],