✨ Better attachment fullscreen (support exif meta)
This commit is contained in:
parent
aa17a5d52a
commit
6e00a99803
@ -486,5 +486,6 @@
|
|||||||
"shareImage": "Share as image",
|
"shareImage": "Share as image",
|
||||||
"shareImageFooter": "Only on the Solar Network",
|
"shareImageFooter": "Only on the Solar Network",
|
||||||
"fileSavedAt": "File saved at @path",
|
"fileSavedAt": "File saved at @path",
|
||||||
"showIp": "Show IP Address"
|
"showIp": "Show IP Address",
|
||||||
|
"shotOn": "Shot on @device"
|
||||||
}
|
}
|
||||||
|
@ -482,5 +482,6 @@
|
|||||||
"shareImage": "分享图片",
|
"shareImage": "分享图片",
|
||||||
"shareImageFooter": "上 Solar Network 看更多有趣帖子",
|
"shareImageFooter": "上 Solar Network 看更多有趣帖子",
|
||||||
"fileSavedAt": "文件保存于 @path",
|
"fileSavedAt": "文件保存于 @path",
|
||||||
"showIp": "显示 IP 地址"
|
"showIp": "显示 IP 地址",
|
||||||
|
"shotOn": "由 @device 拍摄"
|
||||||
}
|
}
|
||||||
|
@ -199,6 +199,8 @@ class _BootstrapperShellState extends State<BootstrapperShell> {
|
|||||||
final AuthProvider auth = Get.find();
|
final AuthProvider auth = Get.find();
|
||||||
try {
|
try {
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
|
if (auth.isAuthorized.isTrue)
|
||||||
|
Get.find<NotificationProvider>().fetchNotification(),
|
||||||
if (auth.isAuthorized.isTrue)
|
if (auth.isAuthorized.isTrue)
|
||||||
Get.find<RelationshipProvider>().refreshRelativeList(),
|
Get.find<RelationshipProvider>().refreshRelativeList(),
|
||||||
if (auth.isAuthorized.isTrue)
|
if (auth.isAuthorized.isTrue)
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
part 'notification.g.dart';
|
part 'notification.g.dart';
|
||||||
|
|
||||||
|
const Map<String, IconData> NotificationTopicIcons = {
|
||||||
|
'passport.security.alert': Icons.gpp_maybe,
|
||||||
|
};
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Notification {
|
class Notification {
|
||||||
int id;
|
int id;
|
||||||
@ -9,11 +14,13 @@ class Notification {
|
|||||||
DateTime updatedAt;
|
DateTime updatedAt;
|
||||||
DateTime? deletedAt;
|
DateTime? deletedAt;
|
||||||
DateTime? readAt;
|
DateTime? readAt;
|
||||||
|
String topic;
|
||||||
String title;
|
String title;
|
||||||
String? subtitle;
|
String? subtitle;
|
||||||
String body;
|
String body;
|
||||||
String? avatar;
|
String? avatar;
|
||||||
String? picture;
|
String? picture;
|
||||||
|
Map<String, dynamic>? metadata;
|
||||||
int? senderId;
|
int? senderId;
|
||||||
int accountId;
|
int accountId;
|
||||||
|
|
||||||
@ -23,11 +30,13 @@ class Notification {
|
|||||||
required this.updatedAt,
|
required this.updatedAt,
|
||||||
required this.deletedAt,
|
required this.deletedAt,
|
||||||
required this.readAt,
|
required this.readAt,
|
||||||
|
required this.topic,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.subtitle,
|
required this.subtitle,
|
||||||
required this.body,
|
required this.body,
|
||||||
required this.avatar,
|
required this.avatar,
|
||||||
required this.picture,
|
required this.picture,
|
||||||
|
required this.metadata,
|
||||||
required this.senderId,
|
required this.senderId,
|
||||||
required this.accountId,
|
required this.accountId,
|
||||||
});
|
});
|
||||||
|
@ -16,11 +16,13 @@ Notification _$NotificationFromJson(Map<String, dynamic> json) => Notification(
|
|||||||
readAt: json['read_at'] == null
|
readAt: json['read_at'] == null
|
||||||
? null
|
? null
|
||||||
: DateTime.parse(json['read_at'] as String),
|
: DateTime.parse(json['read_at'] as String),
|
||||||
|
topic: json['topic'] as String,
|
||||||
title: json['title'] as String,
|
title: json['title'] as String,
|
||||||
subtitle: json['subtitle'] as String?,
|
subtitle: json['subtitle'] as String?,
|
||||||
body: json['body'] as String,
|
body: json['body'] as String,
|
||||||
avatar: json['avatar'] as String?,
|
avatar: json['avatar'] as String?,
|
||||||
picture: json['picture'] as String?,
|
picture: json['picture'] as String?,
|
||||||
|
metadata: json['metadata'] as Map<String, dynamic>?,
|
||||||
senderId: (json['sender_id'] as num?)?.toInt(),
|
senderId: (json['sender_id'] as num?)?.toInt(),
|
||||||
accountId: (json['account_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(),
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
'read_at': instance.readAt?.toIso8601String(),
|
'read_at': instance.readAt?.toIso8601String(),
|
||||||
|
'topic': instance.topic,
|
||||||
'title': instance.title,
|
'title': instance.title,
|
||||||
'subtitle': instance.subtitle,
|
'subtitle': instance.subtitle,
|
||||||
'body': instance.body,
|
'body': instance.body,
|
||||||
'avatar': instance.avatar,
|
'avatar': instance.avatar,
|
||||||
'picture': instance.picture,
|
'picture': instance.picture,
|
||||||
|
'metadata': instance.metadata,
|
||||||
'sender_id': instance.senderId,
|
'sender_id': instance.senderId,
|
||||||
'account_id': instance.accountId,
|
'account_id': instance.accountId,
|
||||||
};
|
};
|
||||||
|
@ -18,12 +18,6 @@ class NotificationProvider extends GetxController {
|
|||||||
RxList<Notification> notifications =
|
RxList<Notification> notifications =
|
||||||
List<Notification>.empty(growable: true).obs;
|
List<Notification>.empty(growable: true).obs;
|
||||||
|
|
||||||
@override
|
|
||||||
void onInit() {
|
|
||||||
super.onInit();
|
|
||||||
fetchNotification();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> fetchNotification() async {
|
Future<void> fetchNotification() async {
|
||||||
final AuthProvider auth = Get.find();
|
final AuthProvider auth = Get.find();
|
||||||
if (auth.isAuthorized.isFalse) return;
|
if (auth.isAuthorized.isFalse) return;
|
||||||
@ -35,7 +29,6 @@ class NotificationProvider extends GetxController {
|
|||||||
final result = PaginationResult.fromJson(resp.body);
|
final result = PaginationResult.fromJson(resp.body);
|
||||||
final data = result.data?.map((x) => Notification.fromJson(x)).toList();
|
final data = result.data?.map((x) => Notification.fromJson(x)).toList();
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
print(data.map((x) => x.toJson()));
|
|
||||||
notifications.addAll(data);
|
notifications.addAll(data);
|
||||||
notificationUnread.value = data.where((x) => x.readAt == null).length;
|
notificationUnread.value = data.where((x) => x.readAt == null).length;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:solian/models/notification.dart';
|
||||||
import 'package:solian/providers/notifications.dart';
|
import 'package:solian/providers/notifications.dart';
|
||||||
import 'package:solian/widgets/loading_indicator.dart';
|
import 'package:solian/widgets/loading_indicator.dart';
|
||||||
|
import 'package:solian/widgets/markdown_text_content.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class NotificationScreen extends StatefulWidget {
|
class NotificationScreen extends StatefulWidget {
|
||||||
@ -76,7 +79,7 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
return ClipRect(
|
return ClipRect(
|
||||||
child: Dismissible(
|
child: Dismissible(
|
||||||
direction: element.readAt == null
|
direction: element.readAt == null
|
||||||
? DismissDirection.vertical
|
? DismissDirection.horizontal
|
||||||
: DismissDirection.none,
|
: DismissDirection.none,
|
||||||
key: Key(const Uuid().v4()),
|
key: Key(const Uuid().v4()),
|
||||||
background: Container(
|
background: Container(
|
||||||
@ -95,18 +98,45 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
child:
|
child:
|
||||||
const Icon(Icons.check, color: Colors.white),
|
const Icon(Icons.check, color: Colors.white),
|
||||||
),
|
),
|
||||||
child: ListTile(
|
child: Container(
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 24,
|
horizontal: 28,
|
||||||
vertical: 8,
|
vertical: 16,
|
||||||
),
|
),
|
||||||
title: Text(element.title),
|
child: Row(
|
||||||
subtitle: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
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)
|
if (element.subtitle != null)
|
||||||
Text(element.subtitle!),
|
Text(
|
||||||
Text(element.body),
|
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);
|
Theme.of(context).colorScheme.onSurface.withOpacity(0.75);
|
||||||
|
|
||||||
List<Notification> get _pendingNotifications =>
|
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));
|
..sort((a, b) => b.createdAt.compareTo(a.createdAt));
|
||||||
|
|
||||||
List<Post>? _currentPosts;
|
List<Post>? _currentPosts;
|
||||||
@ -254,7 +254,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'notificationUnreadCount'.trParams({
|
'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),
|
).paddingOnly(left: 18, right: 18, bottom: 8),
|
||||||
if (_nty.notifications.isNotEmpty)
|
if (_pendingNotifications.isNotEmpty)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 76,
|
height: 76,
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
|
@ -8,6 +8,7 @@ import 'package:flutter_animate/flutter_animate.dart';
|
|||||||
import 'package:gal/gal.dart';
|
import 'package:gal/gal.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:solian/exts.dart';
|
import 'package:solian/exts.dart';
|
||||||
import 'package:solian/models/attachment.dart';
|
import 'package:solian/models/attachment.dart';
|
||||||
import 'package:solian/platform.dart';
|
import 'package:solian/platform.dart';
|
||||||
@ -103,9 +104,10 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final metaTextStyle = TextStyle(
|
final metaTextStyle = GoogleFonts.roboto(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: _unFocusColor,
|
color: _unFocusColor,
|
||||||
|
height: 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
return DismissiblePage(
|
return DismissiblePage(
|
||||||
@ -239,27 +241,43 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> {
|
|||||||
child: Wrap(
|
child: Wrap(
|
||||||
spacing: 6,
|
spacing: 6,
|
||||||
children: [
|
children: [
|
||||||
|
if (widget.item.metadata?['exif'] == null)
|
||||||
Text(
|
Text(
|
||||||
'#${widget.item.rid}',
|
'#${widget.item.rid}',
|
||||||
style: metaTextStyle,
|
style: metaTextStyle,
|
||||||
),
|
),
|
||||||
if (widget.item.metadata?['width'] != null &&
|
if (widget.item.metadata?['exif']?['Model'] != null)
|
||||||
widget.item.metadata?['height'] != null)
|
|
||||||
Text(
|
Text(
|
||||||
'${widget.item.metadata?['width']}x${widget.item.metadata?['height']}',
|
'shotOn'.trParams({
|
||||||
|
'device': widget.item.metadata?['exif']
|
||||||
|
?['Model']
|
||||||
|
}),
|
||||||
style: metaTextStyle,
|
style: metaTextStyle,
|
||||||
),
|
).paddingOnly(right: 2),
|
||||||
if (widget.item.metadata?['ratio'] != null)
|
if (widget.item.metadata?['exif']?['ShutterSpeed'] !=
|
||||||
|
null)
|
||||||
Text(
|
Text(
|
||||||
'${_getRatio().toPrecision(2)}',
|
widget.item.metadata?['exif']?['ShutterSpeed'],
|
||||||
style: metaTextStyle,
|
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(
|
Text(
|
||||||
widget.item.size.formatBytes(),
|
widget.item.size.formatBytes(),
|
||||||
style: metaTextStyle,
|
style: metaTextStyle,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
widget.item.mimetype,
|
'${widget.item.metadata?['width']}x${widget.item.metadata?['height']}',
|
||||||
style: metaTextStyle,
|
style: metaTextStyle,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
Loading…
Reference in New Issue
Block a user