✨ Allow profile picture (avatar & banner) upload gif
This commit is contained in:
parent
73777fe74e
commit
2b61c372f5
@ -68,38 +68,35 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
_banner = prof.banner;
|
_banner = prof.banner;
|
||||||
_links = prof.profile!.links.entries.map((ele) => (ele.key, ele.value)).toList();
|
_links = prof.profile!.links.entries.map((ele) => (ele.key, ele.value)).toList();
|
||||||
_birthday = prof.profile!.birthday?.toLocal();
|
_birthday = prof.profile!.birthday?.toLocal();
|
||||||
if(_birthday != null) {
|
if (_birthday != null) {
|
||||||
_birthdayController.text = DateFormat(_kDateFormat).format(
|
_birthdayController.text = DateFormat(_kDateFormat).format(prof.profile!.birthday!.toLocal());
|
||||||
prof.profile!.birthday!.toLocal(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _selectBirthday() async {
|
void _selectBirthday() async {
|
||||||
await showCupertinoModalPopup<DateTime?>(
|
await showCupertinoModalPopup<DateTime?>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => Container(
|
builder:
|
||||||
height: 216,
|
(BuildContext context) => Container(
|
||||||
padding: const EdgeInsets.only(top: 6.0),
|
height: 216,
|
||||||
margin: EdgeInsets.only(
|
padding: const EdgeInsets.only(top: 6.0),
|
||||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
margin: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||||
),
|
color: Theme.of(context).colorScheme.surface,
|
||||||
color: Theme.of(context).colorScheme.surface,
|
child: SafeArea(
|
||||||
child: SafeArea(
|
top: false,
|
||||||
top: false,
|
child: CupertinoDatePicker(
|
||||||
child: CupertinoDatePicker(
|
initialDateTime: _birthday?.toLocal(),
|
||||||
initialDateTime: _birthday?.toLocal(),
|
mode: CupertinoDatePickerMode.date,
|
||||||
mode: CupertinoDatePickerMode.date,
|
use24hFormat: true,
|
||||||
use24hFormat: true,
|
onDateTimeChanged: (DateTime newDate) {
|
||||||
onDateTimeChanged: (DateTime newDate) {
|
setState(() {
|
||||||
setState(() {
|
_birthday = newDate;
|
||||||
_birthday = newDate;
|
_birthdayController.text = DateFormat(_kDateFormat).format(_birthday!);
|
||||||
_birthdayController.text = DateFormat(_kDateFormat).format(_birthday!);
|
});
|
||||||
});
|
},
|
||||||
},
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,32 +105,41 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
if (image == null) return;
|
if (image == null) return;
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
final ImageProvider imageProvider = kIsWeb ? NetworkImage(image.path) : FileImage(File(image.path));
|
final skipCrop = image.path.endsWith('.gif');
|
||||||
final aspectRatios =
|
|
||||||
place == 'banner' ? [CropAspectRatio(width: 16, height: 7)] : [CropAspectRatio(width: 1, height: 1)];
|
|
||||||
final result = (!kIsWeb && (Platform.isIOS || Platform.isMacOS))
|
|
||||||
? await showCupertinoImageCropper(
|
|
||||||
// ignore: use_build_context_synchronously
|
|
||||||
context,
|
|
||||||
allowedAspectRatios: aspectRatios,
|
|
||||||
imageProvider: imageProvider,
|
|
||||||
)
|
|
||||||
: await showMaterialImageCropper(
|
|
||||||
// ignore: use_build_context_synchronously
|
|
||||||
context,
|
|
||||||
allowedAspectRatios: aspectRatios,
|
|
||||||
imageProvider: imageProvider,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result == null) return;
|
Uint8List? rawBytes;
|
||||||
|
if (!skipCrop) {
|
||||||
|
final ImageProvider imageProvider = kIsWeb ? NetworkImage(image.path) : FileImage(File(image.path));
|
||||||
|
final aspectRatios =
|
||||||
|
place == 'banner' ? [CropAspectRatio(width: 16, height: 7)] : [CropAspectRatio(width: 1, height: 1)];
|
||||||
|
final result =
|
||||||
|
(!kIsWeb && (Platform.isIOS || Platform.isMacOS))
|
||||||
|
? await showCupertinoImageCropper(
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
context,
|
||||||
|
allowedAspectRatios: aspectRatios,
|
||||||
|
imageProvider: imageProvider,
|
||||||
|
)
|
||||||
|
: await showMaterialImageCropper(
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
context,
|
||||||
|
allowedAspectRatios: aspectRatios,
|
||||||
|
imageProvider: imageProvider,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == null) return;
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
rawBytes = (await result.uiImage.toByteData(format: ImageByteFormat.png))!.buffer.asUint8List();
|
||||||
|
} else {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
rawBytes = await image.readAsBytes();
|
||||||
|
}
|
||||||
|
|
||||||
if (!mounted) return;
|
|
||||||
final attach = context.read<SnAttachmentProvider>();
|
final attach = context.read<SnAttachmentProvider>();
|
||||||
|
|
||||||
setState(() => _isBusy = true);
|
|
||||||
|
|
||||||
final rawBytes = (await result.uiImage.toByteData(format: ImageByteFormat.png))!.buffer.asUint8List();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final attachment = await attach.directUploadOne(
|
final attachment = await attach.directUploadOne(
|
||||||
rawBytes,
|
rawBytes,
|
||||||
@ -145,10 +151,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
await sn.client.put(
|
await sn.client.put('/cgi/id/users/me/$place', data: {'attachment': attachment.rid});
|
||||||
'/cgi/id/users/me/$place',
|
|
||||||
data: {'attachment': attachment.rid},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final ua = context.read<UserProvider>();
|
final ua = context.read<UserProvider>();
|
||||||
@ -184,7 +187,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
'location': _locationController.value.text,
|
'location': _locationController.value.text,
|
||||||
'birthday': _birthday?.toUtc().toIso8601String(),
|
'birthday': _birthday?.toUtc().toIso8601String(),
|
||||||
'links': {
|
'links': {
|
||||||
for (final link in _links!.where((ele) => ele.$1.isNotEmpty && ele.$2.isNotEmpty)) link.$1: link.$2
|
for (final link in _links!.where((ele) => ele.$1.isNotEmpty && ele.$2.isNotEmpty)) link.$1: link.$2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -231,10 +234,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(leading: const PageBackButton(), title: Text('screenAccountProfileEdit').tr()),
|
||||||
leading: const PageBackButton(),
|
|
||||||
title: Text('screenAccountProfileEdit').tr(),
|
|
||||||
),
|
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -253,12 +253,10 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
aspectRatio: 16 / 9,
|
aspectRatio: 16 / 9,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||||
child: _banner != null
|
child:
|
||||||
? AutoResizeUniversalImage(
|
_banner != null
|
||||||
sn.getAttachmentUrl(_banner!),
|
? AutoResizeUniversalImage(sn.getAttachmentUrl(_banner!), fit: BoxFit.cover)
|
||||||
fit: BoxFit.cover,
|
: const SizedBox.shrink(),
|
||||||
)
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -299,10 +297,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
),
|
),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _nicknameController,
|
controller: _nicknameController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(border: const UnderlineInputBorder(), labelText: 'fieldNickname'.tr()),
|
||||||
border: const UnderlineInputBorder(),
|
|
||||||
labelText: 'fieldNickname'.tr(),
|
|
||||||
),
|
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
@ -364,10 +359,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
minLines: 3,
|
minLines: 3,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(border: const UnderlineInputBorder(), labelText: 'fieldDescription'.tr()),
|
||||||
border: const UnderlineInputBorder(),
|
|
||||||
labelText: 'fieldDescription'.tr(),
|
|
||||||
),
|
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
@ -384,42 +376,40 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
StyledWidget(IconButton(
|
StyledWidget(
|
||||||
icon: const Icon(Symbols.calendar_month),
|
IconButton(
|
||||||
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
|
icon: const Icon(Symbols.calendar_month),
|
||||||
padding: EdgeInsets.zero,
|
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
|
||||||
constraints: const BoxConstraints(),
|
padding: EdgeInsets.zero,
|
||||||
onPressed: () async {
|
constraints: const BoxConstraints(),
|
||||||
_timezoneController.text = await FlutterTimezone.getLocalTimezone();
|
onPressed: () async {
|
||||||
},
|
_timezoneController.text = await FlutterTimezone.getLocalTimezone();
|
||||||
)).padding(top: 6),
|
},
|
||||||
|
),
|
||||||
|
).padding(top: 6),
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
StyledWidget(IconButton(
|
StyledWidget(
|
||||||
icon: const Icon(Symbols.clear),
|
IconButton(
|
||||||
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
|
icon: const Icon(Symbols.clear),
|
||||||
padding: EdgeInsets.zero,
|
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
|
||||||
constraints: const BoxConstraints(),
|
padding: EdgeInsets.zero,
|
||||||
onPressed: () {
|
constraints: const BoxConstraints(),
|
||||||
_timezoneController.clear();
|
onPressed: () {
|
||||||
},
|
_timezoneController.clear();
|
||||||
)).padding(top: 6),
|
},
|
||||||
|
),
|
||||||
|
).padding(top: 6),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _locationController,
|
controller: _locationController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(border: const UnderlineInputBorder(), labelText: 'fieldLocation'.tr()),
|
||||||
border: const UnderlineInputBorder(),
|
|
||||||
labelText: 'fieldLocation'.tr(),
|
|
||||||
),
|
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _birthdayController,
|
controller: _birthdayController,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(border: const UnderlineInputBorder(), labelText: 'fieldBirthday'.tr()),
|
||||||
border: const UnderlineInputBorder(),
|
|
||||||
labelText: 'fieldBirthday'.tr(),
|
|
||||||
),
|
|
||||||
onTap: () => _selectBirthday(),
|
onTap: () => _selectBirthday(),
|
||||||
),
|
),
|
||||||
if (_links != null)
|
if (_links != null)
|
||||||
|
@ -108,32 +108,41 @@ class _AccountPublisherEditScreenState extends State<AccountPublisherEditScreen>
|
|||||||
if (image == null) return;
|
if (image == null) return;
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
final ImageProvider imageProvider = kIsWeb ? NetworkImage(image.path) : FileImage(File(image.path));
|
final skipCrop = image.path.endsWith('.gif');
|
||||||
final aspectRatios =
|
|
||||||
place == 'banner' ? [CropAspectRatio(width: 16, height: 7)] : [CropAspectRatio(width: 1, height: 1)];
|
|
||||||
final result = (!kIsWeb && (Platform.isIOS || Platform.isMacOS))
|
|
||||||
? await showCupertinoImageCropper(
|
|
||||||
// ignore: use_build_context_synchronously
|
|
||||||
context,
|
|
||||||
allowedAspectRatios: aspectRatios,
|
|
||||||
imageProvider: imageProvider,
|
|
||||||
)
|
|
||||||
: await showMaterialImageCropper(
|
|
||||||
// ignore: use_build_context_synchronously
|
|
||||||
context,
|
|
||||||
allowedAspectRatios: aspectRatios,
|
|
||||||
imageProvider: imageProvider,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result == null) return;
|
Uint8List? rawBytes;
|
||||||
|
if (!skipCrop) {
|
||||||
|
final ImageProvider imageProvider = kIsWeb ? NetworkImage(image.path) : FileImage(File(image.path));
|
||||||
|
final aspectRatios =
|
||||||
|
place == 'banner' ? [CropAspectRatio(width: 16, height: 7)] : [CropAspectRatio(width: 1, height: 1)];
|
||||||
|
final result =
|
||||||
|
(!kIsWeb && (Platform.isIOS || Platform.isMacOS))
|
||||||
|
? await showCupertinoImageCropper(
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
context,
|
||||||
|
allowedAspectRatios: aspectRatios,
|
||||||
|
imageProvider: imageProvider,
|
||||||
|
)
|
||||||
|
: await showMaterialImageCropper(
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
context,
|
||||||
|
allowedAspectRatios: aspectRatios,
|
||||||
|
imageProvider: imageProvider,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == null) return;
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
rawBytes = (await result.uiImage.toByteData(format: ImageByteFormat.png))!.buffer.asUint8List();
|
||||||
|
} else {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
rawBytes = await image.readAsBytes();
|
||||||
|
}
|
||||||
|
|
||||||
if (!mounted) return;
|
|
||||||
final attach = context.read<SnAttachmentProvider>();
|
final attach = context.read<SnAttachmentProvider>();
|
||||||
|
|
||||||
setState(() => _isBusy = true);
|
|
||||||
|
|
||||||
final rawBytes = (await result.uiImage.toByteData(format: ImageByteFormat.png))!.buffer.asUint8List();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final attachment = await attach.directUploadOne(
|
final attachment = await attach.directUploadOne(
|
||||||
rawBytes,
|
rawBytes,
|
||||||
|
@ -58,18 +58,12 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final nty = context.read<NotificationProvider>();
|
final nty = context.read<NotificationProvider>();
|
||||||
final resp =
|
final resp = await sn.client.get(
|
||||||
await sn.client.get('/cgi/id/notifications', queryParameters: {
|
'/cgi/id/notifications',
|
||||||
'take': 10,
|
queryParameters: {'take': 10, 'offset': _notifications.length},
|
||||||
'offset': _notifications.length,
|
|
||||||
});
|
|
||||||
_totalCount = resp.data['count'];
|
|
||||||
_notifications.addAll(
|
|
||||||
resp.data['data']
|
|
||||||
?.map((e) => SnNotification.fromJson(e))
|
|
||||||
.cast<SnNotification>() ??
|
|
||||||
[],
|
|
||||||
);
|
);
|
||||||
|
_totalCount = resp.data['count'];
|
||||||
|
_notifications.addAll(resp.data['data']?.map((e) => SnNotification.fromJson(e)).cast<SnNotification>() ?? []);
|
||||||
nty.updateTray();
|
nty.updateTray();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@ -104,9 +98,7 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
nty.clear();
|
nty.clear();
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showSnackbar(
|
context.showSnackbar('notificationMarkAllReadPrompt'.plural(resp.data['count']));
|
||||||
'notificationMarkAllReadPrompt'.plural(resp.data['count']),
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@ -130,9 +122,7 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
_fetchNotifications();
|
_fetchNotifications();
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showSnackbar(
|
context.showSnackbar('notificationMarkOneReadPrompt'.tr(args: ['#${notification.id}']));
|
||||||
'notificationMarkOneReadPrompt'.tr(args: ['#${notification.id}']),
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@ -153,13 +143,8 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
|
|
||||||
if (!ua.isAuthorized) {
|
if (!ua.isAuthorized) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(leading: AutoAppBarLeading(), title: Text('screenNotification').tr()),
|
||||||
leading: AutoAppBarLeading(),
|
body: Center(child: UnauthorizedHint()),
|
||||||
title: Text('screenNotification').tr(),
|
|
||||||
),
|
|
||||||
body: Center(
|
|
||||||
child: UnauthorizedHint(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,10 +153,7 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
leading: AutoAppBarLeading(),
|
leading: AutoAppBarLeading(),
|
||||||
title: Text('screenNotification').tr(),
|
title: Text('screenNotification').tr(),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(icon: const Icon(Symbols.checklist), onPressed: _isSubmitting ? null : _markAllAsRead),
|
||||||
icon: const Icon(Symbols.checklist),
|
|
||||||
onPressed: _isSubmitting ? null : _markAllAsRead,
|
|
||||||
),
|
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -185,17 +167,13 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
return _fetchNotifications();
|
return _fetchNotifications();
|
||||||
},
|
},
|
||||||
child: InfiniteList(
|
child: InfiniteList(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(top: 16, bottom: math.max(MediaQuery.of(context).padding.bottom, 16)),
|
||||||
top: 16,
|
|
||||||
bottom: math.max(MediaQuery.of(context).padding.bottom, 16),
|
|
||||||
),
|
|
||||||
itemCount: _notifications.length,
|
itemCount: _notifications.length,
|
||||||
onFetchData: () {
|
onFetchData: () {
|
||||||
_fetchNotifications();
|
_fetchNotifications();
|
||||||
},
|
},
|
||||||
isLoading: _isBusy,
|
isLoading: _isBusy,
|
||||||
hasReachedMax: _totalCount != null &&
|
hasReachedMax: _totalCount != null && _notifications.length >= _totalCount!,
|
||||||
_notifications.length >= _totalCount!,
|
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
final nty = _notifications[idx];
|
final nty = _notifications[idx];
|
||||||
return Row(
|
return Row(
|
||||||
@ -208,45 +186,26 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (nty.readAt == null)
|
if (nty.readAt == null)
|
||||||
StyledWidget(Badge(
|
StyledWidget(Badge(label: Text('notificationUnread').tr())).padding(bottom: 4),
|
||||||
label: Text('notificationUnread').tr(),
|
Text(nty.title, style: Theme.of(context).textTheme.titleMedium),
|
||||||
)).padding(bottom: 4),
|
|
||||||
Text(
|
|
||||||
nty.title,
|
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
|
||||||
),
|
|
||||||
if (nty.subtitle != null)
|
if (nty.subtitle != null)
|
||||||
Text(
|
Text(nty.subtitle!, style: Theme.of(context).textTheme.titleSmall),
|
||||||
nty.subtitle!,
|
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
|
||||||
),
|
|
||||||
if (nty.subtitle != null) const Gap(4),
|
if (nty.subtitle != null) const Gap(4),
|
||||||
SelectionArea(
|
SelectionArea(child: MarkdownTextContent(content: nty.body, isAutoWarp: true)),
|
||||||
child: MarkdownTextContent(
|
|
||||||
content: nty.body,
|
|
||||||
isAutoWarp: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if ([
|
if ([
|
||||||
'interactive.reply',
|
'interactive.reply',
|
||||||
'interactive.feedback',
|
'interactive.feedback',
|
||||||
'interactive.subscription'
|
'interactive.subscription',
|
||||||
].contains(nty.topic) &&
|
].contains(nty.topic) &&
|
||||||
nty.metadata['related_post'] != null)
|
nty.metadata['related_post'] != null)
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: const BorderRadius.all(
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
Radius.circular(8)),
|
border: Border.all(color: Theme.of(context).dividerColor, width: 1),
|
||||||
border: Border.all(
|
|
||||||
color: Theme.of(context).dividerColor,
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: PostItem(
|
child: PostItem(
|
||||||
data: SnPost.fromJson(
|
data: SnPost.fromJson(nty.metadata['related_post']!),
|
||||||
nty.metadata['related_post']!,
|
|
||||||
),
|
|
||||||
showComments: false,
|
showComments: false,
|
||||||
showReactions: false,
|
showReactions: false,
|
||||||
showMenu: false,
|
showMenu: false,
|
||||||
@ -255,29 +214,18 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
'postDetail',
|
'postDetail',
|
||||||
pathParameters: {
|
pathParameters: {'slug': nty.metadata['related_post']!['id'].toString()},
|
||||||
'slug': nty
|
|
||||||
.metadata['related_post']!['id']
|
|
||||||
.toString(),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).padding(top: 8),
|
).padding(top: 8),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(DateFormat('yy/MM/dd').format(nty.createdAt)).fontSize(12),
|
||||||
DateFormat('yy/MM/dd').format(nty.createdAt),
|
|
||||||
).fontSize(12),
|
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
Text(
|
Text('·', style: TextStyle(fontSize: 12)),
|
||||||
'·',
|
|
||||||
style: TextStyle(fontSize: 12),
|
|
||||||
),
|
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
Text(
|
Text(RelativeTime(context).format(nty.createdAt)).fontSize(12),
|
||||||
RelativeTime(context).format(nty.createdAt),
|
|
||||||
).fontSize(12),
|
|
||||||
],
|
],
|
||||||
).opacity(0.75),
|
).opacity(0.75),
|
||||||
],
|
],
|
||||||
@ -287,10 +235,8 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Symbols.check),
|
icon: const Icon(Symbols.check),
|
||||||
padding: EdgeInsets.all(0),
|
padding: EdgeInsets.all(0),
|
||||||
visualDensity:
|
visualDensity: const VisualDensity(horizontal: -4, vertical: -4),
|
||||||
const VisualDensity(horizontal: -4, vertical: -4),
|
onPressed: _isSubmitting ? null : () => _markOneAsRead(nty),
|
||||||
onPressed:
|
|
||||||
_isSubmitting ? null : () => _markOneAsRead(nty),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16);
|
).padding(horizontal: 16);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user