Skip to content

Commit

Permalink
feat(mobile): Rework of the exif sheet (immich-app#1213)
Browse files Browse the repository at this point in the history
* Draggable sheet in asset viewer page

* Minor improvements

* Fix display bug

* Fix some styling

Co-authored-by: Alex <[email protected]>
  • Loading branch information
matthinc and alextran1502 authored Jan 11, 2023
1 parent b597cd8 commit 89a6ed2
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 169 deletions.
Binary file added mobile/flutter_01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
214 changes: 111 additions & 103 deletions mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down Expand Up @@ -65,127 +66,134 @@ class ExifBottomSheet extends ConsumerWidget {
);
}

final textColor = Theme.of(context).primaryColor;

ExifResponseDto? exifInfo = assetDetail.remote?.exifInfo;

buildLocationText() {
return Text(
"${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(),
),
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(),
),

// 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]),
)
],
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(),
),
),
// 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],
const SizedBox(height: 12),
if (exifInfo?.dateTimeOriginal != null)
Text(
DateFormat('date_format'.tr()).format(
exifInfo!.dateTimeOriginal!.toLocal(),
),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
"exif_bottom_sheet_details",
style: TextStyle(fontSize: 11, color: Colors.grey[400]),
).tr(),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
),
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,
),

// Location
if (assetDetail.latitude != null)
Padding(
padding: const EdgeInsets.only(top: 32.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Divider(
thickness: 1,
),
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),
)
],
),
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),
),
// 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],
),
subtitle: Text(
"ƒ/${exifInfo.fNumber} 1/${(1 / (exifInfo.exposureTime ?? 1)).toStringAsFixed(0)} ${exifInfo.focalLength} mm ISO${exifInfo.iso} ",
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,
),
),
],
],
),
),
),
);
}
Expand Down
8 changes: 6 additions & 2 deletions mobile/lib/modules/asset_viewer/views/gallery_viewer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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!);
Expand Down Expand Up @@ -162,6 +165,7 @@ class GalleryViewerPage extends HookConsumerWidget {
heroTag: assetList[index].id,
loadPreview: isLoadPreview.value,
loadOriginal: isLoadOriginal.value,
showExifSheet: showInfo,
);
}
} else {
Expand Down
17 changes: 3 additions & 14 deletions mobile/lib/modules/asset_viewer/views/image_viewer_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;

Expand All @@ -29,6 +29,7 @@ class ImageViewerPage extends HookConsumerWidget {
required this.isZoomedListener,
required this.loadPreview,
required this.loadOriginal,
this.showExifSheet,
}) : super(key: key);

Asset? assetDetail;
Expand Down Expand Up @@ -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(
Expand All @@ -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! : () {},
),
),
),
Expand Down
51 changes: 1 addition & 50 deletions mobile/lib/modules/home/ui/control_bottom_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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),
),
],
),
);
}
}
Loading

0 comments on commit 89a6ed2

Please sign in to comment.