Compare commits

..

No commits in common. "289aa17a7ac4ce23375793d1213dbe4fe3ed59a7" and "1153fbdeeedcbc4833b2b2493e51d32253bc9387" have entirely different histories.

5 changed files with 250 additions and 405 deletions

View File

@ -44,9 +44,7 @@ class SnNetworkProvider {
settings: const TalkerDioLoggerSettings( settings: const TalkerDioLoggerSettings(
printRequestHeaders: false, printRequestHeaders: false,
printResponseHeaders: false, printResponseHeaders: false,
printResponseMessage: false, printResponseMessage: true,
printResponseData: false,
printRequestData: false,
), ),
), ),
); );

View File

@ -95,9 +95,8 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [], resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [],
); );
final beforeId = config.prefs.getInt('int_last_publisher_id'); final beforeId = config.prefs.getInt('int_last_publisher_id');
_writeController.setPublisher( _writeController
_publishers?.where((ele) => ele.id == beforeId).firstOrNull ?? .setPublisher(_publishers?.where((ele) => ele.id == beforeId).firstOrNull ?? _publishers?.firstOrNull);
_publishers?.firstOrNull);
} catch (err) { } catch (err) {
if (!mounted) return; if (!mounted) return;
context.showErrorDialog(err); context.showErrorDialog(err);
@ -126,11 +125,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
final HotKey _pasteHotKey = HotKey( final HotKey _pasteHotKey = HotKey(
key: PhysicalKeyboardKey.keyV, key: PhysicalKeyboardKey.keyV,
modifiers: [ modifiers: [(!kIsWeb && Platform.isMacOS) ? HotKeyModifier.meta : HotKeyModifier.control],
(!kIsWeb && Platform.isMacOS)
? HotKeyModifier.meta
: HotKeyModifier.control
],
scope: HotKeyScope.inapp, scope: HotKeyScope.inapp,
); );
@ -237,8 +232,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
if (widget.extraProps != null) { if (widget.extraProps != null) {
_writeController.contentController.text = widget.extraProps!.text ?? ''; _writeController.contentController.text = widget.extraProps!.text ?? '';
_writeController.titleController.text = widget.extraProps!.title ?? ''; _writeController.titleController.text = widget.extraProps!.title ?? '';
_writeController.descriptionController.text = _writeController.descriptionController.text = widget.extraProps!.description ?? '';
widget.extraProps!.description ?? '';
_writeController.addAttachments(widget.extraProps!.attachments ?? []); _writeController.addAttachments(widget.extraProps!.attachments ?? []);
} }
} }
@ -259,9 +253,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
textAlign: TextAlign.center, textAlign: TextAlign.center,
text: TextSpan(children: [ text: TextSpan(children: [
TextSpan( TextSpan(
text: _writeController.title.isNotEmpty text: _writeController.title.isNotEmpty ? _writeController.title : 'untitled'.tr(),
? _writeController.title
: 'untitled'.tr(),
style: Theme.of(context).textTheme.titleLarge!.copyWith( style: Theme.of(context).textTheme.titleLarge!.copyWith(
color: Theme.of(context).appBarTheme.foregroundColor!, color: Theme.of(context).appBarTheme.foregroundColor!,
), ),
@ -288,8 +280,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
children: [ children: [
if (_writeController.editingPost != null) if (_writeController.editingPost != null)
Container( Container(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(top: 4, bottom: 4, left: 20, right: 20),
top: 4, bottom: 4, left: 20, right: 20),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border( border: Border(
bottom: BorderSide( bottom: BorderSide(
@ -303,16 +294,13 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
children: [ children: [
const Icon(Icons.edit, size: 16), const Icon(Icons.edit, size: 16),
const Gap(10), const Gap(10),
Text('postEditingNotice').tr(args: [ Text('postEditingNotice').tr(args: ['@${_writeController.editingPost!.publisher.name}']),
'@${_writeController.editingPost!.publisher.name}'
]),
], ],
), ),
), ),
if (_writeController.replyingPost != null) if (_writeController.replyingPost != null)
Container( Container(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(top: 4, bottom: 4, left: 20, right: 20),
top: 4, bottom: 4, left: 20, right: 20),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border( border: Border(
bottom: BorderSide( bottom: BorderSide(
@ -326,8 +314,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
children: [ children: [
const Icon(Symbols.reply, size: 16), const Icon(Symbols.reply, size: 16),
const Gap(10), const Gap(10),
Text('@${_writeController.replyingPost!.publisher.name}') Text('@${_writeController.replyingPost!.publisher.name}').bold(),
.bold(),
const Gap(4), const Gap(4),
Expanded( Expanded(
child: Text( child: Text(
@ -341,8 +328,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
), ),
if (_writeController.repostingPost != null) if (_writeController.repostingPost != null)
Container( Container(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(top: 4, bottom: 4, left: 20, right: 20),
top: 4, bottom: 4, left: 20, right: 20),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border( border: Border(
bottom: BorderSide( bottom: BorderSide(
@ -356,8 +342,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
children: [ children: [
const Icon(Symbols.forward, size: 16), const Icon(Symbols.forward, size: 16),
const Gap(10), const Gap(10),
Text('@${_writeController.repostingPost!.publisher.name}') Text('@${_writeController.repostingPost!.publisher.name}').bold(),
.bold(),
const Gap(4), const Gap(4),
Expanded( Expanded(
child: Text( child: Text(
@ -399,8 +384,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
}) })
.padding(top: 8), .padding(top: 8),
), ),
if (_writeController.attachments.isNotEmpty || if (_writeController.attachments.isNotEmpty || _writeController.thumbnail != null)
_writeController.thumbnail != null)
Positioned( Positioned(
bottom: 0, bottom: 0,
left: 0, left: 0,
@ -409,19 +393,16 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
attachments: _writeController.attachments, attachments: _writeController.attachments,
isBusy: _writeController.isBusy, isBusy: _writeController.isBusy,
onUpload: (int idx) async { onUpload: (int idx) async {
await _writeController.uploadSingleAttachment( await _writeController.uploadSingleAttachment(context, idx);
context, idx);
}, },
onInsertLink: (int idx) async { onInsertLink: (int idx) async {
_writeController.contentController.text += _writeController.contentController.text +=
'\n![](solink://attachments/${_writeController.attachments[idx].attachment!.rid})'; '\n![](solink://attachments/${_writeController.attachments[idx].attachment!.rid})';
}, },
onUpdate: onUpdate: (int idx, PostWriteMedia updatedMedia) async {
(int idx, PostWriteMedia updatedMedia) async {
_writeController.setIsBusy(true); _writeController.setIsBusy(true);
try { try {
_writeController.setAttachmentAt( _writeController.setAttachmentAt(idx, updatedMedia);
idx, updatedMedia);
} finally { } finally {
_writeController.setIsBusy(false); _writeController.setIsBusy(false);
} }
@ -434,8 +415,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
_writeController.setIsBusy(false); _writeController.setIsBusy(false);
} }
}, },
onUpdateBusy: (state) => onUpdateBusy: (state) => _writeController.setIsBusy(state),
_writeController.setIsBusy(state),
).padding(bottom: 8), ).padding(bottom: 8),
), ),
], ],
@ -446,13 +426,11 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (_writeController.isBusy && if (_writeController.isBusy && _writeController.progress != null)
_writeController.progress != null)
TweenAnimationBuilder<double>( TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: _writeController.progress), tween: Tween(begin: 0, end: _writeController.progress),
duration: Duration(milliseconds: 300), duration: Duration(milliseconds: 300),
builder: (context, value, _) => builder: (context, value, _) => LinearProgressIndicator(value: value, minHeight: 2),
LinearProgressIndicator(value: value, minHeight: 2),
) )
else if (_writeController.isBusy) else if (_writeController.isBusy)
const LinearProgressIndicator(value: null, minHeight: 2), const LinearProgressIndicator(value: null, minHeight: 2),
@ -461,14 +439,12 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
Container( Container(
child: _writeController.temporaryRestored child: _writeController.temporaryRestored
? Container( ? Container(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(top: 4, bottom: 4, left: 28, right: 22),
top: 4, bottom: 4, left: 28, right: 22),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border( border: Border(
bottom: BorderSide( bottom: BorderSide(
color: Theme.of(context).dividerColor, color: Theme.of(context).dividerColor,
width: 1 / width: 1 / MediaQuery.of(context).devicePixelRatio,
MediaQuery.of(context).devicePixelRatio,
), ),
), ),
), ),
@ -477,9 +453,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
children: [ children: [
const Icon(Icons.restore, size: 20), const Icon(Icons.restore, size: 20),
const Gap(8), const Gap(8),
Expanded( Expanded(child: Text('postLocalDraftRestored').tr()),
child:
Text('postLocalDraftRestored').tr()),
InkWell( InkWell(
child: Text('dialogDismiss').tr(), child: Text('dialogDismiss').tr(),
onTap: () { onTap: () {
@ -490,10 +464,8 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
)) ))
: const SizedBox.shrink(), : const SizedBox.shrink(),
) )
.height(_writeController.temporaryRestored ? 32 : 0, .height(_writeController.temporaryRestored ? 32 : 0, animate: true)
animate: true) .animate(const Duration(milliseconds: 300), Curves.fastLinearToSlowEaseIn),
.animate(const Duration(milliseconds: 300),
Curves.fastLinearToSlowEaseIn),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -513,18 +485,11 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
), ),
if (_writeController.mode == 'stories') if (_writeController.mode == 'stories')
IconButton( IconButton(
icon: Icon(Symbols.poll, icon: Icon(Symbols.poll, color: Theme.of(context).colorScheme.primary),
color: Theme.of(context)
.colorScheme
.primary),
style: ButtonStyle( style: ButtonStyle(
backgroundColor: backgroundColor: _writeController.poll == null
_writeController.poll == null ? null
? null : WidgetStatePropertyAll(Theme.of(context).colorScheme.surfaceContainer),
: WidgetStatePropertyAll(
Theme.of(context)
.colorScheme
.surfaceContainer),
), ),
onPressed: () { onPressed: () {
_showPollEditorDialog(); _showPollEditorDialog();
@ -532,22 +497,14 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
), ),
if (_writeController.mode == 'articles') if (_writeController.mode == 'articles')
IconButton( IconButton(
icon: Icon(Symbols.full_coverage, icon: Icon(Symbols.full_coverage, color: Theme.of(context).colorScheme.primary),
color: Theme.of(context)
.colorScheme
.primary),
style: ButtonStyle( style: ButtonStyle(
backgroundColor: backgroundColor: _writeController.thumbnail == null
_writeController.thumbnail == null ? null
? null : WidgetStatePropertyAll(Theme.of(context).colorScheme.surfaceContainer),
: WidgetStatePropertyAll(
Theme.of(context)
.colorScheme
.surfaceContainer),
), ),
onPressed: () { onPressed: () {
if (_writeController.thumbnail != if (_writeController.thumbnail != null) {
null) {
_writeController.setThumbnail(null); _writeController.setThumbnail(null);
return; return;
} }
@ -560,8 +517,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
), ),
), ),
TextButton.icon( TextButton.icon(
onPressed: (_writeController.isBusy || onPressed: (_writeController.isBusy || _writeController.publisher == null)
_writeController.publisher == null)
? null ? null
: () { : () {
_writeController.sendPost(context).then((_) { _writeController.sendPost(context).then((_) {
@ -600,8 +556,7 @@ class _PostPublisherPopup extends StatelessWidget {
final List<SnPublisher>? publishers; final List<SnPublisher>? publishers;
final Function onUpdate; final Function onUpdate;
const _PostPublisherPopup( const _PostPublisherPopup({required this.controller, this.publishers, required this.onUpdate});
{required this.controller, this.publishers, required this.onUpdate});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -613,9 +568,7 @@ class _PostPublisherPopup extends StatelessWidget {
children: [ children: [
const Icon(Symbols.face, size: 24), const Icon(Symbols.face, size: 24),
const Gap(16), const Gap(16),
Text('accountPublishers', Text('accountPublishers', style: Theme.of(context).textTheme.titleLarge).tr(),
style: Theme.of(context).textTheme.titleLarge)
.tr(),
], ],
).padding(horizontal: 20, top: 16, bottom: 12), ).padding(horizontal: 20, top: 16, bottom: 12),
ListTile( ListTile(
@ -659,8 +612,7 @@ class _PostRealmPopup extends StatelessWidget {
final List<SnRealm>? realms; final List<SnRealm>? realms;
final Function onUpdate; final Function onUpdate;
const _PostRealmPopup( const _PostRealmPopup({required this.controller, this.realms, required this.onUpdate});
{required this.controller, this.realms, required this.onUpdate});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -672,8 +624,7 @@ class _PostRealmPopup extends StatelessWidget {
children: [ children: [
const Icon(Symbols.face, size: 24), const Icon(Symbols.face, size: 24),
const Gap(16), const Gap(16),
Text('accountRealms', style: Theme.of(context).textTheme.titleLarge) Text('accountRealms', style: Theme.of(context).textTheme.titleLarge).tr(),
.tr(),
], ],
).padding(horizontal: 20, top: 16, bottom: 12), ).padding(horizontal: 20, top: 16, bottom: 12),
ListTile( ListTile(
@ -714,8 +665,7 @@ class _PostStoryEditor extends StatelessWidget {
final Function? onTapPublisher; final Function? onTapPublisher;
final Function? onTapRealm; final Function? onTapRealm;
const _PostStoryEditor( const _PostStoryEditor({required this.controller, this.onTapPublisher, this.onTapRealm});
{required this.controller, this.onTapPublisher, this.onTapRealm});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -767,8 +717,7 @@ class _PostStoryEditor extends StatelessWidget {
border: InputBorder.none, border: InputBorder.none,
), ),
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.titleLarge,
onTapOutside: (_) => onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
FocusManager.instance.primaryFocus?.unfocus(),
).padding(horizontal: 16), ).padding(horizontal: 16),
const Gap(8), const Gap(8),
TextField( TextField(
@ -783,10 +732,8 @@ class _PostStoryEditor extends StatelessWidget {
), ),
border: InputBorder.none, border: InputBorder.none,
), ),
onTapOutside: (_) => onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
FocusManager.instance.primaryFocus?.unfocus(), contentInsertionConfiguration: controller.contentInsertionConfiguration,
contentInsertionConfiguration:
controller.contentInsertionConfiguration,
), ),
], ],
), ),
@ -802,8 +749,7 @@ class _PostArticleEditor extends StatelessWidget {
final Function? onTapPublisher; final Function? onTapPublisher;
final Function? onTapRealm; final Function? onTapRealm;
const _PostArticleEditor( const _PostArticleEditor({required this.controller, this.onTapPublisher, this.onTapRealm});
{required this.controller, this.onTapPublisher, this.onTapRealm});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -911,10 +857,8 @@ class _PostArticleEditor extends StatelessWidget {
), ),
border: InputBorder.none, border: InputBorder.none,
), ),
onTapOutside: (_) => onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
FocusManager.instance.primaryFocus?.unfocus(), contentInsertionConfiguration: controller.contentInsertionConfiguration,
contentInsertionConfiguration:
controller.contentInsertionConfiguration,
), ),
), ),
const Gap(8), const Gap(8),
@ -949,8 +893,7 @@ class _PostArticleEditor extends StatelessWidget {
border: InputBorder.none, border: InputBorder.none,
), ),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
contentInsertionConfiguration: contentInsertionConfiguration: controller.contentInsertionConfiguration,
controller.contentInsertionConfiguration,
), ),
), ),
], ],
@ -963,8 +906,7 @@ class _PostQuestionEditor extends StatelessWidget {
final Function? onTapPublisher; final Function? onTapPublisher;
final Function? onTapRealm; final Function? onTapRealm;
const _PostQuestionEditor( const _PostQuestionEditor({required this.controller, this.onTapPublisher, this.onTapRealm});
{required this.controller, this.onTapPublisher, this.onTapRealm});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -1016,8 +958,7 @@ class _PostQuestionEditor extends StatelessWidget {
border: InputBorder.none, border: InputBorder.none,
), ),
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.titleLarge,
onTapOutside: (_) => onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
FocusManager.instance.primaryFocus?.unfocus(),
).padding(horizontal: 16), ).padding(horizontal: 16),
const Gap(8), const Gap(8),
TextField( TextField(
@ -1028,8 +969,7 @@ class _PostQuestionEditor extends StatelessWidget {
border: InputBorder.none, border: InputBorder.none,
isCollapsed: true, isCollapsed: true,
), ),
onTapOutside: (_) => onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
FocusManager.instance.primaryFocus?.unfocus(),
).padding(horizontal: 16), ).padding(horizontal: 16),
const Gap(8), const Gap(8),
TextField( TextField(
@ -1044,10 +984,8 @@ class _PostQuestionEditor extends StatelessWidget {
), ),
border: InputBorder.none, border: InputBorder.none,
), ),
onTapOutside: (_) => onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
FocusManager.instance.primaryFocus?.unfocus(), contentInsertionConfiguration: controller.contentInsertionConfiguration,
contentInsertionConfiguration:
controller.contentInsertionConfiguration,
), ),
], ],
), ),
@ -1063,8 +1001,7 @@ class _PostVideoEditor extends StatelessWidget {
final Function? onTapPublisher; final Function? onTapPublisher;
final Function? onTapRealm; final Function? onTapRealm;
const _PostVideoEditor( const _PostVideoEditor({required this.controller, this.onTapPublisher, this.onTapRealm});
{required this.controller, this.onTapPublisher, this.onTapRealm});
void _selectVideo(BuildContext context) async { void _selectVideo(BuildContext context) async {
final video = await showDialog<SnAttachment?>( final video = await showDialog<SnAttachment?>(
@ -1085,8 +1022,7 @@ class _PostVideoEditor extends StatelessWidget {
final result = await showDialog<SnAttachment?>( final result = await showDialog<SnAttachment?>(
context: context, context: context,
builder: (context) => PendingAttachmentAltDialog( builder: (context) => PendingAttachmentAltDialog(media: PostWriteMedia(controller.videoAttachment)),
media: PostWriteMedia(controller.videoAttachment)),
); );
if (result == null) return; if (result == null) return;
@ -1098,8 +1034,7 @@ class _PostVideoEditor extends StatelessWidget {
final result = await showDialog<SnAttachmentBoost?>( final result = await showDialog<SnAttachmentBoost?>(
context: context, context: context,
builder: (context) => PendingAttachmentBoostDialog( builder: (context) => PendingAttachmentBoostDialog(media: PostWriteMedia(controller.videoAttachment)),
media: PostWriteMedia(controller.videoAttachment)),
); );
if (result == null) return; if (result == null) return;
@ -1142,8 +1077,7 @@ class _PostVideoEditor extends StatelessWidget {
try { try {
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
await sn.client await sn.client.delete('/cgi/uc/attachments/${controller.videoAttachment!.id}');
.delete('/cgi/uc/attachments/${controller.videoAttachment!.id}');
controller.setVideoAttachment(null); controller.setVideoAttachment(null);
} catch (err) { } catch (err) {
if (!context.mounted) return; if (!context.mounted) return;
@ -1153,159 +1087,143 @@ class _PostVideoEditor extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Column(
padding: const EdgeInsets.symmetric(horizontal: 12), children: [
constraints: const BoxConstraints(maxWidth: 640), Column(
child: Row( children: [
crossAxisAlignment: CrossAxisAlignment.start, Material(
children: [ elevation: 2,
Column( borderRadius: const BorderRadius.all(Radius.circular(24)),
children: [ child: GestureDetector(
Material( onTap: () {
elevation: 2, onTapPublisher?.call();
borderRadius: const BorderRadius.all(Radius.circular(24)), },
child: GestureDetector( child: AccountImage(
onTap: () { content: controller.publisher?.avatar,
onTapPublisher?.call();
},
child: AccountImage(
content: controller.publisher?.avatar,
),
), ),
), ),
const Gap(11), ),
Material( const Gap(11),
elevation: 1, Material(
borderRadius: const BorderRadius.all(Radius.circular(24)), elevation: 1,
child: GestureDetector( borderRadius: const BorderRadius.all(Radius.circular(24)),
onTap: () { child: GestureDetector(
onTapRealm?.call(); onTap: () {
}, onTapRealm?.call();
child: AccountImage( },
content: controller.realm?.avatar, child: AccountImage(
fallbackWidget: const Icon(Symbols.globe, size: 20), content: controller.realm?.avatar,
radius: 14, fallbackWidget: const Icon(Symbols.globe, size: 20),
), radius: 14,
), ),
), ),
], ),
],
),
const Gap(16),
TextField(
controller: controller.titleController,
decoration: InputDecoration.collapsed(
hintText: 'fieldPostTitle'.tr(),
border: InputBorder.none,
), ),
Expanded( style: Theme.of(context).textTheme.titleLarge,
child: Column( onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
children: [ ).padding(horizontal: 16),
const Gap(6), const Gap(8),
TextField( TextField(
controller: controller.titleController, controller: controller.descriptionController,
decoration: InputDecoration.collapsed( decoration: InputDecoration.collapsed(
hintText: 'fieldPostTitle'.tr(), hintText: 'fieldPostDescription'.tr(),
border: InputBorder.none, border: InputBorder.none,
), ),
style: Theme.of(context).textTheme.titleLarge, maxLines: null,
onTapOutside: (_) => keyboardType: TextInputType.multiline,
FocusManager.instance.primaryFocus?.unfocus(), style: Theme.of(context).textTheme.bodyLarge,
).padding(horizontal: 16), onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
const Gap(8), ).padding(horizontal: 16),
TextField( const Gap(12),
controller: controller.descriptionController, Container(
decoration: InputDecoration.collapsed( margin: const EdgeInsets.only(left: 16, right: 16),
hintText: 'fieldPostDescription'.tr(), decoration: BoxDecoration(
border: InputBorder.none, borderRadius: BorderRadius.circular(16),
), border: Border.all(color: Theme.of(context).dividerColor),
maxLines: null, ),
keyboardType: TextInputType.multiline, child: ContextMenuRegion(
style: Theme.of(context).textTheme.bodyLarge, contextMenu: ContextMenu(
onTapOutside: (_) => entries: [
FocusManager.instance.primaryFocus?.unfocus(), MenuItem(
).padding(horizontal: 16), label: 'attachmentSetAlt'.tr(),
const Gap(12), icon: Symbols.description,
Container( onSelected: () {
margin: const EdgeInsets.only(left: 16, right: 16), _setAlt(context);
decoration: BoxDecoration( },
borderRadius: BorderRadius.circular(16), ),
border: Border.all(color: Theme.of(context).dividerColor), MenuItem(
), label: 'attachmentBoost'.tr(),
child: ContextMenuRegion( icon: Symbols.bolt,
contextMenu: ContextMenu( onSelected: () {
entries: [ _createBoost(context);
MenuItem( },
label: 'attachmentSetAlt'.tr(), ),
icon: Symbols.description, MenuItem(
onSelected: () { label: 'attachmentSetThumbnail'.tr(),
_setAlt(context); icon: Symbols.image,
}, onSelected: () {
), _setThumbnail(context);
MenuItem( },
label: 'attachmentBoost'.tr(), ),
icon: Symbols.bolt, MenuItem(
onSelected: () { label: 'attachmentCopyRandomId'.tr(),
_createBoost(context); icon: Symbols.content_copy,
}, onSelected: () {
), Clipboard.setData(ClipboardData(text: controller.videoAttachment!.rid));
MenuItem( },
label: 'attachmentSetThumbnail'.tr(), ),
icon: Symbols.image, MenuItem(
onSelected: () { label: 'delete'.tr(),
_setThumbnail(context); icon: Symbols.delete,
}, onSelected: () => _deleteAttachment(context),
), ),
MenuItem( MenuItem(
label: 'attachmentCopyRandomId'.tr(), label: 'unlink'.tr(),
icon: Symbols.content_copy, icon: Symbols.link_off,
onSelected: () { onSelected: () {
Clipboard.setData(ClipboardData( controller.setVideoAttachment(null);
text: controller.videoAttachment!.rid)); },
},
),
MenuItem(
label: 'delete'.tr(),
icon: Symbols.delete,
onSelected: () => _deleteAttachment(context),
),
MenuItem(
label: 'unlink'.tr(),
icon: Symbols.link_off,
onSelected: () {
controller.setVideoAttachment(null);
},
),
],
),
child: InkWell(
borderRadius: BorderRadius.circular(16),
onTap: controller.videoAttachment == null
? () => _selectVideo(context)
: null,
child: AspectRatio(
aspectRatio: 16 / 9,
child: controller.videoAttachment == null
? Center(
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.add),
const Gap(4),
Text('postVideoUpload'.tr()),
],
),
)
: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: AttachmentItem(
data: controller.videoAttachment!,
heroTag: const Uuid().v4(),
),
),
),
),
),
), ),
], ],
), ),
child: InkWell(
borderRadius: BorderRadius.circular(16),
onTap: controller.videoAttachment == null ? () => _selectVideo(context) : null,
child: AspectRatio(
aspectRatio: 16 / 9,
child: controller.videoAttachment == null
? Center(
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.add),
const Gap(4),
Text('postVideoUpload'.tr()),
],
),
)
: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: AttachmentItem(
data: controller.videoAttachment!,
heroTag: const Uuid().v4(),
),
),
),
),
), ),
], ),
), ],
); );
} }
} }

View File

@ -229,10 +229,7 @@ class ChatMessage extends StatelessWidget {
bordered: true, bordered: true,
maxHeight: 360, maxHeight: 360,
maxWidth: 480 - 48 - padding.left, maxWidth: 480 - 48 - padding.left,
padding: padding.copyWith( padding: padding.copyWith(top: 8, left: 48 + padding.left),
top: 8,
left: isCompact ? padding.left : 48 + padding.left,
),
), ),
if (!hasMerged && !isCompact) if (!hasMerged && !isCompact)
const Gap(12) const Gap(12)

View File

@ -28,8 +28,7 @@ class ChatMessageInput extends StatefulWidget {
final ChatMessageController controller; final ChatMessageController controller;
final SnChannelMember? otherMember; final SnChannelMember? otherMember;
const ChatMessageInput( const ChatMessageInput({super.key, required this.controller, this.otherMember});
{super.key, required this.controller, this.otherMember});
@override @override
State<ChatMessageInput> createState() => ChatMessageInputState(); State<ChatMessageInput> createState() => ChatMessageInputState();
@ -46,20 +45,12 @@ class ChatMessageInputState extends State<ChatMessageInput> {
final HotKey _pasteHotKey = HotKey( final HotKey _pasteHotKey = HotKey(
key: PhysicalKeyboardKey.keyV, key: PhysicalKeyboardKey.keyV,
modifiers: [ modifiers: [(!kIsWeb && Platform.isMacOS) ? HotKeyModifier.meta : HotKeyModifier.control],
(!kIsWeb && Platform.isMacOS)
? HotKeyModifier.meta
: HotKeyModifier.control
],
scope: HotKeyScope.inapp, scope: HotKeyScope.inapp,
); );
final HotKey _newLineHotKey = HotKey( final HotKey _newLineHotKey = HotKey(
key: PhysicalKeyboardKey.enter, key: PhysicalKeyboardKey.enter,
modifiers: [ modifiers: [(!kIsWeb && Platform.isMacOS) ? HotKeyModifier.meta : HotKeyModifier.control],
(!kIsWeb && Platform.isMacOS)
? HotKeyModifier.meta
: HotKeyModifier.control
],
scope: HotKeyScope.inapp, scope: HotKeyScope.inapp,
); );
@ -109,8 +100,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
void setEdit(SnChatMessage? value) { void setEdit(SnChatMessage? value) {
_contentController.text = value?.body['text'] ?? ''; _contentController.text = value?.body['text'] ?? '';
_attachments.clear(); _attachments.clear();
_attachments.addAll( _attachments.addAll(value?.preload?.attachments?.map((e) => PostWriteMedia(e)) ?? []);
value?.preload?.attachments?.map((e) => PostWriteMedia(e)) ?? []);
setState(() => _editingMessage = value); setState(() => _editingMessage = value);
} }
@ -149,9 +139,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
media.name, media.name,
'messaging', 'messaging',
null, null,
mimetype: media.raw != null && media.type == SnMediaType.image mimetype: media.raw != null && media.type == SnMediaType.image ? 'image/png' : null,
? 'image/png'
: null,
); );
final item = await attach.chunkedUploadParts( final item = await attach.chunkedUploadParts(
@ -183,10 +171,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
widget.controller.sendMessage( widget.controller.sendMessage(
_editingMessage != null ? 'messages.edit' : 'messages.new', _editingMessage != null ? 'messages.edit' : 'messages.new',
_contentController.text, _contentController.text,
attachments: _attachments attachments: _attachments.where((e) => e.attachment != null).map((e) => e.attachment!.rid).toList(),
.where((e) => e.attachment != null)
.map((e) => e.attachment!.rid)
.toList(),
relatedId: _editingMessage?.id, relatedId: _editingMessage?.id,
quoteId: _replyingMessage?.id, quoteId: _replyingMessage?.id,
editingMessage: _editingMessage, editingMessage: _editingMessage,
@ -247,15 +232,12 @@ class ChatMessageInputState extends State<ChatMessageInput> {
TweenAnimationBuilder<double>( TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: _progress), tween: Tween(begin: 0, end: _progress),
duration: Duration(milliseconds: 300), duration: Duration(milliseconds: 300),
builder: (context, value, _) => builder: (context, value, _) => LinearProgressIndicator(value: value, minHeight: 2),
LinearProgressIndicator(value: value, minHeight: 2),
) )
else if (_isBusy) else if (_isBusy)
const LinearProgressIndicator(value: null, minHeight: 2), const LinearProgressIndicator(value: null, minHeight: 2),
Padding( Padding(
padding: _attachments.isNotEmpty padding: _attachments.isNotEmpty ? const EdgeInsets.only(top: 8) : EdgeInsets.zero,
? const EdgeInsets.only(top: 8)
: EdgeInsets.zero,
child: PostMediaPendingList( child: PostMediaPendingList(
attachments: _attachments, attachments: _attachments,
isBusy: _isBusy, isBusy: _isBusy,
@ -267,8 +249,9 @@ class ChatMessageInputState extends State<ChatMessageInput> {
}, },
onUpdateBusy: (state) => setState(() => _isBusy = state), onUpdateBusy: (state) => setState(() => _isBusy = state),
), ),
).height(_attachments.isNotEmpty ? 80 + 8 : 0, animate: true).animate( )
const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut), .height(_attachments.isNotEmpty ? 80 + 8 : 0, animate: true)
.animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
SingleChildScrollView( SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
child: _replyingMessage != null child: _replyingMessage != null
@ -289,8 +272,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
const Gap(8), const Gap(8),
Expanded( Expanded(
child: Text( child: Text(
_replyingMessage?.body['text'] ?? _replyingMessage?.body['text'] ?? '${_replyingMessage?.sender.nick}',
'${_replyingMessage?.sender.nick}',
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@ -307,8 +289,9 @@ class ChatMessageInputState extends State<ChatMessageInput> {
).padding(vertical: 8), ).padding(vertical: 8),
) )
: const SizedBox.shrink(), : const SizedBox.shrink(),
).height(_replyingMessage != null ? 38 : 0, animate: true).animate( )
const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut), .height(_replyingMessage != null ? 38 : 0, animate: true)
.animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
SingleChildScrollView( SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
child: _editingMessage != null child: _editingMessage != null
@ -329,8 +312,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
const Gap(8), const Gap(8),
Expanded( Expanded(
child: Text( child: Text(
_editingMessage?.body['text'] ?? _editingMessage?.body['text'] ?? '${_editingMessage?.sender.nick}',
'${_editingMessage?.sender.nick}',
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@ -348,13 +330,12 @@ class ChatMessageInputState extends State<ChatMessageInput> {
).padding(vertical: 8), ).padding(vertical: 8),
) )
: const SizedBox.shrink(), : const SizedBox.shrink(),
).height(_editingMessage != null ? 38 : 0, animate: true).animate( )
const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut), .height(_editingMessage != null ? 38 : 0, animate: true)
Container( .animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
padding: EdgeInsets.symmetric(horizontal: 16), SizedBox(
constraints: BoxConstraints(minHeight: 56, maxHeight: 240), height: 56,
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
Expanded( Expanded(
child: TextField( child: TextField(
@ -366,14 +347,11 @@ class ChatMessageInputState extends State<ChatMessageInput> {
? 'fieldChatMessageDirect'.tr(args: [ ? 'fieldChatMessageDirect'.tr(args: [
'@${ud.getAccountFromCache(widget.otherMember?.accountId)?.name}', '@${ud.getAccountFromCache(widget.otherMember?.accountId)?.name}',
]) ])
: 'fieldChatMessage'.tr(args: [ : 'fieldChatMessage'.tr(args: [widget.controller.channel?.name ?? 'loading'.tr()]),
widget.controller.channel?.name ?? 'loading'.tr()
]),
border: InputBorder.none, border: InputBorder.none,
), ),
textInputAction: TextInputAction.send, textInputAction: TextInputAction.send,
onTapOutside: (_) => onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
FocusManager.instance.primaryFocus?.unfocus(),
onSubmitted: (_) { onSubmitted: (_) {
if (_isBusy) return; if (_isBusy) return;
_sendMessage(); _sendMessage();
@ -388,8 +366,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
Symbols.mood, Symbols.mood,
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
visualDensity: visualDensity: const VisualDensity(horizontal: -4, vertical: -4),
const VisualDensity(horizontal: -4, vertical: -4),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
constraints: const BoxConstraints(), constraints: const BoxConstraints(),
onPressed: () { onPressed: () {
@ -409,14 +386,13 @@ class ChatMessageInputState extends State<ChatMessageInput> {
Symbols.send, Symbols.send,
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
visualDensity: visualDensity: const VisualDensity(horizontal: -4, vertical: -4),
const VisualDensity(horizontal: -4, vertical: -4),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
constraints: const BoxConstraints(), constraints: const BoxConstraints(),
), ),
], ],
), ),
), ).padding(horizontal: 16),
], ],
); );
} }
@ -427,8 +403,7 @@ class _StickerPicker extends StatelessWidget {
final Function? onDismiss; final Function? onDismiss;
final Function(String)? onInsert; final Function(String)? onInsert;
const _StickerPicker( const _StickerPicker({this.onDismiss, required this.originalText, this.onInsert});
{this.onDismiss, required this.originalText, this.onInsert});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -438,9 +413,7 @@ class _StickerPicker extends StatelessWidget {
onDismiss?.call(); onDismiss?.call();
}, },
child: Container( child: Container(
constraints: BoxConstraints( constraints: BoxConstraints(maxWidth: min(360, MediaQuery.of(context).size.width), maxHeight: 240),
maxWidth: min(360, MediaQuery.of(context).size.width),
maxHeight: 240),
child: Material( child: Material(
elevation: 8, elevation: 8,
borderRadius: const BorderRadius.all(Radius.circular(8)), borderRadius: const BorderRadius.all(Radius.circular(8)),
@ -453,10 +426,8 @@ class _StickerPicker extends StatelessWidget {
return <Widget>[ return <Widget>[
Container( Container(
margin: EdgeInsets.only(bottom: 8), margin: EdgeInsets.only(bottom: 8),
padding: padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
EdgeInsets.symmetric(horizontal: 8, vertical: 4), color: Theme.of(context).colorScheme.surfaceContainerHigh,
color:
Theme.of(context).colorScheme.surfaceContainerHigh,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -468,8 +439,7 @@ class _StickerPicker extends StatelessWidget {
), ),
GridView.builder( GridView.builder(
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
padding: padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
const EdgeInsets.only(left: 8, right: 8, bottom: 8),
shrinkWrap: true, shrinkWrap: true,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 48, maxCrossAxisExtent: 48,
@ -492,8 +462,7 @@ class _StickerPicker extends StatelessWidget {
richMessage: TextSpan( richMessage: TextSpan(
children: [ children: [
TextSpan( TextSpan(
text: text: ':${element.pack.prefix}${element.alias}:\n',
':${element.pack.prefix}${element.alias}:\n',
style: GoogleFonts.robotoMono()), style: GoogleFonts.robotoMono()),
TextSpan(text: element.name).bold(), TextSpan(text: element.name).bold(),
], ],
@ -502,15 +471,11 @@ class _StickerPicker extends StatelessWidget {
width: 48, width: 48,
height: 48, height: 48,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: const BorderRadius.all( borderRadius: const BorderRadius.all(Radius.circular(8)),
Radius.circular(8)), color: Theme.of(context).colorScheme.surfaceContainerHigh,
color: Theme.of(context)
.colorScheme
.surfaceContainerHigh,
), ),
child: ClipRRect( child: ClipRRect(
borderRadius: const BorderRadius.all( borderRadius: const BorderRadius.all(Radius.circular(8)),
Radius.circular(8)),
child: UniversalImage( child: UniversalImage(
sn.getAttachmentUrl(element.attachment.rid), sn.getAttachmentUrl(element.attachment.rid),
width: 48, width: 48,

View File

@ -67,10 +67,7 @@ class PostMediaPendingList extends StatelessWidget {
if (result == null) return; if (result == null) return;
final rawBytes = final rawBytes = (await result.uiImage.toByteData(format: ImageByteFormat.png))!.buffer.asUint8List();
(await result.uiImage.toByteData(format: ImageByteFormat.png))!
.buffer
.asUint8List();
if (onUpdate != null) { if (onUpdate != null) {
final updatedMedia = PostWriteMedia.fromBytes( final updatedMedia = PostWriteMedia.fromBytes(
@ -136,8 +133,7 @@ class PostMediaPendingList extends StatelessWidget {
final result = await showDialog<SnAttachmentBoost?>( final result = await showDialog<SnAttachmentBoost?>(
context: context, context: context,
builder: (context) => builder: (context) => PendingAttachmentBoostDialog(media: attachments[idx]),
PendingAttachmentBoostDialog(media: attachments[idx]),
); );
if (result == null) return; if (result == null) return;
@ -169,15 +165,11 @@ class PostMediaPendingList extends StatelessWidget {
onUpdate!(idx, PostWriteMedia(result)); onUpdate!(idx, PostWriteMedia(result));
} }
ContextMenu _createContextMenu( ContextMenu _createContextMenu(BuildContext context, int idx, PostWriteMedia media) {
BuildContext context, int idx, PostWriteMedia media) { final canCompressVideo = !kIsWeb && (Platform.isAndroid || Platform.isIOS || Platform.isMacOS);
final canCompressVideo =
!kIsWeb && (Platform.isAndroid || Platform.isIOS || Platform.isMacOS);
return ContextMenu( return ContextMenu(
entries: [ entries: [
if (media.attachment == null && if (media.attachment == null && media.type == SnMediaType.video && canCompressVideo)
media.type == SnMediaType.video &&
canCompressVideo)
MenuItem( MenuItem(
label: 'attachmentCompressVideo'.tr(), label: 'attachmentCompressVideo'.tr(),
icon: Symbols.compress, icon: Symbols.compress,
@ -320,15 +312,12 @@ class _PostMediaPendingItem extends StatelessWidget {
AspectRatio( AspectRatio(
aspectRatio: 1, aspectRatio: 1,
child: switch (media.type) { child: switch (media.type) {
SnMediaType.image => SnMediaType.image => LayoutBuilder(builder: (context, constraints) {
LayoutBuilder(builder: (context, constraints) {
return Image( return Image(
image: media.getImageProvider( image: media.getImageProvider(
context, context,
width: width: (constraints.maxWidth * devicePixelRatio).round(),
(constraints.maxWidth * devicePixelRatio).round(), height: (constraints.maxHeight * devicePixelRatio).round(),
height:
(constraints.maxHeight * devicePixelRatio).round(),
)!, )!,
fit: BoxFit.contain, fit: BoxFit.contain,
); );
@ -337,34 +326,28 @@ class _PostMediaPendingItem extends StatelessWidget {
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
if (media.attachment?.thumbnail != null) if (media.attachment?.thumbnail != null)
AutoResizeUniversalImage(sn.getAttachmentUrl( AutoResizeUniversalImage(sn.getAttachmentUrl(media.attachment!.thumbnail!.rid)),
media.attachment!.thumbnail!.rid)), const Icon(Symbols.videocam, color: Colors.white, shadows: [
const Icon(Symbols.videocam, Shadow(
color: Colors.white, offset: Offset(1, 1),
shadows: [ blurRadius: 8.0,
Shadow( color: Color.fromARGB(255, 0, 0, 0),
offset: Offset(1, 1), ),
blurRadius: 8.0, ]),
color: Color.fromARGB(255, 0, 0, 0),
),
]),
], ],
), ),
SnMediaType.audio => Stack( SnMediaType.audio => Stack(
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
if (media.attachment?.thumbnail != null) if (media.attachment?.thumbnail != null)
AutoResizeUniversalImage(sn.getAttachmentUrl( AutoResizeUniversalImage(sn.getAttachmentUrl(media.attachment!.thumbnail!.rid)),
media.attachment!.thumbnail!.rid)), const Icon(Symbols.audio_file, color: Colors.white, shadows: [
const Icon(Symbols.audio_file, Shadow(
color: Colors.white, offset: Offset(1, 1),
shadows: [ blurRadius: 8.0,
Shadow( color: Color.fromARGB(255, 0, 0, 0),
offset: Offset(1, 1), ),
blurRadius: 8.0, ]),
color: Color.fromARGB(255, 0, 0, 0),
),
]),
], ],
), ),
_ => Container( _ => Container(
@ -373,8 +356,7 @@ class _PostMediaPendingItem extends StatelessWidget {
), ),
}, },
), ),
if (media.type != SnMediaType.image) if (media.type != SnMediaType.image) const VerticalDivider(width: 1, thickness: 1),
const VerticalDivider(width: 1, thickness: 1),
if (media.type != SnMediaType.image) if (media.type != SnMediaType.image)
SizedBox( SizedBox(
width: 160, width: 160,
@ -392,8 +374,7 @@ class _PostMediaPendingItem extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
) )
else if (media.file != null) else if (media.file != null)
Text(media.file!.name, Text(media.file!.name, maxLines: 1, overflow: TextOverflow.ellipsis)
maxLines: 1, overflow: TextOverflow.ellipsis)
else else
Text('unknown'.tr()), Text('unknown'.tr()),
if (media.attachment != null) if (media.attachment != null)
@ -406,8 +387,7 @@ class _PostMediaPendingItem extends StatelessWidget {
FutureBuilder<int?>( FutureBuilder<int?>(
future: media.length(), future: media.length(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData) if (!snapshot.hasData) return const SizedBox.shrink();
return const SizedBox.shrink();
return Text( return Text(
snapshot.data!.formatBytes(), snapshot.data!.formatBytes(),
style: GoogleFonts.robotoMono(fontSize: 13), style: GoogleFonts.robotoMono(fontSize: 13),
@ -418,8 +398,7 @@ class _PostMediaPendingItem extends StatelessWidget {
], ],
), ),
), ),
if (media.attachment != null && if (media.attachment != null && media.attachment!.boosts.isNotEmpty)
media.attachment!.boosts.isNotEmpty)
Row( Row(
children: [ children: [
Icon(Symbols.bolt, size: 16), Icon(Symbols.bolt, size: 16),
@ -427,8 +406,7 @@ class _PostMediaPendingItem extends StatelessWidget {
Text('attachmentGotBoosted').tr().fontSize(13), Text('attachmentGotBoosted').tr().fontSize(13),
], ],
), ),
if (media.attachment != null && if (media.attachment != null && media.attachment!.compressedId != null)
media.attachment!.compressedId != null)
Row( Row(
children: [ children: [
Icon(Symbols.compress, size: 16), Icon(Symbols.compress, size: 16),
@ -549,20 +527,12 @@ class AddPostMediaButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PopupMenuButton( return PopupMenuButton(
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
style: ButtonStyle(
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
),
icon: Icon( icon: Icon(
Symbols.add_photo_alternate, Symbols.add_photo_alternate,
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
itemBuilder: (context) => [ itemBuilder: (context) => [
if (!kIsWeb && if (!kIsWeb && !Platform.isLinux && !Platform.isMacOS && !Platform.isWindows)
!Platform.isLinux &&
!Platform.isMacOS &&
!Platform.isWindows)
PopupMenuItem( PopupMenuItem(
child: Row( child: Row(
children: [ children: [
@ -575,10 +545,7 @@ class AddPostMediaButton extends StatelessWidget {
_takeMedia(false); _takeMedia(false);
}, },
), ),
if (!kIsWeb && if (!kIsWeb && !Platform.isLinux && !Platform.isMacOS && !Platform.isWindows)
!Platform.isLinux &&
!Platform.isMacOS &&
!Platform.isWindows)
PopupMenuItem( PopupMenuItem(
child: Row( child: Row(
children: [ children: [