Integrate APIs to fetch shared folders
This commit is contained in:
parent
496e1d2f81
commit
2f3951f124
6 changed files with 237 additions and 41 deletions
105
lib/db/folder_db.dart
Normal file
105
lib/db/folder_db.dart
Normal file
|
@ -0,0 +1,105 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart';
|
||||
import 'package:photos/models/folder.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class FolderDB {
|
||||
static final _databaseName = "ente.db";
|
||||
static final _databaseVersion = 1;
|
||||
|
||||
static final table = 'folders';
|
||||
|
||||
static final columnId = 'id';
|
||||
static final columnName = 'name';
|
||||
static final columnOwner = 'owner';
|
||||
static final columnDeviceFolder = 'device_folder';
|
||||
static final columnSharedWith = 'shared_with';
|
||||
static final columnUpdateTimestamp = 'update_timestamp';
|
||||
|
||||
FolderDB._privateConstructor();
|
||||
static final FolderDB instance = FolderDB._privateConstructor();
|
||||
|
||||
static Database _database;
|
||||
Future<Database> get database async {
|
||||
if (_database != null) return _database;
|
||||
_database = await _initDatabase();
|
||||
return _database;
|
||||
}
|
||||
|
||||
_initDatabase() async {
|
||||
Directory documentsDirectory = await getApplicationDocumentsDirectory();
|
||||
String path = join(documentsDirectory.path, _databaseName);
|
||||
return await openDatabase(path,
|
||||
version: _databaseVersion, onCreate: _onCreate);
|
||||
}
|
||||
|
||||
Future _onCreate(Database db, int version) async {
|
||||
await db.execute('''
|
||||
CREATE TABLE $table (
|
||||
$columnId INTEGER PRIMARY KEY NOT NULL,
|
||||
$columnName TEXT NOT NULL,
|
||||
$columnOwner TEXT NOT NULL,
|
||||
$columnDeviceFolder TEXT NOT NULL,
|
||||
$columnSharedWith TEXT NOT NULL,
|
||||
$columnUpdateTimestamp INTEGER NOT NULL,
|
||||
UNIQUE($columnOwner, $columnDeviceFolder)
|
||||
)
|
||||
''');
|
||||
}
|
||||
|
||||
Future<int> putFolder(Folder folder) async {
|
||||
final db = await instance.database;
|
||||
return await db.insert(table, _getRowForFolder(folder),
|
||||
conflictAlgorithm: ConflictAlgorithm.replace);
|
||||
}
|
||||
|
||||
Future<List<Folder>> getFolders() async {
|
||||
final db = await instance.database;
|
||||
final results = await db.query(
|
||||
table,
|
||||
orderBy: '$columnUpdateTimestamp DESC',
|
||||
);
|
||||
return _convertToFolders(results);
|
||||
}
|
||||
|
||||
Future<int> deleteFolder(Folder folder) async {
|
||||
final db = await instance.database;
|
||||
return db.delete(
|
||||
table,
|
||||
where: '$columnId =?',
|
||||
whereArgs: [folder.id],
|
||||
);
|
||||
}
|
||||
|
||||
List<Folder> _convertToFolders(List<Map<String, dynamic>> results) {
|
||||
var folders = List<Folder>();
|
||||
for (var result in results) {
|
||||
folders.add(_getFolderFromRow(result));
|
||||
}
|
||||
return folders;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _getRowForFolder(Folder folder) {
|
||||
var row = new Map<String, dynamic>();
|
||||
row[columnId] = folder.id;
|
||||
row[columnName] = folder.name;
|
||||
row[columnOwner] = folder.owner;
|
||||
row[columnDeviceFolder] = folder.deviceFolder;
|
||||
row[columnSharedWith] = folder.sharedWith.toString();
|
||||
row[columnUpdateTimestamp] = folder.updateTimestamp;
|
||||
return row;
|
||||
}
|
||||
|
||||
Folder _getFolderFromRow(Map<String, dynamic> row) {
|
||||
return Folder(
|
||||
row[columnId],
|
||||
row[columnName],
|
||||
row[columnOwner],
|
||||
row[columnDeviceFolder],
|
||||
Set<String>.from(row[columnSharedWith]),
|
||||
row[columnUpdateTimestamp],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ class PhotoDB {
|
|||
static final columnLocalId = 'local_id';
|
||||
static final columnTitle = 'title';
|
||||
static final columnDeviceFolder = 'device_folder';
|
||||
static final columnRemoteFolderId = 'remote_folder_id';
|
||||
static final columnRemotePath = 'remote_path';
|
||||
static final columnIsDeleted = 'is_deleted';
|
||||
static final columnCreateTimestamp = 'create_timestamp';
|
||||
|
@ -51,6 +52,7 @@ class PhotoDB {
|
|||
$columnUploadedFileId INTEGER NOT NULL,
|
||||
$columnTitle TEXT NOT NULL,
|
||||
$columnDeviceFolder TEXT NOT NULL,
|
||||
$columnRemoteFolderId INTEGER DEFAULT -1,
|
||||
$columnRemotePath TEXT,
|
||||
$columnIsDeleted INTEGER DEFAULT 0,
|
||||
$columnCreateTimestamp TEXT NOT NULL,
|
||||
|
@ -175,6 +177,15 @@ class PhotoDB {
|
|||
);
|
||||
}
|
||||
|
||||
Future<int> deletePhotosInRemoteFolder(int folderId) async {
|
||||
final db = await instance.database;
|
||||
return db.delete(
|
||||
table,
|
||||
where: '$columnRemoteFolderId =?',
|
||||
whereArgs: [folderId],
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<String>> getDistinctPaths() async {
|
||||
final db = await instance.database;
|
||||
final rows = await db.query(
|
||||
|
@ -205,6 +216,22 @@ class PhotoDB {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Photo> getLatestPhotoInRemoteFolder(int folderId) async {
|
||||
final db = await instance.database;
|
||||
var rows = await db.query(
|
||||
table,
|
||||
where: '$columnRemoteFolderId =?',
|
||||
whereArgs: [folderId],
|
||||
orderBy: '$columnCreateTimestamp DESC',
|
||||
limit: 1,
|
||||
);
|
||||
if (rows.isNotEmpty) {
|
||||
return _getPhotoFromRow(rows[0]);
|
||||
} else {
|
||||
throw ("No photo found in path");
|
||||
}
|
||||
}
|
||||
|
||||
Future<Photo> getLatestPhotoAmongGeneratedIds(
|
||||
List<String> generatedIds) async {
|
||||
final db = await instance.database;
|
||||
|
@ -236,6 +263,7 @@ class PhotoDB {
|
|||
photo.uploadedFileId == null ? -1 : photo.uploadedFileId;
|
||||
row[columnTitle] = photo.title;
|
||||
row[columnDeviceFolder] = photo.deviceFolder;
|
||||
row[columnRemoteFolderId] = photo.remoteFolderId;
|
||||
row[columnRemotePath] = photo.remotePath;
|
||||
row[columnCreateTimestamp] = photo.createTimestamp;
|
||||
row[columnSyncTimestamp] = photo.syncTimestamp;
|
||||
|
@ -249,6 +277,7 @@ class PhotoDB {
|
|||
photo.uploadedFileId = row[columnUploadedFileId];
|
||||
photo.title = row[columnTitle];
|
||||
photo.deviceFolder = row[columnDeviceFolder];
|
||||
photo.remoteFolderId = row[columnRemoteFolderId];
|
||||
photo.remotePath = row[columnRemotePath];
|
||||
photo.createTimestamp = int.parse(row[columnCreateTimestamp]);
|
||||
photo.syncTimestamp = row[columnSyncTimestamp] == null
|
||||
|
|
|
@ -1,18 +1,77 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/db/folder_db.dart';
|
||||
import 'package:photos/db/photo_db.dart';
|
||||
import 'package:photos/models/folder.dart';
|
||||
import 'package:photos/models/photo.dart';
|
||||
|
||||
class FolderSharingService {
|
||||
final _logger = Logger("FolderSharingService");
|
||||
final _dio = Dio();
|
||||
static final _diffLimit = 100;
|
||||
|
||||
FolderSharingService._privateConstructor();
|
||||
static final FolderSharingService instance =
|
||||
FolderSharingService._privateConstructor();
|
||||
|
||||
void sync() {
|
||||
// TODO
|
||||
getFolders().then((f) async {
|
||||
var folders = f.toSet();
|
||||
var currentFolders = await FolderDB.instance.getFolders();
|
||||
for (final currentFolder in currentFolders) {
|
||||
if (!folders.contains(currentFolder)) {
|
||||
await FolderDB.instance.deleteFolder(currentFolder);
|
||||
await PhotoDB.instance.deletePhotosInRemoteFolder(currentFolder.id);
|
||||
}
|
||||
}
|
||||
for (final folder in folders) {
|
||||
await syncDiff(folder);
|
||||
await FolderDB.instance.putFolder(folder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> syncDiff(Folder folder) async {
|
||||
int lastSyncTimestamp = 0;
|
||||
try {
|
||||
Photo photo =
|
||||
await PhotoDB.instance.getLatestPhotoInRemoteFolder(folder.id);
|
||||
lastSyncTimestamp = photo.syncTimestamp;
|
||||
} catch (e) {
|
||||
// Folder has never been synced
|
||||
}
|
||||
var photos = await getDiff(folder.id, lastSyncTimestamp, _diffLimit);
|
||||
await PhotoDB.instance.insertPhotos(photos);
|
||||
if (photos.length == _diffLimit) {
|
||||
await syncDiff(folder);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Photo>> getDiff(
|
||||
int folderId, int sinceTimestamp, int limit) async {
|
||||
Response response = await _dio.get(
|
||||
Configuration.instance.getHttpEndpoint() +
|
||||
"/folders/diff" +
|
||||
folderId.toString(),
|
||||
options:
|
||||
Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
|
||||
queryParameters: {
|
||||
"sinceTimestamp": sinceTimestamp,
|
||||
"limit": limit,
|
||||
},
|
||||
).catchError((e) => _logger.severe(e));
|
||||
if (response != null) {
|
||||
return (response.data["diff"] as List).map((p) {
|
||||
Photo photo = new Photo.fromJson(p);
|
||||
photo.remoteFolderId = folderId;
|
||||
return photo;
|
||||
}).toList();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Folder>> getFolders() async {
|
||||
|
@ -29,46 +88,46 @@ class FolderSharingService {
|
|||
});
|
||||
}
|
||||
|
||||
Future<Map<String, bool>> getSharingStatus(String path) async {
|
||||
Future<Folder> getFolder(String deviceFolder) async {
|
||||
return _dio
|
||||
.get(
|
||||
Configuration.instance.getHttpEndpoint() + "/folders/folder/",
|
||||
queryParameters: {
|
||||
"deviceFolder": deviceFolder,
|
||||
},
|
||||
options: Options(
|
||||
headers: {"X-Auth-Token": Configuration.instance.getToken()}),
|
||||
)
|
||||
.then((response) => Folder.fromMap(response.data));
|
||||
}
|
||||
|
||||
Future<Map<String, bool>> getSharingStatus(Folder folder) async {
|
||||
return _dio
|
||||
.get(
|
||||
Configuration.instance.getHttpEndpoint() + "/users",
|
||||
options:
|
||||
Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
|
||||
)
|
||||
.then((usersResponse) {
|
||||
return getFolders().then((folders) {
|
||||
Folder sharedFolder;
|
||||
for (var folder in folders) {
|
||||
if (folder.owner == Configuration.instance.getUsername() &&
|
||||
folder.deviceFolder == path) {
|
||||
sharedFolder = folder;
|
||||
break;
|
||||
}
|
||||
.then((response) {
|
||||
final users = (response.data["users"] as List).toList();
|
||||
final result = Map<String, bool>();
|
||||
for (final user in users) {
|
||||
if (user != Configuration.instance.getUsername()) {
|
||||
result[user] = folder.sharedWith.contains(user);
|
||||
}
|
||||
var sharedUsers = Set<String>();
|
||||
if (sharedFolder != null) {
|
||||
sharedUsers.addAll(sharedFolder.sharedWith);
|
||||
}
|
||||
final result = Map<String, bool>();
|
||||
(usersResponse.data["users"] as List).forEach((user) {
|
||||
if (user != Configuration.instance.getUsername()) {
|
||||
result[user] = sharedUsers.contains(user);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
});
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> shareFolder(String name, String path, Set<String> users) {
|
||||
var folder = Folder(0, name, Configuration.instance.getUsername(), path,
|
||||
users.toList(), -1);
|
||||
Future<void> updateFolder(Folder folder) {
|
||||
log("Updating folder: " + folder.toString());
|
||||
return _dio
|
||||
.put(Configuration.instance.getHttpEndpoint() + "/folders/",
|
||||
options: Options(
|
||||
headers: {"X-Auth-Token": Configuration.instance.getToken()}),
|
||||
data: folder.toMap())
|
||||
.then((response) => log(response.toString()));
|
||||
.then((response) => log(response.toString()))
|
||||
.catchError((error) => log(error.toString()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ class Folder {
|
|||
final String name;
|
||||
final String owner;
|
||||
final String deviceFolder;
|
||||
final List<String> sharedWith;
|
||||
final Set<String> sharedWith;
|
||||
final int updateTimestamp;
|
||||
|
||||
Folder(
|
||||
|
@ -27,7 +27,7 @@ class Folder {
|
|||
map['name'],
|
||||
map['owner'],
|
||||
map['deviceFolder'],
|
||||
List<String>.from(map['sharedWith']),
|
||||
Set<String>.from(map['sharedWith']),
|
||||
map['updateTimestamp'],
|
||||
);
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class Folder {
|
|||
'name': name,
|
||||
'owner': owner,
|
||||
'deviceFolder': deviceFolder,
|
||||
'sharedWith': sharedWith,
|
||||
'sharedWith': sharedWith.toList(),
|
||||
'updateTimestamp': updateTimestamp,
|
||||
};
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ class Folder {
|
|||
o.name == name &&
|
||||
o.owner == owner &&
|
||||
o.deviceFolder == deviceFolder &&
|
||||
listEquals(o.sharedWith, sharedWith) &&
|
||||
setEquals(o.sharedWith, sharedWith) &&
|
||||
o.updateTimestamp == updateTimestamp;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ class Photo {
|
|||
String localId;
|
||||
String title;
|
||||
String deviceFolder;
|
||||
int remoteFolderId;
|
||||
String remotePath;
|
||||
int createTimestamp;
|
||||
int syncTimestamp;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:photos/folder_service.dart';
|
||||
import 'package:photos/models/folder.dart';
|
||||
import 'package:photos/ui/loading_widget.dart';
|
||||
|
||||
class ShareFolderWidget extends StatefulWidget {
|
||||
|
@ -18,15 +19,18 @@ class ShareFolderWidget extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _ShareFolderWidgetState extends State<ShareFolderWidget> {
|
||||
Map<String, bool> _sharingStatus;
|
||||
Folder _folder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<Map<String, bool>>(
|
||||
future: FolderSharingService.instance.getSharingStatus(widget.path),
|
||||
future:
|
||||
FolderSharingService.instance.getFolder(widget.path).then((folder) {
|
||||
_folder = folder;
|
||||
return FolderSharingService.instance.getSharingStatus(folder);
|
||||
}),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
_sharingStatus = snapshot.data;
|
||||
return _getSharingDialog(snapshot.data);
|
||||
} else if (snapshot.hasError) {
|
||||
return Text(snapshot.error.toString());
|
||||
|
@ -52,16 +56,14 @@ class _ShareFolderWidgetState extends State<ShareFolderWidget> {
|
|||
child: Text("Share"),
|
||||
onPressed: () async {
|
||||
var sharedWith = Set<String>();
|
||||
for (var user in _sharingStatus.keys) {
|
||||
if (_sharingStatus[user]) {
|
||||
for (var user in sharingStatus.keys) {
|
||||
if (sharingStatus[user]) {
|
||||
sharedWith.add(user);
|
||||
}
|
||||
}
|
||||
await FolderSharingService.instance.shareFolder(
|
||||
"namewa",
|
||||
widget.path,
|
||||
sharedWith,
|
||||
);
|
||||
_folder.sharedWith.clear();
|
||||
_folder.sharedWith.addAll(sharedWith);
|
||||
await FolderSharingService.instance.updateFolder(_folder);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
|
|
Loading…
Add table
Reference in a new issue