Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 114 additions & 4 deletions lib/src/models/yust_file.dart
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Url deprecaten

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oder follow up :)

Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

import 'package:collection/collection.dart';
import 'package:json_annotation/json_annotation.dart';

import '../../yust.dart';

part 'yust_file.g.dart';

typedef YustFileJson = Map<String, dynamic>;
typedef YustFilesJson = List<YustFileJson>;

/// The size of the thumbnail.
enum YustFileThumbnailSize {
small;

/// Converts a JSON string to a [YustFileThumbnailSize].
///
/// If the size is not found, [YustFileThumbnailSize.small] is returned.
static YustFileThumbnailSize fromJson(String size) =>
YustFileThumbnailSize.values.firstWhereOrNull((e) => e.name == size) ??
YustFileThumbnailSize.small;

/// Converts a [YustFileThumbnailSize] to a JSON string.
String toJson() => name;
}

/// A binary file handled by database and file storage.
/// A file is stored in Firebase Storage and linked to a document in the database.
/// For offline caching a file can also be stored on the device.
Expand All @@ -31,6 +50,14 @@ class YustFile {
/// On mobile devices, this can also be the time the file was uploaded into device cache.
DateTime? createdAt;

/// The path to the file in the storage.
String? path;

/// The thumbnails of the file.
///
/// Map of thumbnail size to path in the storage.
Map<YustFileThumbnailSize, String>? thumbnails;

/// The binary file. This attribute is used for iOS and Android. For web [bytes] is used instead.
@JsonKey(includeFromJson: false, includeToJson: false)
File? file;
Expand Down Expand Up @@ -59,6 +86,10 @@ class YustFile {
@JsonKey(includeFromJson: false, includeToJson: false)
String? lastError;

/// True if a thumbnail should be created.
@JsonKey(includeFromJson: false, includeToJson: false)
bool? createThumbnail;

/// True if the files should be stored as a Map of hash and file
/// inside the linked document.
///
Expand Down Expand Up @@ -98,8 +129,11 @@ class YustFile {
this.linkedDocAttribute,
this.processing = false,
this.lastError,
this.createThumbnail,
this.linkedDocStoresFilesAsMap,
this.createdAt,
this.path,
this.thumbnails,
bool setCreatedAtToNow = true,
}) {
if (setCreatedAtToNow) {
Expand All @@ -124,6 +158,11 @@ class YustFile {
: json['createdAt'] is DateTime
? json['createdAt'] as DateTime
: DateTime.parse(json['createdAt'] as String),
path: json['path'] as String?,
thumbnails: (json['thumbnails'] as Map?)?.map(
(key, value) =>
MapEntry(YustFileThumbnailSize.fromJson(key), value as String),
),
setCreatedAtToNow: false,
);
}
Expand All @@ -141,6 +180,8 @@ class YustFile {
name = file.name;
hash = file.hash;
createdAt = file.createdAt;
path = file.path;
thumbnails = file.thumbnails;
}

/// Converts the file to JSON for local device. Only relevant attributes are converted.
Expand All @@ -154,13 +195,24 @@ class YustFile {
linkedDocPath: json['linkedDocPath'] as String,
linkedDocAttribute: json['linkedDocAttribute'] as String,
lastError: json['lastError'] as String?,
createThumbnail: json['createThumbnail'] == 'true',
linkedDocStoresFilesAsMap: json['linkedDocStoresFilesAsMap'] == 'true',
modifiedAt: json['modifiedAt'] != null
? DateTime.parse(json['modifiedAt'] as String)
: null,
createdAt: json['createdAt'] != null
? DateTime.parse(json['createdAt'] as String)
: null,
path: json['path'] as String?,
thumbnails: json['thumbnails'] != null
? (jsonDecode(json['thumbnails'] as String) as Map).map(
(key, value) => MapEntry(
YustFileThumbnailSize.fromJson(key),
value as String,
),
)
: null,

setCreatedAtToNow: false,
);
}
Expand All @@ -170,16 +222,22 @@ class YustFile {
/// This is used for offline file handling only (Caching on mobile devices)
Map<String, String?> toLocalJson() {
if (name == null) {
throw ('Error: Each cached file needs a name. Should be unique for each path!');
throw YustException(
'Error: Each cached file needs a name. Should be unique for each path!',
);
}
if (devicePath == null) {
throw ('Error: Device Path has to be a String.');
throw YustException('Error: Device Path has to be a String.');
}
if (storageFolderPath == null) {
throw ('Error: StorageFolderPath has to be set for a successful upload.');
throw YustException(
'Error: StorageFolderPath has to be set for a successful upload.',
);
}
if (linkedDocPath == null || linkedDocAttribute == null) {
throw ('Error: linkedDocPath and linkedDocAttribute have to be set for a successful upload.');
throw YustException(
'Error: linkedDocPath and linkedDocAttribute have to be set for a successful upload.',
);
}
return {
'name': name,
Expand All @@ -188,14 +246,39 @@ class YustFile {
'linkedDocAttribute': linkedDocAttribute,
'devicePath': devicePath,
'lastError': lastError,
'createThumbnail': (createThumbnail ?? false).toString(),
'linkedDocStoresFilesAsMap': (linkedDocStoresFilesAsMap ?? false)
.toString(),
'modifiedAt': modifiedAt?.toIso8601String(),
'createdAt': createdAt?.toIso8601String(),
'type': type,
'path': path,
'thumbnails': thumbnails != null ? jsonEncode(thumbnails) : null,
};
}

/// Creates a new file with the same properties but with a new URL.
YustFile copyWithUrl(String? url) => YustFile(
key: key,
name: name,
modifiedAt: modifiedAt,
url: url,
hash: hash,
file: file,
bytes: bytes,
devicePath: devicePath,
storageFolderPath: storageFolderPath,
linkedDocPath: linkedDocPath,
linkedDocAttribute: linkedDocAttribute,
processing: processing,
lastError: lastError,
createThumbnail: createThumbnail,
createdAt: createdAt,
path: path,
thumbnails: thumbnails,
setCreatedAtToNow: false,
);

dynamic operator [](String key) {
switch (key) {
case 'name':
Expand All @@ -206,6 +289,9 @@ class YustFile {
return url;
case 'createdAt':
return createdAt;
case 'path':
return path;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auch rausnehmen

case 'thumbnails': // Thumbnails should not be accessible
default:
throw ArgumentError();
}
Expand Down Expand Up @@ -234,4 +320,28 @@ class YustFile {

return name!.contains('.') ? name!.split('.').last : '';
}

bool hasThumbnail() => thumbnails?.isNotEmpty ?? false;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kommentare

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getter


String? getOriginalUrl() {
final baseUrl = Yust.fileAccessService.originalCdnBaseUrl;
final grant = Yust.fileAccessService.getGrantForFile(this);

if (baseUrl == null || grant == null || path == null) return url;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kommentar wann passiert das


return '${_tryAppendSlash(baseUrl)}$path?${grant.originalSignedUrlPart}';
}

String? getThumbnailUrl() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

size parameter

final baseUrl = Yust.fileAccessService.thumbnailCdnBaseUrl;
final grant = Yust.fileAccessService.getGrantForFile(this);

if (baseUrl == null || grant == null || !hasThumbnail()) return null;
final thumbnailPath = thumbnails![YustFileThumbnailSize.small];

return '${_tryAppendSlash(baseUrl)}$thumbnailPath?${grant.thumbnailSignedUrlPart}';
}

String? _tryAppendSlash(String baseUrl) =>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enforcen im service

baseUrl.endsWith('/') ? baseUrl : '$baseUrl/';
}
2 changes: 2 additions & 0 deletions lib/src/models/yust_file.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions lib/src/models/yust_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,17 @@ class YustImage extends YustFile {
super.linkedDocAttribute,
super.processing = false,
super.lastError,
super.createThumbnail,
super.linkedDocStoresFilesAsMap,
super.createdAt,
super.path,
super.thumbnails,
this.location,
});

YustGeoLocation? location;

/// Creates a new image from a file.
factory YustImage.fromYustFile(YustFile file) => file is YustImage
? file
: YustImage(
Expand All @@ -45,8 +49,11 @@ class YustImage extends YustFile {
linkedDocAttribute: file.linkedDocAttribute,
processing: file.processing,
lastError: file.lastError,
createThumbnail: file.createThumbnail,
linkedDocStoresFilesAsMap: file.linkedDocStoresFilesAsMap,
createdAt: file.createdAt,
path: file.path,
thumbnails: file.thumbnails,
);

/// Create a list of images from a list of files
Expand Down
9 changes: 9 additions & 0 deletions lib/src/models/yust_image.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions lib/src/services/yust_file_access_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export './yust_file_access_service_dart.dart'
if (dart.library.ui) './yust_file_access_service_flutter.dart';
export './yust_file_access_service_interface.dart';
72 changes: 72 additions & 0 deletions lib/src/services/yust_file_access_service_dart.dart
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createGrant

2 CDN configurations & pathPrefix, validUntil

Um den Flow zu verstehen

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import 'package:collection/collection.dart';

import '../models/yust_file.dart';
import '../util/file_access/yust_cdn_configuration.dart';
import '../util/file_access/yust_file_access_grant.dart';
import '../util/google_cloud_cdn_helper.dart';
import 'yust_file_access_service_interface.dart';

class YustFileAccessService implements IYustFileAccessService {
@override
final String? originalCdnBaseUrl;

@override
final String? thumbnailCdnBaseUrl;

@override
List<YustFileAccessGrant> grants = [];

@override
Future<String?> Function(YustFile)? generateDownloadUrl;

YustFileAccessService({
required this.originalCdnBaseUrl,
required this.thumbnailCdnBaseUrl,
});

@override
String createSignedUrlForFile({
required String path,
required String name,
required Duration validFor,
required YustCdnConfiguration cdnConfiguration,
Map<String, String>? additionalQueryParams,
}) {
final helper = GoogleCloudCdnHelper(
baseUrl: cdnConfiguration.baseUrl,
keyName: cdnConfiguration.keyName,
keyBase64: cdnConfiguration.keyBase64,
);
return helper.signFilePath(
objectPath: '$path/$name',
validFor: validFor,
additionalQueryParams: additionalQueryParams,
);
}

@override
String createSignedUrlPartForFolder({
required String path,
required Duration validFor,
required YustCdnConfiguration cdnConfiguration,
}) {
final helper = GoogleCloudCdnHelper(
baseUrl: cdnConfiguration.baseUrl,
keyName: cdnConfiguration.keyName,
keyBase64: cdnConfiguration.keyBase64,
);
return helper.signPrefix(prefixPath: path, validFor: validFor);
}

@override
void setGrants(List<YustFileAccessGrant> grants) {
this.grants = grants;
}

@override
YustFileAccessGrant? getGrantForFile(YustFile file) {
return grants.firstWhereOrNull(
(grant) => file.path?.startsWith(grant.pathPrefix) ?? false,
);
}
}
Loading