diff --git a/mobile-v2/lib/domain/models/render_list_element.model.dart b/mobile-v2/lib/domain/models/render_list_element.model.dart index 1ac768ea96..47655bea60 100644 --- a/mobile-v2/lib/domain/models/render_list_element.model.dart +++ b/mobile-v2/lib/domain/models/render_list_element.model.dart @@ -73,9 +73,11 @@ class RenderListMonthHeaderElement extends RenderListElement { } class RenderListDayHeaderElement extends RenderListElement { - final String header; + late final String header; - const RenderListDayHeaderElement({required super.date, required this.header}); + RenderListDayHeaderElement({required super.date}) { + header = DateFormat.yMMMMd().format(date); + } @override String toString() => diff --git a/mobile-v2/lib/domain/repositories/renderlist.repository.dart b/mobile-v2/lib/domain/repositories/renderlist.repository.dart index c217f4e2e0..7e9c1ca7d0 100644 --- a/mobile-v2/lib/domain/repositories/renderlist.repository.dart +++ b/mobile-v2/lib/domain/repositories/renderlist.repository.dart @@ -16,7 +16,7 @@ class RenderListRepository with LogMixin implements IRenderListRepository { final assetCountExp = _db.asset.id.count(); final createdTimeExp = _db.asset.createdTime; final modifiedTimeExp = _db.asset.modifiedTime.max(); - final monthYearExp = createdTimeExp.strftime('%m-%Y'); + final monthYearExp = createdTimeExp.strftime('%d-%m-%Y'); final query = _db.asset.selectOnly() ..addColumns([assetCountExp, createdTimeExp, modifiedTimeExp]) @@ -40,7 +40,7 @@ class RenderListRepository with LogMixin implements IRenderListRepository { } return [ - RenderListMonthHeaderElement(date: createdTime), + RenderListDayHeaderElement(date: createdTime), RenderListAssetElement( date: createdTime, assetCount: assetCount, @@ -63,7 +63,7 @@ class RenderListRepository with LogMixin implements IRenderListRepository { final assetCountExp = _db.asset.id.count(); final createdTimeExp = _db.asset.createdTime; final modifiedTimeExp = _db.asset.modifiedTime.max(); - final monthYearExp = createdTimeExp.strftime('%m-%Y'); + final monthYearExp = createdTimeExp.strftime('%d-%m-%Y'); final query = _db.asset.selectOnly() ..addColumns([assetCountExp, createdTimeExp, modifiedTimeExp]) @@ -86,7 +86,7 @@ class RenderListRepository with LogMixin implements IRenderListRepository { } return [ - RenderListMonthHeaderElement(date: createdTime), + RenderListDayHeaderElement(date: createdTime), RenderListAssetElement( date: createdTime, assetCount: assetCount, diff --git a/mobile-v2/lib/presentation/components/grid/asset_grid.widget.dart b/mobile-v2/lib/presentation/components/grid/asset_grid.widget.dart index cf56c539cc..772833d7a0 100644 --- a/mobile-v2/lib/presentation/components/grid/asset_grid.widget.dart +++ b/mobile-v2/lib/presentation/components/grid/asset_grid.widget.dart @@ -9,12 +9,10 @@ import 'package:immich_mobile/domain/models/render_list_element.model.dart'; import 'package:immich_mobile/i18n/strings.g.dart'; import 'package:immich_mobile/presentation/components/common/page_empty.widget.dart'; import 'package:immich_mobile/presentation/components/grid/asset_grid.state.dart'; -import 'package:immich_mobile/presentation/components/grid/asset_render_grid.widget.dart'; import 'package:immich_mobile/presentation/components/grid/draggable_scrollbar.dart'; import 'package:immich_mobile/presentation/components/image/immich_image.widget.dart'; import 'package:immich_mobile/presentation/components/image/immich_thumbnail.widget.dart'; import 'package:immich_mobile/utils/constants/size_constants.dart'; -import 'package:immich_mobile/utils/extensions/async_snapshot.extension.dart'; import 'package:immich_mobile/utils/extensions/build_context.extension.dart'; import 'package:immich_mobile/utils/extensions/color.extension.dart'; import 'package:intl/intl.dart'; @@ -177,7 +175,7 @@ class _Section extends StatelessWidget { } if (section is RenderListDayHeaderElement) { - return Text(section.header); + return _DayHeader(text: section.header); } if (section is! RenderListAssetElement) { @@ -194,7 +192,7 @@ class _Section extends StatelessWidget { ? Future.value([]) : context .read() - .loadAssets(section.assetCount, section.assetCount); + .loadAssets(section.assetOffset, section.assetCount); return FutureBuilder( future: assetsToRender, builder: (_, snap) => Column( @@ -202,7 +200,7 @@ class _Section extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ for (int i = 0; i < rows; i++) - scrolling || snap.isWaiting + scrolling || !snap.hasData ? _PlaceholderRow( key: ValueKey(i), number: assetsPerRow, @@ -266,115 +264,6 @@ class _AssetRow extends StatelessWidget { } } -class ImAssetGrid extends StatefulWidget { - /// The padding for the grid - final double? topPadding; - - const ImAssetGrid({this.topPadding, super.key}); - - @override - State createState() => _ImAssetGridState(); -} - -class _ImAssetGridState extends State { - final ItemScrollController _itemScrollController = ItemScrollController(); - final ScrollOffsetController _scrollOffsetController = - ScrollOffsetController(); - final ItemPositionsListener _itemPositionsListener = - ItemPositionsListener.create(); - - Text? _labelBuilder(List elements, int currentPosition) { - final element = elements.elementAtOrNull(currentPosition); - if (element == null) { - return null; - } - - return Text( - DateFormat.yMMMM().format(element.date), - style: TextStyle( - color: context.colorScheme.onSurface, - fontWeight: FontWeight.bold, - ), - ); - } - - @override - Widget build(BuildContext context) => - BlocBuilder( - builder: (_, state) { - final elements = state.renderList.elements; - - if (state.renderList.totalCount == 0) { - return const _ImGridEmpty(); - } - - // Append padding if required - if (widget.topPadding != null && - elements.firstOrNull is! RenderListPaddingElement) { - elements.insert( - 0, - RenderListPaddingElement.beforeElement( - top: widget.topPadding!, - before: elements.firstOrNull, - ), - ); - } else if (widget.topPadding == null && - elements.firstOrNull is RenderListPaddingElement) { - elements.removeAt(0); - } - - final EdgeInsets? padding = null; - - final grid = ScrollablePositionedList.builder( - itemCount: state.renderList.elements.length, - itemBuilder: (_, sectionIndex) { - // ignore: avoid-unsafe-collection-methods - final section = elements[sectionIndex]; - - return switch (section) { - RenderListPaddingElement() => Padding( - padding: EdgeInsets.only(top: section.topPadding), - ), - RenderListMonthHeaderElement() => - _MonthHeader(text: section.header), - RenderListDayHeaderElement() => Text(section.header), - RenderListAssetElement() => ImStaticGrid(section: section), - }; - }, - itemScrollController: _itemScrollController, - itemPositionsListener: _itemPositionsListener, - scrollOffsetController: _scrollOffsetController, - padding: padding, - addRepaintBoundaries: true, - ); - - return DraggableScrollbar.semicircle( - alwaysVisibleScrollThumb: true, - controller: _itemScrollController, - itemPositionsListener: _itemPositionsListener, - scrollStateListener: - context.read().setDragScrolling, - backgroundColor: context.colorScheme.surfaceContainerHighest, - foregroundColor: context.colorScheme.onSurface, - padding: EdgeInsets.only(top: 120), - heightOffset: 100, - scrollbarAnimationDuration: const Duration(milliseconds: 300), - scrollbarTimeToFade: const Duration(milliseconds: 1000), - labelTextBuilder: (int position) => - _labelBuilder(elements, position), - labelConstraints: const BoxConstraints(maxHeight: 28), - child: grid, - ); - }, - // no.of elements are not equal or is modified - buildWhen: (previous, current) => - (previous.renderList.elements.length != - current.renderList.elements.length) || - !previous.renderList.modifiedTime - .isAtSameMomentAs(current.renderList.modifiedTime), - ); -} - class _ImGridEmpty extends StatelessWidget { const _ImGridEmpty(); @@ -394,26 +283,6 @@ class _ImGridEmpty extends StatelessWidget { } extension ListExtension on List { - List uniqueConsecutive({ - int Function(E a, E b)? compare, - void Function(E a, E b)? onDuplicate, - }) { - compare ??= (E a, E b) => a == b ? 0 : 1; - int i = 1, j = 1; - for (; i < length; i++) { - if (compare(this[i - 1], this[i]) != 0) { - if (i != j) { - this[j] = this[i]; - } - j++; - } else if (onDuplicate != null) { - onDuplicate(this[i - 1], this[i]); - } - } - length = length == 0 ? 0 : j; - return this; - } - ListSlice nestedSlice(int start, int end) { if (this is ListSlice) { final ListSlice self = this as ListSlice; diff --git a/mobile-v2/lib/presentation/components/grid/asset_grid_header.widget.dart b/mobile-v2/lib/presentation/components/grid/asset_grid_header.widget.dart index 96929f74a0..4d4859c4fd 100644 --- a/mobile-v2/lib/presentation/components/grid/asset_grid_header.widget.dart +++ b/mobile-v2/lib/presentation/components/grid/asset_grid_header.widget.dart @@ -48,3 +48,21 @@ class _MonthHeader extends StatelessWidget { ); } } + +class _DayHeader extends StatelessWidget { + final String text; + + const _DayHeader({required this.text}); + + @override + Widget build(BuildContext context) { + return _HeaderText( + text: text, + style: context.textTheme.bodyMedium?.copyWith( + color: context.colorScheme.onSurface, + fontSize: 20.0, + fontWeight: FontWeight.w500, + ), + ); + } +}