mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-03-11 17:25:32 +00:00
added calendar
This commit is contained in:
parent
6e9b882b83
commit
715f077c13
26 changed files with 798 additions and 25 deletions
|
|
@ -510,5 +510,10 @@
|
|||
"internal_player_info": "Progress, controls, orientation",
|
||||
"subtitle_delay_text": "Subtitle delay",
|
||||
"subtitle_delay": "Delay (ms)",
|
||||
"subtitle_speed": "Speed"
|
||||
"subtitle_speed": "Speed",
|
||||
"calendar": "Calendar",
|
||||
"calendar_no_data": "No data yet.",
|
||||
"calendar_info": "The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!",
|
||||
"in_n_day": "in {days} day",
|
||||
"in_n_days": "in {days} days"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3140,6 +3140,36 @@ abstract class AppLocalizations {
|
|||
/// In en, this message translates to:
|
||||
/// **'Speed'**
|
||||
String get subtitle_speed;
|
||||
|
||||
/// No description provided for @calendar.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Calendar'**
|
||||
String get calendar;
|
||||
|
||||
/// No description provided for @calendar_no_data.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No data yet.'**
|
||||
String get calendar_no_data;
|
||||
|
||||
/// No description provided for @calendar_info.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!'**
|
||||
String get calendar_info;
|
||||
|
||||
/// No description provided for @in_n_day.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'in {days} day'**
|
||||
String in_n_day(Object days);
|
||||
|
||||
/// No description provided for @in_n_days.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'in {days} days'**
|
||||
String in_n_days(Object days);
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
|
|
|||
|
|
@ -1617,4 +1617,24 @@ class AppLocalizationsAr extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1619,4 +1619,24 @@ class AppLocalizationsAs extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1630,4 +1630,24 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1618,4 +1618,24 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1635,6 +1635,26 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
||||
/// The translations for Spanish Castilian, as used in Latin America and the Caribbean (`es_419`).
|
||||
|
|
|
|||
|
|
@ -1636,4 +1636,24 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1620,4 +1620,24 @@ class AppLocalizationsHi extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1624,4 +1624,24 @@ class AppLocalizationsId extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1633,4 +1633,24 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1632,6 +1632,26 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
||||
/// The translations for Portuguese, as used in Brazil (`pt_BR`).
|
||||
|
|
|
|||
|
|
@ -1634,4 +1634,24 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1618,4 +1618,24 @@ class AppLocalizationsTh extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1624,4 +1624,24 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1589,4 +1589,24 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get subtitle_speed => 'Speed';
|
||||
|
||||
@override
|
||||
String get calendar => 'Calendar';
|
||||
|
||||
@override
|
||||
String get calendar_no_data => 'No data yet.';
|
||||
|
||||
@override
|
||||
String get calendar_info =>
|
||||
'The calendar is only able to predict the next chapter upload based on the older uploads. Some data might not be 100% accurate!';
|
||||
|
||||
@override
|
||||
String in_n_day(Object days) {
|
||||
return 'in $days day';
|
||||
}
|
||||
|
||||
@override
|
||||
String in_n_days(Object days) {
|
||||
return 'in $days days';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,17 +252,7 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
|||
),
|
||||
);
|
||||
late final hwdecMode = ref.read(hwdecModeStateProvider());
|
||||
late final VideoController _controller = VideoController(
|
||||
_player,
|
||||
configuration: VideoControllerConfiguration(
|
||||
hwdec: hwdecMode,
|
||||
vo: Platform.isAndroid
|
||||
? useGpuNext
|
||||
? "gpu-next"
|
||||
: "gpu"
|
||||
: "libmpv",
|
||||
),
|
||||
);
|
||||
late final VideoController _controller;
|
||||
late final _streamController = ref.read(
|
||||
animeStreamControllerProvider(episode: widget.episode).notifier,
|
||||
);
|
||||
|
|
@ -846,6 +836,17 @@ mp.register_script_message('call_button_${button.id}_long', button${button.id}lo
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = VideoController(
|
||||
_player,
|
||||
configuration: VideoControllerConfiguration(
|
||||
hwdec: hwdecMode,
|
||||
vo: Platform.isAndroid
|
||||
? useGpuNext
|
||||
? "gpu-next"
|
||||
: "gpu"
|
||||
: "libmpv",
|
||||
),
|
||||
);
|
||||
// If player is being launched the first time,
|
||||
// use global "Use Fullscreen" setting.
|
||||
// Else (if user already watches an episode and just changes it),
|
||||
|
|
|
|||
372
lib/modules/calendar/calendar_screen.dart
Normal file
372
lib/modules/calendar/calendar_screen.dart
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/modules/calendar/providers/calendar_provider.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_sliver_grouped_list_view.dart';
|
||||
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
import 'package:mangayomi/utils/date.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
import 'package:mangayomi/utils/headers.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
|
||||
class CalendarScreen extends ConsumerStatefulWidget {
|
||||
const CalendarScreen({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<CalendarScreen> createState() => _CalendarScreenState();
|
||||
}
|
||||
|
||||
class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
late final ValueNotifier<List<Manga>> _selectedEntries;
|
||||
CalendarFormat _calendarFormat = CalendarFormat.month;
|
||||
RangeSelectionMode _rangeSelectionMode = RangeSelectionMode.toggledOff;
|
||||
final firstDay = DateTime.now();
|
||||
final lastDay = DateTime.now().add(const Duration(days: 1000));
|
||||
DateTime _focusedDay = DateTime.now();
|
||||
DateTime? _selectedDay;
|
||||
DateTime? _rangeStart;
|
||||
DateTime? _rangeEnd;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedDay = _focusedDay;
|
||||
_selectedEntries = ValueNotifier([]);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_selectedEntries.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final locale = ref.watch(l10nLocaleStateProvider);
|
||||
final data = ref.watch(getCalendarStreamProvider);
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(l10n.calendar)),
|
||||
body: data.when(
|
||||
data: (data) {
|
||||
if (data.isEmpty) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(l10n.calendar_no_data, textAlign: TextAlign.center),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (_selectedDay != null) {
|
||||
_selectedEntries.value = _getEntriesForDay(_selectedDay!, data);
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 3),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.warning_amber_outlined,
|
||||
color: context.secondaryColor,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
l10n.calendar_info,
|
||||
softWrap: true,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: context.secondaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
TableCalendar(
|
||||
firstDay: firstDay,
|
||||
lastDay: lastDay,
|
||||
focusedDay: _focusedDay,
|
||||
locale: locale.toLanguageTag(),
|
||||
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
|
||||
rangeStartDay: _rangeStart,
|
||||
rangeEndDay: _rangeEnd,
|
||||
calendarFormat: _calendarFormat,
|
||||
rangeSelectionMode: _rangeSelectionMode,
|
||||
eventLoader: (day) => _getEntriesForDay(day, data),
|
||||
startingDayOfWeek: StartingDayOfWeek.monday,
|
||||
calendarStyle: const CalendarStyle(outsideDaysVisible: false),
|
||||
onDaySelected: (selectedDay, focusedDay) =>
|
||||
_onDaySelected(selectedDay, focusedDay, data),
|
||||
onRangeSelected: (start, end, focusedDay) =>
|
||||
_onRangeSelected(start, end, focusedDay, data),
|
||||
onFormatChanged: (format) {
|
||||
if (_calendarFormat != format) {
|
||||
setState(() {
|
||||
_calendarFormat = format;
|
||||
});
|
||||
}
|
||||
},
|
||||
onPageChanged: (focusedDay) {
|
||||
_focusedDay = focusedDay;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Expanded(
|
||||
child: ValueListenableBuilder<List<Manga>>(
|
||||
valueListenable: _selectedEntries,
|
||||
builder: (context, value, _) {
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
CustomSliverGroupedListView<Manga, String>(
|
||||
elements: value,
|
||||
groupBy: (element) {
|
||||
return dateFormat(
|
||||
_selectedDay?.millisecondsSinceEpoch
|
||||
.toString() ??
|
||||
DateTime.now()
|
||||
.add(
|
||||
Duration(
|
||||
days: element.smartUpdateDays!,
|
||||
),
|
||||
)
|
||||
.millisecondsSinceEpoch
|
||||
.toString(),
|
||||
context: context,
|
||||
ref: ref,
|
||||
forHistoryValue: true,
|
||||
useRelativeTimesTamps: false,
|
||||
);
|
||||
},
|
||||
groupSeparatorBuilder: (String groupByValue) => Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 8,
|
||||
left: 12,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
"${dateFormat(null, context: context, stringDate: groupByValue, ref: ref, useRelativeTimesTamps: true, showInDaysFuture: true)} - ${dateFormat(null, context: context, stringDate: groupByValue, ref: ref, useRelativeTimesTamps: false)}",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
itemBuilder: (context, element) {
|
||||
return CalendarListTileWidget(
|
||||
manga: element,
|
||||
selectedDay: _selectedDay,
|
||||
);
|
||||
},
|
||||
order: GroupedListOrder.ASC,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
error: (Object error, StackTrace stackTrace) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(l10n.calendar_no_data, textAlign: TextAlign.center),
|
||||
),
|
||||
);
|
||||
},
|
||||
loading: () {
|
||||
return const ProgressCenter();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Manga> _getEntriesForDay(DateTime day, List<Manga> data) {
|
||||
return data.where((e) {
|
||||
final temp = DateTime.now().add(Duration(days: e.smartUpdateDays!));
|
||||
final predictedDay = "${temp.year}-${temp.month}-${temp.day}";
|
||||
final selectedDay = "${day.year}-${day.month}-${day.day}";
|
||||
return predictedDay == selectedDay;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
List<Manga> _getEntriesForRange(
|
||||
DateTime start,
|
||||
DateTime end,
|
||||
List<Manga> data,
|
||||
) {
|
||||
final days = _daysInRange(start, end);
|
||||
|
||||
return [for (final d in days) ..._getEntriesForDay(d, data)];
|
||||
}
|
||||
|
||||
void _onDaySelected(
|
||||
DateTime selectedDay,
|
||||
DateTime focusedDay,
|
||||
List<Manga> data,
|
||||
) {
|
||||
if (!isSameDay(_selectedDay, selectedDay)) {
|
||||
setState(() {
|
||||
_selectedDay = selectedDay;
|
||||
_focusedDay = focusedDay;
|
||||
_rangeStart = null;
|
||||
_rangeEnd = null;
|
||||
_rangeSelectionMode = RangeSelectionMode.toggledOff;
|
||||
});
|
||||
|
||||
_selectedEntries.value = _getEntriesForDay(selectedDay, data);
|
||||
}
|
||||
}
|
||||
|
||||
void _onRangeSelected(
|
||||
DateTime? start,
|
||||
DateTime? end,
|
||||
DateTime focusedDay,
|
||||
List<Manga> data,
|
||||
) {
|
||||
setState(() {
|
||||
_selectedDay = null;
|
||||
_focusedDay = focusedDay;
|
||||
_rangeStart = start;
|
||||
_rangeEnd = end;
|
||||
_rangeSelectionMode = RangeSelectionMode.toggledOn;
|
||||
});
|
||||
|
||||
if (start != null && end != null) {
|
||||
_selectedEntries.value = _getEntriesForRange(start, end, data);
|
||||
} else if (start != null) {
|
||||
_selectedEntries.value = _getEntriesForDay(start, data);
|
||||
} else if (end != null) {
|
||||
_selectedEntries.value = _getEntriesForDay(end, data);
|
||||
}
|
||||
}
|
||||
|
||||
List<DateTime> _daysInRange(DateTime first, DateTime last) {
|
||||
final dayCount = last.difference(first).inDays + 1;
|
||||
return List.generate(
|
||||
dayCount,
|
||||
(index) => DateTime.utc(first.year, first.month, first.day + index),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CalendarListTileWidget extends ConsumerWidget {
|
||||
final Manga manga;
|
||||
final DateTime? selectedDay;
|
||||
const CalendarListTileWidget({
|
||||
required this.manga,
|
||||
required this.selectedDay,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Material(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Colors.transparent,
|
||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||
child: InkWell(
|
||||
onTap: () => context.push('/manga-reader/detail', extra: manga.id),
|
||||
onLongPress: () {},
|
||||
onSecondaryTap: () {},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 5),
|
||||
child: Container(
|
||||
height: 45,
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5)),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
child: Material(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.push(
|
||||
'/manga-reader/detail',
|
||||
extra: manga.id,
|
||||
);
|
||||
},
|
||||
child: Ink.image(
|
||||
fit: BoxFit.cover,
|
||||
width: 40,
|
||||
height: 45,
|
||||
image: manga.customCoverImage != null
|
||||
? MemoryImage(
|
||||
manga.customCoverImage as Uint8List,
|
||||
)
|
||||
as ImageProvider
|
||||
: CustomExtendedNetworkImageProvider(
|
||||
toImgUrl(
|
||||
manga.customCoverFromTracker ??
|
||||
manga.imageUrl!,
|
||||
),
|
||||
headers: ref.watch(
|
||||
headersProvider(
|
||||
source: manga.source!,
|
||||
lang: manga.lang!,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: InkWell(child: Container()),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
manga.name!,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).textTheme.bodyLarge!.color,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
context.l10n.n_chapters(
|
||||
manga.chapters.countSync(),
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontStyle: FontStyle.italic,
|
||||
color: context.secondaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
21
lib/modules/calendar/providers/calendar_provider.dart
Normal file
21
lib/modules/calendar/providers/calendar_provider.dart
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
part 'calendar_provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
Stream<List<Manga>> getCalendarStream(Ref ref) async* {
|
||||
yield* isar.mangas
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.anyOf([
|
||||
Status.ongoing,
|
||||
Status.unknown,
|
||||
Status.publishingFinished,
|
||||
], (q, status) => q.statusEqualTo(status))
|
||||
.smartUpdateDaysIsNotNull()
|
||||
.smartUpdateDaysGreaterThan(0)
|
||||
.watch(fireImmediately: true);
|
||||
}
|
||||
28
lib/modules/calendar/providers/calendar_provider.g.dart
Normal file
28
lib/modules/calendar/providers/calendar_provider.g.dart
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'calendar_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$getCalendarStreamHash() => r'9095ecdef36259f84eb6562a2ca9e253f50d4b8d';
|
||||
|
||||
/// See also [getCalendarStream].
|
||||
@ProviderFor(getCalendarStream)
|
||||
final getCalendarStreamProvider =
|
||||
AutoDisposeStreamProvider<List<Manga>>.internal(
|
||||
getCalendarStream,
|
||||
name: r'getCalendarStreamProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$getCalendarStreamHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef GetCalendarStreamRef = AutoDisposeStreamProviderRef<List<Manga>>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||
import 'package:mangayomi/eval/model/m_manga.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
|
|
@ -147,9 +149,10 @@ Future<dynamic> updateMangaDetail(
|
|||
isar.mangas.putSync(
|
||||
manga
|
||||
..id = mangaId
|
||||
..smartUpdateDays = median != 0
|
||||
? median
|
||||
: daysBetweenUploads.arithmeticMean(),
|
||||
..smartUpdateDays = max(
|
||||
median,
|
||||
daysBetweenUploads.arithmeticMean(),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -60,6 +60,13 @@ class MoreScreen extends StatelessWidget {
|
|||
icon: Icons.query_stats_outlined,
|
||||
title: l10n.statistics,
|
||||
),
|
||||
ListTileWidget(
|
||||
onTap: () {
|
||||
context.push('/calendarScreen');
|
||||
},
|
||||
icon: Icons.calendar_month_outlined,
|
||||
title: l10n.calendar,
|
||||
),
|
||||
ListTileWidget(
|
||||
onTap: () {
|
||||
context.push('/dataAndStorage');
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import 'package:mangayomi/modules/browse/extension/edit_code.dart';
|
|||
import 'package:mangayomi/modules/browse/extension/extension_detail.dart';
|
||||
import 'package:mangayomi/modules/browse/extension/widgets/create_extension.dart';
|
||||
import 'package:mangayomi/modules/browse/sources/sources_filter_screen.dart';
|
||||
import 'package:mangayomi/modules/calendar/calendar_screen.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/migrate_screen.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/create_backup.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/data_and_storage.dart';
|
||||
|
|
@ -233,6 +234,7 @@ class RouterNotifier extends ChangeNotifier {
|
|||
name: "playerAdvancedScreen",
|
||||
child: const PlayerAdvancedScreen(),
|
||||
),
|
||||
_genericRoute(name: "calendarScreen", child: const CalendarScreen()),
|
||||
_genericRoute<Manga>(
|
||||
name: "migrate",
|
||||
builder: (manga) => MigrationScreen(manga: manga),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ String dateFormat(
|
|||
bool useRelativeTimesTamps = true,
|
||||
String dateFormat = "",
|
||||
bool showHOURorMINUTE = false,
|
||||
bool showInDaysFuture = false,
|
||||
}) {
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
final locale = currentLocale(context);
|
||||
|
|
@ -70,8 +71,14 @@ String dateFormat(
|
|||
date.isAfter(aWeekAgo)) {
|
||||
final difference = today.difference(date).inDays;
|
||||
return switch (difference) {
|
||||
1 => l10n.n_day_ago(difference),
|
||||
!= 7 => l10n.n_days_ago(difference),
|
||||
1 =>
|
||||
showInDaysFuture
|
||||
? l10n.in_n_day(difference.abs())
|
||||
: l10n.n_day_ago(difference),
|
||||
!= 7 =>
|
||||
showInDaysFuture
|
||||
? l10n.in_n_days(difference.abs())
|
||||
: l10n.n_days_ago(difference),
|
||||
_ => l10n.a_week_ago,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
32
pubspec.lock
32
pubspec.lock
|
|
@ -1164,7 +1164,7 @@ packages:
|
|||
description:
|
||||
path: media_kit
|
||||
ref: HEAD
|
||||
resolved-ref: "58c7aaf631e9cfeaed622074b19f81464fb51bec"
|
||||
resolved-ref: d0e86e0177c4da3786c72a396259be530e4a6354
|
||||
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||
source: git
|
||||
version: "1.2.0"
|
||||
|
|
@ -1173,7 +1173,7 @@ packages:
|
|||
description:
|
||||
path: "libs/android/media_kit_libs_android_video"
|
||||
ref: HEAD
|
||||
resolved-ref: "58c7aaf631e9cfeaed622074b19f81464fb51bec"
|
||||
resolved-ref: d0e86e0177c4da3786c72a396259be530e4a6354
|
||||
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||
source: git
|
||||
version: "1.3.7"
|
||||
|
|
@ -1182,7 +1182,7 @@ packages:
|
|||
description:
|
||||
path: "libs/ios/media_kit_libs_ios_video"
|
||||
ref: HEAD
|
||||
resolved-ref: "58c7aaf631e9cfeaed622074b19f81464fb51bec"
|
||||
resolved-ref: d0e86e0177c4da3786c72a396259be530e4a6354
|
||||
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||
source: git
|
||||
version: "1.1.4"
|
||||
|
|
@ -1191,7 +1191,7 @@ packages:
|
|||
description:
|
||||
path: "libs/linux/media_kit_libs_linux"
|
||||
ref: HEAD
|
||||
resolved-ref: "58c7aaf631e9cfeaed622074b19f81464fb51bec"
|
||||
resolved-ref: d0e86e0177c4da3786c72a396259be530e4a6354
|
||||
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||
source: git
|
||||
version: "1.2.1"
|
||||
|
|
@ -1200,7 +1200,7 @@ packages:
|
|||
description:
|
||||
path: "libs/macos/media_kit_libs_macos_video"
|
||||
ref: HEAD
|
||||
resolved-ref: "58c7aaf631e9cfeaed622074b19f81464fb51bec"
|
||||
resolved-ref: d0e86e0177c4da3786c72a396259be530e4a6354
|
||||
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||
source: git
|
||||
version: "1.1.4"
|
||||
|
|
@ -1209,7 +1209,7 @@ packages:
|
|||
description:
|
||||
path: "libs/universal/media_kit_libs_video"
|
||||
ref: HEAD
|
||||
resolved-ref: "58c7aaf631e9cfeaed622074b19f81464fb51bec"
|
||||
resolved-ref: d0e86e0177c4da3786c72a396259be530e4a6354
|
||||
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||
source: git
|
||||
version: "1.0.6"
|
||||
|
|
@ -1218,7 +1218,7 @@ packages:
|
|||
description:
|
||||
path: "libs/windows/media_kit_libs_windows_video"
|
||||
ref: HEAD
|
||||
resolved-ref: "58c7aaf631e9cfeaed622074b19f81464fb51bec"
|
||||
resolved-ref: d0e86e0177c4da3786c72a396259be530e4a6354
|
||||
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||
source: git
|
||||
version: "1.0.11"
|
||||
|
|
@ -1227,7 +1227,7 @@ packages:
|
|||
description:
|
||||
path: media_kit_video
|
||||
ref: HEAD
|
||||
resolved-ref: "58c7aaf631e9cfeaed622074b19f81464fb51bec"
|
||||
resolved-ref: d0e86e0177c4da3786c72a396259be530e4a6354
|
||||
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||
source: git
|
||||
version: "1.3.0"
|
||||
|
|
@ -1743,6 +1743,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
simple_gesture_detector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: simple_gesture_detector
|
||||
sha256: ba2cd5af24ff20a0b8d609cec3f40e5b0744d2a71804a2616ae086b9c19d19a3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
|
@ -1876,6 +1884,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.1"
|
||||
table_calendar:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: table_calendar
|
||||
sha256: "0c0c6219878b363a2d5f40c7afb159d845f253d061dc3c822aa0d5fe0f721982"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ dependencies:
|
|||
git:
|
||||
url: https://github.com/Schnitzel5/flutter-discord-rpc.git
|
||||
ref: main
|
||||
table_calendar: ^3.2.0
|
||||
|
||||
dependency_overrides:
|
||||
ffi: ^2.1.3
|
||||
|
|
|
|||
Loading…
Reference in a new issue