:refactor: Central post fetching logic

This commit is contained in:
LittleSheep 2024-11-26 00:00:09 +08:00
parent 41e2b08bcc
commit 356d3d4d3e
10 changed files with 206 additions and 140 deletions

View File

@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart'; import 'package:mime/mime.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:surface/providers/post.dart';
import 'package:surface/providers/sn_attachment.dart'; import 'package:surface/providers/sn_attachment.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/attachment.dart'; import 'package:surface/types/attachment.dart';
@ -180,53 +181,35 @@ class PostWriteController extends ChangeNotifier {
int? reposting, int? reposting,
int? replying, int? replying,
}) async { }) async {
final sn = context.read<SnNetworkProvider>(); final pt = context.read<SnPostContentProvider>();
final attach = context.read<SnAttachmentProvider>();
isLoading = true; isLoading = true;
notifyListeners(); notifyListeners();
try { try {
if (editing != null) { if (editing != null) {
final resp = await sn.client.get('/cgi/co/posts/$editing'); final post = await pt.getPost(editing);
final post = SnPost.fromJson(resp.data);
final alts = await attach
.getMultiple(post.body['attachments']?.cast<String>() ?? []);
publisher = post.publisher; publisher = post.publisher;
titleController.text = post.body['title'] ?? ''; titleController.text = post.body['title'] ?? '';
descriptionController.text = post.body['description'] ?? ''; descriptionController.text = post.body['description'] ?? '';
contentController.text = post.body['content'] ?? ''; contentController.text = post.body['content'] ?? '';
publishedAt = post.publishedAt; publishedAt = post.publishedAt;
publishedUntil = post.publishedUntil; publishedUntil = post.publishedUntil;
attachments.addAll(alts.map((ele) => PostWriteMedia(ele))); attachments.addAll(
post.preload?.attachments?.map((ele) => PostWriteMedia(ele)) ?? [],
editingPost = post.copyWith(
preload: SnPostPreload(
attachments: alts,
),
); );
editingPost = post;
} }
if (replying != null) { if (replying != null) {
final resp = await sn.client.get('/cgi/co/posts/$replying'); final post = await pt.getPost(replying);
final post = SnPost.fromJson(resp.data); replyingPost = post;
replyingPost = post.copyWith(
preload: SnPostPreload(
attachments: await attach
.getMultiple(post.body['attachments']?.cast<String>() ?? []),
),
);
} }
if (reposting != null) { if (reposting != null) {
final resp = await sn.client.get('/cgi/co/posts/$reposting'); final post = await pt.getPost(reposting);
final post = SnPost.fromJson(resp.data); replyingPost = post;
repostingPost = post.copyWith(
preload: SnPostPreload(
attachments: await attach
.getMultiple(post.body['attachments']?.cast<String>() ?? []),
),
);
} }
} catch (err) { } catch (err) {
if (!context.mounted) return; if (!context.mounted) return;

View File

@ -15,6 +15,7 @@ import 'package:surface/providers/channel.dart';
import 'package:surface/providers/chat_call.dart'; import 'package:surface/providers/chat_call.dart';
import 'package:surface/providers/navigation.dart'; import 'package:surface/providers/navigation.dart';
import 'package:surface/providers/notification.dart'; import 'package:surface/providers/notification.dart';
import 'package:surface/providers/post.dart';
import 'package:surface/providers/sn_attachment.dart'; import 'package:surface/providers/sn_attachment.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/theme.dart'; import 'package:surface/providers/theme.dart';
@ -82,6 +83,7 @@ class SolianApp extends StatelessWidget {
// Data layer // Data layer
Provider(create: (_) => SnNetworkProvider()), Provider(create: (_) => SnNetworkProvider()),
Provider(create: (ctx) => SnAttachmentProvider(ctx)), Provider(create: (ctx) => SnAttachmentProvider(ctx)),
Provider(create: (ctx) => SnPostContentProvider(ctx)),
Provider(create: (ctx) => UserDirectoryProvider(ctx)), Provider(create: (ctx) => UserDirectoryProvider(ctx)),
ChangeNotifierProvider(create: (ctx) => UserProvider(ctx)), ChangeNotifierProvider(create: (ctx) => UserProvider(ctx)),
ChangeNotifierProvider(create: (ctx) => WebSocketProvider(ctx)), ChangeNotifierProvider(create: (ctx) => WebSocketProvider(ctx)),

119
lib/providers/post.dart Normal file
View File

@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:surface/providers/sn_attachment.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/post.dart';
class SnPostContentProvider {
late final SnNetworkProvider _sn;
late final SnAttachmentProvider _attach;
SnPostContentProvider(BuildContext context) {
_sn = context.read<SnNetworkProvider>();
_attach = context.read<SnAttachmentProvider>();
}
Future<List<SnPost>> _preloadRelatedDataInBatch(List<SnPost> out) async {
Set<String> rids = {};
for (var i = 0; i < out.length; i++) {
rids.addAll(out[i].body['attachments']?.cast<String>() ?? []);
if (out[i].body['thumbnail'] != null) {
rids.add(out[i].body['thumbnail']);
}
}
final attachments = await _attach.getMultiple(rids.toList());
for (var i = 0; i < out.length; i++) {
out[i] = out[i].copyWith(
preload: SnPostPreload(
thumbnail: attachments
.where((ele) => ele?.rid == out[i].body['thumbnail'])
.firstOrNull,
attachments: attachments
.where((ele) =>
out[i].body['attachments']?.contains(ele?.rid) ?? false)
.toList(),
),
);
}
return out;
}
Future<SnPost> _preloadRelatedDataSingle(SnPost out) async {
Set<String> rids = {};
rids.addAll(out.body['attachments']?.cast<String>() ?? []);
if (out.body['thumbnail'] != null) {
rids.add(out.body['thumbnail']);
}
final attachments = await _attach.getMultiple(rids.toList());
out = out.copyWith(
preload: SnPostPreload(
thumbnail: attachments
.where((ele) => ele?.rid == out.body['thumbnail'])
.firstOrNull,
attachments: attachments
.where(
(ele) => out.body['attachments']?.contains(ele?.rid) ?? false)
.toList(),
),
);
return out;
}
Future<(List<SnPost>, int)> listPosts({int take = 10, int offset = 0}) async {
final resp = await _sn.client.get('/cgi/co/posts', queryParameters: {
'take': take,
'offset': offset,
});
final List<SnPost> out = await _preloadRelatedDataInBatch(
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []),
);
return (out, resp.data['count'] as int);
}
Future<(List<SnPost>, int)> listPostReplies(
dynamic parentId, {
int take = 10,
int offset = 0,
}) async {
final resp = await _sn.client
.get('/cgi/co/posts/$parentId/replies', queryParameters: {
'take': take,
'offset': offset,
});
final List<SnPost> out = await _preloadRelatedDataInBatch(
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []),
);
return (out, resp.data['count'] as int);
}
Future<(List<SnPost>, int)> searchPosts(
String searchTerm, {
int take = 10,
int offset = 0,
}) async {
final resp = await _sn.client.get('/cgi/co/posts/search', queryParameters: {
'take': take,
'offset': offset,
'probe': searchTerm,
});
final List<SnPost> out = await _preloadRelatedDataInBatch(
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []),
);
return (out, resp.data['count'] as int);
}
Future<SnPost> getPost(dynamic id) async {
final resp = await _sn.client.get('/cgi/co/posts/$id');
final out = _preloadRelatedDataSingle(
SnPost.fromJson(resp.data['data']),
);
return out;
}
}

View File

@ -5,8 +5,7 @@ import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:surface/providers/sn_attachment.dart'; import 'package:surface/providers/post.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/post.dart'; import 'package:surface/types/post.dart';
import 'package:surface/widgets/post/post_item.dart'; import 'package:surface/widgets/post/post_item.dart';
import 'package:very_good_infinite_list/very_good_infinite_list.dart'; import 'package:very_good_infinite_list/very_good_infinite_list.dart';
@ -31,36 +30,13 @@ class _ExploreScreenState extends State<ExploreScreen> {
setState(() => _isBusy = true); setState(() => _isBusy = true);
final sn = context.read<SnNetworkProvider>(); final pt = context.read<SnPostContentProvider>();
final resp = await sn.client.get('/cgi/co/posts', queryParameters: { final result = await pt.listPosts(take: 10, offset: _posts.length);
'take': 10, final out = result.$1;
'offset': _posts.length,
});
final List<SnPost> out =
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []);
Set<String> rids = {};
for (var i = 0; i < out.length; i++) {
rids.addAll(out[i].body['attachments']?.cast<String>() ?? []);
}
if (!mounted) return; if (!mounted) return;
final attach = context.read<SnAttachmentProvider>();
final attachments = await attach.getMultiple(rids.toList());
for (var i = 0; i < out.length; i++) {
out[i] = out[i].copyWith(
preload: SnPostPreload(
attachments: attachments
.where(
(ele) =>
out[i].body['attachments']?.contains(ele?.rid) ?? false,
)
.toList(),
),
);
}
_postCount = resp.data['count']; _postCount = result.$2;
_posts.addAll(out); _posts.addAll(out);
if (mounted) setState(() => _isBusy = false); if (mounted) setState(() => _isBusy = false);

View File

@ -7,8 +7,7 @@ import 'package:go_router/go_router.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/sn_attachment.dart'; import 'package:surface/providers/post.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/userinfo.dart'; import 'package:surface/providers/userinfo.dart';
import 'package:surface/types/post.dart'; import 'package:surface/types/post.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
@ -39,19 +38,10 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
setState(() => _isBusy = true); setState(() => _isBusy = true);
try { try {
final sn = context.read<SnNetworkProvider>(); final pt = context.read<SnPostContentProvider>();
final attach = context.read<SnAttachmentProvider>(); final post = await pt.getPost(widget.slug);
final resp = await sn.client.get('/cgi/co/posts/${widget.slug}');
if (!mounted) return; if (!mounted) return;
final attachments = await attach.getMultiple( _data = post;
resp.data['body']['attachments']?.cast<String>() ?? [],
);
if (!mounted) return;
_data = SnPost.fromJson(resp.data).copyWith(
preload: SnPostPreload(
attachments: attachments,
),
);
} catch (err) { } catch (err) {
context.showErrorDialog(err); context.showErrorDialog(err);
} finally { } finally {

View File

@ -5,8 +5,7 @@ import 'package:go_router/go_router.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/sn_attachment.dart'; import 'package:surface/providers/post.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/post.dart'; import 'package:surface/types/post.dart';
import 'package:surface/widgets/post/post_item.dart'; import 'package:surface/widgets/post/post_item.dart';
import 'package:very_good_infinite_list/very_good_infinite_list.dart'; import 'package:very_good_infinite_list/very_good_infinite_list.dart';
@ -35,40 +34,20 @@ class _PostSearchScreenState extends State<PostSearchScreen> {
final stopwatch = Stopwatch()..start(); final stopwatch = Stopwatch()..start();
final sn = context.read<SnNetworkProvider>(); final pt = context.read<SnPostContentProvider>();
final resp = await sn.client.get('/cgi/co/posts/search', queryParameters: { final result = await pt.searchPosts(
'take': 10, _searchTerm,
'offset': _posts.length, take: 10,
'probe': _searchTerm, offset: _posts.length,
}); );
final List<SnPost> out = final List<SnPost> out = result.$1;
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []);
Set<String> rids = {};
for (var i = 0; i < out.length; i++) {
rids.addAll(out[i].body['attachments']?.cast<String>() ?? []);
}
if (!mounted) return; if (!mounted) return;
final attach = context.read<SnAttachmentProvider>();
final attachments = await attach.getMultiple(rids.toList());
for (var i = 0; i < out.length; i++) {
out[i] = out[i].copyWith(
preload: SnPostPreload(
attachments: attachments
.where(
(ele) =>
out[i].body['attachments']?.contains(ele?.rid) ?? false,
)
.toList(),
),
);
}
stopwatch.stop(); stopwatch.stop();
_lastTook = stopwatch.elapsed; _lastTook = stopwatch.elapsed;
_postCount = resp.data['count']; _postCount = result.$2;
_posts.addAll(out); _posts.addAll(out);
if (mounted) setState(() => _isBusy = false); if (mounted) setState(() => _isBusy = false);

View File

@ -53,6 +53,7 @@ class SnPost with _$SnPost {
@freezed @freezed
class SnPostPreload with _$SnPostPreload { class SnPostPreload with _$SnPostPreload {
const factory SnPostPreload({ const factory SnPostPreload({
required SnAttachment? thumbnail,
required List<SnAttachment?>? attachments, required List<SnAttachment?>? attachments,
}) = _SnPostPreload; }) = _SnPostPreload;

View File

@ -953,6 +953,7 @@ SnPostPreload _$SnPostPreloadFromJson(Map<String, dynamic> json) {
/// @nodoc /// @nodoc
mixin _$SnPostPreload { mixin _$SnPostPreload {
SnAttachment? get thumbnail => throw _privateConstructorUsedError;
List<SnAttachment?>? get attachments => throw _privateConstructorUsedError; List<SnAttachment?>? get attachments => throw _privateConstructorUsedError;
/// Serializes this SnPostPreload to a JSON map. /// Serializes this SnPostPreload to a JSON map.
@ -971,7 +972,9 @@ abstract class $SnPostPreloadCopyWith<$Res> {
SnPostPreload value, $Res Function(SnPostPreload) then) = SnPostPreload value, $Res Function(SnPostPreload) then) =
_$SnPostPreloadCopyWithImpl<$Res, SnPostPreload>; _$SnPostPreloadCopyWithImpl<$Res, SnPostPreload>;
@useResult @useResult
$Res call({List<SnAttachment?>? attachments}); $Res call({SnAttachment? thumbnail, List<SnAttachment?>? attachments});
$SnAttachmentCopyWith<$Res>? get thumbnail;
} }
/// @nodoc /// @nodoc
@ -989,15 +992,34 @@ class _$SnPostPreloadCopyWithImpl<$Res, $Val extends SnPostPreload>
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? thumbnail = freezed,
Object? attachments = freezed, Object? attachments = freezed,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
thumbnail: freezed == thumbnail
? _value.thumbnail
: thumbnail // ignore: cast_nullable_to_non_nullable
as SnAttachment?,
attachments: freezed == attachments attachments: freezed == attachments
? _value.attachments ? _value.attachments
: attachments // ignore: cast_nullable_to_non_nullable : attachments // ignore: cast_nullable_to_non_nullable
as List<SnAttachment?>?, as List<SnAttachment?>?,
) as $Val); ) as $Val);
} }
/// Create a copy of SnPostPreload
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAttachmentCopyWith<$Res>? get thumbnail {
if (_value.thumbnail == null) {
return null;
}
return $SnAttachmentCopyWith<$Res>(_value.thumbnail!, (value) {
return _then(_value.copyWith(thumbnail: value) as $Val);
});
}
} }
/// @nodoc /// @nodoc
@ -1008,7 +1030,10 @@ abstract class _$$SnPostPreloadImplCopyWith<$Res>
__$$SnPostPreloadImplCopyWithImpl<$Res>; __$$SnPostPreloadImplCopyWithImpl<$Res>;
@override @override
@useResult @useResult
$Res call({List<SnAttachment?>? attachments}); $Res call({SnAttachment? thumbnail, List<SnAttachment?>? attachments});
@override
$SnAttachmentCopyWith<$Res>? get thumbnail;
} }
/// @nodoc /// @nodoc
@ -1024,9 +1049,14 @@ class __$$SnPostPreloadImplCopyWithImpl<$Res>
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? thumbnail = freezed,
Object? attachments = freezed, Object? attachments = freezed,
}) { }) {
return _then(_$SnPostPreloadImpl( return _then(_$SnPostPreloadImpl(
thumbnail: freezed == thumbnail
? _value.thumbnail
: thumbnail // ignore: cast_nullable_to_non_nullable
as SnAttachment?,
attachments: freezed == attachments attachments: freezed == attachments
? _value._attachments ? _value._attachments
: attachments // ignore: cast_nullable_to_non_nullable : attachments // ignore: cast_nullable_to_non_nullable
@ -1038,12 +1068,16 @@ class __$$SnPostPreloadImplCopyWithImpl<$Res>
/// @nodoc /// @nodoc
@JsonSerializable() @JsonSerializable()
class _$SnPostPreloadImpl implements _SnPostPreload { class _$SnPostPreloadImpl implements _SnPostPreload {
const _$SnPostPreloadImpl({required final List<SnAttachment?>? attachments}) const _$SnPostPreloadImpl(
{required this.thumbnail,
required final List<SnAttachment?>? attachments})
: _attachments = attachments; : _attachments = attachments;
factory _$SnPostPreloadImpl.fromJson(Map<String, dynamic> json) => factory _$SnPostPreloadImpl.fromJson(Map<String, dynamic> json) =>
_$$SnPostPreloadImplFromJson(json); _$$SnPostPreloadImplFromJson(json);
@override
final SnAttachment? thumbnail;
final List<SnAttachment?>? _attachments; final List<SnAttachment?>? _attachments;
@override @override
List<SnAttachment?>? get attachments { List<SnAttachment?>? get attachments {
@ -1056,7 +1090,7 @@ class _$SnPostPreloadImpl implements _SnPostPreload {
@override @override
String toString() { String toString() {
return 'SnPostPreload(attachments: $attachments)'; return 'SnPostPreload(thumbnail: $thumbnail, attachments: $attachments)';
} }
@override @override
@ -1064,14 +1098,16 @@ class _$SnPostPreloadImpl implements _SnPostPreload {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _$SnPostPreloadImpl && other is _$SnPostPreloadImpl &&
(identical(other.thumbnail, thumbnail) ||
other.thumbnail == thumbnail) &&
const DeepCollectionEquality() const DeepCollectionEquality()
.equals(other._attachments, _attachments)); .equals(other._attachments, _attachments));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash( int get hashCode => Object.hash(runtimeType, thumbnail,
runtimeType, const DeepCollectionEquality().hash(_attachments)); const DeepCollectionEquality().hash(_attachments));
/// Create a copy of SnPostPreload /// Create a copy of SnPostPreload
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -1091,11 +1127,14 @@ class _$SnPostPreloadImpl implements _SnPostPreload {
abstract class _SnPostPreload implements SnPostPreload { abstract class _SnPostPreload implements SnPostPreload {
const factory _SnPostPreload( const factory _SnPostPreload(
{required final List<SnAttachment?>? attachments}) = _$SnPostPreloadImpl; {required final SnAttachment? thumbnail,
required final List<SnAttachment?>? attachments}) = _$SnPostPreloadImpl;
factory _SnPostPreload.fromJson(Map<String, dynamic> json) = factory _SnPostPreload.fromJson(Map<String, dynamic> json) =
_$SnPostPreloadImpl.fromJson; _$SnPostPreloadImpl.fromJson;
@override
SnAttachment? get thumbnail;
@override @override
List<SnAttachment?>? get attachments; List<SnAttachment?>? get attachments;

View File

@ -102,6 +102,9 @@ Map<String, dynamic> _$$SnPostImplToJson(_$SnPostImpl instance) =>
_$SnPostPreloadImpl _$$SnPostPreloadImplFromJson(Map<String, dynamic> json) => _$SnPostPreloadImpl _$$SnPostPreloadImplFromJson(Map<String, dynamic> json) =>
_$SnPostPreloadImpl( _$SnPostPreloadImpl(
thumbnail: json['thumbnail'] == null
? null
: SnAttachment.fromJson(json['thumbnail'] as Map<String, dynamic>),
attachments: (json['attachments'] as List<dynamic>?) attachments: (json['attachments'] as List<dynamic>?)
?.map((e) => e == null ?.map((e) => e == null
? null ? null
@ -111,6 +114,7 @@ _$SnPostPreloadImpl _$$SnPostPreloadImplFromJson(Map<String, dynamic> json) =>
Map<String, dynamic> _$$SnPostPreloadImplToJson(_$SnPostPreloadImpl instance) => Map<String, dynamic> _$$SnPostPreloadImplToJson(_$SnPostPreloadImpl instance) =>
<String, dynamic>{ <String, dynamic>{
'thumbnail': instance.thumbnail?.toJson(),
'attachments': instance.attachments?.map((e) => e?.toJson()).toList(), 'attachments': instance.attachments?.map((e) => e?.toJson()).toList(),
}; };

View File

@ -5,8 +5,7 @@ import 'package:go_router/go_router.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/sn_attachment.dart'; import 'package:surface/providers/post.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/userinfo.dart'; import 'package:surface/providers/userinfo.dart';
import 'package:surface/types/post.dart'; import 'package:surface/types/post.dart';
import 'package:surface/widgets/post/post_item.dart'; import 'package:surface/widgets/post/post_item.dart';
@ -37,39 +36,13 @@ class PostCommentSliverListState extends State<PostCommentSliverList> {
setState(() => _isBusy = true); setState(() => _isBusy = true);
final sn = context.read<SnNetworkProvider>(); final pt = context.read<SnPostContentProvider>();
final resp = await sn.client.get( final result = await pt.listPostReplies(widget.parentPostId);
'/cgi/co/posts/${widget.parentPostId}/replies', final List<SnPost> out = result.$1;
queryParameters: {
'take': 10,
'offset': _posts.length,
},
);
final List<SnPost> out =
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []);
Set<String> rids = {};
for (var i = 0; i < out.length; i++) {
rids.addAll(out[i].body['attachments']?.cast<String>() ?? []);
}
if (!mounted) return; if (!mounted) return;
final attach = context.read<SnAttachmentProvider>();
final attachments = await attach.getMultiple(rids.toList());
for (var i = 0; i < out.length; i++) {
out[i] = out[i].copyWith(
preload: SnPostPreload(
attachments: attachments
.where(
(ele) =>
out[i].body['attachments']?.contains(ele?.rid) ?? false,
)
.toList(),
),
);
}
_postCount = resp.data['count']; _postCount = result.$2;
_posts.addAll(out); _posts.addAll(out);
if (mounted) setState(() => _isBusy = false); if (mounted) setState(() => _isBusy = false);