✨ Better attachment fullscreen (support exif meta)
This commit is contained in:
		| @@ -199,6 +199,8 @@ class _BootstrapperShellState extends State<BootstrapperShell> { | ||||
|         final AuthProvider auth = Get.find(); | ||||
|         try { | ||||
|           await Future.wait([ | ||||
|             if (auth.isAuthorized.isTrue) | ||||
|               Get.find<NotificationProvider>().fetchNotification(), | ||||
|             if (auth.isAuthorized.isTrue) | ||||
|               Get.find<RelationshipProvider>().refreshRelativeList(), | ||||
|             if (auth.isAuthorized.isTrue) | ||||
|   | ||||
| @@ -1,7 +1,12 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:json_annotation/json_annotation.dart'; | ||||
|  | ||||
| part 'notification.g.dart'; | ||||
|  | ||||
| const Map<String, IconData> NotificationTopicIcons = { | ||||
|   'passport.security.alert': Icons.gpp_maybe, | ||||
| }; | ||||
|  | ||||
| @JsonSerializable() | ||||
| class Notification { | ||||
|   int id; | ||||
| @@ -9,11 +14,13 @@ class Notification { | ||||
|   DateTime updatedAt; | ||||
|   DateTime? deletedAt; | ||||
|   DateTime? readAt; | ||||
|   String topic; | ||||
|   String title; | ||||
|   String? subtitle; | ||||
|   String body; | ||||
|   String? avatar; | ||||
|   String? picture; | ||||
|   Map<String, dynamic>? metadata; | ||||
|   int? senderId; | ||||
|   int accountId; | ||||
|  | ||||
| @@ -23,11 +30,13 @@ class Notification { | ||||
|     required this.updatedAt, | ||||
|     required this.deletedAt, | ||||
|     required this.readAt, | ||||
|     required this.topic, | ||||
|     required this.title, | ||||
|     required this.subtitle, | ||||
|     required this.body, | ||||
|     required this.avatar, | ||||
|     required this.picture, | ||||
|     required this.metadata, | ||||
|     required this.senderId, | ||||
|     required this.accountId, | ||||
|   }); | ||||
|   | ||||
| @@ -16,11 +16,13 @@ Notification _$NotificationFromJson(Map<String, dynamic> json) => Notification( | ||||
|       readAt: json['read_at'] == null | ||||
|           ? null | ||||
|           : DateTime.parse(json['read_at'] as String), | ||||
|       topic: json['topic'] as String, | ||||
|       title: json['title'] as String, | ||||
|       subtitle: json['subtitle'] as String?, | ||||
|       body: json['body'] as String, | ||||
|       avatar: json['avatar'] as String?, | ||||
|       picture: json['picture'] as String?, | ||||
|       metadata: json['metadata'] as Map<String, dynamic>?, | ||||
|       senderId: (json['sender_id'] as num?)?.toInt(), | ||||
|       accountId: (json['account_id'] as num).toInt(), | ||||
|     ); | ||||
| @@ -32,11 +34,13 @@ Map<String, dynamic> _$NotificationToJson(Notification instance) => | ||||
|       'updated_at': instance.updatedAt.toIso8601String(), | ||||
|       'deleted_at': instance.deletedAt?.toIso8601String(), | ||||
|       'read_at': instance.readAt?.toIso8601String(), | ||||
|       'topic': instance.topic, | ||||
|       'title': instance.title, | ||||
|       'subtitle': instance.subtitle, | ||||
|       'body': instance.body, | ||||
|       'avatar': instance.avatar, | ||||
|       'picture': instance.picture, | ||||
|       'metadata': instance.metadata, | ||||
|       'sender_id': instance.senderId, | ||||
|       'account_id': instance.accountId, | ||||
|     }; | ||||
|   | ||||
| @@ -18,12 +18,6 @@ class NotificationProvider extends GetxController { | ||||
|   RxList<Notification> notifications = | ||||
|       List<Notification>.empty(growable: true).obs; | ||||
|  | ||||
|   @override | ||||
|   void onInit() { | ||||
|     super.onInit(); | ||||
|     fetchNotification(); | ||||
|   } | ||||
|  | ||||
|   Future<void> fetchNotification() async { | ||||
|     final AuthProvider auth = Get.find(); | ||||
|     if (auth.isAuthorized.isFalse) return; | ||||
| @@ -35,7 +29,6 @@ class NotificationProvider extends GetxController { | ||||
|       final result = PaginationResult.fromJson(resp.body); | ||||
|       final data = result.data?.map((x) => Notification.fromJson(x)).toList(); | ||||
|       if (data != null) { | ||||
|         print(data.map((x) => x.toJson())); | ||||
|         notifications.addAll(data); | ||||
|         notificationUnread.value = data.where((x) => x.readAt == null).length; | ||||
|       } | ||||
|   | ||||
| @@ -1,7 +1,10 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:gap/gap.dart'; | ||||
| import 'package:get/get.dart'; | ||||
| import 'package:solian/models/notification.dart'; | ||||
| import 'package:solian/providers/notifications.dart'; | ||||
| import 'package:solian/widgets/loading_indicator.dart'; | ||||
| import 'package:solian/widgets/markdown_text_content.dart'; | ||||
| import 'package:uuid/uuid.dart'; | ||||
|  | ||||
| class NotificationScreen extends StatefulWidget { | ||||
| @@ -76,7 +79,7 @@ class _NotificationScreenState extends State<NotificationScreen> { | ||||
|                         return ClipRect( | ||||
|                           child: Dismissible( | ||||
|                             direction: element.readAt == null | ||||
|                                 ? DismissDirection.vertical | ||||
|                                 ? DismissDirection.horizontal | ||||
|                                 : DismissDirection.none, | ||||
|                             key: Key(const Uuid().v4()), | ||||
|                             background: Container( | ||||
| @@ -95,18 +98,45 @@ class _NotificationScreenState extends State<NotificationScreen> { | ||||
|                               child: | ||||
|                                   const Icon(Icons.check, color: Colors.white), | ||||
|                             ), | ||||
|                             child: ListTile( | ||||
|                               contentPadding: const EdgeInsets.symmetric( | ||||
|                                 horizontal: 24, | ||||
|                                 vertical: 8, | ||||
|                             child: Container( | ||||
|                               padding: const EdgeInsets.symmetric( | ||||
|                                 horizontal: 28, | ||||
|                                 vertical: 16, | ||||
|                               ), | ||||
|                               title: Text(element.title), | ||||
|                               subtitle: Column( | ||||
|                               child: Row( | ||||
|                                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                                 children: [ | ||||
|                                   if (element.subtitle != null) | ||||
|                                     Text(element.subtitle!), | ||||
|                                   Text(element.body), | ||||
|                                   Icon(NotificationTopicIcons[element.topic]), | ||||
|                                   const Gap(12), | ||||
|                                   Expanded( | ||||
|                                     child: Column( | ||||
|                                       crossAxisAlignment: | ||||
|                                           CrossAxisAlignment.start, | ||||
|                                       children: [ | ||||
|                                         Text( | ||||
|                                           element.title, | ||||
|                                           style: Theme.of(context) | ||||
|                                               .textTheme | ||||
|                                               .titleMedium, | ||||
|                                         ), | ||||
|                                         if (element.subtitle != null) | ||||
|                                           Text( | ||||
|                                             element.subtitle!, | ||||
|                                             style: Theme.of(context) | ||||
|                                                 .textTheme | ||||
|                                                 .titleSmall, | ||||
|                                           ), | ||||
|                                         const Gap(4), | ||||
|                                         MarkdownTextContent( | ||||
|                                           content: element.body, | ||||
|                                           isAutoWarp: true, | ||||
|                                           isSelectable: true, | ||||
|                                           parentId: | ||||
|                                               'notification-${element.id}', | ||||
|                                         ), | ||||
|                                       ], | ||||
|                                     ), | ||||
|                                   ), | ||||
|                                 ], | ||||
|                               ), | ||||
|                             ), | ||||
|   | ||||
| @@ -46,7 +46,7 @@ class _DashboardScreenState extends State<DashboardScreen> { | ||||
|       Theme.of(context).colorScheme.onSurface.withOpacity(0.75); | ||||
|  | ||||
|   List<Notification> get _pendingNotifications => | ||||
|       List<Notification>.from(_nty.notifications) | ||||
|       List<Notification>.from(_nty.notifications.where((x) => x.readAt == null)) | ||||
|         ..sort((a, b) => b.createdAt.compareTo(a.createdAt)); | ||||
|  | ||||
|   List<Post>? _currentPosts; | ||||
| @@ -254,7 +254,7 @@ class _DashboardScreenState extends State<DashboardScreen> { | ||||
|                         ), | ||||
|                         Text( | ||||
|                           'notificationUnreadCount'.trParams({ | ||||
|                             'count': _nty.notifications.length.toString(), | ||||
|                             'count': _pendingNotifications.length.toString(), | ||||
|                           }), | ||||
|                         ), | ||||
|                       ], | ||||
| @@ -272,7 +272,7 @@ class _DashboardScreenState extends State<DashboardScreen> { | ||||
|                     ), | ||||
|                   ], | ||||
|                 ).paddingOnly(left: 18, right: 18, bottom: 8), | ||||
|                 if (_nty.notifications.isNotEmpty) | ||||
|                 if (_pendingNotifications.isNotEmpty) | ||||
|                   SizedBox( | ||||
|                     height: 76, | ||||
|                     child: ListView.separated( | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import 'package:flutter_animate/flutter_animate.dart'; | ||||
| import 'package:gal/gal.dart'; | ||||
| import 'package:gap/gap.dart'; | ||||
| import 'package:get/get.dart'; | ||||
| import 'package:google_fonts/google_fonts.dart'; | ||||
| import 'package:solian/exts.dart'; | ||||
| import 'package:solian/models/attachment.dart'; | ||||
| import 'package:solian/platform.dart'; | ||||
| @@ -103,9 +104,10 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final metaTextStyle = TextStyle( | ||||
|     final metaTextStyle = GoogleFonts.roboto( | ||||
|       fontSize: 12, | ||||
|       color: _unFocusColor, | ||||
|       height: 1, | ||||
|     ); | ||||
|  | ||||
|     return DismissiblePage( | ||||
| @@ -239,27 +241,43 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> { | ||||
|                       child: Wrap( | ||||
|                         spacing: 6, | ||||
|                         children: [ | ||||
|                           Text( | ||||
|                             '#${widget.item.rid}', | ||||
|                             style: metaTextStyle, | ||||
|                           ), | ||||
|                           if (widget.item.metadata?['width'] != null && | ||||
|                               widget.item.metadata?['height'] != null) | ||||
|                           if (widget.item.metadata?['exif'] == null) | ||||
|                             Text( | ||||
|                               '${widget.item.metadata?['width']}x${widget.item.metadata?['height']}', | ||||
|                               '#${widget.item.rid}', | ||||
|                               style: metaTextStyle, | ||||
|                             ), | ||||
|                           if (widget.item.metadata?['ratio'] != null) | ||||
|                           if (widget.item.metadata?['exif']?['Model'] != null) | ||||
|                             Text( | ||||
|                               '${_getRatio().toPrecision(2)}', | ||||
|                               'shotOn'.trParams({ | ||||
|                                 'device': widget.item.metadata?['exif'] | ||||
|                                     ?['Model'] | ||||
|                               }), | ||||
|                               style: metaTextStyle, | ||||
|                             ).paddingOnly(right: 2), | ||||
|                           if (widget.item.metadata?['exif']?['ShutterSpeed'] != | ||||
|                               null) | ||||
|                             Text( | ||||
|                               widget.item.metadata?['exif']?['ShutterSpeed'], | ||||
|                               style: metaTextStyle, | ||||
|                             ).paddingOnly(right: 2), | ||||
|                           if (widget.item.metadata?['exif']?['ISO'] != null) | ||||
|                             Text( | ||||
|                               'ISO${widget.item.metadata?['exif']?['ISO']}', | ||||
|                               style: metaTextStyle, | ||||
|                             ).paddingOnly(right: 2), | ||||
|                           if (widget.item.metadata?['exif']?['Megapixels'] != | ||||
|                               null) | ||||
|                             Text( | ||||
|                               '${widget.item.metadata?['exif']?['Megapixels']}MP', | ||||
|                               style: metaTextStyle, | ||||
|                             ) | ||||
|                           else | ||||
|                             Text( | ||||
|                               widget.item.size.formatBytes(), | ||||
|                               style: metaTextStyle, | ||||
|                             ), | ||||
|                           Text( | ||||
|                             widget.item.size.formatBytes(), | ||||
|                             style: metaTextStyle, | ||||
|                           ), | ||||
|                           Text( | ||||
|                             widget.item.mimetype, | ||||
|                             '${widget.item.metadata?['width']}x${widget.item.metadata?['height']}', | ||||
|                             style: metaTextStyle, | ||||
|                           ), | ||||
|                         ], | ||||
|   | ||||
		Reference in New Issue
	
	Block a user