mirror of
https://gitlab.com/mysocialportal/relatica
synced 2024-10-18 13:33:32 +00:00
Add TimelineEntry with supporting classes
This commit is contained in:
parent
1f30e70550
commit
4956208928
8 changed files with 420 additions and 0 deletions
6
lib/globals.dart
Normal file
6
lib/globals.dart
Normal file
|
@ -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();
|
19
lib/models/image_entry.dart
Normal file
19
lib/models/image_entry.dart
Normal file
|
@ -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<String, dynamic> json)
|
||||||
|
: postId = json['postId'] ?? '',
|
||||||
|
localFilename = json['localFilename'] ?? '',
|
||||||
|
url = json['url'] ?? '';
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'postId': postId,
|
||||||
|
'localFilename': localFilename,
|
||||||
|
'url': url,
|
||||||
|
};
|
||||||
|
}
|
19
lib/models/link_data.dart
Normal file
19
lib/models/link_data.dart
Normal file
|
@ -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<String, dynamic> json) => LinkData(
|
||||||
|
url: json['url'] ?? 'Unknown',
|
||||||
|
title: json['title'] ?? 'Unknown',
|
||||||
|
description: json['description'] ?? '',
|
||||||
|
imageUrl: json['image'] ?? '',
|
||||||
|
);
|
||||||
|
}
|
83
lib/models/location_data.dart
Normal file
83
lib/models/location_data.dart
Normal file
|
@ -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<String, dynamic> 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);
|
||||||
|
}
|
||||||
|
}
|
104
lib/models/media_attachment.dart
Normal file
104
lib/models/media_attachment.dart
Normal file
|
@ -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<String, String> 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<String, dynamic> 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<String, dynamic> 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;
|
||||||
|
}
|
||||||
|
}
|
138
lib/models/timeline_entry.dart
Normal file
138
lib/models/timeline_entry.dart
Normal file
|
@ -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<LinkData> links;
|
||||||
|
|
||||||
|
final List<Connection> likes;
|
||||||
|
|
||||||
|
final List<Connection> dislikes;
|
||||||
|
|
||||||
|
final List<MediaAttachment> 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<LinkData>? links,
|
||||||
|
List<Connection>? likes,
|
||||||
|
List<Connection>? dislikes,
|
||||||
|
List<MediaAttachment>? 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}';
|
||||||
|
}
|
||||||
|
}
|
40
lib/utils/dateutils.dart
Normal file
40
lib/utils/dateutils.dart
Normal file
|
@ -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<int, ExecError> 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<int, ExecError> 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);
|
||||||
|
}
|
||||||
|
}
|
11
lib/utils/json_printer.dart
Normal file
11
lib/utils/json_printer.dart
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
class PrettyJsonEncoder {
|
||||||
|
late JsonEncoder encoder;
|
||||||
|
|
||||||
|
PrettyJsonEncoder() {
|
||||||
|
encoder = JsonEncoder.withIndent('\t');
|
||||||
|
}
|
||||||
|
|
||||||
|
String convert(Object json) => encoder.convert(json);
|
||||||
|
}
|
Loading…
Reference in a new issue