Merge branch 'main' into uncategorized

This commit is contained in:
Neeraj Gupta 2023-01-18 12:46:16 +05:30
commit 98a22c34a5
No known key found for this signature in database
GPG key ID: 3C5A1684DC1729E1
14 changed files with 219 additions and 88 deletions

View file

@ -477,6 +477,7 @@ class RemoteSyncService {
final int toBeUploaded = filesToBeUploaded.length + updatedFileIDs.length;
if (toBeUploaded > 0) {
Bus.instance.fire(SyncStatusUpdate(SyncStatus.preparingForUpload));
await _uploader.checkNetworkForUpload();
// verify if files upload is allowed based on their subscription plan and
// storage limit. To avoid creating new endpoint, we are using
// fetchUploadUrls as alternative method.

View file

@ -21,6 +21,7 @@ class EnteColorScheme {
// Fill Colors
final Color fillBase;
final Color fillBasePressed;
final Color fillMuted;
final Color fillFaint;
final Color fillFaintPressed;
@ -44,6 +45,7 @@ class EnteColorScheme {
final Color warning700;
final Color warning500;
final Color warning400;
final Color warning800;
final Color caution500;
//other colors
@ -62,6 +64,7 @@ class EnteColorScheme {
this.textFaint,
this.blurTextBase,
this.fillBase,
this.fillBasePressed,
this.fillMuted,
this.fillFaint,
this.fillFaintPressed,
@ -78,9 +81,10 @@ class EnteColorScheme {
this.primary500 = _primary500,
this.primary400 = _primary400,
this.primary300 = _primary300,
this.warning800 = _warning800,
this.warning700 = _warning700,
this.warning500 = _warning500,
this.warning400 = _warning700,
this.warning400 = _warning400,
this.caution500 = _caution500,
});
}
@ -97,6 +101,7 @@ const EnteColorScheme lightScheme = EnteColorScheme(
textFaintLight,
blurTextBaseLight,
fillBaseLight,
fillBasePressedLight,
fillMutedLight,
fillFaintLight,
fillFaintPressedLight,
@ -123,6 +128,7 @@ const EnteColorScheme darkScheme = EnteColorScheme(
textFaintDark,
blurTextBaseDark,
fillBaseDark,
fillBasePressedDark,
fillMutedDark,
fillFaintDark,
fillFaintPressedDark,
@ -168,11 +174,13 @@ const Color blurTextBaseDark = Color.fromRGBO(255, 255, 255, 0.95);
// Fill Colors
const Color fillBaseLight = Color.fromRGBO(0, 0, 0, 1);
const Color fillBasePressedLight = Color.fromRGBO(0, 0, 0, 0.87);
const Color fillMutedLight = Color.fromRGBO(0, 0, 0, 0.12);
const Color fillFaintLight = Color.fromRGBO(0, 0, 0, 0.04);
const Color fillFaintPressedLight = Color.fromRGBO(0, 0, 0, 0.08);
const Color fillBaseDark = Color.fromRGBO(255, 255, 255, 1);
const Color fillBasePressedDark = Color.fromRGBO(255, 255, 255, 0.9);
const Color fillMutedDark = Color.fromRGBO(255, 255, 255, 0.16);
const Color fillFaintDark = Color.fromRGBO(255, 255, 255, 0.12);
const Color fillFaintPressedDark = Color.fromRGBO(255, 255, 255, 0.06);
@ -212,6 +220,7 @@ const Color _warning700 = Color.fromRGBO(234, 63, 63, 1);
const Color _warning500 = Color.fromRGBO(255, 101, 101, 1);
const Color warning500 = Color.fromRGBO(255, 101, 101, 1);
const Color _warning400 = Color.fromRGBO(255, 111, 111, 1);
const Color _warning800 = Color(0xFFF53434);
const Color _caution500 = Color.fromRGBO(255, 194, 71, 1);

View file

@ -4,18 +4,22 @@ import 'package:photos/models/collection.dart';
import 'package:photos/models/collection_items.dart';
import 'package:photos/models/gallery_type.dart';
import 'package:photos/theme/ente_theme.dart';
import 'package:photos/ui/viewer/file/file_icons_widget.dart';
import 'package:photos/ui/viewer/file/no_thumbnail_widget.dart';
import 'package:photos/ui/viewer/file/thumbnail_widget.dart';
import 'package:photos/ui/viewer/gallery/collection_page.dart';
import 'package:photos/utils/navigation_util.dart';
import 'package:visibility_detector/visibility_detector.dart';
class CollectionItem extends StatelessWidget {
final CollectionWithThumbnail c;
final double sideOfThumbnail;
final bool shouldRender;
CollectionItem(
this.c,
this.sideOfThumbnail, {
this.shouldRender = false,
Key? key,
}) : super(key: Key(c.collection.id.toString()));
@ -39,11 +43,10 @@ class CollectionItem extends StatelessWidget {
child: Hero(
tag: heroTag,
child: c.thumbnail != null
? ThumbnailWidget(
c.thumbnail,
shouldShowArchiveStatus: c.collection.isArchived(),
showFavForAlbumOnly: true,
key: Key(heroTag),
? CollectionItemThumbnailWidget(
c: c,
heroTag: heroTag,
shouldRender: shouldRender,
)
: const NoThumbnailWidget(),
),
@ -98,3 +101,54 @@ class CollectionItem extends StatelessWidget {
);
}
}
class CollectionItemThumbnailWidget extends StatefulWidget {
const CollectionItemThumbnailWidget({
Key? key,
required this.c,
required this.heroTag,
this.shouldRender = false,
}) : super(key: key);
final CollectionWithThumbnail c;
final String heroTag;
final bool shouldRender;
@override
State<CollectionItemThumbnailWidget> createState() =>
_CollectionItemThumbnailWidgetState();
}
class _CollectionItemThumbnailWidgetState
extends State<CollectionItemThumbnailWidget> {
bool _shouldRender = false;
@override
void initState() {
super.initState();
_shouldRender = widget.shouldRender;
}
@override
Widget build(BuildContext context) {
return VisibilityDetector(
key: Key("collection_item" + widget.c.thumbnail!.tag),
onVisibilityChanged: (visibility) {
final shouldRender = visibility.visibleFraction > 0;
if (mounted && shouldRender && !_shouldRender) {
setState(() {
_shouldRender = shouldRender;
});
}
},
child: _shouldRender
? ThumbnailWidget(
widget.c.thumbnail,
shouldShowArchiveStatus: widget.c.collection.isArchived(),
showFavForAlbumOnly: true,
key: Key(widget.heroTag),
)
: const ThumbnailPlaceHolder(),
);
}
}

View file

@ -14,6 +14,7 @@ class RemoteCollectionsGridViewWidget extends StatelessWidget {
static const maxThumbnailWidth = 224.0;
static const fixedGapBetweenAlbum = 8.0;
static const minGapForHorizontalPadding = 8.0;
static const collectionItemsToPreload = 20;
final List<CollectionWithThumbnail>? collections;
@ -45,7 +46,11 @@ class RemoteCollectionsGridViewWidget extends StatelessWidget {
// to disable GridView's scrolling
itemBuilder: (context, index) {
if (index < collections!.length) {
return CollectionItem(collections![index], sideOfThumbnail);
return CollectionItem(
collections![index],
sideOfThumbnail,
shouldRender: index < collectionItemsToPreload,
);
} else {
return const CreateNewAlbumWidget();
}

View file

@ -152,7 +152,7 @@ class ContentContainerWidget extends StatelessWidget {
? const SizedBox.shrink()
: Text(
title!,
style: textTheme.h3Bold
style: textTheme.largeBold
.copyWith(color: textBaseDark), //constant color
),
title == null || body == null

View file

@ -115,7 +115,6 @@ class ButtonWidget extends StatelessWidget {
buttonType.defaultBorderColor(colorScheme, buttonSize);
buttonStyle.pressedBorderColor = buttonType.pressedBorderColor(
colorScheme: colorScheme,
inverseColorScheme: inverseColorScheme,
buttonSize: buttonSize,
);
buttonStyle.disabledBorderColor =
@ -206,26 +205,19 @@ class _ButtonChildWidgetState extends State<ButtonChildWidget> {
ExecutionState executionState = ExecutionState.idle;
@override
Widget build(BuildContext context) {
progressStatus = widget.progressStatus;
checkIconColor = widget.buttonStyle.checkIconColor ??
widget.buttonStyle.defaultIconColor;
loadingIconColor = widget.buttonStyle.defaultIconColor;
if (widget.isDisabled) {
buttonColor = widget.buttonStyle.disabledButtonColor ??
widget.buttonStyle.defaultButtonColor;
borderColor = widget.buttonStyle.disabledBorderColor ??
widget.buttonStyle.defaultBorderColor;
iconColor = widget.buttonStyle.disabledIconColor ??
widget.buttonStyle.defaultIconColor;
labelStyle = widget.buttonStyle.disabledLabelStyle ??
widget.buttonStyle.defaultLabelStyle;
} else {
buttonColor = widget.buttonStyle.defaultButtonColor;
borderColor = widget.buttonStyle.defaultBorderColor;
iconColor = widget.buttonStyle.defaultIconColor;
labelStyle = widget.buttonStyle.defaultLabelStyle;
void initState() {
_setButtonTheme();
super.initState();
}
@override
void didUpdateWidget(covariant ButtonChildWidget oldWidget) {
_setButtonTheme();
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
if (executionState == ExecutionState.successful) {
Future.delayed(Duration(seconds: widget.isInAlert ? 1 : 2), () {
setState(() {
@ -241,7 +233,9 @@ class _ButtonChildWidgetState extends State<ButtonChildWidget> {
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(4)),
border: Border.all(color: borderColor),
border: widget.buttonType == ButtonType.tertiaryCritical
? Border.all(color: borderColor)
: null,
),
child: AnimatedContainer(
duration: const Duration(milliseconds: 16),
@ -383,6 +377,28 @@ class _ButtonChildWidgetState extends State<ButtonChildWidget> {
);
}
void _setButtonTheme() {
progressStatus = widget.progressStatus;
checkIconColor = widget.buttonStyle.checkIconColor ??
widget.buttonStyle.defaultIconColor;
loadingIconColor = widget.buttonStyle.defaultIconColor;
if (widget.isDisabled) {
buttonColor = widget.buttonStyle.disabledButtonColor ??
widget.buttonStyle.defaultButtonColor;
borderColor = widget.buttonStyle.disabledBorderColor ??
widget.buttonStyle.defaultBorderColor;
iconColor = widget.buttonStyle.disabledIconColor ??
widget.buttonStyle.defaultIconColor;
labelStyle = widget.buttonStyle.disabledLabelStyle ??
widget.buttonStyle.defaultLabelStyle;
} else {
buttonColor = widget.buttonStyle.defaultButtonColor;
borderColor = widget.buttonStyle.defaultBorderColor;
iconColor = widget.buttonStyle.defaultIconColor;
labelStyle = widget.buttonStyle.defaultLabelStyle;
}
}
bool get _shouldRegisterGestures =>
!widget.isDisabled && executionState == ExecutionState.idle;

View file

@ -117,7 +117,7 @@ class ContentContainer extends StatelessWidget {
],
),
icon == null ? const SizedBox.shrink() : const SizedBox(height: 19),
Text(title, style: textTheme.h3Bold),
Text(title, style: textTheme.largeBold),
body != null ? const SizedBox(height: 19) : const SizedBox.shrink(),
body != null
? Text(

View file

@ -55,6 +55,15 @@ enum ButtonType {
if (isPrimary) {
return colorScheme.primary700;
}
if (isSecondary) {
return colorScheme.fillFaintPressed;
}
if (isNeutral) {
return colorScheme.fillBasePressed;
}
if (this == ButtonType.critical) {
return colorScheme.warning800;
}
return null;
}
@ -85,20 +94,10 @@ enum ButtonType {
//Returning null to fallback to default color
Color? pressedBorderColor({
required EnteColorScheme colorScheme,
required EnteColorScheme inverseColorScheme,
required ButtonSize buttonSize,
}) {
if (isPrimary) {
return colorScheme.strokeMuted;
}
if (buttonSize == ButtonSize.small && this == ButtonType.tertiaryCritical) {
return null;
}
if (isSecondary || isCritical) {
return colorScheme.strokeBase;
}
if (isNeutral) {
return inverseColorScheme.strokeBase;
if (this == ButtonType.tertiaryCritical && buttonSize == ButtonSize.large) {
return colorScheme.warning700;
}
return null;
}
@ -133,8 +132,11 @@ enum ButtonType {
//Returning null to fallback to default color
Color? pressedIconColor(EnteColorScheme colorScheme, ButtonSize buttonSize) {
if (this == ButtonType.tertiaryCritical && buttonSize == ButtonSize.large) {
return colorScheme.strokeBase;
if (this == ButtonType.tertiaryCritical) {
return colorScheme.warning700;
}
if (this == ButtonType.tertiary && buttonSize == ButtonSize.small) {
return colorScheme.fillBasePressed;
}
return null;
}
@ -176,8 +178,11 @@ enum ButtonType {
EnteColorScheme colorScheme,
ButtonSize buttonSize,
) {
if (this == ButtonType.tertiaryCritical && buttonSize == ButtonSize.large) {
return textTheme.bodyBold.copyWith(color: colorScheme.strokeBase);
if (this == ButtonType.tertiaryCritical) {
return textTheme.bodyBold.copyWith(color: colorScheme.warning700);
}
if (this == ButtonType.tertiary && buttonSize == ButtonSize.small) {
return textTheme.bodyBold.copyWith(color: colorScheme.fillBasePressed);
}
return null;
}

View file

@ -240,34 +240,6 @@ class RefreshIndicatorWidget extends StatelessWidget {
}
}
class BrandingWidget extends StatelessWidget {
const BrandingWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
children: [
Container(
height: kContainerHeight,
padding: const EdgeInsets.only(left: 12, top: 4),
child: const Align(
alignment: Alignment.centerLeft,
child: Text(
"ente",
style: TextStyle(
fontWeight: FontWeight.bold,
fontFamily: 'Montserrat',
fontSize: 24,
height: 1,
),
),
),
),
],
);
}
}
class SyncStatusCompletedWidget extends StatelessWidget {
const SyncStatusCompletedWidget({Key? key}) : super(key: key);

View file

@ -352,7 +352,7 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
Widget _getRecyclableView() {
return VisibilityDetector(
key: UniqueKey(),
key: Key("gallery" + widget.filesInDay.first.tag),
onVisibilityChanged: (visibility) {
final shouldRender = visibility.visibleFraction > 0;
if (mounted && shouldRender != _shouldRender) {
@ -370,7 +370,7 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
Widget _getNonRecyclableView() {
if (!_shouldRender!) {
return VisibilityDetector(
key: UniqueKey(),
key: Key("gallery" + widget.filesInDay.first.tag),
onVisibilityChanged: (visibility) {
if (mounted && visibility.visibleFraction > 0 && !_shouldRender!) {
setState(() {

View file

@ -23,7 +23,10 @@ class UnSyncedIcon extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const _BottomLeftOverlayIcon(Icons.cloud_off_outlined);
return const _BottomLeftOverlayIcon(
Icons.cloud_off_outlined,
baseSize: 18,
);
}
}
@ -186,7 +189,7 @@ class _BottomLeftOverlayIcon extends StatelessWidget {
if (constraints.hasBoundedWidth) {
final w = constraints.maxWidth;
if (w > 120) {
if (w > 125) {
size = 24;
} else if (w < 75) {
inset = 3;

View file

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
@ -13,6 +14,7 @@ import 'package:photos/events/local_photos_updated_event.dart';
import 'package:photos/models/file.dart';
import 'package:photos/ui/common/loading_widget.dart';
import 'package:photos/utils/file_util.dart';
import 'package:photos/utils/image_util.dart';
import 'package:photos/utils/thumbnail_util.dart';
class ZoomableImage extends StatefulWidget {
@ -35,7 +37,7 @@ class ZoomableImage extends StatefulWidget {
class _ZoomableImageState extends State<ZoomableImage>
with SingleTickerProviderStateMixin {
final Logger _logger = Logger("ZoomableImage");
late Logger _logger;
late File _photo;
ImageProvider? _imageProvider;
bool _loadedSmallThumbnail = false;
@ -45,10 +47,13 @@ class _ZoomableImageState extends State<ZoomableImage>
bool _loadedFinalImage = false;
ValueChanged<PhotoViewScaleState>? _scaleStateChangedCallback;
bool _isZooming = false;
PhotoViewController _photoViewController = PhotoViewController();
int? _thumbnailWidth;
@override
void initState() {
_photo = widget.photo;
_logger = Logger("ZoomableImage_" + _photo.displayName);
debugPrint('initState for ${_photo.toString()}');
_scaleStateChangedCallback = (value) {
if (widget.shouldDisableScroll != null) {
@ -61,6 +66,12 @@ class _ZoomableImageState extends State<ZoomableImage>
super.initState();
}
@override
void dispose() {
_photoViewController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_photo.isRemoteFile) {
@ -75,6 +86,7 @@ class _ZoomableImageState extends State<ZoomableImage>
axis: Axis.vertical,
child: PhotoView(
imageProvider: _imageProvider,
controller: _photoViewController,
scaleStateChangedCallback: _scaleStateChangedCallback,
minScale: PhotoViewComputedScale.contained,
gaplessPlayback: true,
@ -106,6 +118,7 @@ class _ZoomableImageState extends State<ZoomableImage>
if (cachedThumbnail != null) {
_imageProvider = Image.memory(cachedThumbnail).image;
_loadedSmallThumbnail = true;
_captureThumbnailDimensions(_imageProvider!);
} else {
getThumbnailFromServer(_photo).then((file) {
final imageProvider = Image.memory(file).image;
@ -115,6 +128,7 @@ class _ZoomableImageState extends State<ZoomableImage>
setState(() {
_imageProvider = imageProvider;
_loadedSmallThumbnail = true;
_captureThumbnailDimensions(_imageProvider!);
});
}
}).catchError((e) {
@ -125,7 +139,8 @@ class _ZoomableImageState extends State<ZoomableImage>
});
}
}
if (!_loadedFinalImage) {
if (!_loadedFinalImage && !_loadingFinalImage) {
_loadingFinalImage = true;
getFileFromServer(_photo).then((file) {
_onFinalImageLoaded(
Image.file(
@ -209,16 +224,42 @@ class _ZoomableImageState extends State<ZoomableImage>
void _onFinalImageLoaded(ImageProvider imageProvider) {
if (mounted) {
precacheImage(imageProvider, context).then((value) {
precacheImage(imageProvider, context).then((value) async {
if (mounted) {
await _updatePhotoViewController(imageProvider);
setState(() {
_imageProvider = imageProvider;
_loadedFinalImage = true;
_logger.info("Final image loaded");
});
}
});
}
}
Future<void> _captureThumbnailDimensions(ImageProvider imageProvider) async {
final imageInfo = await getImageInfo(imageProvider);
_thumbnailWidth = imageInfo.image.width;
}
Future<void> _updatePhotoViewController(ImageProvider imageProvider) async {
if (_thumbnailWidth == null || _photoViewController.scale == null) {
return;
}
final imageInfo = await getImageInfo(imageProvider);
final scale = _photoViewController.scale! /
(imageInfo.image.width / _thumbnailWidth!);
final currentPosition = _photoViewController.value.position;
final positionScaleFactor = 1 / scale;
final newPosition = currentPosition.scale(
positionScaleFactor,
positionScaleFactor,
);
_photoViewController = PhotoViewController(
initialPosition: newPosition,
initialScale: scale,
);
}
bool _isGIF() => _photo.displayName.toLowerCase().endsWith(".gif");
}

View file

@ -271,18 +271,27 @@ class FileUploader {
}
}
Future<void> checkNetworkForUpload({bool isForceUpload = false}) async {
// Note: We don't support force uploading currently. During force upload,
// network check is skipped completely
if (isForceUpload) {
return;
}
final connectivityResult = await (Connectivity().checkConnectivity());
final canUploadUnderCurrentNetworkConditions =
(connectivityResult == ConnectivityResult.wifi ||
Configuration.instance.shouldBackupOverMobileData());
if (!canUploadUnderCurrentNetworkConditions) {
throw WiFiUnavailableError();
}
}
Future<File> _tryToUpload(
File file,
int collectionID,
bool forcedUpload,
) async {
final connectivityResult = await (Connectivity().checkConnectivity());
final canUploadUnderCurrentNetworkConditions =
(connectivityResult == ConnectivityResult.wifi ||
Configuration.instance.shouldBackupOverMobileData());
if (!canUploadUnderCurrentNetworkConditions && !forcedUpload) {
throw WiFiUnavailableError();
}
await checkNetworkForUpload(isForceUpload: forcedUpload);
final fileOnDisk = await FilesDB.instance.getFile(file.generatedID!);
final wasAlreadyUploaded = fileOnDisk != null &&
fileOnDisk.uploadedFileID != null &&

16
lib/utils/image_util.dart Normal file
View file

@ -0,0 +1,16 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
Future<ImageInfo> getImageInfo(ImageProvider imageProvider) {
final completer = Completer<ImageInfo>();
final imageStream = imageProvider.resolve(const ImageConfiguration());
final listener = ImageStreamListener(
((imageInfo, _) {
completer.complete(imageInfo);
}),
);
imageStream.addListener(listener);
completer.future.whenComplete(() => imageStream.removeListener(listener));
return completer.future;
}