💄 Better attachments list styles
This commit is contained in:
parent
42c3e5ff0a
commit
a5b6ace79b
@ -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
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -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>
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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),
|
||||||
|
@ -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,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user