From 495620892831910d53bd0c79f9b3262274a7060d Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Tue, 8 Nov 2022 20:23:56 -0600 Subject: [PATCH] Add TimelineEntry with supporting classes --- lib/globals.dart | 6 ++ lib/models/image_entry.dart | 19 +++++ lib/models/link_data.dart | 19 +++++ lib/models/location_data.dart | 83 +++++++++++++++++++ lib/models/media_attachment.dart | 104 +++++++++++++++++++++++ lib/models/timeline_entry.dart | 138 +++++++++++++++++++++++++++++++ lib/utils/dateutils.dart | 40 +++++++++ lib/utils/json_printer.dart | 11 +++ 8 files changed, 420 insertions(+) create mode 100644 lib/globals.dart create mode 100644 lib/models/image_entry.dart create mode 100644 lib/models/link_data.dart create mode 100644 lib/models/location_data.dart create mode 100644 lib/models/media_attachment.dart create mode 100644 lib/models/timeline_entry.dart create mode 100644 lib/utils/dateutils.dart create mode 100644 lib/utils/json_printer.dart diff --git a/lib/globals.dart b/lib/globals.dart new file mode 100644 index 0000000..8d54766 --- /dev/null +++ b/lib/globals.dart @@ -0,0 +1,6 @@ +import 'package:get_it/get_it.dart'; +import 'package:uuid/uuid.dart'; + +final getIt = GetIt.instance; + +String randomId() => const Uuid().v4().toString(); diff --git a/lib/models/image_entry.dart b/lib/models/image_entry.dart new file mode 100644 index 0000000..0772393 --- /dev/null +++ b/lib/models/image_entry.dart @@ -0,0 +1,19 @@ +class ImageEntry { + final String postId; + final String localFilename; + final String url; + + ImageEntry( + {required this.postId, required this.localFilename, required this.url}); + + ImageEntry.fromJson(Map json) + : postId = json['postId'] ?? '', + localFilename = json['localFilename'] ?? '', + url = json['url'] ?? ''; + + Map toJson() => { + 'postId': postId, + 'localFilename': localFilename, + 'url': url, + }; +} diff --git a/lib/models/link_data.dart b/lib/models/link_data.dart new file mode 100644 index 0000000..0436ba6 --- /dev/null +++ b/lib/models/link_data.dart @@ -0,0 +1,19 @@ +class LinkData { + final String url; + final String title; + final String description; + final String imageUrl; + + LinkData( + {required this.url, + required this.title, + required this.description, + required this.imageUrl}); + + factory LinkData.fromMastodonJson(Map json) => LinkData( + url: json['url'] ?? 'Unknown', + title: json['title'] ?? 'Unknown', + description: json['description'] ?? '', + imageUrl: json['image'] ?? '', + ); +} diff --git a/lib/models/location_data.dart b/lib/models/location_data.dart new file mode 100644 index 0000000..28c5a53 --- /dev/null +++ b/lib/models/location_data.dart @@ -0,0 +1,83 @@ +import 'dart:math'; + +import '../globals.dart'; + +class LocationData { + final String name; + + final double latitude; + + final double longitude; + + final double altitude; + + final bool hasPosition; + + final String address; + + final String url; + + const LocationData( + {this.name = '', + this.latitude = 0.0, + this.longitude = 0.0, + this.altitude = 0.0, + this.hasPosition = false, + this.address = '', + this.url = ''}); + + LocationData.randomBuilt() + : name = 'Location name ${randomId()}', + latitude = Random().nextDouble(), + longitude = Random().nextDouble(), + altitude = Random().nextDouble(), + hasPosition = true, + address = 'Address ${randomId()}', + url = 'http://localhost/${randomId()}'; + + @override + String toString() { + return 'LocationData{name: $name, latitude: $latitude, longitude: $longitude, altitude: $altitude, hasPosition: $hasPosition, address: $address, url: $url}'; + } + + String toHumanString() { + if (!hasPosition) { + return ''; + } + + return [ + if (name.isNotEmpty) 'Name: $name', + if (address.isNotEmpty) 'Address: $address', + 'Latitude: $latitude', + 'Longitude: $longitude', + ].join('\n'); + } + + bool hasData() => + name.isNotEmpty || address.isNotEmpty || url.isNotEmpty || hasPosition; + + static LocationData fromJson(Map json) { + final name = json['name'] ?? ''; + final address = json['address'] ?? ''; + final url = json['url'] ?? ''; + var latitude = 0.0; + var longitude = 0.0; + var altitude = 0.0; + var hasPosition = json.containsKey('coordinate'); + if (hasPosition) { + final position = json['coordinate']; + latitude = position['latitude'] ?? 0.0; + longitude = position['longitude'] ?? 0.0; + altitude = position['altitude'] ?? 0.0; + } + + return LocationData( + name: name, + address: address, + url: url, + hasPosition: hasPosition, + latitude: latitude, + longitude: longitude, + altitude: altitude); + } +} diff --git a/lib/models/media_attachment.dart b/lib/models/media_attachment.dart new file mode 100644 index 0000000..0d32653 --- /dev/null +++ b/lib/models/media_attachment.dart @@ -0,0 +1,104 @@ +import '../globals.dart'; +import 'package:path/path.dart' as p; + +import 'attachment_media_type_enum.dart'; + +class MediaAttachment { + static final _graphicsExtensions = ['jpg', 'png', 'gif', 'tif']; + static final _movieExtensions = ['avi', 'mp4', 'mpg', 'wmv']; + + final Uri uri; + + final int creationTimestamp; + + final Map metadata; + + final AttachmentMediaType explicitType; + + final Uri thumbnailUri; + + final String title; + + final String description; + + MediaAttachment( + {required this.uri, + required this.creationTimestamp, + required this.metadata, + required this.thumbnailUri, + required this.title, + required this.explicitType, + required this.description}); + + MediaAttachment.randomBuilt() + : uri = Uri.parse('http://localhost/${randomId()}'), + creationTimestamp = DateTime.now().millisecondsSinceEpoch, + title = 'Random title ${randomId()}', + thumbnailUri = Uri.parse('${randomId()}.jpg'), + description = 'Random description ${randomId()}', + explicitType = AttachmentMediaType.image, + metadata = {'value1': randomId(), 'value2': randomId()}; + + MediaAttachment.fromUriOnly(this.uri) + : creationTimestamp = 0, + thumbnailUri = Uri.file(''), + title = '', + explicitType = mediaTypeFromString(uri.path), + description = '', + metadata = {}; + + MediaAttachment.fromUriAndTime(this.uri, this.creationTimestamp) + : thumbnailUri = Uri.file(''), + title = '', + explicitType = mediaTypeFromString(uri.path), + description = '', + metadata = {}; + + MediaAttachment.blank() + : uri = Uri(), + creationTimestamp = 0, + thumbnailUri = Uri.file(''), + explicitType = AttachmentMediaType.unknown, + title = '', + description = '', + metadata = {}; + + factory MediaAttachment.fromMastodonJson(Map json) => + MediaAttachment( + uri: Uri.parse(json['url'] ?? 'http://localhost'), + creationTimestamp: 0, + metadata: {}, + thumbnailUri: Uri.parse(json['preview_url'] ?? 'http://localhost'), + title: '', + explicitType: AttachmentMediaType.parse(json['type']), + description: json['description'] ?? ''); + + @override + String toString() { + return 'FriendicaMediaAttachment{uri: $uri, creationTimestamp: $creationTimestamp, type: $explicitType, metadata: $metadata, title: $title, description: $description}'; + } + + Map toJson() => { + 'uri': uri.toString(), + 'creationTimestamp': creationTimestamp, + 'metadata': metadata, + 'type': explicitType, + 'thumbnailUri': thumbnailUri.toString(), + 'title': title, + 'description': description, + }; + + static AttachmentMediaType mediaTypeFromString(String path) { + final extension = p.extension(path); + + if (_graphicsExtensions.contains(extension)) { + return AttachmentMediaType.image; + } + + if (_movieExtensions.contains(extension)) { + return AttachmentMediaType.video; + } + + return AttachmentMediaType.unknown; + } +} diff --git a/lib/models/timeline_entry.dart b/lib/models/timeline_entry.dart new file mode 100644 index 0000000..3bc68b9 --- /dev/null +++ b/lib/models/timeline_entry.dart @@ -0,0 +1,138 @@ +import '../globals.dart'; +import 'connection.dart'; +import 'engagement_summary.dart'; +import 'link_data.dart'; +import 'location_data.dart'; +import 'media_attachment.dart'; + +class TimelineEntry { + final String id; + + final String parentId; + + final String parentAuthor; + + final String parentAuthorId; + + final int creationTimestamp; + + final int backdatedTimestamp; + + final int modificationTimestamp; + + final String body; + + final String title; + + final bool isReshare; + + final String author; + + final String authorId; + + final String externalLink; + + final LocationData locationData; + + final List links; + + final List likes; + + final List dislikes; + + final List mediaAttachments; + + final EngagementSummary engagementSummary; + + TimelineEntry({ + this.id = '', + this.parentId = '', + this.creationTimestamp = 0, + this.backdatedTimestamp = 0, + this.modificationTimestamp = 0, + this.isReshare = false, + this.body = '', + this.title = '', + this.author = '', + this.authorId = '', + this.parentAuthor = '', + this.parentAuthorId = '', + this.externalLink = '', + this.locationData = const LocationData(), + this.links = const [], + this.likes = const [], + this.dislikes = const [], + this.mediaAttachments = const [], + this.engagementSummary = const EngagementSummary(), + }); + + TimelineEntry.randomBuilt() + : creationTimestamp = DateTime.now().millisecondsSinceEpoch, + backdatedTimestamp = DateTime.now().millisecondsSinceEpoch, + modificationTimestamp = DateTime.now().millisecondsSinceEpoch, + id = randomId(), + isReshare = false, + parentId = randomId(), + externalLink = 'Random external link ${randomId()}', + body = 'Random post text ${randomId()}', + title = 'Random title ${randomId()}', + author = 'Random author ${randomId()}', + authorId = 'Random authorId ${randomId()}', + parentAuthor = 'Random parent author ${randomId()}', + parentAuthorId = 'Random parent author id ${randomId()}', + locationData = LocationData.randomBuilt(), + links = [], + likes = [], + dislikes = [], + mediaAttachments = [], + engagementSummary = const EngagementSummary(); + + TimelineEntry copy( + {int? creationTimestamp, + int? backdatedTimestamp, + int? modificationTimestamp, + bool? isReshare, + String? id, + String? parentId, + String? externalLink, + String? body, + String? title, + String? author, + String? authorId, + String? parentAuthor, + String? parentAuthorId, + LocationData? locationData, + List? links, + List? likes, + List? dislikes, + List? mediaAttachments, + EngagementSummary? engagementSummary}) { + return TimelineEntry( + creationTimestamp: creationTimestamp ?? this.creationTimestamp, + backdatedTimestamp: backdatedTimestamp ?? this.backdatedTimestamp, + modificationTimestamp: + modificationTimestamp ?? this.modificationTimestamp, + id: id ?? this.id, + isReshare: isReshare ?? this.isReshare, + parentId: parentId ?? this.parentId, + externalLink: externalLink ?? this.externalLink, + body: body ?? this.body, + title: title ?? this.title, + author: author ?? this.author, + authorId: authorId ?? this.authorId, + parentAuthor: parentAuthor ?? this.parentAuthor, + parentAuthorId: parentAuthorId ?? this.parentAuthorId, + locationData: locationData ?? this.locationData, + links: links ?? this.links, + likes: likes ?? this.likes, + dislikes: dislikes ?? this.dislikes, + mediaAttachments: mediaAttachments ?? this.mediaAttachments, + engagementSummary: engagementSummary ?? this.engagementSummary, + ); + } + + @override + String toString() { + return 'TimelineEntry{id: $id, isReshare: $isReshare, parentId: $parentId, creationTimestamp: $creationTimestamp, modificationTimestamp: $modificationTimestamp, backdatedTimeStamp: $backdatedTimestamp, post: $body, title: $title, author: $author, parentAuthor: $parentAuthor externalLink:$externalLink}'; + } +} diff --git a/lib/utils/dateutils.dart b/lib/utils/dateutils.dart new file mode 100644 index 0000000..11e62e7 --- /dev/null +++ b/lib/utils/dateutils.dart @@ -0,0 +1,40 @@ +import 'package:result_monad/result_monad.dart'; +import 'package:time_machine/time_machine_text_patterns.dart'; + +import '../models/exec_error.dart'; + +class OffsetDateTimeUtils { + static final _offsetTimeParser = + OffsetDateTimePattern.createWithInvariantCulture( + 'ddd MMM dd HH:mm:ss o<+HHmm> yyyy'); + + static Result epochSecTimeFromFriendicaString( + String dateString) { + final offsetDateTime = _offsetTimeParser.parse(dateString); + if (!offsetDateTime.success) { + return Result.error(ExecError( + type: ErrorType.parsingError, + message: offsetDateTime.error.toString())); + } + + return Result.ok(offsetDateTime.value.localDateTime + .toDateTimeLocal() + .millisecondsSinceEpoch ~/ + 1000); + } + + static Result epochSecTimeFromTimeZoneString( + String dateString) { + final offsetDateTime = OffsetDateTimePattern.extendedIso.parse(dateString); + if (!offsetDateTime.success) { + return Result.error(ExecError( + type: ErrorType.parsingError, + message: offsetDateTime.error.toString())); + } + + return Result.ok(offsetDateTime.value.localDateTime + .toDateTimeLocal() + .millisecondsSinceEpoch ~/ + 1000); + } +} diff --git a/lib/utils/json_printer.dart b/lib/utils/json_printer.dart new file mode 100644 index 0000000..c5a4661 --- /dev/null +++ b/lib/utils/json_printer.dart @@ -0,0 +1,11 @@ +import 'dart:convert'; + +class PrettyJsonEncoder { + late JsonEncoder encoder; + + PrettyJsonEncoder() { + encoder = JsonEncoder.withIndent('\t'); + } + + String convert(Object json) => encoder.convert(json); +}