Compare commits

...

16 commits

Author SHA1 Message Date
Neeraj Gupta f5093ddf63 Stick to old grouping behaviour for magicsearch 2024-05-27 18:07:07 +05:30
Neeraj Gupta ac3c6b10a5 Gallery: Support for group by size 2024-05-27 18:03:26 +05:30
Neeraj Gupta 95bb363aba Add widget to show fileSize overlay 2024-05-27 15:33:29 +05:30
Neeraj Gupta bcf3084d97 Merge branch 'main' into generic_group_by 2024-05-27 11:54:37 +05:30
Neeraj Gupta 5e4d530b93 [mob] Fix range calculation while refresh lazy gallery group 2024-05-23 15:18:02 +05:30
Neeraj Gupta 22ff318249 [mob] Fix bug in daysInSameWeek check 2024-05-23 15:11:41 +05:30
Neeraj Gupta 643b77e81e Fix title for month 2024-05-23 14:18:37 +05:30
Neeraj Gupta 3ce8a09e39 Merge branch 'main' into generic_group_by 2024-05-23 14:13:35 +05:30
Neeraj Gupta ce6160a06a Merge branch 'main' into generic_group_by 2024-05-22 15:18:44 +05:30
Neeraj Gupta c21a0cfdb4 [mob] Lint fix 2024-05-21 17:46:23 +05:30
Neeraj Gupta 241c755446 Merge branch 'main' into generic_group_by 2024-05-21 17:45:27 +05:30
Neeraj Gupta cff695dd02 [mob] Fix title for month grouping 2024-05-04 12:35:33 +05:30
Neeraj Gupta 5f9b0d11f2 [mob] Gallery: Support grouping by day/week/month/year 2024-05-04 12:31:08 +05:30
Neeraj Gupta e75be714d9 [mob] Refactor groupHeader to use groupType 2024-05-04 12:07:58 +05:30
Neeraj Gupta da329c498c [mob] Add groupType in Gallery context state 2024-05-04 12:00:16 +05:30
Neeraj Gupta cc74e08155 [mob] Add groupType with common extn methods 2024-05-04 11:56:29 +05:30
9 changed files with 345 additions and 59 deletions

View file

@ -5,9 +5,11 @@ import 'package:flutter/material.dart';
import 'package:photos/ente_theme_data.dart';
import "package:photos/generated/l10n.dart";
import "package:photos/models/api/collection/user.dart";
import "package:photos/models/file/file.dart";
import 'package:photos/models/file/trash_file.dart';
import 'package:photos/theme/colors.dart';
import 'package:photos/ui/sharing/user_avator_widget.dart';
import "package:photos/utils/data_util.dart";
class ThumbnailPlaceHolder extends StatelessWidget {
const ThumbnailPlaceHolder({Key? key}) : super(key: key);
@ -143,15 +145,38 @@ class OwnerAvatarOverlayIcon extends StatelessWidget {
class TrashedFileOverlayText extends StatelessWidget {
final TrashFile file;
const TrashedFileOverlayText(this.file, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final int daysLeft =
((file.deleteBy - DateTime.now().microsecondsSinceEpoch) /
Duration.microsecondsPerDay)
.ceil();
final text = S.of(context).trashDaysLeft(daysLeft);
return FileOverlayText(text);
}
}
class FileSizeOverlayText extends StatelessWidget {
final EnteFile file;
const FileSizeOverlayText(this.file, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
if (file.fileSize == null) {
return const SizedBox.shrink();
}
final text = convertBytesToReadableFormat(file.fileSize!);
return FileOverlayText(text);
}
}
class FileOverlayText extends StatelessWidget {
final String text;
const FileOverlayText(this.text, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
@ -163,7 +188,7 @@ class TrashedFileOverlayText extends StatelessWidget {
alignment: Alignment.bottomCenter,
padding: const EdgeInsets.only(bottom: 5),
child: Text(
S.of(context).trashDaysLeft(daysLeft),
text,
style: Theme.of(context)
.textTheme
.titleSmall!

View file

@ -18,6 +18,8 @@ import 'package:photos/models/file/trash_file.dart';
import 'package:photos/services/collections_service.dart';
import 'package:photos/services/favorites_service.dart';
import 'package:photos/ui/viewer/file/file_icons_widget.dart';
import "package:photos/ui/viewer/gallery/component/group/type.dart";
import "package:photos/ui/viewer/gallery/state/gallery_context_state.dart";
import 'package:photos/utils/file_util.dart';
import 'package:photos/utils/thumbnail_util.dart';
@ -178,6 +180,8 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
if (widget.file.isTrash) {
viewChildren.add(TrashedFileOverlayText(widget.file as TrashFile));
} else if (GalleryContextState.of(context)?.type == GroupType.size) {
viewChildren.add(FileSizeOverlayText(widget.file));
}
// todo: Move this icon overlay to the collection widget.
if (widget.shouldShowArchiveStatus) {

View file

@ -1,16 +1,15 @@
import "package:flutter/cupertino.dart";
import "package:intl/intl.dart";
import 'package:photos/core/constants.dart';
import "package:photos/generated/l10n.dart";
import "package:photos/theme/ente_theme.dart";
class GroupHeaderWidget extends StatelessWidget {
final int timestamp;
final String title;
final int gridSize;
const GroupHeaderWidget({
super.key,
required this.timestamp,
required this.title,
required this.gridSize,
});
@ -22,7 +21,7 @@ class GroupHeaderWidget extends StatelessWidget {
gridSize < photoGridSizeMax ? textTheme.body : textTheme.small;
final double horizontalPadding = gridSize < photoGridSizeMax ? 12.0 : 8.0;
final double verticalPadding = gridSize < photoGridSizeMax ? 12.0 : 14.0;
final String dayTitle = _getDayTitle(context, timestamp);
return Padding(
padding: EdgeInsets.symmetric(
horizontal: horizontalPadding,
@ -31,33 +30,12 @@ class GroupHeaderWidget extends StatelessWidget {
child: Container(
alignment: Alignment.centerLeft,
child: Text(
dayTitle,
style: (dayTitle == S.of(context).dayToday)
title,
style: (title == S.of(context).dayToday)
? textStyle
: textStyle.copyWith(color: colorScheme.textMuted),
),
),
);
}
String _getDayTitle(BuildContext context, int timestamp) {
final date = DateTime.fromMicrosecondsSinceEpoch(timestamp);
final now = DateTime.now();
if (date.year == now.year && date.month == now.month) {
if (date.day == now.day) {
return S.of(context).dayToday;
} else if (date.day == now.day - 1) {
return S.of(context).dayYesterday;
}
}
if (date.year != DateTime.now().year) {
return DateFormat.yMMMEd(Localizations.localeOf(context).languageCode)
.format(date);
} else {
return DateFormat.MMMEd(Localizations.localeOf(context).languageCode)
.format(date);
}
}
}

View file

@ -11,6 +11,7 @@ import 'package:photos/theme/ente_theme.dart';
import "package:photos/ui/viewer/gallery/component/grid/place_holder_grid_view_widget.dart";
import "package:photos/ui/viewer/gallery/component/group/group_gallery.dart";
import "package:photos/ui/viewer/gallery/component/group/group_header_widget.dart";
import "package:photos/ui/viewer/gallery/component/group/type.dart";
import 'package:photos/ui/viewer/gallery/gallery.dart';
import "package:photos/ui/viewer/gallery/state/gallery_context_state.dart";
@ -104,32 +105,30 @@ class _LazyGroupGalleryState extends State<LazyGroupGallery> {
if (_filesInGroup.isEmpty) {
return;
}
final DateTime groupDate =
DateTime.fromMicrosecondsSinceEpoch(_filesInGroup[0].creationTime!);
final galleryState = context.findAncestorStateOfType<GalleryState>();
final groupType = GalleryContextState.of(context)!.type;
// iterate over files and check if any of the belongs to this group
final anyCandidateForGroup = event.updatedFiles.any((file) {
final fileDate = DateTime.fromMicrosecondsSinceEpoch(file.creationTime!);
return fileDate.year == groupDate.year &&
fileDate.month == groupDate.month &&
fileDate.day == groupDate.day;
});
final anyCandidateForGroup = groupType.areModifiedFilesPartOfGroup(
event.updatedFiles,
_filesInGroup[0],
lastFile: _filesInGroup.last,
);
if (anyCandidateForGroup) {
late int startRange, endRange;
(startRange, endRange) = groupType.getGroupRange(_filesInGroup[0]);
if (kDebugMode) {
_logger.info(
" files were updated due to ${event.reason} on " +
DateTime.fromMicrosecondsSinceEpoch(
groupDate.microsecondsSinceEpoch,
).toIso8601String(),
" files were updated due to ${event.reason} on type ${groupType.name} from ${DateTime.fromMicrosecondsSinceEpoch(startRange).toIso8601String()}"
" to ${DateTime.fromMicrosecondsSinceEpoch(endRange).toIso8601String()}",
);
}
if (event.type == EventType.addedOrUpdated ||
widget.removalEventTypes.contains(event.type)) {
// We are reloading the whole group
final dayStartTime =
DateTime(groupDate.year, groupDate.month, groupDate.day);
final result = await widget.asyncLoader(
dayStartTime.microsecondsSinceEpoch,
dayStartTime.microsecondsSinceEpoch + microSecondsInDay - 1,
startRange,
endRange,
asc: GalleryContextState.of(context)!.sortOrderAsc,
);
@ -144,7 +143,7 @@ class _LazyGroupGalleryState extends State<LazyGroupGallery> {
//[galleryState] will never be null except when LazyLoadingGallery is
//used without Gallery as an ancestor.
final galleryState = context.findAncestorStateOfType<GalleryState>();
if (galleryState?.mounted ?? false) {
galleryState!.setState(() {});
_filesInGroup = result.files;
@ -178,6 +177,7 @@ class _LazyGroupGalleryState extends State<LazyGroupGallery> {
if (_filesInGroup.isEmpty) {
return const SizedBox.shrink();
}
final groupType = GalleryContextState.of(context)!.type;
return Column(
children: [
Row(
@ -185,7 +185,11 @@ class _LazyGroupGalleryState extends State<LazyGroupGallery> {
children: [
if (widget.enableFileGrouping)
GroupHeaderWidget(
timestamp: _filesInGroup[0].creationTime!,
title: groupType.getTitle(
context,
_filesInGroup[0],
lastFile: _filesInGroup.last,
),
gridSize: widget.photoGridSize,
),
Expanded(child: Container()),

View file

@ -0,0 +1,198 @@
import "package:flutter/widgets.dart";
import "package:intl/intl.dart";
import "package:photos/core/constants.dart";
import "package:photos/generated/l10n.dart";
import "package:photos/models/file/file.dart";
import "package:photos/utils/date_time_util.dart";
enum GroupType {
day,
week,
month,
size,
year,
none,
}
extension GroupTypeExtension on GroupType {
String get name {
switch (this) {
case GroupType.day:
return "day";
case GroupType.week:
return "week";
case GroupType.month:
return "month";
case GroupType.size:
return "size";
case GroupType.year:
return "year";
case GroupType.none:
return "none";
}
}
bool timeGrouping() {
return this == GroupType.day ||
this == GroupType.week ||
this == GroupType.month ||
this == GroupType.year;
}
bool showGroupHeader() {
if (this == GroupType.size || this == GroupType.none) {
return false;
}
return true;
}
String getTitle(BuildContext context, EnteFile file, {EnteFile? lastFile}) {
if (this == GroupType.day) {
return _getDayTitle(context, file.creationTime!);
} else if (this == GroupType.week) {
// return weeks starting date to end date based on file
final date = DateTime.fromMicrosecondsSinceEpoch(file.creationTime!);
final startOfWeek = date.subtract(Duration(days: date.weekday - 1));
final endOfWeek = startOfWeek.add(const Duration(days: 6));
return "${DateFormat.MMMd(Localizations.localeOf(context).languageCode).format(startOfWeek)} - ${DateFormat.MMMd(Localizations.localeOf(context).languageCode).format(endOfWeek)}, ${endOfWeek.year}";
} else if (this == GroupType.year) {
final date = DateTime.fromMicrosecondsSinceEpoch(file.creationTime!);
return DateFormat.y(Localizations.localeOf(context).languageCode)
.format(date);
} else if (this == GroupType.month) {
final date = DateTime.fromMicrosecondsSinceEpoch(file.creationTime!);
return DateFormat.yMMM(Localizations.localeOf(context).languageCode)
.format(date);
} else {
throw UnimplementedError("getTitle not implemented for $this");
}
}
// returns true if the group should be refreshed.
// If groupType is day, it should return true if the list of modified files contains a file that was created on the same day as the first file.
// If groupType is week, it should return true if the list of modified files contains a file that was created in the same week as the first file.
// If groupType is month, it should return true if the list of modified files contains a file that was created in the same month as the first file.
// If groupType is year, it should return true if the list of modified files contains a file that was created in the same year as the first file.
bool areModifiedFilesPartOfGroup(
List<EnteFile> modifiedFiles,
EnteFile fistFile, {
EnteFile? lastFile,
}) {
switch (this) {
case GroupType.day:
return modifiedFiles.any(
(file) => areFromSameDay(fistFile.creationTime!, file.creationTime!),
);
case GroupType.week:
return modifiedFiles.any((file) {
final firstDate =
DateTime.fromMicrosecondsSinceEpoch(fistFile.creationTime!);
final fileDate =
DateTime.fromMicrosecondsSinceEpoch(file.creationTime!);
return areDatesInSameWeek(firstDate, fileDate);
});
case GroupType.month:
return modifiedFiles.any((file) {
final firstDate =
DateTime.fromMicrosecondsSinceEpoch(fistFile.creationTime!);
final fileDate =
DateTime.fromMicrosecondsSinceEpoch(file.creationTime!);
return firstDate.year == fileDate.year &&
firstDate.month == fileDate.month;
});
case GroupType.year:
return modifiedFiles.any((file) {
final firstDate =
DateTime.fromMicrosecondsSinceEpoch(fistFile.creationTime!);
final fileDate =
DateTime.fromMicrosecondsSinceEpoch(file.creationTime!);
return firstDate.year == fileDate.year;
});
default:
throw UnimplementedError("not implemented for $this");
}
}
// for day, year, month, year type, return the microsecond range of the group
(int, int) getGroupRange(EnteFile file) {
switch (this) {
case GroupType.day:
final date = DateTime.fromMicrosecondsSinceEpoch(file.creationTime!);
final startOfDay = DateTime(date.year, date.month, date.day);
return (
startOfDay.microsecondsSinceEpoch,
(startOfDay.microsecondsSinceEpoch + microSecondsInDay - 1),
);
case GroupType.week:
final date = DateTime.fromMicrosecondsSinceEpoch(file.creationTime!);
final startOfWeek = DateTime(date.year, date.month, date.day)
.subtract(Duration(days: date.weekday - 1));
final endOfWeek = startOfWeek.add(const Duration(days: 7));
return (
startOfWeek.microsecondsSinceEpoch,
endOfWeek.microsecondsSinceEpoch - 1
);
case GroupType.month:
final date = DateTime.fromMicrosecondsSinceEpoch(file.creationTime!);
final startOfMonth = DateTime(date.year, date.month);
final endOfMonth = DateTime(date.year, date.month + 1);
return (
startOfMonth.microsecondsSinceEpoch,
endOfMonth.microsecondsSinceEpoch - 1
);
case GroupType.year:
final date = DateTime.fromMicrosecondsSinceEpoch(file.creationTime!);
final startOfYear = DateTime(date.year);
final endOfYear = DateTime(date.year + 1);
return (
startOfYear.microsecondsSinceEpoch,
endOfYear.microsecondsSinceEpoch - 1
);
default:
throw UnimplementedError("not implemented for $this");
}
}
bool areFromSameGroup(EnteFile first, EnteFile second) {
switch (this) {
case GroupType.day:
return areFromSameDay(first.creationTime!, second.creationTime!);
case GroupType.month:
return DateTime.fromMicrosecondsSinceEpoch(first.creationTime!).year ==
DateTime.fromMicrosecondsSinceEpoch(second.creationTime!)
.year &&
DateTime.fromMicrosecondsSinceEpoch(first.creationTime!).month ==
DateTime.fromMicrosecondsSinceEpoch(second.creationTime!).month;
case GroupType.year:
return DateTime.fromMicrosecondsSinceEpoch(first.creationTime!).year ==
DateTime.fromMicrosecondsSinceEpoch(second.creationTime!).year;
case GroupType.week:
final firstDate =
DateTime.fromMicrosecondsSinceEpoch(first.creationTime!);
final secondDate =
DateTime.fromMicrosecondsSinceEpoch(second.creationTime!);
return areDatesInSameWeek(firstDate, secondDate);
default:
throw UnimplementedError("not implemented for $this");
}
}
String _getDayTitle(BuildContext context, int timestamp) {
final date = DateTime.fromMicrosecondsSinceEpoch(timestamp);
final now = DateTime.now();
if (date.year == now.year && date.month == now.month) {
if (date.day == now.day) {
return S.of(context).dayToday;
} else if (date.day == now.day - 1) {
return S.of(context).dayYesterday;
}
}
if (date.year != DateTime.now().year) {
return DateFormat.yMMMEd(Localizations.localeOf(context).languageCode)
.format(date);
} else {
return DateFormat.MMMEd(Localizations.localeOf(context).languageCode)
.format(date);
}
}
}

View file

@ -9,7 +9,10 @@ import "package:photos/models/selected_files.dart";
import "package:photos/ui/common/loading_widget.dart";
import "package:photos/ui/huge_listview/huge_listview.dart";
import 'package:photos/ui/viewer/gallery/component/group/lazy_group_gallery.dart';
import "package:photos/ui/viewer/gallery/component/group/type.dart";
import "package:photos/ui/viewer/gallery/gallery.dart";
import "package:photos/ui/viewer/gallery/state/gallery_context_state.dart";
import "package:photos/utils/data_util.dart";
import "package:photos/utils/local_settings.dart";
import "package:scrollable_positioned_list/scrollable_positioned_list.dart";
@ -65,6 +68,7 @@ class MultipleGroupsGalleryView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final gType = GalleryContextState.of(context)!.type;
return HugeListView<List<EnteFile>>(
controller: itemScroller,
startIndex: 0,
@ -123,10 +127,17 @@ class MultipleGroupsGalleryView extends StatelessWidget {
},
labelTextBuilder: (int index) {
try {
final EnteFile file = groupedFiles[index][0];
if (gType == GroupType.size) {
return file.fileSize != null
? convertBytesToReadableFormat(file.fileSize!)
: "";
}
return DateFormat.yMMM(Localizations.localeOf(context).languageCode)
.format(
DateTime.fromMicrosecondsSinceEpoch(
groupedFiles[index][0].creationTime!,
file.creationTime!,
),
);
} catch (e) {

View file

@ -12,10 +12,10 @@ import 'package:photos/models/file/file.dart';
import 'package:photos/models/file_load_result.dart';
import 'package:photos/models/selected_files.dart';
import 'package:photos/ui/common/loading_widget.dart';
import "package:photos/ui/viewer/gallery/component/group/type.dart";
import "package:photos/ui/viewer/gallery/component/multiple_groups_gallery_view.dart";
import 'package:photos/ui/viewer/gallery/empty_state.dart';
import "package:photos/ui/viewer/gallery/state/gallery_context_state.dart";
import 'package:photos/utils/date_time_util.dart';
import "package:photos/utils/debouncer.dart";
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
@ -59,6 +59,7 @@ class Gallery extends StatefulWidget {
// add a Function variable to get sort value in bool
final SortAscFn? sortAsyncFn;
final GroupType groupType;
const Gallery({
required this.asyncLoader,
@ -73,6 +74,7 @@ class Gallery extends StatefulWidget {
this.emptyState = const EmptyState(),
this.scrollBottomSafeArea = 120.0,
this.albumName = '',
this.groupType = GroupType.day,
this.enableFileGrouping = true,
this.loadingWidget = const EnteLoadingWidget(),
this.disableScroll = false,
@ -212,7 +214,9 @@ class GalleryState extends State<Gallery> {
// gallery reload
bool _onFilesLoaded(List<EnteFile> files) {
final updatedGroupedFiles =
widget.enableFileGrouping ? _groupFiles(files) : [files];
widget.enableFileGrouping && widget.groupType.timeGrouping()
? _groupBasedOnTime(files)
: _genericGroupForPerf(files);
if (currentGroupedFiles.length != updatedGroupedFiles.length ||
currentGroupedFiles.isEmpty) {
if (mounted) {
@ -248,6 +252,7 @@ class GalleryState extends State<Gallery> {
return GalleryContextState(
sortOrderAsc: _sortOrderAsc,
inSelectionMode: widget.inSelectionMode,
type: widget.groupType,
child: MultipleGroupsGalleryView(
itemScroller: _itemScroller,
groupedFiles: currentGroupedFiles,
@ -258,28 +263,64 @@ class GalleryState extends State<Gallery> {
tagPrefix: widget.tagPrefix,
scrollBottomSafeArea: widget.scrollBottomSafeArea,
limitSelectionToOne: widget.limitSelectionToOne,
enableFileGrouping: widget.enableFileGrouping,
enableFileGrouping:
widget.enableFileGrouping && widget.groupType.showGroupHeader(),
logTag: _logTag,
logger: _logger,
reloadEvent: widget.reloadEvent,
header: widget.header,
footer: widget.footer,
selectedFiles: widget.selectedFiles,
showSelectAllByDefault: widget.showSelectAllByDefault,
showSelectAllByDefault:
widget.showSelectAllByDefault && widget.groupType.showGroupHeader(),
isScrollablePositionedList: widget.isScrollablePositionedList,
),
);
}
List<List<EnteFile>> _groupFiles(List<EnteFile> files) {
// create groups of 200 files for performance
List<List<EnteFile>> _genericGroupForPerf(List<EnteFile> files) {
if (widget.groupType == GroupType.size) {
// sort files by fileSize on the bases of _sortOrderAsc
files.sort((a, b) {
if (_sortOrderAsc) {
return a.fileSize!.compareTo(b.fileSize!);
} else {
return b.fileSize!.compareTo(a.fileSize!);
}
});
}
// todo:(neeraj) Stick to default group behaviour for magicSearch and editLocationGallery
// In case of Magic search, we need to hide the scrollbar title (can be done
// by specifying none as groupType)
if (widget.groupType != GroupType.size) {
return [files];
}
final List<List<EnteFile>> resultGroupedFiles = [];
List<EnteFile> singleGroupFile = [];
const int groupSize = 40;
for (int i = 0; i < files.length; i += 1) {
singleGroupFile.add(files[i]);
if (singleGroupFile.length == groupSize) {
resultGroupedFiles.add(singleGroupFile);
singleGroupFile = [];
}
}
if (singleGroupFile.isNotEmpty) {
resultGroupedFiles.add(singleGroupFile);
}
_logger.info('Grouped files into ${resultGroupedFiles.length} groups');
return resultGroupedFiles;
}
List<List<EnteFile>> _groupBasedOnTime(List<EnteFile> files) {
List<EnteFile> dailyFiles = [];
final List<List<EnteFile>> resultGroupedFiles = [];
for (int index = 0; index < files.length; index++) {
if (index > 0 &&
!areFromSameDay(
files[index - 1].creationTime!,
files[index].creationTime!,
)) {
!widget.groupType.areFromSameGroup(files[index - 1], files[index])) {
resultGroupedFiles.add(dailyFiles);
dailyFiles = [];
}

View file

@ -1,12 +1,15 @@
import "package:flutter/material.dart";
import "package:photos/ui/viewer/gallery/component/group/type.dart";
class GalleryContextState extends InheritedWidget {
///Sorting by creation time
final bool sortOrderAsc;
final bool inSelectionMode;
final GroupType type;
const GalleryContextState({
this.inSelectionMode = false,
this.type = GroupType.day,
required this.sortOrderAsc,
required Widget child,
Key? key,
@ -19,6 +22,7 @@ class GalleryContextState extends InheritedWidget {
@override
bool updateShouldNotify(GalleryContextState oldWidget) {
return sortOrderAsc != oldWidget.sortOrderAsc ||
inSelectionMode != oldWidget.inSelectionMode;
inSelectionMode != oldWidget.inSelectionMode ||
type != oldWidget.type;
}
}

View file

@ -28,6 +28,27 @@ bool areFromSameDay(int firstCreationTime, int secondCreationTime) {
firstDate.day == secondDate.day;
}
bool areDatesInSameWeek(DateTime date1, DateTime date2) {
if (date1.year == date2.year &&
date1.month == date2.month &&
date1.day == date2.day) {
return true;
}
final int dayOfWeek1 = date1.weekday;
final int dayOfWeek2 = date2.weekday;
// Calculate the start and end dates of the week for both dates
final DateTime startOfWeek1 = date1.subtract(Duration(days: dayOfWeek1 - 1));
final DateTime endOfWeek1 = startOfWeek1.add(const Duration(days: 6));
final DateTime startOfWeek2 = date2.subtract(Duration(days: dayOfWeek2 - 1));
final DateTime endOfWeek2 = startOfWeek2.add(const Duration(days: 6));
// Check if the two dates fall within the same week range
if ((date1.isAfter(startOfWeek2) && date1.isBefore(endOfWeek2)) ||
(date2.isAfter(startOfWeek1) && date2.isBefore(endOfWeek1))) {
return true;
}
return false;
}
// Create link default names:
// Same day: "Dec 19, 2022"
// Same month: "Dec 19 - 22, 2022"