import 'package:logging/logging.dart'; import '../../globals.dart'; import '../../models/delivery_data.dart'; import '../../models/engagement_summary.dart'; import '../../models/link_data.dart'; import '../../models/location_data.dart'; import '../../models/timeline_entry.dart'; import '../../models/timeline_network_info.dart'; import '../../models/visibility.dart'; import '../../services/auth_service.dart'; import '../../services/connections_manager.dart'; import '../../services/hashtag_service.dart'; import '../../services/reshared_via_service.dart'; import '../../utils/active_profile_selector.dart'; import '../../utils/dateutils.dart'; import '../../utils/html_to_edit_text_helper.dart'; import 'connection_mastodon_extensions.dart'; import 'hashtag_mastodon_extensions.dart'; import 'link_preview_mastodon_extensions.dart'; import 'media_attachment_mastodon_extension.dart'; import 'timeline_network_info_mastodon_extensions.dart'; final _logger = Logger('TimelineEntryMastodonExtensions'); extension TimelineEntryMastodonExtensions on TimelineEntry { static TimelineEntry fromJson(Map json) { final activeProfile = getIt().currentProfile; final resharedViaService = getIt>() .getForProfile(activeProfile) .fold( onSuccess: (s) => s, onError: (error) { _logger.severe('Error getting reshared via service: $error'); return null; }); final networkInfo = TimelineNetworkInfoMastodonExtensions.fromJson(json); final connectionManager = getIt>() .getForProfile(activeProfile) .fold( onSuccess: (m) => m, onError: (error) { _logger.severe('Error getting connection manager: $error'); return null; }); final id = json['id'] ?? ''; final parentId = json['in_reply_to_id'] ?? ''; final parentAuthor = json['in_reply_to_account_id'] ?? ''; final parentAuthorId = json['in_reply_to_account_id'] ?? ''; final author = json['account']['display_name']; final authorId = json['account']['id']; final resharePostData = json['reblog']; if (resharePostData != null) { final rebloggedUser = ConnectionMastodonExtensions.fromJson(resharePostData['account']); connectionManager?.upsertConnection(rebloggedUser); final resharedPost = fromJson(resharePostData); resharedViaService?.upsertResharedVia( postId: resharedPost.id, resharerId: authorId, ); return resharedPost; } final int timestamp = json.containsKey('created_at') ? OffsetDateTimeUtils.epochSecTimeFromTimeZoneString(json['created_at']) .fold( onSuccess: (value) => value, onError: (error) { _logger.severe("Couldn't read date time string: $error"); return 0; }) : 0; final youReshared = json['reblogged'] ?? false; late final Visibility visibility; final visibilityString = json['visibility']; if (visibilityString == 'public') { visibility = Visibility.public(); } else if (visibilityString == 'unlisted') { visibility = Visibility.unlisted(); } else if (visibilityString == 'private') { final allowedUserIds = json['friendica']?['visibility']?['allow_cid'] as List? ?? []; final excludedUserIds = json['friendica']?['visibility']?['deny_cid'] as List? ?? []; final allowedCircleIds = json['friendica']?['visibility']?['allow_gid'] as List? ?? []; final excludedCircleIds = json['friendica']?['visibility']?['deny_gid'] as List? ?? []; visibility = Visibility( type: VisibilityType.private, allowedUserIds: allowedUserIds.map((e) => e.toString()).toList(), excludedUserIds: excludedUserIds.map((e) => e.toString()).toList(), allowedCircleIds: allowedCircleIds.map((e) => e.toString()).toList(), excludedCircleIds: excludedCircleIds.map((e) => e.toString()).toList(), ); } else { visibility = Visibility.private(); } const title = ''; final spoilerText = json['spoiler_text'] ?? ''; final externalLink = switch (networkInfo.network) { KnownNetworks.bluesky || KnownNetworks.threads => json['url'] ?? '', _ => json['uri'] ?? '', }; const actualLocationData = LocationData(); final modificationTimestamp = timestamp; final backdatedTimestamp = timestamp; final isFavorited = json['favourited'] ?? false; final linkData = json['card'] == null ? [] : [LinkData.fromMastodonJson(json['card'])]; final mediaAttachments = (json['media_attachments'] as List? ?? []) .map((json) => MediaAttachmentMastodonExtension.fromJson(json, visibility)) .toList(); final favoritesCount = json['favourites_count'] ?? 0; final repliesCount = json['replies_count'] ?? 0; final rebloggedCount = json['reblogs_count'] ?? 0; final engagementSummary = EngagementSummary( favoritesCount: favoritesCount, rebloggedCount: rebloggedCount, repliesCount: repliesCount, ); final linkPreviewData = LinkPreviewMastodonExtensions.fromJson( json['card'], ); final List? tagsJson = json['tags']; final tags = []; if (tagsJson?.isNotEmpty ?? false) { final tagManager = getIt(); for (final tagJson in tagsJson!) { final tag = HashtagMastodonExtensions.fromJson(tagJson); tags.add(tag.tag); tagManager.add(tag); } } final rawBody = json['content'] ?? ''; final body = htmlWithTagLinkSwap(rawBody, tags); final connection = ConnectionMastodonExtensions.fromJson(json['account']); connectionManager?.upsertConnection(connection); final ddj = json['friendica']?['delivery_data']; final deliveryData = ddj == null ? DeliveryData.empty : DeliveryData( total: ddj['delivery_queue_count'] ?? 0, done: ddj['delivery_queue_done'] ?? 0, failed: ddj['delivery_queue_failed'] ?? 0, ); return TimelineEntry( creationTimestamp: timestamp, modificationTimestamp: modificationTimestamp, backdatedTimestamp: backdatedTimestamp, locationData: actualLocationData, spoilerText: spoilerText, body: body, youReshared: youReshared, visibility: visibility, id: id, parentId: parentId, parentAuthorId: parentAuthorId, isFavorited: isFavorited, externalLink: externalLink, author: author, authorId: authorId, parentAuthor: parentAuthor, title: title, links: linkData, tags: tags, mediaAttachments: mediaAttachments, engagementSummary: engagementSummary, networkInfo: networkInfo, linkPreviewData: linkPreviewData, deliveryData: deliveryData, ); } }