diff --git a/client/lib/libphylum/actions/action_resource_mkdir.dart b/client/lib/libphylum/actions/action_resource_mkdir.dart index 92f02c5a..4bd2486c 100644 --- a/client/lib/libphylum/actions/action_resource_mkdir.dart +++ b/client/lib/libphylum/actions/action_resource_mkdir.dart @@ -51,7 +51,7 @@ class ResourceMkdirAction extends ApiAction with JsonApiAction { @override FutureOr applyOptimisticUpdate(PhylumAccount account) { - account.resourceRepository.createResource(id, true, parent, resourceName); + account.resourceRepository.createResource(id, true, parent, resourceName, ''); } @override diff --git a/client/lib/libphylum/actions/action_resource_upload.dart b/client/lib/libphylum/actions/action_resource_upload.dart index b600248d..10da2c15 100644 --- a/client/lib/libphylum/actions/action_resource_upload.dart +++ b/client/lib/libphylum/actions/action_resource_upload.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'dart:io'; import 'package:http/http.dart'; +import 'package:http_parser/http_parser.dart'; +import 'package:mime/mime.dart'; import 'package:offtheline/offtheline.dart'; import 'package:phylum/libphylum/phylum_account.dart'; import 'package:phylum/libphylum/phylum_datastore.dart'; @@ -22,21 +24,17 @@ class ResourceUploadAction extends ApiAction with FileUploadApiAc get tag => resourceSummaryResponse; @override - Future get file => MultipartFile.fromPath('contents', path, filename: resourceName); + Future get file => MultipartFile.fromPath('contents', path, filename: resourceName, contentType: MediaType.parse(contentType)); final String id; final String parent; final String resourceName; final String path; final int size; + final String contentType; - ResourceUploadAction._({ - required this.id, - required this.parent, - required this.resourceName, - required this.path, - required this.size, - }); + ResourceUploadAction._( + {required this.id, required this.parent, required this.resourceName, required this.path, required this.size, required this.contentType}); static Future fromPath({ required String parent, @@ -51,6 +49,7 @@ class ResourceUploadAction extends ApiAction with FileUploadApiAc resourceName: resourceName, path: path, size: size, + contentType: lookupMimeType(path) ?? 'application/octet-stream', ); } @@ -61,6 +60,7 @@ class ResourceUploadAction extends ApiAction with FileUploadApiAc resourceName: map['resourceName'], path: map['path'], size: map['size'], + contentType: map['contentType'], ); } @@ -71,7 +71,7 @@ class ResourceUploadAction extends ApiAction with FileUploadApiAc @override FutureOr applyOptimisticUpdate(PhylumAccount account) { - account.resourceRepository.createResource(id, true, parent, resourceName); + account.resourceRepository.createResource(id, true, parent, resourceName, contentType); } @override @@ -86,6 +86,7 @@ class ResourceUploadAction extends ApiAction with FileUploadApiAc 'resourceName': resourceName, 'path': path, 'size': size, + 'contentType': contentType, }; @override diff --git a/client/lib/libphylum/db/db.g.dart b/client/lib/libphylum/db/db.g.dart index 4e522d47..1883bdc1 100644 --- a/client/lib/libphylum/db/db.g.dart +++ b/client/lib/libphylum/db/db.g.dart @@ -51,6 +51,12 @@ class $ResourcesTable extends Resources late final GeneratedColumn etag = GeneratedColumn( 'etag', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _contentTypeMeta = + const VerificationMeta('contentType'); + @override + late final GeneratedColumn contentType = GeneratedColumn( + 'content_type', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); static const VerificationMeta _deletedMeta = const VerificationMeta('deleted'); @override @@ -68,8 +74,18 @@ class $ResourcesTable extends Resources 'last_fetch', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); @override - List get $columns => - [id, parent, name, dir, modified, size, etag, deleted, lastFetch]; + List get $columns => [ + id, + parent, + name, + dir, + modified, + size, + etag, + contentType, + deleted, + lastFetch + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -119,6 +135,14 @@ class $ResourcesTable extends Resources } else if (isInserting) { context.missing(_etagMeta); } + if (data.containsKey('content_type')) { + context.handle( + _contentTypeMeta, + contentType.isAcceptableOrUnknown( + data['content_type']!, _contentTypeMeta)); + } else if (isInserting) { + context.missing(_contentTypeMeta); + } if (data.containsKey('deleted')) { context.handle(_deletedMeta, deleted.isAcceptableOrUnknown(data['deleted']!, _deletedMeta)); @@ -154,6 +178,8 @@ class $ResourcesTable extends Resources .read(DriftSqlType.int, data['${effectivePrefix}size'])!, etag: attachedDatabase.typeMapping .read(DriftSqlType.string, data['${effectivePrefix}etag'])!, + contentType: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}content_type'])!, deleted: attachedDatabase.typeMapping .read(DriftSqlType.bool, data['${effectivePrefix}deleted'])!, lastFetch: attachedDatabase.typeMapping @@ -175,6 +201,7 @@ class Resource extends DataClass implements Insertable { final DateTime modified; final int size; final String etag; + final String contentType; final bool deleted; final DateTime? lastFetch; const Resource( @@ -185,6 +212,7 @@ class Resource extends DataClass implements Insertable { required this.modified, required this.size, required this.etag, + required this.contentType, required this.deleted, this.lastFetch}); @override @@ -199,6 +227,7 @@ class Resource extends DataClass implements Insertable { map['modified'] = Variable(modified); map['size'] = Variable(size); map['etag'] = Variable(etag); + map['content_type'] = Variable(contentType); map['deleted'] = Variable(deleted); if (!nullToAbsent || lastFetch != null) { map['last_fetch'] = Variable(lastFetch); @@ -216,6 +245,7 @@ class Resource extends DataClass implements Insertable { modified: Value(modified), size: Value(size), etag: Value(etag), + contentType: Value(contentType), deleted: Value(deleted), lastFetch: lastFetch == null && nullToAbsent ? const Value.absent() @@ -234,6 +264,7 @@ class Resource extends DataClass implements Insertable { modified: serializer.fromJson(json['modified']), size: serializer.fromJson(json['size']), etag: serializer.fromJson(json['etag']), + contentType: serializer.fromJson(json['contentType']), deleted: serializer.fromJson(json['deleted']), lastFetch: serializer.fromJson(json['lastFetch']), ); @@ -249,6 +280,7 @@ class Resource extends DataClass implements Insertable { 'modified': serializer.toJson(modified), 'size': serializer.toJson(size), 'etag': serializer.toJson(etag), + 'contentType': serializer.toJson(contentType), 'deleted': serializer.toJson(deleted), 'lastFetch': serializer.toJson(lastFetch), }; @@ -262,6 +294,7 @@ class Resource extends DataClass implements Insertable { DateTime? modified, int? size, String? etag, + String? contentType, bool? deleted, Value lastFetch = const Value.absent()}) => Resource( @@ -272,6 +305,7 @@ class Resource extends DataClass implements Insertable { modified: modified ?? this.modified, size: size ?? this.size, etag: etag ?? this.etag, + contentType: contentType ?? this.contentType, deleted: deleted ?? this.deleted, lastFetch: lastFetch.present ? lastFetch.value : this.lastFetch, ); @@ -284,6 +318,8 @@ class Resource extends DataClass implements Insertable { modified: data.modified.present ? data.modified.value : this.modified, size: data.size.present ? data.size.value : this.size, etag: data.etag.present ? data.etag.value : this.etag, + contentType: + data.contentType.present ? data.contentType.value : this.contentType, deleted: data.deleted.present ? data.deleted.value : this.deleted, lastFetch: data.lastFetch.present ? data.lastFetch.value : this.lastFetch, ); @@ -299,6 +335,7 @@ class Resource extends DataClass implements Insertable { ..write('modified: $modified, ') ..write('size: $size, ') ..write('etag: $etag, ') + ..write('contentType: $contentType, ') ..write('deleted: $deleted, ') ..write('lastFetch: $lastFetch') ..write(')')) @@ -306,8 +343,8 @@ class Resource extends DataClass implements Insertable { } @override - int get hashCode => Object.hash( - id, parent, name, dir, modified, size, etag, deleted, lastFetch); + int get hashCode => Object.hash(id, parent, name, dir, modified, size, etag, + contentType, deleted, lastFetch); @override bool operator ==(Object other) => identical(this, other) || @@ -319,6 +356,7 @@ class Resource extends DataClass implements Insertable { other.modified == this.modified && other.size == this.size && other.etag == this.etag && + other.contentType == this.contentType && other.deleted == this.deleted && other.lastFetch == this.lastFetch); } @@ -331,6 +369,7 @@ class ResourcesCompanion extends UpdateCompanion { final Value modified; final Value size; final Value etag; + final Value contentType; final Value deleted; final Value lastFetch; final Value rowid; @@ -342,6 +381,7 @@ class ResourcesCompanion extends UpdateCompanion { this.modified = const Value.absent(), this.size = const Value.absent(), this.etag = const Value.absent(), + this.contentType = const Value.absent(), this.deleted = const Value.absent(), this.lastFetch = const Value.absent(), this.rowid = const Value.absent(), @@ -354,6 +394,7 @@ class ResourcesCompanion extends UpdateCompanion { required DateTime modified, required int size, required String etag, + required String contentType, this.deleted = const Value.absent(), this.lastFetch = const Value.absent(), this.rowid = const Value.absent(), @@ -362,7 +403,8 @@ class ResourcesCompanion extends UpdateCompanion { dir = Value(dir), modified = Value(modified), size = Value(size), - etag = Value(etag); + etag = Value(etag), + contentType = Value(contentType); static Insertable custom({ Expression? id, Expression? parent, @@ -371,6 +413,7 @@ class ResourcesCompanion extends UpdateCompanion { Expression? modified, Expression? size, Expression? etag, + Expression? contentType, Expression? deleted, Expression? lastFetch, Expression? rowid, @@ -383,6 +426,7 @@ class ResourcesCompanion extends UpdateCompanion { if (modified != null) 'modified': modified, if (size != null) 'size': size, if (etag != null) 'etag': etag, + if (contentType != null) 'content_type': contentType, if (deleted != null) 'deleted': deleted, if (lastFetch != null) 'last_fetch': lastFetch, if (rowid != null) 'rowid': rowid, @@ -397,6 +441,7 @@ class ResourcesCompanion extends UpdateCompanion { Value? modified, Value? size, Value? etag, + Value? contentType, Value? deleted, Value? lastFetch, Value? rowid}) { @@ -408,6 +453,7 @@ class ResourcesCompanion extends UpdateCompanion { modified: modified ?? this.modified, size: size ?? this.size, etag: etag ?? this.etag, + contentType: contentType ?? this.contentType, deleted: deleted ?? this.deleted, lastFetch: lastFetch ?? this.lastFetch, rowid: rowid ?? this.rowid, @@ -438,6 +484,9 @@ class ResourcesCompanion extends UpdateCompanion { if (etag.present) { map['etag'] = Variable(etag.value); } + if (contentType.present) { + map['content_type'] = Variable(contentType.value); + } if (deleted.present) { map['deleted'] = Variable(deleted.value); } @@ -460,6 +509,7 @@ class ResourcesCompanion extends UpdateCompanion { ..write('modified: $modified, ') ..write('size: $size, ') ..write('etag: $etag, ') + ..write('contentType: $contentType, ') ..write('deleted: $deleted, ') ..write('lastFetch: $lastFetch, ') ..write('rowid: $rowid') @@ -487,6 +537,7 @@ typedef $$ResourcesTableCreateCompanionBuilder = ResourcesCompanion Function({ required DateTime modified, required int size, required String etag, + required String contentType, Value deleted, Value lastFetch, Value rowid, @@ -499,6 +550,7 @@ typedef $$ResourcesTableUpdateCompanionBuilder = ResourcesCompanion Function({ Value modified, Value size, Value etag, + Value contentType, Value deleted, Value lastFetch, Value rowid, @@ -528,6 +580,7 @@ class $$ResourcesTableTableManager extends RootTableManager< Value modified = const Value.absent(), Value size = const Value.absent(), Value etag = const Value.absent(), + Value contentType = const Value.absent(), Value deleted = const Value.absent(), Value lastFetch = const Value.absent(), Value rowid = const Value.absent(), @@ -540,6 +593,7 @@ class $$ResourcesTableTableManager extends RootTableManager< modified: modified, size: size, etag: etag, + contentType: contentType, deleted: deleted, lastFetch: lastFetch, rowid: rowid, @@ -552,6 +606,7 @@ class $$ResourcesTableTableManager extends RootTableManager< required DateTime modified, required int size, required String etag, + required String contentType, Value deleted = const Value.absent(), Value lastFetch = const Value.absent(), Value rowid = const Value.absent(), @@ -564,6 +619,7 @@ class $$ResourcesTableTableManager extends RootTableManager< modified: modified, size: size, etag: etag, + contentType: contentType, deleted: deleted, lastFetch: lastFetch, rowid: rowid, @@ -604,6 +660,11 @@ class $$ResourcesTableFilterComposer builder: (column, joinBuilders) => ColumnFilters(column, joinBuilders: joinBuilders)); + ColumnFilters get contentType => $state.composableBuilder( + column: $state.table.contentType, + builder: (column, joinBuilders) => + ColumnFilters(column, joinBuilders: joinBuilders)); + ColumnFilters get deleted => $state.composableBuilder( column: $state.table.deleted, builder: (column, joinBuilders) => @@ -660,6 +721,11 @@ class $$ResourcesTableOrderingComposer builder: (column, joinBuilders) => ColumnOrderings(column, joinBuilders: joinBuilders)); + ColumnOrderings get contentType => $state.composableBuilder( + column: $state.table.contentType, + builder: (column, joinBuilders) => + ColumnOrderings(column, joinBuilders: joinBuilders)); + ColumnOrderings get deleted => $state.composableBuilder( column: $state.table.deleted, builder: (column, joinBuilders) => diff --git a/client/lib/libphylum/db/resources.dart b/client/lib/libphylum/db/resources.dart index c6cfb6c0..59bd8240 100644 --- a/client/lib/libphylum/db/resources.dart +++ b/client/lib/libphylum/db/resources.dart @@ -8,6 +8,7 @@ class Resources extends Table { DateTimeColumn get modified => dateTime()(); IntColumn get size => integer()(); TextColumn get etag => text()(); + TextColumn get contentType => text()(); BoolColumn get deleted => boolean().withDefault(const Constant(false))(); DateTimeColumn get lastFetch => dateTime().nullable()(); diff --git a/client/lib/libphylum/phylum_datastore.dart b/client/lib/libphylum/phylum_datastore.dart index 1b54469c..959b6392 100644 --- a/client/lib/libphylum/phylum_datastore.dart +++ b/client/lib/libphylum/phylum_datastore.dart @@ -53,6 +53,7 @@ class PhylumDatastore with AccountListener f.id.isIn(ids)); } - Future createResource(String id, bool dir, String parent, String name) { + Future createResource(String id, bool dir, String parent, String name, String contentType) { return account.db.resources.insertOne(ResourcesCompanion.insert( id: id, name: name, @@ -50,6 +50,7 @@ class ResourceRepository { modified: DateTime.now(), size: 0, etag: "", + contentType: contentType, )); } diff --git a/client/pubspec.lock b/client/pubspec.lock index 5aff20f0..d4092035 100644 --- a/client/pubspec.lock +++ b/client/pubspec.lock @@ -430,7 +430,7 @@ packages: source: hosted version: "3.2.1" http_parser: - dependency: transitive + dependency: "direct main" description: name: http_parser sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" @@ -558,7 +558,7 @@ packages: source: hosted version: "1.15.0" mime: - dependency: transitive + dependency: "direct main" description: name: mime sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" diff --git a/client/pubspec.yaml b/client/pubspec.yaml index 780812b4..909831b6 100644 --- a/client/pubspec.yaml +++ b/client/pubspec.yaml @@ -17,19 +17,21 @@ dependencies: hive: http: logger: + mime: offtheline: git: url: https://codeberg.org/shroff/offtheline.git ref: 7e4457c8e7f86662cd6d8424383423ceec7c06bb - state_notifier: path: path_provider: provider: + state_notifier: stream_transform: super_clipboard: uri: uuid: sqlite3: ^2.4.6 + http_parser: ^4.0.2 dev_dependencies: flutter_test: