💄 Better attachments list styles

This commit is contained in:
LittleSheep 2024-07-26 01:16:32 +08:00
parent 42c3e5ff0a
commit a5b6ace79b
10 changed files with 278 additions and 217 deletions

View File

@ -107,7 +107,7 @@ PODS:
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- image_picker_ios (0.0.1): - image_picker_ios (0.0.1):
- Flutter - Flutter
- livekit_client (2.2.1): - livekit_client (2.2.2):
- Flutter - Flutter
- WebRTC-SDK (= 125.6422.04) - WebRTC-SDK (= 125.6422.04)
- media_kit_libs_ios_video (1.0.4): - media_kit_libs_ios_video (1.0.4):
@ -138,11 +138,11 @@ PODS:
- SDWebImage (5.19.4): - SDWebImage (5.19.4):
- SDWebImage/Core (= 5.19.4) - SDWebImage/Core (= 5.19.4)
- SDWebImage/Core (5.19.4) - SDWebImage/Core (5.19.4)
- Sentry/HybridSDK (8.30.1) - Sentry/HybridSDK (8.32.0)
- sentry_flutter (8.4.0): - sentry_flutter (8.5.0):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- Sentry/HybridSDK (= 8.30.1) - Sentry/HybridSDK (= 8.32.0)
- sqflite (0.0.3): - sqflite (0.0.3):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
@ -268,7 +268,7 @@ SPEC CHECKSUMS:
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
livekit_client: 411d387fd6f993851081069afbe7f04a8e974f1b livekit_client: c767049a635d5b6d43de3273dca3c439b8a6e970
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
@ -281,8 +281,8 @@ SPEC CHECKSUMS:
protocol_handler_ios: a5db8abc38526ee326988b808be621e5fd568990 protocol_handler_ios: a5db8abc38526ee326988b808be621e5fd568990
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d
Sentry: 514a3ea653886e9a48c6287d8b7bf05ec24bf3be Sentry: 96ae1dcdf01a644bc3a3b1dc279cecaf48a833fb
sentry_flutter: edc037f7af0dc1512d6c33a5c2c7c838bd0d6806 sentry_flutter: f1d86adcb93a959bc47a40d8d55059bdf7569bc5
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe

View File

@ -1,10 +1,8 @@
import 'package:solian/models/account.dart';
class Attachment { class Attachment {
int id; int id;
DateTime createdAt; DateTime createdAt;
DateTime updatedAt; DateTime updatedAt;
dynamic deletedAt; DateTime? deletedAt;
String uuid; String uuid;
int size; int size;
String name; String name;
@ -15,7 +13,6 @@ class Attachment {
String destination; String destination;
Map<String, dynamic>? metadata; Map<String, dynamic>? metadata;
bool isMature; bool isMature;
Account account;
int accountId; int accountId;
Attachment({ Attachment({
@ -33,7 +30,6 @@ class Attachment {
required this.destination, required this.destination,
required this.metadata, required this.metadata,
required this.isMature, required this.isMature,
required this.account,
required this.accountId, required this.accountId,
}); });
@ -41,7 +37,7 @@ class Attachment {
id: json['id'], id: json['id'],
createdAt: DateTime.parse(json['created_at']), createdAt: DateTime.parse(json['created_at']),
updatedAt: DateTime.parse(json['updated_at']), updatedAt: DateTime.parse(json['updated_at']),
deletedAt: json['deleted_at'], deletedAt: json['deleted_at'] != null ? DateTime.parse(json['deleted_at']) : null,
uuid: json['uuid'], uuid: json['uuid'],
size: json['size'], size: json['size'],
name: json['name'], name: json['name'],
@ -52,7 +48,6 @@ class Attachment {
destination: json['destination'], destination: json['destination'],
metadata: json['metadata'], metadata: json['metadata'],
isMature: json['is_mature'], isMature: json['is_mature'],
account: Account.fromJson(json['account']),
accountId: json['account_id'], accountId: json['account_id'],
); );
@ -60,7 +55,7 @@ class Attachment {
'id': id, 'id': id,
'created_at': createdAt.toIso8601String(), 'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(), 'updated_at': updatedAt.toIso8601String(),
'deleted_at': deletedAt, 'deleted_at': deletedAt?.toIso8601String(),
'uuid': uuid, 'uuid': uuid,
'size': size, 'size': size,
'name': name, 'name': name,
@ -71,7 +66,6 @@ class Attachment {
'destination': destination, 'destination': destination,
'metadata': metadata, 'metadata': metadata,
'is_mature': isMature, 'is_mature': isMature,
'account': account.toJson(),
'account_id': accountId, 'account_id': accountId,
}; };
} }

View File

@ -44,8 +44,6 @@ class _HomeScreenState extends State<HomeScreen>
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Material( return Material(
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
child: SafeArea(
bottom: false,
child: Scaffold( child: Scaffold(
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add), child: const Icon(Icons.add),
@ -59,8 +57,7 @@ class _HomeScreenState extends State<HomeScreen>
}, },
), ),
body: NestedScrollView( body: NestedScrollView(
headerSliverBuilder: headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
(BuildContext context, bool innerBoxIsScrolled) {
return [ return [
SliverAppBar( SliverAppBar(
title: AppBarTitle('home'.tr), title: AppBarTitle('home'.tr),
@ -109,7 +106,6 @@ class _HomeScreenState extends State<HomeScreen>
}), }),
), ),
), ),
),
); );
} }

View File

@ -1,3 +1,4 @@
import 'dart:math' show min;
import 'dart:ui'; import 'dart:ui';
import 'package:carousel_slider/carousel_slider.dart'; import 'package:carousel_slider/carousel_slider.dart';
@ -11,7 +12,7 @@ import 'package:solian/widgets/attachments/attachment_list_fullscreen.dart';
class AttachmentList extends StatefulWidget { class AttachmentList extends StatefulWidget {
final String parentId; final String parentId;
final List<int> attachmentsId; final List<int> attachmentsId;
final bool divided; final bool isGrid;
final double? width; final double? width;
final double? viewport; final double? viewport;
@ -20,7 +21,7 @@ class AttachmentList extends StatefulWidget {
super.key, super.key,
required this.parentId, required this.parentId,
required this.attachmentsId, required this.attachmentsId,
this.divided = false, this.isGrid = false,
this.width, this.width,
this.viewport, this.viewport,
}); });
@ -101,12 +102,43 @@ class _AttachmentListState extends State<AttachmentList> {
} }
} }
Widget buildEntry(Attachment element, int idx) { Widget _buildEntry(Attachment? element, int idx) {
if (element == null) {
return Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 280),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.close, size: 32),
const SizedBox(height: 8),
Text(
'attachmentLoadFailed'.tr,
style:
const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
Text(
'attachmentLoadFailedCaption'.tr,
textAlign: TextAlign.center,
),
],
),
),
);
}
return GestureDetector( return GestureDetector(
child: Container( child: Container(
width: widget.width ?? MediaQuery.of(context).size.width, width: widget.width ?? MediaQuery.of(context).size.width,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHigh, border: widget.attachmentsId.length > 1
? Border.symmetric(
vertical: BorderSide(
width: 0.3,
color: Theme.of(context).dividerColor,
),
)
: null,
), ),
child: Stack( child: Stack(
fit: StackFit.expand, fit: StackFit.expand,
@ -115,7 +147,7 @@ class _AttachmentListState extends State<AttachmentList> {
parentId: widget.parentId, parentId: widget.parentId,
key: Key('a${element.uuid}'), key: Key('a${element.uuid}'),
item: element, item: element,
badge: _attachmentsMeta.length > 1 badge: _attachmentsMeta.length > 1 && !widget.isGrid
? '${idx + 1}/${_attachmentsMeta.length}' ? '${idx + 1}/${_attachmentsMeta.length}'
: null, : null,
showHideButton: !element.isMature || _showMature, showHideButton: !element.isMature || _showMature,
@ -139,15 +171,19 @@ class _AttachmentListState extends State<AttachmentList> {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon(Icons.visibility_off, const Icon(
color: Colors.white, size: 32), Icons.visibility_off,
color: Colors.white,
size: 32,
),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
'matureContent'.tr, 'matureContent'.tr,
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: Colors.white,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 16), fontSize: 16,
),
), ),
Text( Text(
'matureContentCaption'.tr, 'matureContentCaption'.tr,
@ -193,47 +229,27 @@ class _AttachmentListState extends State<AttachmentList> {
if (_isLoading) { if (_isLoading) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHigh), color: Theme.of(context).colorScheme.surfaceContainerHigh,
),
child: const LinearProgressIndicator(), child: const LinearProgressIndicator(),
); );
} }
return CarouselSlider.builder( if (widget.isGrid) {
options: CarouselOptions( const radius = BorderRadius.all(Radius.circular(8));
aspectRatio: _aspectRatio, return GridView.builder(
viewportFraction: widget.viewport ?? (widget.divided ? 0.9 : 1), padding: EdgeInsets.zero,
enableInfiniteScroll: false, primary: false,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: min(3, widget.attachmentsId.length),
mainAxisSpacing: 8.0,
crossAxisSpacing: 8.0,
), ),
itemCount: _attachmentsMeta.length, itemCount: widget.attachmentsId.length,
itemBuilder: (context, idx, _) { itemBuilder: (context, idx) {
final element = _attachmentsMeta[idx]; final element = _attachmentsMeta[idx];
if (element == null) {
return Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 280),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.close, size: 32),
const SizedBox(height: 8),
Text(
'attachmentLoadFailed'.tr,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 16),
),
Text(
'attachmentLoadFailedCaption'.tr,
textAlign: TextAlign.center,
),
],
),
),
);
}
if (widget.divided) {
const radius = BorderRadius.all(Radius.circular(16));
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: border:
@ -242,13 +258,36 @@ class _AttachmentListState extends State<AttachmentList> {
), ),
child: ClipRRect( child: ClipRRect(
borderRadius: radius, borderRadius: radius,
child: buildEntry(element, idx), child: _buildEntry(element, idx),
), )
).paddingSymmetric(horizontal: widget.divided ? 4 : 0); );
} else {
return buildEntry(element, idx);
}
}, },
).paddingSymmetric(horizontal: 24);
}
return Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHigh,
border: Border.symmetric(
horizontal: BorderSide(
width: 0.3,
color: Theme.of(context).dividerColor,
),
),
),
child: CarouselSlider.builder(
options: CarouselOptions(
aspectRatio: _aspectRatio,
viewportFraction:
widget.viewport ?? (widget.attachmentsId.length > 1 ? 0.95 : 1),
enableInfiniteScroll: false,
),
itemCount: _attachmentsMeta.length,
itemBuilder: (context, idx, _) {
final element = _attachmentsMeta[idx];
return _buildEntry(element, idx);
},
),
); );
} }
} }

View File

@ -5,6 +5,7 @@ import 'package:solian/controllers/chat_events_controller.dart';
import 'package:solian/models/event.dart'; import 'package:solian/models/event.dart';
import 'package:solian/widgets/account/account_avatar.dart'; import 'package:solian/widgets/account/account_avatar.dart';
import 'package:solian/widgets/account/account_profile_popup.dart'; import 'package:solian/widgets/account/account_profile_popup.dart';
import 'package:solian/widgets/attachments/attachment_list.dart';
import 'package:solian/widgets/chat/chat_event_action_log.dart'; import 'package:solian/widgets/chat/chat_event_action_log.dart';
import 'package:solian/widgets/chat/chat_event_message.dart'; import 'package:solian/widgets/chat/chat_event_message.dart';
import 'package:timeago/timeago.dart' show format; import 'package:timeago/timeago.dart' show format;
@ -36,7 +37,30 @@ class ChatEvent extends StatelessWidget {
return '$negativeSign${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds'; return '$negativeSign${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds';
} }
Widget buildQuote() { Widget _buildAttachment(BuildContext context) {
final attachments = item.body['attachments'] != null
? List<int>.from(item.body['attachments'].map((x) => x))
: List<int>.empty();
if (attachments.isEmpty) return const SizedBox();
return Container(
key: Key('m${item.uuid}attachments-box'),
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.only(top: isMerged ? 0 : 4),
constraints: const BoxConstraints(
maxHeight: 720,
),
child: AttachmentList(
key: Key('m${item.uuid}attachments'),
parentId: item.uuid,
attachmentsId: attachments,
viewport: 1,
),
);
}
Widget _buildQuote() {
return FutureBuilder( return FutureBuilder(
future: chatController!.getEvent( future: chatController!.getEvent(
item.body['quote_event'], item.body['quote_event'],
@ -55,7 +79,7 @@ class ChatEvent extends StatelessWidget {
); );
} }
Widget buildContent() { Widget _buildContent() {
switch (item.type) { switch (item.type) {
case 'messages.new': case 'messages.new':
return ChatEventMessage( return ChatEventMessage(
@ -121,17 +145,17 @@ class ChatEvent extends StatelessWidget {
} }
} }
Widget buildBody(BuildContext context) { Widget _buildBody(BuildContext context) {
if (isContentPreviewing || (isMerged && !isQuote)) { if (isContentPreviewing || (isMerged && !isQuote)) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (item.body['quote_event'] != null && chatController != null) if (item.body['quote_event'] != null && chatController != null)
buildQuote(), _buildQuote(),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Expanded(child: buildContent()), Expanded(child: _buildContent()),
if (item.isPending) if (item.isPending)
const SizedBox( const SizedBox(
width: 12, width: 12,
@ -139,9 +163,10 @@ class ChatEvent extends StatelessWidget {
child: CircularProgressIndicator(strokeWidth: 2), child: CircularProgressIndicator(strokeWidth: 2),
), ),
], ],
), ).paddingOnly(right: 12),
_buildAttachment(context),
], ],
).paddingOnly(right: 12); );
} else if (isQuote) { } else if (isQuote) {
return Card( return Card(
child: Row( child: Row(
@ -170,7 +195,7 @@ class ChatEvent extends StatelessWidget {
Text(format(item.createdAt, locale: 'en_short')), Text(format(item.createdAt, locale: 'en_short')),
], ],
), ),
buildContent().paddingOnly(left: 0.5), _buildContent().paddingOnly(left: 0.5),
], ],
), ),
), ),
@ -214,11 +239,11 @@ class ChatEvent extends StatelessWidget {
).paddingSymmetric(horizontal: 12), ).paddingSymmetric(horizontal: 12),
if (item.body['quote_event'] != null && if (item.body['quote_event'] != null &&
chatController != null) chatController != null)
buildQuote(), _buildQuote(),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Expanded(child: buildContent()), Expanded(child: _buildContent()),
if (item.isPending) if (item.isPending)
const SizedBox( const SizedBox(
width: 12, width: 12,
@ -232,6 +257,7 @@ class ChatEvent extends StatelessWidget {
), ),
], ],
).paddingSymmetric(horizontal: 12), ).paddingSymmetric(horizontal: 12),
_buildAttachment(context),
], ],
); );
} }
@ -239,6 +265,6 @@ class ChatEvent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return buildBody(context); return _buildBody(context);
} }
} }

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:solian/models/event.dart'; import 'package:solian/models/event.dart';
import 'package:solian/widgets/attachments/attachment_list.dart';
import 'package:solian/widgets/markdown_text_content.dart'; import 'package:solian/widgets/markdown_text_content.dart';
class ChatEventMessage extends StatelessWidget { class ChatEventMessage extends StatelessWidget {
@ -20,66 +19,52 @@ class ChatEventMessage extends StatelessWidget {
this.isQuote = false, this.isQuote = false,
}); });
Widget buildAttachment(BuildContext context) { Widget _buildContent(BuildContext context) {
final body = EventMessageBody.fromJson(item.body);
return Container(
key: Key('m${item.uuid}attachments-box'),
width: MediaQuery.of(context).size.width,
constraints: const BoxConstraints(
maxHeight: 720,
maxWidth: 640,
),
child: AttachmentList(
key: Key('m${item.uuid}attachments'),
parentId: item.uuid,
attachmentsId: body.attachments ?? List.empty(),
divided: true,
viewport: 1,
),
);
}
Widget buildContent() {
final body = EventMessageBody.fromJson(item.body); final body = EventMessageBody.fromJson(item.body);
final hasAttachment = body.attachments?.isNotEmpty ?? false; final hasAttachment = body.attachments?.isNotEmpty ?? false;
return MarkdownTextContent(content: body.text).paddingOnly( if (body.text.isEmpty && hasAttachment) {
final unFocusColor = Theme.of(context).colorScheme.onSurface.withOpacity(0.75);
return Row(
children: [
Icon(
Icons.attachment,
size: 18,
color: unFocusColor,
).paddingOnly(right: 6),
Text(
'postAttachmentTip'.trParams(
{'count': body.attachments?.length.toString() ?? 0.toString()},
),
style: TextStyle(color: unFocusColor),
)
],
);
}
return MarkdownTextContent(content: body.text);
}
Widget _buildBody(BuildContext context) {
if (isContentPreviewing) {
return _buildContent(context);
} else if (isMerged) {
return _buildContent(context).paddingOnly(left: 52);
} else {
return _buildContent(context);
}
}
@override
Widget build(BuildContext context) {
final body = EventMessageBody.fromJson(item.body);
final hasAttachment = body.attachments?.isNotEmpty ?? false;
return _buildBody(context).paddingOnly(
left: isQuote ? 0 : 12, left: isQuote ? 0 : 12,
right: isQuote ? 0 : 12, right: isQuote ? 0 : 12,
top: body.quoteEvent == null ? 2 : 0, top: body.quoteEvent == null ? 2 : 0,
bottom: hasAttachment ? 4 : (isHasMerged ? 2 : 0), bottom: hasAttachment ? 4 : (isHasMerged ? 2 : 0),
); );
} }
Widget buildBody(BuildContext context) {
final body = EventMessageBody.fromJson(item.body);
if (isContentPreviewing) {
return buildContent();
} else if (isMerged) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildContent().paddingOnly(left: 52),
if (body.attachments?.isNotEmpty ?? false)
buildAttachment(context).paddingOnly(left: 52, bottom: 4),
],
);
} else {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildContent(),
if (body.attachments?.isNotEmpty ?? false)
buildAttachment(context).paddingOnly(bottom: 4),
],
);
}
}
@override
Widget build(BuildContext context) {
return buildBody(context);
}
} }

View File

@ -52,15 +52,21 @@ class _PostItemState extends State<PostItem> {
super.initState(); super.initState();
} }
Widget buildDate() { Widget _buildDate() {
if (widget.isFullDate) { if (widget.isFullDate) {
return Text(DateFormat('y/M/d H:m').format(item.createdAt.toLocal())); return Text(DateFormat('y/M/d H:m')
.format(item.publishedAt?.toLocal() ?? DateTime.now()));
} else { } else {
return Text(format(item.createdAt.toLocal(), locale: 'en_short')); return Text(
format(
item.publishedAt?.toLocal() ?? DateTime.now(),
locale: 'en_short',
),
);
} }
} }
Widget buildHeader() { Widget _buildHeader() {
return Row( return Row(
children: [ children: [
if (widget.isCompact) if (widget.isCompact)
@ -72,12 +78,12 @@ class _PostItemState extends State<PostItem> {
item.author.nick, item.author.nick,
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: FontWeight.bold),
).paddingOnly(left: widget.isCompact ? 6 : 12), ).paddingOnly(left: widget.isCompact ? 6 : 12),
buildDate().paddingOnly(left: 4), _buildDate().paddingOnly(left: 4),
], ],
); );
} }
Widget buildFooter() { Widget _buildFooter() {
List<String> labels = List.empty(growable: true); List<String> labels = List.empty(growable: true);
if (widget.item.createdAt != widget.item.updatedAt) { if (widget.item.createdAt != widget.item.updatedAt) {
labels.add('postEdited'.trParams({ labels.add('postEdited'.trParams({
@ -116,7 +122,7 @@ class _PostItemState extends State<PostItem> {
} }
} }
Widget buildReply(BuildContext context) { Widget _buildReply(BuildContext context) {
return Column( return Column(
children: [ children: [
Row( Row(
@ -148,7 +154,7 @@ class _PostItemState extends State<PostItem> {
); );
} }
Widget buildRepost(BuildContext context) { Widget _buildRepost(BuildContext context) {
return Column( return Column(
children: [ children: [
Row( Row(
@ -191,7 +197,7 @@ class _PostItemState extends State<PostItem> {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
buildHeader().paddingSymmetric(horizontal: 12), _buildHeader().paddingSymmetric(horizontal: 12),
MarkdownTextContent( MarkdownTextContent(
content: item.body['content'], content: item.body['content'],
isSelectable: widget.isContentSelectable, isSelectable: widget.isContentSelectable,
@ -201,7 +207,7 @@ class _PostItemState extends State<PostItem> {
top: 2, top: 2,
bottom: hasAttachment ? 4 : 0, bottom: hasAttachment ? 4 : 0,
), ),
buildFooter().paddingOnly(left: 16), _buildFooter().paddingOnly(left: 16),
if (attachments.isNotEmpty) if (attachments.isNotEmpty)
Row( Row(
children: [ children: [
@ -246,14 +252,14 @@ class _PostItemState extends State<PostItem> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
buildHeader(), _buildHeader(),
MarkdownTextContent( MarkdownTextContent(
content: item.body['content'], content: item.body['content'],
isSelectable: widget.isContentSelectable, isSelectable: widget.isContentSelectable,
).paddingOnly(left: 12, right: 8), ).paddingOnly(left: 12, right: 8),
if (widget.item.replyTo != null && widget.isShowEmbed) if (widget.item.replyTo != null && widget.isShowEmbed)
GestureDetector( GestureDetector(
child: buildReply(context).paddingOnly(top: 4), child: _buildReply(context).paddingOnly(top: 4),
onTap: () { onTap: () {
if (!widget.isClickable) return; if (!widget.isClickable) return;
AppRouter.instance.pushNamed( AppRouter.instance.pushNamed(
@ -266,7 +272,7 @@ class _PostItemState extends State<PostItem> {
), ),
if (widget.item.repostTo != null && widget.isShowEmbed) if (widget.item.repostTo != null && widget.isShowEmbed)
GestureDetector( GestureDetector(
child: buildRepost(context).paddingOnly(top: 4), child: _buildRepost(context).paddingOnly(top: 4),
onTap: () { onTap: () {
if (!widget.isClickable) return; if (!widget.isClickable) return;
AppRouter.instance.pushNamed( AppRouter.instance.pushNamed(
@ -277,7 +283,7 @@ class _PostItemState extends State<PostItem> {
); );
}, },
), ),
buildFooter().paddingOnly(left: 12), _buildFooter().paddingOnly(left: 12),
], ],
), ),
) )
@ -297,7 +303,7 @@ class _PostItemState extends State<PostItem> {
child: AttachmentList( child: AttachmentList(
parentId: widget.item.id.toString(), parentId: widget.item.id.toString(),
attachmentsId: attachments, attachmentsId: attachments,
divided: true, isGrid: attachments.length > 1,
), ),
), ),
if (widget.isShowReply && widget.isReactable) if (widget.isShowReply && widget.isReactable)

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:solian/models/post.dart'; import 'package:solian/models/post.dart';
import 'package:solian/providers/auth.dart';
import 'package:solian/router.dart'; import 'package:solian/router.dart';
import 'package:solian/widgets/sized_container.dart'; import 'package:solian/widgets/sized_container.dart';
import 'package:solian/widgets/posts/post_action.dart'; import 'package:solian/widgets/posts/post_action.dart';
@ -79,6 +80,9 @@ class PostListEntryWidget extends StatelessWidget {
); );
}, },
onLongPress: () { onLongPress: () {
final AuthProvider auth = Get.find();
if (auth.isAuthorized.isFalse) return;
showModalBottomSheet( showModalBottomSheet(
useRootNavigator: true, useRootNavigator: true,
context: context, context: context,

View File

@ -40,6 +40,9 @@ class _PostShuffleSwiperState extends State<PostShuffleSwiper> {
return PostSingleDisplay( return PostSingleDisplay(
key: Key('p${element.id}'), key: Key('p${element.id}'),
item: element, item: element,
onUpdate: () {
widget.controller.reloadAllOver();
},
); );
}, },
padding: const EdgeInsets.all(24), padding: const EdgeInsets.all(24),

View File

@ -1,12 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:solian/models/post.dart'; import 'package:solian/models/post.dart';
import 'package:solian/widgets/posts/post_item.dart'; import 'package:solian/widgets/posts/post_list.dart';
class PostSingleDisplay extends StatelessWidget { class PostSingleDisplay extends StatelessWidget {
final Post item; final Post item;
final Function onUpdate;
const PostSingleDisplay({super.key, required this.item}); const PostSingleDisplay({
super.key,
required this.item,
required this.onUpdate,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -14,9 +18,13 @@ class PostSingleDisplay extends StatelessWidget {
alignment: Alignment.center, alignment: Alignment.center,
child: Card( child: Card(
child: SingleChildScrollView( child: SingleChildScrollView(
child: PostItem( child: PostListEntryWidget(
item: item, item: item,
).paddingSymmetric(horizontal: 10, vertical: 16), isClickable: true,
isShowEmbed: true,
isNestedClickable: true,
onUpdate: onUpdate,
),
), ),
), ),
); );