WiP
This commit is contained in:
parent
1417ceac5a
commit
4d09bff81e
|
@ -321,7 +321,7 @@ SPEC CHECKSUMS:
|
|||
FirebaseInstallations: 0a115432c4e223c5ab20b0dbbe4cbefa793a0e8e
|
||||
FirebaseMessaging: 732623518591384f61c287e3d8f65294beb7ffb3
|
||||
fk_user_agent: 1f47ec39291e8372b1d692b50084b0d54103c545
|
||||
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
|
||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||
flutter_email_sender: 02d7443217d8c41483223627972bfdc09f74276b
|
||||
flutter_image_compress: 5a5e9aee05b6553048b8df1c3bc456d0afaac433
|
||||
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
|
||||
|
|
2
lib/core/cache/thumbnail_cache.dart
vendored
2
lib/core/cache/thumbnail_cache.dart
vendored
|
@ -5,7 +5,7 @@ import 'package:photos/core/constants.dart';
|
|||
import 'package:photos/models/ente_file.dart';
|
||||
|
||||
class ThumbnailLruCache {
|
||||
static final LRUMap<String, Uint8List?> _map = LRUMap(1000);
|
||||
static final LRUMap<String, Uint8List?> _map = LRUMap(250);
|
||||
|
||||
static Uint8List? get(EnteFile enteFile, [int? size]) {
|
||||
return _map.get(
|
||||
|
|
|
@ -5,8 +5,8 @@ import 'dart:math';
|
|||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:local_hero/local_hero.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
|
@ -33,6 +33,7 @@ class LazyLoadingGallery extends StatefulWidget {
|
|||
final String tag;
|
||||
final String logTag;
|
||||
final Stream<int> currentIndexStream;
|
||||
final int imagesPerRow;
|
||||
|
||||
LazyLoadingGallery(
|
||||
this.files,
|
||||
|
@ -44,6 +45,7 @@ class LazyLoadingGallery extends StatefulWidget {
|
|||
this.tag,
|
||||
this.currentIndexStream, {
|
||||
this.logTag = "",
|
||||
this.imagesPerRow,
|
||||
Key key,
|
||||
}) : super(key: key ?? UniqueKey());
|
||||
|
||||
|
@ -251,6 +253,7 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
_files.length > kRecycleLimit,
|
||||
_toggleSelectAllFromDay,
|
||||
_areAllFromDaySelected,
|
||||
widget.imagesPerRow,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -278,6 +281,7 @@ class LazyLoadingGridView extends StatefulWidget {
|
|||
final bool shouldRecycle;
|
||||
final ValueNotifier toggleSelectAllFromDay;
|
||||
final ValueNotifier areAllFilesSelected;
|
||||
final int imagesPerRow;
|
||||
|
||||
LazyLoadingGridView(
|
||||
this.tag,
|
||||
|
@ -287,7 +291,8 @@ class LazyLoadingGridView extends StatefulWidget {
|
|||
this.shouldRender,
|
||||
this.shouldRecycle,
|
||||
this.toggleSelectAllFromDay,
|
||||
this.areAllFilesSelected, {
|
||||
this.areAllFilesSelected,
|
||||
this.imagesPerRow, {
|
||||
Key key,
|
||||
}) : super(key: key ?? UniqueKey());
|
||||
|
||||
|
@ -383,10 +388,10 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
|
|||
return _buildFile(context, widget.filesInDay[index]);
|
||||
},
|
||||
itemCount: widget.filesInDay.length,
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisSpacing: 2,
|
||||
mainAxisSpacing: 2,
|
||||
crossAxisCount: 4,
|
||||
crossAxisCount: widget.imagesPerRow ?? 4,
|
||||
),
|
||||
padding: const EdgeInsets.all(0),
|
||||
);
|
||||
|
@ -405,8 +410,8 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
|
|||
HapticFeedback.lightImpact();
|
||||
_selectFile(file);
|
||||
},
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(1),
|
||||
child: LocalHero(
|
||||
tag: widget.tag + file.tag,
|
||||
child: Stack(
|
||||
children: [
|
||||
Hero(
|
||||
|
|
|
@ -179,7 +179,6 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
_loadNetworkImage();
|
||||
} else {
|
||||
if (await doesLocalFileExist(widget.file) == false) {
|
||||
_logger.info("Deleting file " + widget.file.tag);
|
||||
FilesDB.instance.deleteLocalFile(widget.file);
|
||||
Bus.instance.fire(
|
||||
LocalPhotosUpdatedEvent(
|
||||
|
@ -197,7 +196,6 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
final imageProvider = Image.memory(thumbData).image;
|
||||
_cacheAndRender(imageProvider);
|
||||
}
|
||||
ThumbnailLruCache.put(widget.file, thumbData, thumbnailSmallSize);
|
||||
}).catchError((e) {
|
||||
_logger.warning("Could not load image: ", e);
|
||||
_errorLoadingLocalThumbnail = true;
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:local_hero/local_hero.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
|
@ -203,76 +205,118 @@ class _GalleryState extends State<Gallery> {
|
|||
return _getListView();
|
||||
}
|
||||
|
||||
int _imagesPerRow = 4;
|
||||
ScaleUpdateDetails _lastScaleUpdateDetails;
|
||||
|
||||
Widget _getListView() {
|
||||
return HugeListView<List<File>>(
|
||||
key: _hugeListViewKey,
|
||||
controller: _itemScroller,
|
||||
startIndex: 0,
|
||||
totalCount: _collatedFiles.length,
|
||||
isDraggableScrollbarEnabled: _collatedFiles.length > 10,
|
||||
waitBuilder: (_) {
|
||||
return const EnteLoadingWidget();
|
||||
},
|
||||
emptyResultBuilder: (_) {
|
||||
final List<Widget> children = [];
|
||||
if (widget.header != null) {
|
||||
children.add(widget.header);
|
||||
}
|
||||
children.add(
|
||||
Expanded(
|
||||
child: widget.emptyState,
|
||||
),
|
||||
);
|
||||
if (widget.footer != null) {
|
||||
children.add(widget.footer);
|
||||
}
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: children,
|
||||
);
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
Widget gallery;
|
||||
gallery = LazyLoadingGallery(
|
||||
_collatedFiles[index],
|
||||
index,
|
||||
widget.reloadEvent,
|
||||
widget.removalEventTypes,
|
||||
widget.asyncLoader,
|
||||
widget.selectedFiles,
|
||||
widget.tagPrefix,
|
||||
Bus.instance
|
||||
.on<GalleryIndexUpdatedEvent>()
|
||||
.where((event) => event.tag == widget.tagPrefix)
|
||||
.map((event) => event.index),
|
||||
logTag: _logTag,
|
||||
);
|
||||
if (widget.header != null && index == 0) {
|
||||
gallery = Column(children: [widget.header, gallery]);
|
||||
}
|
||||
if (widget.footer != null && index == _collatedFiles.length - 1) {
|
||||
gallery = Column(children: [gallery, widget.footer]);
|
||||
}
|
||||
return gallery;
|
||||
},
|
||||
labelTextBuilder: (int index) {
|
||||
return getMonthAndYear(
|
||||
DateTime.fromMicrosecondsSinceEpoch(
|
||||
_collatedFiles[index][0].creationTime,
|
||||
),
|
||||
);
|
||||
},
|
||||
thumbBackgroundColor:
|
||||
Theme.of(context).colorScheme.galleryThumbBackgroundColor,
|
||||
thumbDrawColor: Theme.of(context).colorScheme.galleryThumbDrawColor,
|
||||
thumbPadding: widget.header != null
|
||||
? const EdgeInsets.only(top: 60)
|
||||
: const EdgeInsets.all(0),
|
||||
bottomSafeArea: widget.scrollBottomSafeArea,
|
||||
firstShown: (int firstIndex) {
|
||||
Bus.instance
|
||||
.fire(GalleryIndexUpdatedEvent(widget.tagPrefix, firstIndex));
|
||||
},
|
||||
return LocalHeroScope(
|
||||
duration: const Duration(milliseconds: 100),
|
||||
child:
|
||||
RawGestureDetector(
|
||||
gestures: {
|
||||
AllowMultipleGestureRecognizer: GestureRecognizerFactoryWithHandlers<
|
||||
AllowMultipleGestureRecognizer>(
|
||||
() => AllowMultipleGestureRecognizer(), //constructor
|
||||
(AllowMultipleGestureRecognizer instance) {
|
||||
instance.onUpdate = (details) {
|
||||
if (details.pointerCount == 2) {
|
||||
_lastScaleUpdateDetails = details;
|
||||
}
|
||||
};
|
||||
instance.onEnd = (details) {
|
||||
if (_lastScaleUpdateDetails != null) {
|
||||
if (_lastScaleUpdateDetails.verticalScale > 1 &&
|
||||
_lastScaleUpdateDetails.horizontalScale > 1) {
|
||||
_logger.info("zoomed in");
|
||||
if (_imagesPerRow > 2) {
|
||||
_imagesPerRow--;
|
||||
}
|
||||
} else if (_lastScaleUpdateDetails.verticalScale < 1 &&
|
||||
_lastScaleUpdateDetails.horizontalScale < 1) {
|
||||
_logger.info("zoomed out");
|
||||
if (_imagesPerRow < 4) {
|
||||
_imagesPerRow++;
|
||||
}
|
||||
}
|
||||
_lastScaleUpdateDetails = null;
|
||||
setState(() {});
|
||||
}
|
||||
};
|
||||
},
|
||||
)
|
||||
},
|
||||
child: HugeListView<List<File>>(
|
||||
key: _hugeListViewKey,
|
||||
controller: _itemScroller,
|
||||
startIndex: 0,
|
||||
totalCount: _collatedFiles.length,
|
||||
isDraggableScrollbarEnabled: _collatedFiles.length > 10,
|
||||
waitBuilder: (_) {
|
||||
return const EnteLoadingWidget();
|
||||
},
|
||||
emptyResultBuilder: (_) {
|
||||
final List<Widget> children = [];
|
||||
if (widget.header != null) {
|
||||
children.add(widget.header);
|
||||
}
|
||||
children.add(
|
||||
Expanded(
|
||||
child: widget.emptyState,
|
||||
),
|
||||
);
|
||||
if (widget.footer != null) {
|
||||
children.add(widget.footer);
|
||||
}
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: children,
|
||||
);
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
Widget gallery;
|
||||
gallery = LazyLoadingGallery(
|
||||
_collatedFiles[index],
|
||||
index,
|
||||
widget.reloadEvent,
|
||||
widget.removalEventTypes,
|
||||
widget.asyncLoader,
|
||||
widget.selectedFiles,
|
||||
widget.tagPrefix,
|
||||
Bus.instance
|
||||
.on<GalleryIndexUpdatedEvent>()
|
||||
.where((event) => event.tag == widget.tagPrefix)
|
||||
.map((event) => event.index),
|
||||
logTag: _logTag,
|
||||
imagesPerRow: _imagesPerRow,
|
||||
);
|
||||
if (widget.header != null && index == 0) {
|
||||
gallery = Column(children: [widget.header, gallery]);
|
||||
}
|
||||
if (widget.footer != null && index == _collatedFiles.length - 1) {
|
||||
gallery = Column(children: [gallery, widget.footer]);
|
||||
}
|
||||
return gallery;
|
||||
},
|
||||
labelTextBuilder: (int index) {
|
||||
return getMonthAndYear(
|
||||
DateTime.fromMicrosecondsSinceEpoch(
|
||||
_collatedFiles[index][0].creationTime,
|
||||
),
|
||||
);
|
||||
},
|
||||
thumbBackgroundColor:
|
||||
Theme.of(context).colorScheme.galleryThumbBackgroundColor,
|
||||
thumbDrawColor: Theme.of(context).colorScheme.galleryThumbDrawColor,
|
||||
thumbPadding: widget.header != null
|
||||
? const EdgeInsets.only(top: 60)
|
||||
: const EdgeInsets.all(0),
|
||||
bottomSafeArea: widget.scrollBottomSafeArea,
|
||||
firstShown: (int firstIndex) {
|
||||
Bus.instance
|
||||
.fire(GalleryIndexUpdatedEvent(widget.tagPrefix, firstIndex));
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -315,3 +359,14 @@ class GalleryIndexUpdatedEvent {
|
|||
|
||||
GalleryIndexUpdatedEvent(this.tag, this.index);
|
||||
}
|
||||
|
||||
// Custom Gesture Recognizer.
|
||||
// rejectGesture() is overridden. When a gesture is rejected, this is the function that is called. By default, it disposes of the
|
||||
// Recognizer and runs clean up. However we modified it so that instead the Recognizer is disposed of, it is actually manually added.
|
||||
// The result is instead you have one Recognizer winning the Arena, you have two. It is a win-win.
|
||||
class AllowMultipleGestureRecognizer extends ScaleGestureRecognizer {
|
||||
@override
|
||||
void rejectGesture(int pointer) {
|
||||
acceptGesture(pointer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ Future<Uint8List> getThumbnailFromServer(File file) async {
|
|||
|
||||
Future<Uint8List> getThumbnailFromLocal(
|
||||
File file, {
|
||||
int size = thumbnailSmallSize,
|
||||
int size = thumbnailLargeSize,
|
||||
int quality = thumbnailQuality,
|
||||
}) async {
|
||||
final lruCachedThumbnail = ThumbnailLruCache.get(file, size);
|
||||
|
|
42
pubspec.lock
42
pubspec.lock
|
@ -49,7 +49,7 @@ packages:
|
|||
name: async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.8.2"
|
||||
version: "2.9.0"
|
||||
background_fetch:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -98,14 +98,7 @@ packages:
|
|||
name: characters
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
version: "1.2.1"
|
||||
chewie:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -119,7 +112,7 @@ packages:
|
|||
name: clock
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.1.1"
|
||||
collection:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -308,7 +301,7 @@ packages:
|
|||
name: fake_async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.3.1"
|
||||
fast_base58:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -742,6 +735,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.11"
|
||||
local_hero:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: local_hero
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
logging:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -762,14 +762,14 @@ packages:
|
|||
name: matcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.11"
|
||||
version: "0.12.12"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.4"
|
||||
version: "0.1.5"
|
||||
media_extension:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -785,7 +785,7 @@ packages:
|
|||
name: meta
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.7.0"
|
||||
version: "1.8.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -913,7 +913,7 @@ packages:
|
|||
name: path
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.1"
|
||||
version: "1.8.2"
|
||||
path_drawing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1254,7 +1254,7 @@ packages:
|
|||
name: source_span
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.2"
|
||||
version: "1.9.0"
|
||||
sprintf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1310,7 +1310,7 @@ packages:
|
|||
name: string_scanner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.1.1"
|
||||
syncfusion_flutter_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1338,28 +1338,28 @@ packages:
|
|||
name: term_glyph
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.2.1"
|
||||
test:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: test
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.21.1"
|
||||
version: "1.21.4"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.9"
|
||||
version: "0.4.12"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.13"
|
||||
version: "0.4.16"
|
||||
timezone:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -75,6 +75,7 @@ dependencies:
|
|||
like_button: ^2.0.2
|
||||
loading_animations: ^2.1.0
|
||||
local_auth: ^1.1.5
|
||||
local_hero: ^0.2.0
|
||||
logging: ^1.0.1
|
||||
lottie: ^1.2.2
|
||||
media_extension:
|
||||
|
|
Loading…
Reference in a new issue