mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-04-20 19:12:04 +00:00
Merge pull request #520 from NBA2K1/better-UX
Improved UX for Migration, Anime Playback & Navigation
This commit is contained in:
commit
936a91403b
5 changed files with 125 additions and 116 deletions
|
|
@ -49,7 +49,7 @@ class _DesktopControllerWidgetState
|
|||
bool visible = true;
|
||||
bool cursorVisible = true;
|
||||
Duration controlsTransitionDuration = const Duration(milliseconds: 300);
|
||||
Color backdropColor = const Color(0x66000000);
|
||||
// Color backdropColor = const Color(0x66000000);
|
||||
Timer? _timer;
|
||||
|
||||
int swipeDuration = 0; // Duration to seek in video
|
||||
|
|
@ -63,6 +63,7 @@ class _DesktopControllerWidgetState
|
|||
|
||||
final List<StreamSubscription> subscriptions = [];
|
||||
DateTime last = DateTime.now();
|
||||
Timer? _tapTimer;
|
||||
|
||||
@override
|
||||
void setState(VoidCallback fn) {
|
||||
|
|
@ -98,6 +99,9 @@ class _DesktopControllerWidgetState
|
|||
for (final subscription in subscriptions) {
|
||||
subscription.cancel();
|
||||
}
|
||||
subscriptions.clear();
|
||||
_timer?.cancel();
|
||||
_tapTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -146,8 +150,8 @@ class _DesktopControllerWidgetState
|
|||
_timer?.cancel();
|
||||
}
|
||||
|
||||
final bool modifyVolumeOnScroll = true;
|
||||
final bool toggleFullscreenOnDoublePress = true;
|
||||
final bool modifyVolumeOnScroll = true; // TODO. The variable is never changed
|
||||
final bool toggleFullscreenOnDoublePress = true; // TODO. variable not changed
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CallbackShortcuts(
|
||||
|
|
@ -250,6 +254,14 @@ class _DesktopControllerWidgetState
|
|||
}
|
||||
: null,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// use own timer with onTapUp instead of onDoubleTap.
|
||||
// onDoubleTap uses 300ms which feels laggy when pausing
|
||||
// https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/gestures/constants.dart#L35
|
||||
_tapTimer = Timer(const Duration(milliseconds: 100), () {
|
||||
widget.videoController.player.playOrPause();
|
||||
});
|
||||
},
|
||||
onLongPressStart: (e) {
|
||||
previousPlaybackSpeed =
|
||||
widget.videoController.player.state.rate;
|
||||
|
|
@ -274,6 +286,8 @@ class _DesktopControllerWidgetState
|
|||
final difference = now.difference(last);
|
||||
last = now;
|
||||
if (difference < const Duration(milliseconds: 400)) {
|
||||
_tapTimer?.cancel();
|
||||
_tapTimer = null;
|
||||
final fullScreen = widget.desktopFullScreenPlayer;
|
||||
await _changeFullScreen(ref, fullScreen);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,34 +126,10 @@ class _LibraryGridViewWidgetState extends State<LibraryGridViewWidget> {
|
|||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
if (!isLongPressed) {
|
||||
ref
|
||||
.read(mangasListStateProvider.notifier)
|
||||
.update(entry);
|
||||
|
||||
ref
|
||||
.read(isLongPressedMangaStateProvider.notifier)
|
||||
.update(!isLongPressed);
|
||||
} else {
|
||||
ref
|
||||
.read(mangasListStateProvider.notifier)
|
||||
.update(entry);
|
||||
}
|
||||
_handleLongOrSecondaryTap(isLongPressed, ref, entry);
|
||||
},
|
||||
onSecondaryTap: () {
|
||||
if (!isLongPressed) {
|
||||
ref
|
||||
.read(mangasListStateProvider.notifier)
|
||||
.update(entry);
|
||||
|
||||
ref
|
||||
.read(isLongPressedMangaStateProvider.notifier)
|
||||
.update(!isLongPressed);
|
||||
} else {
|
||||
ref
|
||||
.read(mangasListStateProvider.notifier)
|
||||
.update(entry);
|
||||
}
|
||||
_handleLongOrSecondaryTap(isLongPressed, ref, entry);
|
||||
},
|
||||
children: [
|
||||
Stack(
|
||||
|
|
@ -446,4 +422,17 @@ class _LibraryGridViewWidgetState extends State<LibraryGridViewWidget> {
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _handleLongOrSecondaryTap(
|
||||
bool isLongPressed,
|
||||
WidgetRef ref,
|
||||
Manga entry,
|
||||
) {
|
||||
if (!isLongPressed) {
|
||||
ref.read(mangasListStateProvider.notifier).update(entry);
|
||||
ref.read(isLongPressedMangaStateProvider.notifier).update(!isLongPressed);
|
||||
} else {
|
||||
ref.read(mangasListStateProvider.notifier).update(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ class SeachFormTextField extends StatelessWidget {
|
|||
final VoidCallback onSuffixPressed;
|
||||
final TextEditingController controller;
|
||||
final Function(String)? onFieldSubmitted;
|
||||
final bool autofocus;
|
||||
const SeachFormTextField({
|
||||
super.key,
|
||||
required this.onChanged,
|
||||
|
|
@ -14,6 +15,7 @@ class SeachFormTextField extends StatelessWidget {
|
|||
required this.controller,
|
||||
this.onFieldSubmitted,
|
||||
required this.onSuffixPressed,
|
||||
this.autofocus = true,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -21,7 +23,7 @@ class SeachFormTextField extends StatelessWidget {
|
|||
final l10n = l10nLocalizations(context)!;
|
||||
return Flexible(
|
||||
child: TextFormField(
|
||||
autofocus: true,
|
||||
autofocus: autofocus,
|
||||
controller: controller,
|
||||
keyboardType: TextInputType.text,
|
||||
onChanged: onChanged,
|
||||
|
|
|
|||
|
|
@ -24,113 +24,116 @@ class ChapterListTileWidget extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isLongPressed = ref.watch(isLongPressedStateProvider);
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
return Container(
|
||||
color: chapterList.contains(chapter)
|
||||
? context.primaryColor.withValues(alpha: 0.4)
|
||||
: null,
|
||||
child: ListTile(
|
||||
textColor: chapter.isRead!
|
||||
? context.isLight
|
||||
? Colors.black.withValues(alpha: 0.4)
|
||||
: Colors.white.withValues(alpha: 0.3)
|
||||
: null,
|
||||
selectedColor: chapter.isRead!
|
||||
? Colors.white.withValues(alpha: 0.3)
|
||||
: Colors.white,
|
||||
onLongPress: () {
|
||||
if (!isLongPressed) {
|
||||
ref.read(chaptersListStateProvider.notifier).update(chapter);
|
||||
|
||||
ref
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(!isLongPressed);
|
||||
} else {
|
||||
ref.read(chaptersListStateProvider.notifier).update(chapter);
|
||||
}
|
||||
},
|
||||
onTap: () async {
|
||||
if (isLongPressed) {
|
||||
ref.read(chaptersListStateProvider.notifier).update(chapter);
|
||||
} else {
|
||||
chapter.pushToReaderView(context, ignoreIsRead: true);
|
||||
}
|
||||
},
|
||||
title: Row(
|
||||
children: [
|
||||
chapter.isBookmarked!
|
||||
? Icon(Icons.bookmark, size: 16, color: context.primaryColor)
|
||||
: Container(),
|
||||
Flexible(child: _buildTitle(chapter.name!, context)),
|
||||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
children: [
|
||||
if ((chapter.manga.value!.isLocalArchive ?? false) == false)
|
||||
Text(
|
||||
chapter.dateUpload == null || chapter.dateUpload!.isEmpty
|
||||
? ""
|
||||
: dateFormat(
|
||||
chapter.dateUpload!,
|
||||
ref: ref,
|
||||
context: context,
|
||||
child: GestureDetector(
|
||||
onLongPress: () => _handleInteraction(ref),
|
||||
onSecondaryTap: () => _handleInteraction(ref),
|
||||
child: ListTile(
|
||||
textColor: chapter.isRead!
|
||||
? context.isLight
|
||||
? Colors.black.withValues(alpha: 0.4)
|
||||
: Colors.white.withValues(alpha: 0.3)
|
||||
: null,
|
||||
selectedColor: chapter.isRead!
|
||||
? Colors.white.withValues(alpha: 0.3)
|
||||
: Colors.white,
|
||||
onTap: () async => _handleInteraction(ref, context),
|
||||
title: Row(
|
||||
children: [
|
||||
chapter.isBookmarked!
|
||||
? Icon(Icons.bookmark, size: 16, color: context.primaryColor)
|
||||
: Container(),
|
||||
Flexible(child: _buildTitle(chapter.name!, context)),
|
||||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
children: [
|
||||
if ((chapter.manga.value!.isLocalArchive ?? false) == false)
|
||||
Text(
|
||||
chapter.dateUpload == null || chapter.dateUpload!.isEmpty
|
||||
? ""
|
||||
: dateFormat(
|
||||
chapter.dateUpload!,
|
||||
ref: ref,
|
||||
context: context,
|
||||
),
|
||||
style: const TextStyle(fontSize: 11),
|
||||
),
|
||||
if (!chapter.isRead!)
|
||||
if (chapter.lastPageRead!.isNotEmpty &&
|
||||
chapter.lastPageRead != "1")
|
||||
Row(
|
||||
children: [
|
||||
const Text(' • '),
|
||||
Text(
|
||||
chapter.manga.value!.itemType == ItemType.anime
|
||||
? l10n.episode_progress(
|
||||
Duration(
|
||||
milliseconds: int.parse(
|
||||
chapter.lastPageRead!,
|
||||
),
|
||||
).toString().substringBefore("."),
|
||||
)
|
||||
: l10n.page(
|
||||
chapter.manga.value!.itemType == ItemType.manga
|
||||
? chapter.lastPageRead!
|
||||
: "${((double.tryParse(chapter.lastPageRead!) ?? 0) * 100).toStringAsFixed(0)} %",
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: context.isLight
|
||||
? Colors.black.withValues(alpha: 0.4)
|
||||
: Colors.white.withValues(alpha: 0.3),
|
||||
),
|
||||
),
|
||||
style: const TextStyle(fontSize: 11),
|
||||
),
|
||||
if (!chapter.isRead!)
|
||||
if (chapter.lastPageRead!.isNotEmpty &&
|
||||
chapter.lastPageRead != "1")
|
||||
],
|
||||
),
|
||||
if (chapter.scanlator?.isNotEmpty ?? false)
|
||||
Row(
|
||||
children: [
|
||||
const Text(' • '),
|
||||
Text(
|
||||
chapter.manga.value!.itemType == ItemType.anime
|
||||
? l10n.episode_progress(
|
||||
Duration(
|
||||
milliseconds: int.parse(chapter.lastPageRead!),
|
||||
).toString().substringBefore("."),
|
||||
)
|
||||
: l10n.page(
|
||||
chapter.manga.value!.itemType == ItemType.manga
|
||||
? chapter.lastPageRead!
|
||||
: "${((double.tryParse(chapter.lastPageRead!) ?? 0) * 100).toStringAsFixed(0)} %",
|
||||
),
|
||||
chapter.scanlator!,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: context.isLight
|
||||
? Colors.black.withValues(alpha: 0.4)
|
||||
: Colors.white.withValues(alpha: 0.3),
|
||||
color: chapter.isRead!
|
||||
? context.isLight
|
||||
? Colors.black.withValues(alpha: 0.4)
|
||||
: Colors.white.withValues(alpha: 0.3)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (chapter.scanlator?.isNotEmpty ?? false)
|
||||
Row(
|
||||
children: [
|
||||
const Text(' • '),
|
||||
Text(
|
||||
chapter.scanlator!,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: chapter.isRead!
|
||||
? context.isLight
|
||||
? Colors.black.withValues(alpha: 0.4)
|
||||
: Colors.white.withValues(alpha: 0.3)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
trailing:
|
||||
!sourceExist || (chapter.manga.value!.isLocalArchive ?? false)
|
||||
? null
|
||||
: ChapterPageDownload(chapter: chapter),
|
||||
),
|
||||
trailing: !sourceExist || (chapter.manga.value!.isLocalArchive ?? false)
|
||||
? null
|
||||
: ChapterPageDownload(chapter: chapter),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleInteraction(WidgetRef ref, [BuildContext? context]) {
|
||||
final isLongPressed = ref.read(isLongPressedStateProvider);
|
||||
if (isLongPressed) {
|
||||
ref.read(chaptersListStateProvider.notifier).update(chapter);
|
||||
} else {
|
||||
if (context != null) {
|
||||
chapter.pushToReaderView(context, ignoreIsRead: true);
|
||||
} else {
|
||||
ref.read(chaptersListStateProvider.notifier).update(chapter);
|
||||
ref.read(isLongPressedStateProvider.notifier).update(!isLongPressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildTitle(String text, BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ class _MigrationScreenScreenState extends ConsumerState<MigrationScreen> {
|
|||
});
|
||||
},
|
||||
controller: _textEditingController,
|
||||
autofocus: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
Loading…
Reference in a new issue