2023-05-16 09:59:31 -04:00
|
|
|
import 'package:contacts_plus_plus/auxiliary.dart';
|
2023-05-17 10:32:23 -04:00
|
|
|
import 'package:contacts_plus_plus/models/message.dart';
|
|
|
|
import 'package:contacts_plus_plus/models/records/asset_digest.dart';
|
2023-09-30 06:22:32 -04:00
|
|
|
import 'package:contacts_plus_plus/models/records/resonite_db_asset.dart';
|
2023-05-16 09:59:31 -04:00
|
|
|
import 'package:contacts_plus_plus/string_formatter.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:uuid/uuid.dart';
|
|
|
|
|
|
|
|
enum RecordType {
|
|
|
|
unknown,
|
|
|
|
link,
|
|
|
|
object,
|
|
|
|
directory,
|
|
|
|
texture,
|
|
|
|
audio;
|
|
|
|
|
|
|
|
factory RecordType.fromName(String? name) {
|
2023-06-17 10:58:32 -04:00
|
|
|
return RecordType.values.firstWhere((element) => element.name.toLowerCase() == name?.toLowerCase().trim(),
|
|
|
|
orElse: () => RecordType.unknown);
|
2023-05-16 09:59:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RecordId {
|
|
|
|
final String? id;
|
|
|
|
final String? ownerId;
|
|
|
|
final bool isValid;
|
|
|
|
|
2023-06-17 10:58:32 -04:00
|
|
|
const RecordId({this.id, this.ownerId, required this.isValid});
|
2023-05-16 09:59:31 -04:00
|
|
|
|
|
|
|
factory RecordId.fromMap(Map? map) {
|
|
|
|
return RecordId(id: map?["id"], ownerId: map?["ownerId"], isValid: map?["isValid"] ?? false);
|
|
|
|
}
|
|
|
|
|
|
|
|
Map toMap() {
|
|
|
|
return {
|
|
|
|
"id": id,
|
|
|
|
"ownerId": ownerId,
|
|
|
|
"isValid": isValid,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Record {
|
2023-06-17 10:58:32 -04:00
|
|
|
static final _rootRecord = Record(
|
|
|
|
id: "0",
|
|
|
|
combinedRecordId: const RecordId(isValid: false),
|
|
|
|
isSynced: true,
|
|
|
|
fetchedOn: DateTimeX.epoch,
|
|
|
|
path: "Inventory",
|
|
|
|
ownerId: "",
|
|
|
|
assetUri: "",
|
|
|
|
name: "Inventory",
|
|
|
|
description: "",
|
|
|
|
tags: [],
|
|
|
|
recordType: RecordType.directory,
|
|
|
|
thumbnailUri: "",
|
|
|
|
isPublic: false,
|
|
|
|
isListed: false,
|
|
|
|
isForPatreons: false,
|
|
|
|
lastModificationTime: DateTimeX.epoch,
|
2023-09-30 06:22:32 -04:00
|
|
|
resoniteDBManifest: [],
|
2023-06-17 10:58:32 -04:00
|
|
|
lastModifyingUserId: "",
|
|
|
|
lastModifyingMachineId: "",
|
|
|
|
creationTime: DateTimeX.epoch,
|
|
|
|
manifest: [],
|
|
|
|
url: "",
|
|
|
|
isValidOwnerId: true,
|
|
|
|
isValidRecordId: true,
|
|
|
|
globalVersion: 1,
|
|
|
|
localVersion: 1,
|
|
|
|
visits: 0,
|
|
|
|
rating: 0,
|
|
|
|
randomOrder: 0,
|
|
|
|
);
|
|
|
|
|
2023-05-17 02:07:10 -04:00
|
|
|
final String id;
|
2023-05-16 09:59:31 -04:00
|
|
|
final RecordId combinedRecordId;
|
|
|
|
final String ownerId;
|
|
|
|
final String assetUri;
|
|
|
|
final int globalVersion;
|
|
|
|
final int localVersion;
|
|
|
|
final String lastModifyingUserId;
|
|
|
|
final String lastModifyingMachineId;
|
|
|
|
final bool isSynced;
|
|
|
|
final DateTime fetchedOn;
|
|
|
|
final String name;
|
|
|
|
final FormatNode formattedName;
|
|
|
|
final String description;
|
|
|
|
final RecordType recordType;
|
|
|
|
final List<String> tags;
|
|
|
|
final String path;
|
|
|
|
final String thumbnailUri;
|
|
|
|
final bool isPublic;
|
|
|
|
final bool isForPatreons;
|
|
|
|
final bool isListed;
|
|
|
|
final DateTime lastModificationTime;
|
|
|
|
final DateTime creationTime;
|
|
|
|
final int visits;
|
|
|
|
final int rating;
|
|
|
|
final int randomOrder;
|
|
|
|
final List<String> manifest;
|
2023-09-30 06:22:32 -04:00
|
|
|
final List<ResoniteDBAsset> resoniteDBManifest;
|
2023-05-16 09:59:31 -04:00
|
|
|
final String url;
|
|
|
|
final bool isValidOwnerId;
|
|
|
|
final bool isValidRecordId;
|
|
|
|
|
|
|
|
Record({
|
|
|
|
required this.id,
|
|
|
|
required this.combinedRecordId,
|
|
|
|
required this.isSynced,
|
|
|
|
required this.fetchedOn,
|
|
|
|
required this.path,
|
|
|
|
required this.ownerId,
|
|
|
|
required this.assetUri,
|
|
|
|
required this.name,
|
|
|
|
required this.description,
|
|
|
|
required this.tags,
|
|
|
|
required this.recordType,
|
|
|
|
required this.thumbnailUri,
|
|
|
|
required this.isPublic,
|
|
|
|
required this.isListed,
|
|
|
|
required this.isForPatreons,
|
|
|
|
required this.lastModificationTime,
|
2023-09-30 06:22:32 -04:00
|
|
|
required this.resoniteDBManifest,
|
2023-05-16 09:59:31 -04:00
|
|
|
required this.lastModifyingUserId,
|
|
|
|
required this.lastModifyingMachineId,
|
|
|
|
required this.creationTime,
|
|
|
|
required this.manifest,
|
|
|
|
required this.url,
|
|
|
|
required this.isValidOwnerId,
|
|
|
|
required this.isValidRecordId,
|
|
|
|
required this.globalVersion,
|
|
|
|
required this.localVersion,
|
|
|
|
required this.visits,
|
|
|
|
required this.rating,
|
|
|
|
required this.randomOrder,
|
|
|
|
}) : formattedName = FormatNode.fromText(name);
|
|
|
|
|
2023-05-17 10:32:23 -04:00
|
|
|
factory Record.fromRequiredData({
|
|
|
|
required RecordType recordType,
|
|
|
|
required String userId,
|
|
|
|
required String machineId,
|
|
|
|
required String assetUri,
|
|
|
|
required String filename,
|
|
|
|
required String thumbnailUri,
|
|
|
|
required List<AssetDigest> digests,
|
2023-05-28 10:38:59 -04:00
|
|
|
List<String>? extraTags,
|
2023-05-17 10:32:23 -04:00
|
|
|
}) {
|
|
|
|
final combinedRecordId = RecordId(id: Record.generateId(), ownerId: userId, isValid: true);
|
|
|
|
return Record(
|
|
|
|
id: combinedRecordId.id.toString(),
|
|
|
|
combinedRecordId: combinedRecordId,
|
|
|
|
assetUri: assetUri,
|
|
|
|
name: filename,
|
2023-06-17 10:58:32 -04:00
|
|
|
tags: ([filename, "message_item", "message_id:${Message.generateId()}", "contacts-plus-plus"] + (extraTags ?? []))
|
|
|
|
.unique(),
|
2023-05-17 10:32:23 -04:00
|
|
|
recordType: recordType,
|
|
|
|
thumbnailUri: thumbnailUri,
|
|
|
|
isPublic: false,
|
|
|
|
isForPatreons: false,
|
|
|
|
isListed: false,
|
2023-09-30 06:22:32 -04:00
|
|
|
resoniteDBManifest: digests.map((e) => e.asset).toList(),
|
2023-05-17 10:32:23 -04:00
|
|
|
globalVersion: 0,
|
|
|
|
localVersion: 1,
|
|
|
|
lastModifyingUserId: userId,
|
|
|
|
lastModifyingMachineId: machineId,
|
|
|
|
lastModificationTime: DateTime.now().toUtc(),
|
|
|
|
creationTime: DateTime.now().toUtc(),
|
|
|
|
ownerId: userId,
|
|
|
|
isSynced: false,
|
|
|
|
fetchedOn: DateTimeX.one,
|
|
|
|
path: '',
|
|
|
|
description: '',
|
|
|
|
manifest: digests.map((e) => e.dbUri).toList(),
|
2023-09-30 06:22:32 -04:00
|
|
|
url: "resrec:///$userId/${combinedRecordId.id}",
|
2023-05-17 10:32:23 -04:00
|
|
|
isValidOwnerId: true,
|
|
|
|
isValidRecordId: true,
|
|
|
|
visits: 0,
|
|
|
|
rating: 0,
|
|
|
|
randomOrder: 0,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-05-16 09:59:31 -04:00
|
|
|
factory Record.fromMap(Map map) {
|
|
|
|
return Record(
|
2023-06-17 10:58:32 -04:00
|
|
|
id: map["id"] ?? "0",
|
|
|
|
combinedRecordId: RecordId.fromMap(map["combinedRecordId"]),
|
|
|
|
ownerId: map["ownerId"] ?? "",
|
|
|
|
assetUri: map["assetUri"] ?? "",
|
|
|
|
globalVersion: map["globalVersion"] ?? 0,
|
|
|
|
localVersion: map["localVersion"] ?? 0,
|
|
|
|
name: map["name"] ?? "",
|
|
|
|
description: map["description"] ?? "",
|
|
|
|
tags: (map["tags"] as List? ?? []).map((e) => e.toString()).toList(),
|
|
|
|
recordType: RecordType.fromName(map["recordType"]),
|
|
|
|
thumbnailUri: map["thumbnailUri"] ?? "",
|
|
|
|
isPublic: map["isPublic"] ?? false,
|
|
|
|
isForPatreons: map["isForPatreons"] ?? false,
|
|
|
|
isListed: map["isListed"] ?? false,
|
|
|
|
lastModificationTime: DateTime.tryParse(map["lastModificationTime"]) ?? DateTimeX.epoch,
|
2023-09-30 06:22:32 -04:00
|
|
|
resoniteDBManifest: (map["resoniteDBManifest"] as List? ?? []).map((e) => ResoniteDBAsset.fromMap(e)).toList(),
|
2023-06-17 10:58:32 -04:00
|
|
|
lastModifyingUserId: map["lastModifyingUserId"] ?? "",
|
|
|
|
lastModifyingMachineId: map["lastModifyingMachineId"] ?? "",
|
|
|
|
creationTime: DateTime.tryParse(map["lastModificationTime"]) ?? DateTimeX.epoch,
|
|
|
|
isSynced: map["isSynced"] ?? false,
|
|
|
|
fetchedOn: DateTime.tryParse(map["fetchedOn"] ?? "") ?? DateTimeX.epoch,
|
|
|
|
path: map["path"] ?? "",
|
2023-09-30 06:22:32 -04:00
|
|
|
manifest: (map["resoniteDBManifest"] as List? ?? []).map((e) => e.toString()).toList(),
|
2023-06-17 10:58:32 -04:00
|
|
|
url: map["url"] ?? "",
|
|
|
|
isValidOwnerId: map["isValidOwnerId"] == "true",
|
|
|
|
isValidRecordId: map["isValidRecordId"] == "true",
|
|
|
|
visits: map["visits"] ?? 0,
|
|
|
|
rating: map["rating"] ?? 0,
|
|
|
|
randomOrder: map["randomOrder"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
factory Record.inventoryRoot() => _rootRecord;
|
|
|
|
|
|
|
|
bool get isRoot => this == _rootRecord;
|
|
|
|
|
|
|
|
String get linkRecordId {
|
2023-09-30 06:22:32 -04:00
|
|
|
if (!assetUri.startsWith("resrec")) {
|
2023-06-17 10:58:32 -04:00
|
|
|
throw "Record is not a link.";
|
|
|
|
}
|
|
|
|
|
|
|
|
final lastSlashIdx = assetUri.lastIndexOf("/");
|
|
|
|
if (lastSlashIdx == -1) {
|
|
|
|
throw "Record has invalid assetUri";
|
|
|
|
}
|
|
|
|
|
|
|
|
return assetUri.substring(lastSlashIdx+1);
|
|
|
|
}
|
|
|
|
|
|
|
|
String get linkOwnerId {
|
2023-09-30 06:22:32 -04:00
|
|
|
if (!assetUri.startsWith("resrec")) {
|
2023-06-17 10:58:32 -04:00
|
|
|
throw "Record is not a link.";
|
|
|
|
}
|
|
|
|
|
2023-09-30 06:22:32 -04:00
|
|
|
String ownerId = assetUri.replaceFirst("resrec:///", "");
|
2023-06-17 10:58:32 -04:00
|
|
|
|
|
|
|
final lastSlashIdx = ownerId.lastIndexOf("/");
|
|
|
|
if (lastSlashIdx == -1) {
|
|
|
|
throw "Record has invalid assetUri";
|
|
|
|
}
|
|
|
|
|
|
|
|
return ownerId.substring(0, lastSlashIdx);
|
2023-05-16 09:59:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Record copyWith({
|
2023-05-17 02:07:10 -04:00
|
|
|
String? id,
|
2023-05-16 09:59:31 -04:00
|
|
|
String? ownerId,
|
|
|
|
String? recordId,
|
|
|
|
String? assetUri,
|
|
|
|
int? globalVersion,
|
|
|
|
int? localVersion,
|
|
|
|
String? name,
|
|
|
|
TextSpan? formattedName,
|
|
|
|
String? description,
|
|
|
|
List<String>? tags,
|
|
|
|
RecordType? recordType,
|
|
|
|
String? thumbnailUri,
|
|
|
|
bool? isPublic,
|
|
|
|
bool? isForPatreons,
|
|
|
|
bool? isListed,
|
|
|
|
bool? isDeleted,
|
|
|
|
DateTime? lastModificationTime,
|
2023-09-30 06:22:32 -04:00
|
|
|
List<ResoniteDBAsset>? resoniteDBManifest,
|
2023-05-16 09:59:31 -04:00
|
|
|
String? lastModifyingUserId,
|
|
|
|
String? lastModifyingMachineId,
|
|
|
|
DateTime? creationTime,
|
|
|
|
RecordId? combinedRecordId,
|
|
|
|
bool? isSynced,
|
|
|
|
DateTime? fetchedOn,
|
|
|
|
String? path,
|
|
|
|
List<String>? manifest,
|
|
|
|
String? url,
|
|
|
|
bool? isValidOwnerId,
|
|
|
|
bool? isValidRecordId,
|
|
|
|
int? visits,
|
|
|
|
int? rating,
|
|
|
|
int? randomOrder,
|
|
|
|
}) {
|
|
|
|
return Record(
|
|
|
|
id: id ?? this.id,
|
|
|
|
ownerId: ownerId ?? this.ownerId,
|
|
|
|
assetUri: assetUri ?? this.assetUri,
|
|
|
|
globalVersion: globalVersion ?? this.globalVersion,
|
|
|
|
localVersion: localVersion ?? this.localVersion,
|
|
|
|
name: name ?? this.name,
|
|
|
|
description: description ?? this.description,
|
|
|
|
tags: tags ?? this.tags,
|
|
|
|
recordType: recordType ?? this.recordType,
|
|
|
|
thumbnailUri: thumbnailUri ?? this.thumbnailUri,
|
|
|
|
isPublic: isPublic ?? this.isPublic,
|
|
|
|
isForPatreons: isForPatreons ?? this.isForPatreons,
|
|
|
|
isListed: isListed ?? this.isListed,
|
|
|
|
lastModificationTime: lastModificationTime ?? this.lastModificationTime,
|
2023-09-30 06:22:32 -04:00
|
|
|
resoniteDBManifest: resoniteDBManifest ?? this.resoniteDBManifest,
|
2023-05-16 09:59:31 -04:00
|
|
|
lastModifyingUserId: lastModifyingUserId ?? this.lastModifyingUserId,
|
|
|
|
lastModifyingMachineId: lastModifyingMachineId ?? this.lastModifyingMachineId,
|
|
|
|
creationTime: creationTime ?? this.creationTime,
|
|
|
|
combinedRecordId: combinedRecordId ?? this.combinedRecordId,
|
|
|
|
isSynced: isSynced ?? this.isSynced,
|
|
|
|
fetchedOn: fetchedOn ?? this.fetchedOn,
|
|
|
|
path: path ?? this.path,
|
|
|
|
manifest: manifest ?? this.manifest,
|
|
|
|
url: url ?? this.url,
|
|
|
|
isValidOwnerId: isValidOwnerId ?? this.isValidOwnerId,
|
|
|
|
isValidRecordId: isValidRecordId ?? this.isValidRecordId,
|
|
|
|
visits: visits ?? this.visits,
|
|
|
|
rating: rating ?? this.rating,
|
|
|
|
randomOrder: randomOrder ?? this.randomOrder,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Map toMap() {
|
|
|
|
return {
|
|
|
|
"id": id,
|
|
|
|
"ownerId": ownerId,
|
|
|
|
"assetUri": assetUri,
|
|
|
|
"globalVersion": globalVersion,
|
|
|
|
"localVersion": localVersion,
|
|
|
|
"name": name,
|
2023-05-17 02:07:10 -04:00
|
|
|
"description": description.asNullable,
|
2023-05-16 09:59:31 -04:00
|
|
|
"tags": tags,
|
|
|
|
"recordType": recordType.name,
|
2023-05-18 07:52:34 -04:00
|
|
|
"thumbnailUri": thumbnailUri.asNullable,
|
2023-05-16 09:59:31 -04:00
|
|
|
"isPublic": isPublic,
|
|
|
|
"isForPatreons": isForPatreons,
|
|
|
|
"isListed": isListed,
|
|
|
|
"lastModificationTime": lastModificationTime.toUtc().toIso8601String(),
|
2023-09-30 06:22:32 -04:00
|
|
|
"resoniteDBManifest": resoniteDBManifest.map((e) => e.toMap()).toList(),
|
2023-05-16 09:59:31 -04:00
|
|
|
"lastModifyingUserId": lastModifyingUserId,
|
|
|
|
"lastModifyingMachineId": lastModifyingMachineId,
|
|
|
|
"creationTime": creationTime.toUtc().toIso8601String(),
|
|
|
|
"combinedRecordId": combinedRecordId.toMap(),
|
|
|
|
"isSynced": isSynced,
|
|
|
|
"fetchedOn": fetchedOn.toUtc().toIso8601String(),
|
2023-05-17 02:07:10 -04:00
|
|
|
"path": path.asNullable,
|
2023-05-16 09:59:31 -04:00
|
|
|
"manifest": manifest,
|
|
|
|
"url": url,
|
|
|
|
"isValidOwnerId": isValidOwnerId,
|
|
|
|
"isValidRecordId": isValidRecordId,
|
|
|
|
"visits": visits,
|
|
|
|
"rating": rating,
|
|
|
|
"randomOrder": randomOrder,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static String generateId() {
|
|
|
|
return "R-${const Uuid().v4()}";
|
|
|
|
}
|
2023-05-18 07:52:34 -04:00
|
|
|
|
|
|
|
String? extractMessageId() {
|
|
|
|
const key = "message_id:";
|
|
|
|
for (final tag in tags) {
|
|
|
|
if (tag.startsWith(key)) {
|
|
|
|
return tag.replaceFirst(key, "");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2023-06-17 10:58:32 -04:00
|
|
|
}
|