mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-04-18 04:51:29 +00:00
Merge `isLongPressedMangaStateProvider` and `isLongPressedStateProvider` Both providers do the same thing and it is impossible to change the value of the provider in different places in the app at the same time. So just use one `isLongPressedStateProvider`. Reduces code duplication and there is no confusion about which one to use.
439 lines
22 KiB
Dart
439 lines
22 KiB
Dart
import 'dart:typed_data';
|
|
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/chapter.dart';
|
|
import 'package:mangayomi/models/download.dart';
|
|
import 'package:mangayomi/models/history.dart';
|
|
import 'package:mangayomi/modules/library/providers/isar_providers.dart';
|
|
import 'package:mangayomi/modules/library/providers/library_state_provider.dart';
|
|
import 'package:mangayomi/models/manga.dart';
|
|
import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart';
|
|
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
|
|
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
|
import 'package:mangayomi/utils/constant.dart';
|
|
import 'package:mangayomi/utils/extensions/chapter.dart';
|
|
import 'package:mangayomi/utils/headers.dart';
|
|
import 'package:mangayomi/modules/more/providers/incognito_mode_state_provider.dart';
|
|
import 'package:mangayomi/modules/widgets/bottom_text_widget.dart';
|
|
import 'package:mangayomi/modules/widgets/cover_view_widget.dart';
|
|
import 'package:mangayomi/modules/widgets/gridview_widget.dart';
|
|
import 'package:mangayomi/modules/widgets/manga_image_card_widget.dart';
|
|
|
|
class LibraryGridViewWidget extends StatefulWidget {
|
|
final bool isCoverOnlyGrid;
|
|
final bool isComfortableGrid;
|
|
final List<int> mangaIdsList;
|
|
final List<Manga> entriesManga;
|
|
final bool language;
|
|
final bool downloadedChapter;
|
|
final bool continueReaderBtn;
|
|
final bool localSource;
|
|
final ItemType itemType;
|
|
const LibraryGridViewWidget({
|
|
super.key,
|
|
required this.entriesManga,
|
|
required this.isCoverOnlyGrid,
|
|
this.isComfortableGrid = false,
|
|
required this.language,
|
|
required this.downloadedChapter,
|
|
required this.continueReaderBtn,
|
|
required this.mangaIdsList,
|
|
required this.localSource,
|
|
required this.itemType,
|
|
});
|
|
|
|
@override
|
|
State<LibraryGridViewWidget> createState() => _LibraryGridViewWidgetState();
|
|
}
|
|
|
|
class _LibraryGridViewWidgetState extends State<LibraryGridViewWidget> {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Consumer(
|
|
builder: (context, ref, child) {
|
|
final isLongPressed = ref.watch(isLongPressedStateProvider);
|
|
final itemType = widget.itemType;
|
|
|
|
final gridSize = ref.watch(
|
|
libraryGridSizeStateProvider(itemType: itemType),
|
|
);
|
|
return GridViewWidget(
|
|
gridSize: gridSize,
|
|
childAspectRatio: widget.isComfortableGrid ? 0.642 : 0.69,
|
|
itemCount: widget.entriesManga.length,
|
|
itemBuilder: (context, index) {
|
|
final entry = widget.entriesManga[index];
|
|
|
|
return Builder(
|
|
builder: (context) {
|
|
bool isLocalArchive = entry.isLocalArchive ?? false;
|
|
return Padding(
|
|
padding: const EdgeInsets.all(2),
|
|
child: CoverViewWidget(
|
|
isLongPressed: widget.mangaIdsList.contains(entry.id),
|
|
bottomTextWidget: BottomTextWidget(
|
|
maxLines: 1,
|
|
text: entry.name!,
|
|
isComfortableGrid: widget.isComfortableGrid,
|
|
),
|
|
isComfortableGrid: widget.isComfortableGrid,
|
|
image: entry.customCoverImage != null
|
|
? MemoryImage(entry.customCoverImage as Uint8List)
|
|
as ImageProvider
|
|
: CustomExtendedNetworkImageProvider(
|
|
toImgUrl(
|
|
entry.customCoverFromTracker ??
|
|
entry.imageUrl ??
|
|
"",
|
|
),
|
|
headers: entry.isLocalArchive!
|
|
? null
|
|
: ref.watch(
|
|
headersProvider(
|
|
source: entry.source!,
|
|
lang: entry.lang!,
|
|
),
|
|
),
|
|
),
|
|
onTap: () async {
|
|
if (isLongPressed) {
|
|
ref
|
|
.read(mangasListStateProvider.notifier)
|
|
.update(entry);
|
|
} else {
|
|
await pushToMangaReaderDetail(
|
|
ref: ref,
|
|
archiveId: isLocalArchive ? entry.id : null,
|
|
context: context,
|
|
lang: entry.lang!,
|
|
mangaM: entry,
|
|
source: entry.source!,
|
|
);
|
|
if (context.mounted) {
|
|
ref.invalidate(
|
|
getAllMangaWithoutCategoriesStreamProvider(
|
|
itemType: widget.itemType,
|
|
),
|
|
);
|
|
ref.invalidate(
|
|
getAllMangaStreamProvider(
|
|
categoryId: null,
|
|
itemType: widget.itemType,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
},
|
|
onLongPress: () {
|
|
_handleLongOrSecondaryTap(isLongPressed, ref, entry);
|
|
},
|
|
onSecondaryTap: () {
|
|
_handleLongOrSecondaryTap(isLongPressed, ref, entry);
|
|
},
|
|
children: [
|
|
Stack(
|
|
children: [
|
|
Positioned(
|
|
top: 0,
|
|
left: 0,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(5),
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(3),
|
|
color: context.primaryColor,
|
|
),
|
|
child: Row(
|
|
children: [
|
|
if (widget.localSource && isLocalArchive)
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: const BorderRadius.only(
|
|
topLeft: Radius.circular(3),
|
|
bottomLeft: Radius.circular(3),
|
|
),
|
|
color: Theme.of(context).hintColor,
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(
|
|
left: 3,
|
|
right: 3,
|
|
),
|
|
child: Text(
|
|
"Local",
|
|
style: TextStyle(
|
|
color: context
|
|
.dynamicBlackWhiteColor,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.only(right: 5),
|
|
child: Consumer(
|
|
builder: (context, ref, child) {
|
|
List nbrDown = [];
|
|
if (widget.downloadedChapter) {
|
|
isar.txnSync(() {
|
|
for (
|
|
var i = 0;
|
|
i < entry.chapters.length;
|
|
i++
|
|
) {
|
|
final entries = isar.downloads
|
|
.filter()
|
|
.idEqualTo(
|
|
entry.chapters
|
|
.toList()[i]
|
|
.id,
|
|
)
|
|
.findAllSync();
|
|
|
|
if (entries.isNotEmpty &&
|
|
entries.first.isDownload!) {
|
|
nbrDown.add(1);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
return Row(
|
|
children: [
|
|
if (nbrDown.isNotEmpty &&
|
|
widget.downloadedChapter)
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius:
|
|
const BorderRadius.only(
|
|
topLeft:
|
|
Radius.circular(
|
|
3,
|
|
),
|
|
bottomLeft:
|
|
Radius.circular(
|
|
3,
|
|
),
|
|
),
|
|
color: Theme.of(
|
|
context,
|
|
).secondaryHeaderColor,
|
|
),
|
|
child: Padding(
|
|
padding:
|
|
const EdgeInsets.only(
|
|
left: 3,
|
|
right: 3,
|
|
),
|
|
child: Text(
|
|
nbrDown.length.toString(),
|
|
),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.only(
|
|
left: 3,
|
|
),
|
|
child: Text(
|
|
entry.chapters
|
|
.where(
|
|
(element) =>
|
|
!element.isRead!,
|
|
)
|
|
.length
|
|
.toString(),
|
|
style: TextStyle(
|
|
color: context
|
|
.dynamicBlackWhiteColor,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (widget.language && entry.lang!.isNotEmpty)
|
|
Positioned(
|
|
top: 0,
|
|
right: 0,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(5),
|
|
child: Container(
|
|
color: context.themeData.cardColor,
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: const BorderRadius.only(
|
|
topLeft: Radius.circular(3),
|
|
bottomLeft: Radius.circular(3),
|
|
),
|
|
color: Theme.of(context).hintColor,
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(
|
|
left: 3,
|
|
right: 3,
|
|
),
|
|
child: Text(
|
|
entry.lang!.toUpperCase(),
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
if (!widget.isComfortableGrid && !widget.isCoverOnlyGrid)
|
|
BottomTextWidget(text: entry.name!),
|
|
if (widget.continueReaderBtn)
|
|
Positioned(
|
|
bottom: 0,
|
|
right: 0,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(9),
|
|
child: Consumer(
|
|
builder: (context, ref, child) {
|
|
return StreamBuilder(
|
|
stream: isar.historys
|
|
.filter()
|
|
.idIsNotNull()
|
|
.and()
|
|
.chapter(
|
|
(q) => q.manga(
|
|
(q) =>
|
|
q.itemTypeEqualTo(entry.itemType),
|
|
),
|
|
)
|
|
.watch(fireImmediately: true),
|
|
builder: (context, snapshot) {
|
|
if (snapshot.hasData &&
|
|
snapshot.data!.isNotEmpty) {
|
|
final incognitoMode = ref.watch(
|
|
incognitoModeStateProvider,
|
|
);
|
|
final entries = snapshot.data!
|
|
.where(
|
|
(element) =>
|
|
element.mangaId == entry.id,
|
|
)
|
|
.toList();
|
|
if (entries.isNotEmpty &&
|
|
!incognitoMode) {
|
|
return GestureDetector(
|
|
onTap: () {
|
|
entries.first.chapter.value!
|
|
.pushToReaderView(context);
|
|
},
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius:
|
|
BorderRadius.circular(5),
|
|
color: context.primaryColor
|
|
.withValues(alpha: 0.9),
|
|
),
|
|
child: const Padding(
|
|
padding: EdgeInsets.all(7),
|
|
child: Icon(
|
|
Icons.play_arrow,
|
|
size: 19,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
return GestureDetector(
|
|
onTap: () {
|
|
entry.chapters
|
|
.toList()
|
|
.reversed
|
|
.toList()
|
|
.last
|
|
.pushToReaderView(context);
|
|
},
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(
|
|
5,
|
|
),
|
|
color: context.primaryColor
|
|
.withValues(alpha: 0.9),
|
|
),
|
|
child: const Padding(
|
|
padding: EdgeInsets.all(7),
|
|
child: Icon(
|
|
Icons.play_arrow,
|
|
size: 19,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
return GestureDetector(
|
|
onTap: () {
|
|
entry.chapters
|
|
.toList()
|
|
.reversed
|
|
.toList()
|
|
.last
|
|
.pushToReaderView(context);
|
|
},
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(
|
|
5,
|
|
),
|
|
color: context.primaryColor
|
|
.withValues(alpha: 0.9),
|
|
),
|
|
child: const Padding(
|
|
padding: EdgeInsets.all(7),
|
|
child: Icon(
|
|
Icons.play_arrow,
|
|
size: 19,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
void _handleLongOrSecondaryTap(
|
|
bool isLongPressed,
|
|
WidgetRef ref,
|
|
Manga entry,
|
|
) {
|
|
if (!isLongPressed) {
|
|
ref.read(mangasListStateProvider.notifier).update(entry);
|
|
ref.read(isLongPressedStateProvider.notifier).update(!isLongPressed);
|
|
} else {
|
|
ref.read(mangasListStateProvider.notifier).update(entry);
|
|
}
|
|
}
|
|
}
|