@@ -164,98 +164,61 @@ class TweetsApiServiceImpl implements TweetsApiService {
164164
165165 for (final raw in postsJson) {
166166 if (raw is ! Map ) continue ;
167- final json = raw as Map ;
167+ final json = raw as Map <String , dynamic >;
168+
169+ // Prefer the new transformed post shape from backend (with
170+ // originalPostData for replies / reposts / quotes). This ensures
171+ // replies and quotes carry their parent tweet tree via
172+ // TweetModel.originalTweet.
173+ try {
174+ if (json.containsKey ('postId' ) && json.containsKey ('date' )) {
175+ final tweet = TweetModel .fromJsonPosts (json);
176+
177+ final tweetId = tweet.id;
178+ final isLikedByMe = json['isLikedByMe' ] ?? false ;
179+ final isRepostedByMe = json['isRepostedByMe' ] ?? false ;
180+ final isRepost = json['isRepost' ] ?? false ;
181+ final isQuote = json['isQuote' ] ?? false ;
182+
183+ final existingFlags = _interactionFlags[tweetId] ?? < String , bool > {};
184+ _interactionFlags[tweetId] = {
185+ ...existingFlags,
186+ 'isLikedByMe' : isLikedByMe,
187+ 'isRepostedByMe' : isRepostedByMe,
188+ 'isRepost' : isRepost,
189+ 'isQuote' : isQuote,
190+ };
191+
192+ tweets.add (tweet);
193+ continue ;
194+ }
195+ } catch (e) {
196+ print ('⚠️ Failed to parse transformed feed post via fromJsonPosts, falling back: $e ' );
197+ }
168198
199+ // Fallback: legacy mapping for older backend shapes.
169200 final tweetId =
170201 (json['postId' ] ??
171202 json['id' ] ??
172203 DateTime .now ().millisecondsSinceEpoch)
173204 .toString ();
174205
175- // Basic user info
176206 final username = json['username' ]? .toString ();
177207 final authorName = json['name' ]? .toString ();
178208 final authorProfileImage = json['avatar' ]? .toString ();
179209
180210 int parseInt (dynamic v) =>
181211 v == null ? 0 : (v is int ? v : int .tryParse (v.toString ()) ?? 0 );
182212
183- // Counts from feed
184213 final likes = parseInt (json['likesCount' ]);
185214 final reposts = parseInt (json['retweetsCount' ]);
186215 final comments = parseInt (json['commentsCount' ]);
187216
188- // Interaction and repost metadata flags
189217 final isLikedByMe = json['isLikedByMe' ] ?? false ;
190218 final isRepostedByMe = json['isRepostedByMe' ] ?? false ;
191219 final isRepost = json['isRepost' ] ?? false ;
192220 final isQuote = json['isQuote' ] ?? false ;
193221
194- // Original post data for reposts/quotes (to show nested parent tweet)
195- Map <String , dynamic >? originalTweetJson;
196- final original = json['originalPostData' ];
197- if (original is Map ) {
198- final o = original;
199-
200- final originalId =
201- (o['postId' ] ?? o['id' ] ?? DateTime .now ().millisecondsSinceEpoch)
202- .toString ();
203-
204- final originalUserId = (o['userId' ] ?? o['user_id' ] ?? '0' )
205- .toString ();
206-
207- final rawDate = o['date' ] ?? o['createdAt' ] ?? o['created_at' ];
208- String originalDateString;
209- if (rawDate is String ) {
210- originalDateString = rawDate;
211- } else if (rawDate is DateTime ) {
212- originalDateString = rawDate.toIso8601String ();
213- } else {
214- originalDateString = DateTime .now ().toIso8601String ();
215- }
216-
217- final originalLikes = parseInt (o['likesCount' ]);
218- final originalReposts = parseInt (o['retweetsCount' ]);
219- final originalComments = parseInt (o['commentsCount' ]);
220-
221- final originalImageUrls = < String > [];
222- final originalVideoUrls = < String > [];
223- if (o['media' ] is List ) {
224- for (final m in (o['media' ] as List )) {
225- if (m is ! Map ) continue ;
226- final url = m['url' ]? .toString ();
227- final type = m['type' ]? .toString ();
228- if (url == null || url.isEmpty) continue ;
229- if (type == 'VIDEO' ) {
230- originalVideoUrls.add (url);
231- } else {
232- originalImageUrls.add (url);
233- }
234- }
235- }
236-
237- originalTweetJson = < String , dynamic > {
238- 'id' : originalId,
239- 'userId' : originalUserId,
240- 'body' : (o['text' ] ?? o['content' ] ?? '' ).toString (),
241- 'date' : originalDateString,
242- 'username' : o['username' ]? .toString (),
243- 'authorName' : o['name' ]? .toString (),
244- 'authorProfileImage' : o['avatar' ]? .toString (),
245- 'likes' : originalLikes,
246- 'repost' : originalReposts,
247- 'comments' : originalComments,
248- 'views' : 0 ,
249- 'qoutes' : 0 ,
250- 'bookmarks' : 0 ,
251- 'mediaImages' : originalImageUrls,
252- 'mediaVideos' : originalVideoUrls,
253- 'isRepost' : false ,
254- 'isQuote' : false ,
255- };
256- }
257-
258- // Media
259222 final imageUrls = < String > [];
260223 final videoUrls = < String > [];
261224 if (json['media' ] is List ) {
@@ -296,13 +259,8 @@ class TweetsApiServiceImpl implements TweetsApiService {
296259 'isQuote' : isQuote,
297260 };
298261
299- if (originalTweetJson != null ) {
300- mappedJson['originalTweet' ] = originalTweetJson;
301- }
302-
303262 final tweet = TweetModel .fromJson (mappedJson);
304263
305- // Store interaction flags and repost metadata for this tweet
306264 final existingFlags = _interactionFlags[tweetId] ?? < String , bool > {};
307265 _interactionFlags[tweetId] = {
308266 ...existingFlags,
@@ -406,7 +364,40 @@ class TweetsApiServiceImpl implements TweetsApiService {
406364 final dynamic json = (data is List && data.isNotEmpty)
407365 ? data.first
408366 : data;
409- // Map backend fields to frontend model (supports both transformed and legacy shapes)
367+ // If backend returns the new transformed post shape (with top-level
368+ // postId/date/username/name/avatar and optional originalPostData), use
369+ // the dedicated mapper so quotes and replies include their parent tree.
370+ if (json is Map <String , dynamic > &&
371+ json.containsKey ('postId' ) &&
372+ json.containsKey ('date' ) &&
373+ (json.containsKey ('username' ) ||
374+ json.containsKey ('name' ) ||
375+ json.containsKey ('avatar' ))) {
376+ try {
377+ final tweet = TweetModel .fromJsonPosts (json);
378+ final tweetId = tweet.id;
379+
380+ final isLikedByMe = json['isLikedByMe' ] ?? false ;
381+ final isRepostedByMe = json['isRepostedByMe' ] ?? false ;
382+ final isRepost = json['isRepost' ] ?? false ;
383+ final isQuote = json['isQuote' ] ?? false ;
384+
385+ final existingFlags = _interactionFlags[tweetId] ?? < String , bool > {};
386+ _interactionFlags[tweetId] = {
387+ ...existingFlags,
388+ 'isLikedByMe' : isLikedByMe,
389+ 'isRepostedByMe' : isRepostedByMe,
390+ 'isRepost' : isRepost,
391+ 'isQuote' : isQuote,
392+ };
393+
394+ return tweet;
395+ } catch (e) {
396+ print ('⚠️ Failed to parse getTweetById via fromJsonPosts, falling back: $e ' );
397+ }
398+ }
399+
400+ // Map backend fields to frontend model (supports legacy and mixed shapes)
410401 final tweetId =
411402 (json['postId' ] ??
412403 json['id' ] ??
0 commit comments