Support server albums shared with you in Collections

This commit is contained in:
Ming Ming 2023-09-09 02:30:27 +08:00
parent c8b132d6d7
commit 66a128bf77
17 changed files with 315 additions and 108 deletions

View file

@ -64,6 +64,9 @@ class Collection with EquatableMixin {
/// See [CollectionContentProvider.isPendingSharedAlbum]
bool get isPendingSharedAlbum => contentProvider.isPendingSharedAlbum;
/// See [CollectionContentProvider.isOwned]
bool get isOwned => contentProvider.isOwned;
@override
List<Object?> get props => [
name,
@ -145,4 +148,7 @@ abstract class CollectionContentProvider with EquatableMixin {
/// In some implementation, shared album does not immediately get added to the
/// collections list
bool get isPendingSharedAlbum;
/// Return if this collection is owned by you
bool get isOwned;
}

View file

@ -7,6 +7,7 @@ import 'package:nc_photos/entity/album/provider.dart';
import 'package:nc_photos/entity/collection.dart';
import 'package:nc_photos/entity/collection/util.dart';
import 'package:nc_photos/entity/collection_item/util.dart';
import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/remote_storage_util.dart' as remote_storage_util;
import 'package:to_string/to_string.dart';
@ -109,6 +110,9 @@ class CollectionAlbumProvider
remote_storage_util.getRemotePendingSharedAlbumsDir(account)) ==
true;
@override
bool get isOwned => album.albumFile?.isOwned(account.userId) ?? true;
@override
List<Object?> get props => [account, album];

View file

@ -56,6 +56,9 @@ class CollectionLocationGroupProvider
@override
bool get isPendingSharedAlbum => false;
@override
bool get isOwned => true;
@override
List<Object?> get props => [account, location];

View file

@ -64,6 +64,9 @@ class CollectionMemoryProvider
@override
bool get isPendingSharedAlbum => false;
@override
bool get isOwned => true;
@override
String toString() => _$toString();

View file

@ -79,6 +79,9 @@ class CollectionNcAlbumProvider
@override
bool get isPendingSharedAlbum => false;
@override
bool get isOwned => album.isOwned;
@override
List<Object?> get props => [account, album];

View file

@ -51,6 +51,9 @@ class CollectionPersonProvider
@override
bool get isPendingSharedAlbum => false;
@override
bool get isOwned => true;
@override
List<Object?> get props => [account, person];

View file

@ -49,6 +49,9 @@ class CollectionTagProvider
@override
bool get isPendingSharedAlbum => false;
@override
bool get isOwned => true;
@override
List<Object?> get props => [account, tags];

View file

@ -68,25 +68,30 @@ class NcAlbum with EquatableMixin {
}
extension NcAlbumExtension on NcAlbum {
/// Return if this album is owned by you, or shared with you by other users
bool get isOwned {
// [albums/sharedalbums]/{strippedPath}
final p = _partialStrippedPath;
return p.startsWith("albums/");
}
/// Return the path of this file with the DAV part stripped
///
/// WebDAV file path: remote.php/dav/photos/{userId}/albums/{strippedPath}.
/// If this path points to the user's root album path, return "."
String get strippedPath {
if (!path.startsWith("${api.ApiPhotos.path}/")) {
// [albums/sharedalbums]/{strippedPath}
final p = _partialStrippedPath;
var begin = 0;
if (p.startsWith("albums")) {
begin += 6;
} else if (p.startsWith("sharedalbums")) {
begin += 12;
} else {
throw ArgumentError("Unsupported path: $path");
}
var begin = "${api.ApiPhotos.path}/".length;
begin = path.indexOf("/", begin);
if (begin == -1) {
throw ArgumentError("Unsupported path: $path");
}
// /albums/{strippedPath}
if (path.slice(begin, begin + 7) != "/albums") {
return path;
}
begin += 8;
final stripped = path.slice(begin);
// /{strippedPath}
final stripped = p.slice(begin + 1);
if (stripped.isEmpty) {
return ".";
} else {
@ -109,6 +114,19 @@ extension NcAlbumExtension on NcAlbum {
}
int get identityHashCode => path.hashCode;
/// Return a new path without the part before albums/sharedalbums
String get _partialStrippedPath {
if (!path.startsWith("${api.ApiPhotos.path}/")) {
throw ArgumentError("Unsupported path: $path");
}
var begin = "${api.ApiPhotos.path}/".length;
begin = path.indexOf("/", begin);
if (begin == -1) {
throw ArgumentError("Unsupported path: $path");
}
return path.slice(begin + 1);
}
}
@toString

View file

@ -23,29 +23,11 @@ class NcAlbumRemoteDataSource implements NcAlbumDataSource {
@override
Future<List<NcAlbum>> getAlbums(Account account) async {
_log.info("[getAlbums] account: ${account.userId}");
final response = await ApiUtil.fromAccount(account)
.photos(account.userId.toString())
.albums()
.propfind(
lastPhoto: 1,
nbItems: 1,
location: 1,
dateRange: 1,
collaborators: 1,
);
if (!response.isGood) {
_log.severe("[getAlbums] Failed requesting server: $response");
throw ApiException(
response: response,
message: "Server responed with an error: HTTP ${response.statusCode}",
);
}
final apiNcAlbums = await api.NcAlbumParser().parse(response.body);
return apiNcAlbums
.map(ApiNcAlbumConverter.fromApi)
.where((a) => a.strippedPath != ".")
.toList();
final results = await Future.wait([
_getAlbums(account),
_getSharedAlbums(account),
]);
return results.flattened.toList();
}
@override
@ -114,6 +96,58 @@ class NcAlbumRemoteDataSource implements NcAlbumDataSource {
.map(ApiNcAlbumItemConverter.fromApi)
.toList();
}
Future<List<NcAlbum>> _getAlbums(Account account) async {
final response = await ApiUtil.fromAccount(account)
.photos(account.userId.toString())
.albums()
.propfind(
lastPhoto: 1,
nbItems: 1,
location: 1,
dateRange: 1,
collaborators: 1,
);
if (!response.isGood) {
_log.severe("[_getAlbums] Failed requesting server: $response");
throw ApiException(
response: response,
message: "Server responed with an error: HTTP ${response.statusCode}",
);
}
final apiNcAlbums = await api.NcAlbumParser().parse(response.body);
return apiNcAlbums
.map(ApiNcAlbumConverter.fromApi)
.where((a) => a.strippedPath != ".")
.toList();
}
Future<List<NcAlbum>> _getSharedAlbums(Account account) async {
final response = await ApiUtil.fromAccount(account)
.photos(account.userId.toString())
.sharedalbums()
.propfind(
lastPhoto: 1,
nbItems: 1,
location: 1,
dateRange: 1,
collaborators: 1,
);
if (!response.isGood) {
_log.severe("[_getSharedAlbums] Failed requesting server: $response");
throw ApiException(
response: response,
message: "Server responed with an error: HTTP ${response.statusCode}",
);
}
final apiNcAlbums = await api.NcAlbumParser().parse(response.body);
return apiNcAlbums
.map(ApiNcAlbumConverter.fromApi)
.where((a) => a.strippedPath != ".")
.toList();
}
}
@npLog
@ -175,8 +209,8 @@ class NcAlbumSqliteDbDataSource implements NcAlbumCacheDataSource {
return dbItems
.map((i) {
try {
return SqliteNcAlbumItemConverter.fromSql(
account.userId.toString(), album.strippedPath, i);
return SqliteNcAlbumItemConverter.fromSql(account.userId.toString(),
album.strippedPath, album.isOwned, i);
} catch (e, stackTrace) {
_log.severe(
"[getItems] Failed while converting DB entry", e, stackTrace);
@ -239,7 +273,7 @@ class NcAlbumSqliteDbDataSource implements NcAlbumCacheDataSource {
final diff = getDiffWith<NcAlbumItem>(
existingItems
.map((e) => SqliteNcAlbumItemConverter.fromSql(
account.userId.raw, album.strippedPath, e))
account.userId.raw, album.strippedPath, album.isOwned, e))
.sorted(NcAlbumItemExtension.identityComparator),
remote.sorted(NcAlbumItemExtension.identityComparator),
NcAlbumItemExtension.identityComparator,

View file

@ -41,19 +41,17 @@ extension NcAlbumItemExtension on NcAlbumItem {
/// WebDAV file path: remote.php/dav/photos/{userId}/albums/{album}/{strippedPath}.
/// If this path points to the user's root album path, return "."
String get strippedPath {
if (!path.startsWith("${api.ApiPhotos.path}/")) {
// [albums/sharedalbums]/{album}/{strippedPath}
final p = _partialStrippedPath;
var begin = 0;
if (p.startsWith("albums")) {
begin += 6;
} else if (p.startsWith("sharedalbums")) {
begin += 12;
} else {
throw ArgumentError("Unsupported path: $path");
}
var begin = "${api.ApiPhotos.path}/".length;
begin = path.indexOf("/", begin);
if (begin == -1) {
throw ArgumentError("Unsupported path: $path");
}
// /albums/{album}/{strippedPath}
if (path.slice(begin, begin + 7) != "/albums") {
throw ArgumentError("Unsupported path: $path");
}
begin += 8;
begin += 1;
// {album}/{strippedPath}
begin = path.indexOf("/", begin);
if (begin == -1) {
@ -89,4 +87,17 @@ extension NcAlbumItemExtension on NcAlbumItem {
metadata: metadata,
);
}
/// Return a new path without the part before albums/sharedalbums
String get _partialStrippedPath {
if (!path.startsWith("${api.ApiPhotos.path}/")) {
throw ArgumentError("Unsupported path: $path");
}
var begin = "${api.ApiPhotos.path}/".length;
begin = path.indexOf("/", begin);
if (begin == -1) {
throw ArgumentError("Unsupported path: $path");
}
return path.slice(begin + 1);
}
}

View file

@ -51,7 +51,7 @@ class SqliteDb extends _$SqliteDb {
}) : super(executor ?? platform.openSqliteConnection());
@override
get schemaVersion => 6;
get schemaVersion => 7;
@override
get migration => MigrationStrategy(
@ -111,6 +111,15 @@ class SqliteDb extends _$SqliteDb {
await m.createTable(recognizeFaces);
await m.createTable(recognizeFaceItems);
}
if (from < 7) {
await m.alterTable(TableMigration(
ncAlbums,
newColumns: [ncAlbums.isOwned],
columnTransformer: {
ncAlbums.isOwned: const Constant(true),
},
));
}
});
} catch (e, stackTrace) {
_log.shout("[onUpgrade] Failed upgrading sqlite db", e, stackTrace);

View file

@ -4271,6 +4271,18 @@ class $NcAlbumsTable extends NcAlbums with TableInfo<$NcAlbumsTable, NcAlbum> {
late final GeneratedColumn<String> collaborators = GeneratedColumn<String>(
'collaborators', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
static const VerificationMeta _isOwnedMeta =
const VerificationMeta('isOwned');
@override
late final GeneratedColumn<bool> isOwned =
GeneratedColumn<bool>('is_owned', aliasedName, false,
type: DriftSqlType.bool,
requiredDuringInsert: true,
defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({
SqlDialect.sqlite: 'CHECK ("is_owned" IN (0, 1))',
SqlDialect.mysql: '',
SqlDialect.postgres: '',
}));
@override
List<GeneratedColumn> get $columns => [
rowId,
@ -4281,7 +4293,8 @@ class $NcAlbumsTable extends NcAlbums with TableInfo<$NcAlbumsTable, NcAlbum> {
location,
dateStart,
dateEnd,
collaborators
collaborators,
isOwned
];
@override
String get aliasedName => _alias ?? 'nc_albums';
@ -4334,6 +4347,12 @@ class $NcAlbumsTable extends NcAlbums with TableInfo<$NcAlbumsTable, NcAlbum> {
} else if (isInserting) {
context.missing(_collaboratorsMeta);
}
if (data.containsKey('is_owned')) {
context.handle(_isOwnedMeta,
isOwned.isAcceptableOrUnknown(data['is_owned']!, _isOwnedMeta));
} else if (isInserting) {
context.missing(_isOwnedMeta);
}
return context;
}
@ -4367,6 +4386,8 @@ class $NcAlbumsTable extends NcAlbums with TableInfo<$NcAlbumsTable, NcAlbum> {
.read(DriftSqlType.dateTime, data['${effectivePrefix}date_end'])),
collaborators: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}collaborators'])!,
isOwned: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}is_owned'])!,
);
}
@ -4395,6 +4416,7 @@ class NcAlbum extends DataClass implements Insertable<NcAlbum> {
final DateTime? dateStart;
final DateTime? dateEnd;
final String collaborators;
final bool isOwned;
const NcAlbum(
{required this.rowId,
required this.account,
@ -4404,7 +4426,8 @@ class NcAlbum extends DataClass implements Insertable<NcAlbum> {
this.location,
this.dateStart,
this.dateEnd,
required this.collaborators});
required this.collaborators,
required this.isOwned});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
@ -4427,6 +4450,7 @@ class NcAlbum extends DataClass implements Insertable<NcAlbum> {
map['date_end'] = Variable<DateTime>(converter.toSql(dateEnd));
}
map['collaborators'] = Variable<String>(collaborators);
map['is_owned'] = Variable<bool>(isOwned);
return map;
}
@ -4449,6 +4473,7 @@ class NcAlbum extends DataClass implements Insertable<NcAlbum> {
? const Value.absent()
: Value(dateEnd),
collaborators: Value(collaborators),
isOwned: Value(isOwned),
);
}
@ -4465,6 +4490,7 @@ class NcAlbum extends DataClass implements Insertable<NcAlbum> {
dateStart: serializer.fromJson<DateTime?>(json['dateStart']),
dateEnd: serializer.fromJson<DateTime?>(json['dateEnd']),
collaborators: serializer.fromJson<String>(json['collaborators']),
isOwned: serializer.fromJson<bool>(json['isOwned']),
);
}
@override
@ -4480,6 +4506,7 @@ class NcAlbum extends DataClass implements Insertable<NcAlbum> {
'dateStart': serializer.toJson<DateTime?>(dateStart),
'dateEnd': serializer.toJson<DateTime?>(dateEnd),
'collaborators': serializer.toJson<String>(collaborators),
'isOwned': serializer.toJson<bool>(isOwned),
};
}
@ -4492,7 +4519,8 @@ class NcAlbum extends DataClass implements Insertable<NcAlbum> {
Value<String?> location = const Value.absent(),
Value<DateTime?> dateStart = const Value.absent(),
Value<DateTime?> dateEnd = const Value.absent(),
String? collaborators}) =>
String? collaborators,
bool? isOwned}) =>
NcAlbum(
rowId: rowId ?? this.rowId,
account: account ?? this.account,
@ -4503,6 +4531,7 @@ class NcAlbum extends DataClass implements Insertable<NcAlbum> {
dateStart: dateStart.present ? dateStart.value : this.dateStart,
dateEnd: dateEnd.present ? dateEnd.value : this.dateEnd,
collaborators: collaborators ?? this.collaborators,
isOwned: isOwned ?? this.isOwned,
);
@override
String toString() {
@ -4515,14 +4544,15 @@ class NcAlbum extends DataClass implements Insertable<NcAlbum> {
..write('location: $location, ')
..write('dateStart: $dateStart, ')
..write('dateEnd: $dateEnd, ')
..write('collaborators: $collaborators')
..write('collaborators: $collaborators, ')
..write('isOwned: $isOwned')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(rowId, account, relativePath, lastPhoto,
nbItems, location, dateStart, dateEnd, collaborators);
nbItems, location, dateStart, dateEnd, collaborators, isOwned);
@override
bool operator ==(Object other) =>
identical(this, other) ||
@ -4535,7 +4565,8 @@ class NcAlbum extends DataClass implements Insertable<NcAlbum> {
other.location == this.location &&
other.dateStart == this.dateStart &&
other.dateEnd == this.dateEnd &&
other.collaborators == this.collaborators);
other.collaborators == this.collaborators &&
other.isOwned == this.isOwned);
}
class NcAlbumsCompanion extends UpdateCompanion<NcAlbum> {
@ -4548,6 +4579,7 @@ class NcAlbumsCompanion extends UpdateCompanion<NcAlbum> {
final Value<DateTime?> dateStart;
final Value<DateTime?> dateEnd;
final Value<String> collaborators;
final Value<bool> isOwned;
const NcAlbumsCompanion({
this.rowId = const Value.absent(),
this.account = const Value.absent(),
@ -4558,6 +4590,7 @@ class NcAlbumsCompanion extends UpdateCompanion<NcAlbum> {
this.dateStart = const Value.absent(),
this.dateEnd = const Value.absent(),
this.collaborators = const Value.absent(),
this.isOwned = const Value.absent(),
});
NcAlbumsCompanion.insert({
this.rowId = const Value.absent(),
@ -4569,10 +4602,12 @@ class NcAlbumsCompanion extends UpdateCompanion<NcAlbum> {
this.dateStart = const Value.absent(),
this.dateEnd = const Value.absent(),
required String collaborators,
required bool isOwned,
}) : account = Value(account),
relativePath = Value(relativePath),
nbItems = Value(nbItems),
collaborators = Value(collaborators);
collaborators = Value(collaborators),
isOwned = Value(isOwned);
static Insertable<NcAlbum> custom({
Expression<int>? rowId,
Expression<int>? account,
@ -4583,6 +4618,7 @@ class NcAlbumsCompanion extends UpdateCompanion<NcAlbum> {
Expression<DateTime>? dateStart,
Expression<DateTime>? dateEnd,
Expression<String>? collaborators,
Expression<bool>? isOwned,
}) {
return RawValuesInsertable({
if (rowId != null) 'row_id': rowId,
@ -4594,6 +4630,7 @@ class NcAlbumsCompanion extends UpdateCompanion<NcAlbum> {
if (dateStart != null) 'date_start': dateStart,
if (dateEnd != null) 'date_end': dateEnd,
if (collaborators != null) 'collaborators': collaborators,
if (isOwned != null) 'is_owned': isOwned,
});
}
@ -4606,7 +4643,8 @@ class NcAlbumsCompanion extends UpdateCompanion<NcAlbum> {
Value<String?>? location,
Value<DateTime?>? dateStart,
Value<DateTime?>? dateEnd,
Value<String>? collaborators}) {
Value<String>? collaborators,
Value<bool>? isOwned}) {
return NcAlbumsCompanion(
rowId: rowId ?? this.rowId,
account: account ?? this.account,
@ -4617,6 +4655,7 @@ class NcAlbumsCompanion extends UpdateCompanion<NcAlbum> {
dateStart: dateStart ?? this.dateStart,
dateEnd: dateEnd ?? this.dateEnd,
collaborators: collaborators ?? this.collaborators,
isOwned: isOwned ?? this.isOwned,
);
}
@ -4652,6 +4691,9 @@ class NcAlbumsCompanion extends UpdateCompanion<NcAlbum> {
if (collaborators.present) {
map['collaborators'] = Variable<String>(collaborators.value);
}
if (isOwned.present) {
map['is_owned'] = Variable<bool>(isOwned.value);
}
return map;
}
@ -4666,7 +4708,8 @@ class NcAlbumsCompanion extends UpdateCompanion<NcAlbum> {
..write('location: $location, ')
..write('dateStart: $dateStart, ')
..write('dateEnd: $dateEnd, ')
..write('collaborators: $collaborators')
..write('collaborators: $collaborators, ')
..write('isOwned: $isOwned')
..write(')'))
.toString();
}

View file

@ -142,6 +142,7 @@ class NcAlbums extends Table {
DateTimeColumn get dateEnd =>
dateTime().map(const SqliteDateTimeConverter()).nullable()();
TextColumn get collaborators => text()();
BoolColumn get isOwned => boolean()();
@override
List<Set<Column>>? get uniqueKeys => [

View file

@ -282,7 +282,8 @@ class SqliteNcAlbumConverter {
final json = ncAlbum.collaborators
.run((obj) => (jsonDecode(obj) as List).cast<Map>());
return NcAlbum(
path: "${api.ApiPhotos.path}/$userId/albums/${ncAlbum.relativePath}",
path:
"${api.ApiPhotos.path}/$userId/${ncAlbum.isOwned ? "albums" : "sharedalbums"}/${ncAlbum.relativePath}",
lastPhoto: ncAlbum.lastPhoto,
nbItems: ncAlbum.nbItems,
location: ncAlbum.location,
@ -306,15 +307,16 @@ class SqliteNcAlbumConverter {
dateEnd: Value(ncAlbum.dateEnd),
collaborators: Value(
jsonEncode(ncAlbum.collaborators.map((c) => c.toJson()).toList())),
isOwned: Value(ncAlbum.isOwned),
);
}
class SqliteNcAlbumItemConverter {
static NcAlbumItem fromSql(
String userId, String albumRelativePath, sql.NcAlbumItem item) =>
static NcAlbumItem fromSql(String userId, String albumRelativePath,
bool isAlbumOwned, sql.NcAlbumItem item) =>
NcAlbumItem(
path:
"${api.ApiPhotos.path}/$userId/albums/$albumRelativePath/${item.relativePath}",
"${api.ApiPhotos.path}/$userId/${isAlbumOwned ? "albums" : "sharedalbums"}/$albumRelativePath/${item.relativePath}",
fileId: item.fileId,
contentLength: item.contentLength,
contentType: item.contentType,

View file

@ -9,7 +9,8 @@ enum _ItemType {
@npLog
class _Item implements SelectableItemMetadata {
_Item(this.collection) : isShared = collection.shares.isNotEmpty {
_Item(this.collection)
: isShared = collection.shares.isNotEmpty || !collection.isOwned {
if (collection.count != null) {
_subtitle = L10n.global().albumSize(collection.count!);
}

View file

@ -70,6 +70,13 @@ extension _$ApiPhotosAlbumsNpLog on ApiPhotosAlbums {
static final log = Logger("src.api.ApiPhotosAlbums");
}
extension _$ApiPhotosSharedAlbumsNpLog on ApiPhotosSharedAlbums {
// ignore: unused_element
Logger get _log => log;
static final log = Logger("src.api.ApiPhotosSharedAlbums");
}
extension _$ApiPhotosAlbumNpLog on ApiPhotosAlbum {
// ignore: unused_element
Logger get _log => log;

View file

@ -4,6 +4,7 @@ class ApiPhotos {
const ApiPhotos(this.api, this.userId);
ApiPhotosAlbums albums() => ApiPhotosAlbums(this);
ApiPhotosSharedAlbums sharedalbums() => ApiPhotosSharedAlbums(this);
ApiPhotosAlbum album(String name) => ApiPhotosAlbum(this, name);
static String get path => "remote.php/dav/photos";
@ -23,51 +24,15 @@ class ApiPhotosAlbums {
location,
dateRange,
collaborators,
}) async {
final endpoint = "${ApiPhotos.path}/${photos.userId}/albums";
}) {
try {
if (lastPhoto == null &&
nbItems == null &&
location == null &&
dateRange == null &&
collaborators == null) {
// no body
return await api.request("PROPFIND", endpoint);
}
final namespaces = <String, String>{
"DAV:": "d",
"http://nextcloud.org/ns": "nc",
};
final builder = XmlBuilder();
builder
..processing("xml", "version=\"1.0\"")
..element("d:propfind", namespaces: namespaces, nest: () {
builder.element("d:prop", nest: () {
if (lastPhoto != null) {
builder.element("nc:last-photo");
}
if (nbItems != null) {
builder.element("nc:nbItems");
}
if (location != null) {
builder.element("nc:location");
}
if (dateRange != null) {
builder.element("nc:dateRange");
}
if (collaborators != null) {
builder.element("nc:collaborators");
}
});
});
return await api.request(
"PROPFIND",
endpoint,
header: {
"Content-Type": "application/xml",
},
body: builder.buildDocument().toXmlString(),
return _ApiPhotosAlbumsAlike(photos).propfind(
endpoint: "${ApiPhotos.path}/${photos.userId}/albums",
lastPhoto: lastPhoto,
nbItems: nbItems,
location: location,
dateRange: dateRange,
collaborators: collaborators,
);
} catch (e) {
_log.severe("[propfind] Failed while propfind", e);
@ -75,6 +40,97 @@ class ApiPhotosAlbums {
}
}
final ApiPhotos photos;
}
@npLog
class ApiPhotosSharedAlbums {
const ApiPhotosSharedAlbums(this.photos);
/// Retrieve all albums associated with a user
Future<Response> propfind({
lastPhoto,
nbItems,
location,
dateRange,
collaborators,
}) {
try {
return _ApiPhotosAlbumsAlike(photos).propfind(
endpoint: "${ApiPhotos.path}/${photos.userId}/sharedalbums",
lastPhoto: lastPhoto,
nbItems: nbItems,
location: location,
dateRange: dateRange,
collaborators: collaborators,
);
} catch (e) {
_log.severe("[propfind] Failed while propfind", e);
rethrow;
}
}
Api get api => photos.api;
final ApiPhotos photos;
}
class _ApiPhotosAlbumsAlike {
const _ApiPhotosAlbumsAlike(this.photos);
/// Retrieve all albums associated with a user
Future<Response> propfind({
required String endpoint,
lastPhoto,
nbItems,
location,
dateRange,
collaborators,
}) async {
if (lastPhoto == null &&
nbItems == null &&
location == null &&
dateRange == null &&
collaborators == null) {
// no body
return await api.request("PROPFIND", endpoint);
}
final namespaces = <String, String>{
"DAV:": "d",
"http://nextcloud.org/ns": "nc",
};
final builder = XmlBuilder();
builder
..processing("xml", "version=\"1.0\"")
..element("d:propfind", namespaces: namespaces, nest: () {
builder.element("d:prop", nest: () {
if (lastPhoto != null) {
builder.element("nc:last-photo");
}
if (nbItems != null) {
builder.element("nc:nbItems");
}
if (location != null) {
builder.element("nc:location");
}
if (dateRange != null) {
builder.element("nc:dateRange");
}
if (collaborators != null) {
builder.element("nc:collaborators");
}
});
});
return await api.request(
"PROPFIND",
endpoint,
header: {
"Content-Type": "application/xml",
},
body: builder.buildDocument().toXmlString(),
);
}
Api get api => photos.api;
final ApiPhotos photos;
}