1
0
mirror of https://github.com/immich-app/immich.git synced 2024-11-28 09:33:27 +02:00

feat(mobile): Rework of the exif sheet (#1213)

* Draggable sheet in asset viewer page

* Minor improvements

* Fix display bug

* Fix some styling

Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
Matthias Rupp 2023-01-11 21:54:12 +01:00 committed by GitHub
parent b597cd891b
commit 89a6ed2a5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 174 additions and 171 deletions

BIN
mobile/flutter_01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 KiB

View File

@ -3,12 +3,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/ui/drag_sheet.dart';
import 'package:openapi/api.dart';
import 'package:path/path.dart' as p;
import 'package:latlong2/latlong.dart';
import 'package:immich_mobile/utils/bytes_units.dart';
class ExifBottomSheet extends ConsumerWidget {
class ExifBottomSheet extends HookConsumerWidget {
final Asset assetDetail;
const ExifBottomSheet({Key? key, required this.assetDetail})
@ -65,6 +66,8 @@ class ExifBottomSheet extends ConsumerWidget {
);
}
final textColor = Theme.of(context).primaryColor;
ExifResponseDto? exifInfo = assetDetail.remote?.exifInfo;
buildLocationText() {
@ -72,120 +75,125 @@ class ExifBottomSheet extends ConsumerWidget {
"${exifInfo?.city}, ${exifInfo?.state}",
style: TextStyle(
fontSize: 12,
color: Colors.grey[200],
fontWeight: FontWeight.bold,
color: textColor,
),
);
}
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 8),
child: ListView(
children: [
if (exifInfo?.dateTimeOriginal != null)
Text(
DateFormat('date_format'.tr()).format(
exifInfo!.dateTimeOriginal!.toLocal(),
return SingleChildScrollView(
child: Card(
margin: const EdgeInsets.all(0),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 12),
const Align(
alignment: Alignment.center,
child: CustomDraggingHandle(),
),
style: TextStyle(
color: Colors.grey[400],
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: Text(
"exif_bottom_sheet_description",
style: TextStyle(
color: Colors.grey[500],
fontSize: 11,
),
).tr(),
),
const SizedBox(height: 12),
if (exifInfo?.dateTimeOriginal != null)
Text(
DateFormat('date_format'.tr()).format(
exifInfo!.dateTimeOriginal!.toLocal(),
),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
// Location
if (assetDetail.latitude != null)
Padding(
padding: const EdgeInsets.only(top: 32.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Divider(
thickness: 1,
color: Colors.grey[600],
),
Text(
"exif_bottom_sheet_location",
style: TextStyle(fontSize: 11, color: Colors.grey[400]),
).tr(),
if (assetDetail.latitude != null &&
assetDetail.longitude != null)
buildMap(),
if (exifInfo != null &&
exifInfo.city != null &&
exifInfo.state != null)
buildLocationText(),
Text(
"${assetDetail.latitude?.toStringAsFixed(4)}, ${assetDetail.longitude?.toStringAsFixed(4)}",
style: TextStyle(fontSize: 12, color: Colors.grey[400]),
)
],
),
),
// Detail
if (exifInfo != null)
Padding(
padding: const EdgeInsets.only(top: 32.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Divider(
thickness: 1,
color: Colors.grey[600],
),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
"exif_bottom_sheet_details",
style: TextStyle(fontSize: 11, color: Colors.grey[400]),
).tr(),
),
ListTile(
contentPadding: const EdgeInsets.all(0),
dense: true,
textColor: Colors.grey[300],
iconColor: Colors.grey[300],
leading: const Icon(Icons.image),
title: Text(
"${exifInfo.imageName!}${p.extension(assetDetail.remote!.originalPath)}",
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: exifInfo.exifImageHeight != null
? Text(
"${exifInfo.exifImageHeight} x ${exifInfo.exifImageWidth} ${formatBytes(exifInfo.fileSizeInByte!)} ",
)
: null,
),
if (exifInfo.make != null)
ListTile(
contentPadding: const EdgeInsets.all(0),
dense: true,
textColor: Colors.grey[300],
iconColor: Colors.grey[300],
leading: const Icon(Icons.camera),
title: Text(
"${exifInfo.make} ${exifInfo.model}",
style: const TextStyle(fontWeight: FontWeight.bold),
// Location
if (assetDetail.latitude != null)
Padding(
padding: const EdgeInsets.only(top: 32.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Divider(
thickness: 1,
),
subtitle: Text(
"ƒ/${exifInfo.fNumber} 1/${(1 / (exifInfo.exposureTime ?? 1)).toStringAsFixed(0)} ${exifInfo.focalLength} mm ISO${exifInfo.iso} ",
Text(
"exif_bottom_sheet_location",
style: TextStyle(fontSize: 11, color: textColor),
).tr(),
if (assetDetail.latitude != null &&
assetDetail.longitude != null)
buildMap(),
if (exifInfo != null &&
exifInfo.city != null &&
exifInfo.state != null)
buildLocationText(),
Text(
"${assetDetail.latitude?.toStringAsFixed(4)}, ${assetDetail.longitude?.toStringAsFixed(4)}",
style: const TextStyle(fontSize: 12),
)
],
),
),
// Detail
if (exifInfo != null)
Padding(
padding: const EdgeInsets.only(top: 32.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Divider(
thickness: 1,
color: Colors.grey[600],
),
),
],
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
"exif_bottom_sheet_details",
style: TextStyle(fontSize: 11, color: textColor),
).tr(),
),
ListTile(
contentPadding: const EdgeInsets.all(0),
dense: true,
leading: const Icon(Icons.image),
title: Text(
"${exifInfo.imageName!}${p.extension(assetDetail.remote!.originalPath)}",
style: TextStyle(
fontWeight: FontWeight.bold,
color: textColor,
),
),
subtitle: exifInfo.exifImageHeight != null
? Text(
"${exifInfo.exifImageHeight} x ${exifInfo.exifImageWidth} ${formatBytes(exifInfo.fileSizeInByte ?? 0)} ",
)
: null,
),
if (exifInfo.make != null)
ListTile(
contentPadding: const EdgeInsets.all(0),
dense: true,
leading: const Icon(Icons.camera),
title: Text(
"${exifInfo.make} ${exifInfo.model}",
style: TextStyle(
color: textColor,
fontWeight: FontWeight.bold,
),
),
subtitle: Text(
"ƒ/${exifInfo.fNumber} 1/${(1 / (exifInfo.exposureTime ?? 1)).toStringAsFixed(0)} ${exifInfo.focalLength} mm ISO${exifInfo.iso} ",
),
),
],
),
),
const SizedBox(
height: 50,
),
),
],
],
),
),
),
);
}

View File

@ -69,9 +69,12 @@ class GalleryViewerPage extends HookConsumerWidget {
void showInfo() {
showModalBottomSheet(
backgroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
barrierColor: Colors.transparent,
isScrollControlled: false,
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context) {
return ExifBottomSheet(assetDetail: assetDetail!);
@ -162,6 +165,7 @@ class GalleryViewerPage extends HookConsumerWidget {
heroTag: assetList[index].id,
loadPreview: isLoadPreview.value,
loadOriginal: isLoadOriginal.value,
showExifSheet: showInfo,
);
}
} else {

View File

@ -4,7 +4,6 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/image_viewer_page_state.provider.dart';
import 'package:immich_mobile/modules/asset_viewer/ui/exif_bottom_sheet.dart';
import 'package:immich_mobile/modules/asset_viewer/ui/remote_photo_view.dart';
import 'package:immich_mobile/modules/home/services/asset.service.dart';
import 'package:immich_mobile/shared/models/asset.dart';
@ -17,6 +16,7 @@ class ImageViewerPage extends HookConsumerWidget {
final String authToken;
final ValueNotifier<bool> isZoomedListener;
final void Function() isZoomedFunction;
final void Function()? showExifSheet;
final bool loadPreview;
final bool loadOriginal;
@ -29,6 +29,7 @@ class ImageViewerPage extends HookConsumerWidget {
required this.isZoomedListener,
required this.loadPreview,
required this.loadOriginal,
this.showExifSheet,
}) : super(key: key);
Asset? assetDetail;
@ -56,18 +57,6 @@ class ImageViewerPage extends HookConsumerWidget {
[],
);
showInfo() {
showModalBottomSheet(
backgroundColor: Colors.black,
barrierColor: Colors.transparent,
isScrollControlled: false,
context: context,
builder: (context) {
return ExifBottomSheet(assetDetail: assetDetail ?? asset);
},
);
}
return Stack(
children: [
Center(
@ -81,7 +70,7 @@ class ImageViewerPage extends HookConsumerWidget {
isZoomedFunction: isZoomedFunction,
isZoomedListener: isZoomedListener,
onSwipeDown: () => AutoRouter.of(context).pop(),
onSwipeUp: asset.isRemote ? showInfo : () {},
onSwipeUp: (asset.isRemote && showExifSheet != null) ? showExifSheet! : () {},
),
),
),

View File

@ -5,6 +5,7 @@ import 'package:hive/hive.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/hive_box.dart';
import 'package:immich_mobile/modules/home/ui/delete_diaglog.dart';
import 'package:immich_mobile/shared/ui/drag_sheet.dart';
import 'package:immich_mobile/utils/image_url_builder.dart';
import 'package:openapi/api.dart';
@ -200,53 +201,3 @@ class AddToAlbumTitleRow extends StatelessWidget {
);
}
}
class CustomDraggingHandle extends StatelessWidget {
const CustomDraggingHandle({super.key});
@override
Widget build(BuildContext context) {
return Container(
height: 5,
width: 30,
decoration: BoxDecoration(
color: Colors.grey[500],
borderRadius: BorderRadius.circular(16),
),
);
}
}
class ControlBoxButton extends StatelessWidget {
const ControlBoxButton({
Key? key,
required this.label,
required this.iconData,
required this.onPressed,
}) : super(key: key);
final String label;
final IconData iconData;
final Function onPressed;
@override
Widget build(BuildContext context) {
return MaterialButton(
padding: const EdgeInsets.all(10),
shape: const CircleBorder(),
onPressed: () => onPressed(),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(iconData, size: 24),
const SizedBox(height: 6),
Text(
label,
style: const TextStyle(fontSize: 12.0),
),
],
),
);
}
}

View File

@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
class CustomDraggingHandle extends StatelessWidget {
const CustomDraggingHandle({super.key});
@override
Widget build(BuildContext context) {
return Container(
height: 5,
width: 30,
decoration: BoxDecoration(
color: Colors.grey[500],
borderRadius: BorderRadius.circular(16),
),
);
}
}
class ControlBoxButton extends StatelessWidget {
const ControlBoxButton({
Key? key,
required this.label,
required this.iconData,
required this.onPressed,
}) : super(key: key);
final String label;
final IconData iconData;
final Function onPressed;
@override
Widget build(BuildContext context) {
return MaterialButton(
padding: const EdgeInsets.all(10),
shape: const CircleBorder(),
onPressed: () => onPressed(),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(iconData, size: 24),
const SizedBox(height: 6),
Text(
label,
style: const TextStyle(fontSize: 12.0),
),
],
),
);
}
}