Add function to handle sharing actions
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import "dart:async";
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
@@ -15,6 +16,7 @@ import 'package:locker/services/collections/models/collection_file_item.dart';
|
||||
import 'package:locker/services/collections/models/collection_magic.dart';
|
||||
import 'package:locker/services/collections/models/diff.dart';
|
||||
import "package:locker/services/collections/models/public_url.dart";
|
||||
import "package:locker/services/collections/models/user.dart";
|
||||
import 'package:locker/services/configuration.dart';
|
||||
import "package:locker/services/files/sync/metadata_updater_service.dart";
|
||||
import 'package:locker/services/files/sync/models/file.dart';
|
||||
@@ -33,7 +35,11 @@ class CollectionApiClient {
|
||||
final _enteDio = Network.instance.enteDio;
|
||||
final _config = Configuration.instance;
|
||||
|
||||
Future<void> init() async {}
|
||||
late CollectionDB _db;
|
||||
|
||||
Future<void> init() async {
|
||||
_db = CollectionDB.instance;
|
||||
}
|
||||
|
||||
Future<List<Collection>> getCollections(int sinceTime) async {
|
||||
try {
|
||||
@@ -414,7 +420,7 @@ class CollectionApiClient {
|
||||
},
|
||||
);
|
||||
collection.publicURLs.add(PublicURL.fromMap(response.data["result"]));
|
||||
await CollectionDB.instance.updateCollections([collection]);
|
||||
await _db.updateCollections([collection]);
|
||||
CollectionService.instance.updateCollectionCache(collection);
|
||||
Bus.instance.fire(CollectionsUpdatedEvent());
|
||||
} catch (e, s) {
|
||||
@@ -429,7 +435,7 @@ class CollectionApiClient {
|
||||
"/collections/share-url/" + collection.id.toString(),
|
||||
);
|
||||
collection.publicURLs.clear();
|
||||
await CollectionDB.instance.updateCollections(List.from([collection]));
|
||||
await _db.updateCollections(List.from([collection]));
|
||||
CollectionService.instance.updateCollectionCache(collection);
|
||||
Bus.instance.fire(CollectionsUpdatedEvent());
|
||||
} on DioException catch (e) {
|
||||
@@ -451,7 +457,7 @@ class CollectionApiClient {
|
||||
// remove existing url information
|
||||
collection.publicURLs.clear();
|
||||
collection.publicURLs.add(PublicURL.fromMap(response.data["result"]));
|
||||
await CollectionDB.instance.updateCollections(List.from([collection]));
|
||||
await _db.updateCollections(List.from([collection]));
|
||||
CollectionService.instance.updateCollectionCache(collection);
|
||||
Bus.instance.fire(CollectionsUpdatedEvent());
|
||||
} on DioException catch (e) {
|
||||
@@ -464,6 +470,45 @@ class CollectionApiClient {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<User>> share(
|
||||
int collectionID,
|
||||
String email,
|
||||
String publicKey,
|
||||
CollectionParticipantRole role,
|
||||
) async {
|
||||
final collectionKey =
|
||||
CollectionService.instance.getCollectionKey(collectionID);
|
||||
final encryptedKey = CryptoUtil.sealSync(
|
||||
collectionKey,
|
||||
CryptoUtil.base642bin(publicKey),
|
||||
);
|
||||
try {
|
||||
final response = await _enteDio.post(
|
||||
"/collections/share",
|
||||
data: {
|
||||
"collectionID": collectionID,
|
||||
"email": email,
|
||||
"encryptedKey": CryptoUtil.bin2base64(encryptedKey),
|
||||
"role": role.toStringVal(),
|
||||
},
|
||||
);
|
||||
final sharees = <User>[];
|
||||
for (final user in response.data["sharees"]) {
|
||||
sharees.add(User.fromMap(user));
|
||||
}
|
||||
final collection = CollectionService.instance.getFromCache(collectionID);
|
||||
final updatedCollection = collection!.copyWith(sharees: sharees);
|
||||
CollectionService.instance.updateCollectionCache(updatedCollection);
|
||||
unawaited(_db.updateCollections([updatedCollection]));
|
||||
return sharees;
|
||||
} on DioException catch (e) {
|
||||
if (e.response?.statusCode == 402) {
|
||||
throw SharingNotPermittedForFreeAccountsError();
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CreateRequest {
|
||||
|
||||
@@ -12,6 +12,7 @@ import "package:locker/services/collections/collections_db.dart";
|
||||
import 'package:locker/services/collections/models/collection.dart';
|
||||
import "package:locker/services/collections/models/collection_items.dart";
|
||||
import "package:locker/services/collections/models/public_url.dart";
|
||||
import "package:locker/services/collections/models/user.dart";
|
||||
import 'package:locker/services/configuration.dart';
|
||||
import 'package:locker/services/files/sync/models/file.dart';
|
||||
import 'package:locker/services/trash/models/trash_item_request.dart';
|
||||
@@ -385,6 +386,71 @@ class CollectionService {
|
||||
}
|
||||
}
|
||||
|
||||
// getActiveCollections returns list of collections which are not deleted yet
|
||||
List<Collection> getActiveCollections() {
|
||||
return _collectionIDToCollections.values
|
||||
.toList()
|
||||
.where((element) => !element.isDeleted)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Returns Contacts(Users) that are relevant to the account owner.
|
||||
/// Note: "User" refers to the account owner in the points below.
|
||||
/// This includes:
|
||||
/// - Collaborators and viewers of collections owned by user
|
||||
/// - Owners of collections shared to user.
|
||||
/// - All collaborators of collections in which user is a collaborator or
|
||||
/// a viewer.
|
||||
/// - All family members of user.
|
||||
/// - All contacts linked to a person.
|
||||
List<User> getRelevantContacts() {
|
||||
final List<User> relevantUsers = [];
|
||||
final existingEmails = <String>{};
|
||||
final int ownerID = Configuration.instance.getUserID()!;
|
||||
final String ownerEmail = Configuration.instance.getEmail()!;
|
||||
existingEmails.add(ownerEmail);
|
||||
|
||||
for (final c in getActiveCollections()) {
|
||||
// Add collaborators and viewers of collections owned by user
|
||||
if (c.owner.id == ownerID) {
|
||||
for (final User u in c.sharees) {
|
||||
if (u.id != null && u.email.isNotEmpty) {
|
||||
if (!existingEmails.contains(u.email)) {
|
||||
relevantUsers.add(u);
|
||||
existingEmails.add(u.email);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (c.owner.id != null && c.owner.email.isNotEmpty) {
|
||||
// Add owners of collections shared with user
|
||||
if (!existingEmails.contains(c.owner.email)) {
|
||||
relevantUsers.add(c.owner);
|
||||
existingEmails.add(c.owner.email);
|
||||
}
|
||||
// Add collaborators of collections shared with user where user is a
|
||||
// viewer or a collaborator
|
||||
for (final User u in c.sharees) {
|
||||
if (u.id != null &&
|
||||
u.email.isNotEmpty &&
|
||||
u.email == ownerEmail &&
|
||||
(u.isCollaborator || u.isViewer)) {
|
||||
for (final User u in c.sharees) {
|
||||
if (u.id != null && u.email.isNotEmpty && u.isCollaborator) {
|
||||
if (!existingEmails.contains(u.email)) {
|
||||
relevantUsers.add(u);
|
||||
existingEmails.add(u.email);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return relevantUsers;
|
||||
}
|
||||
|
||||
String getPublicUrl(Collection c) {
|
||||
final PublicURL url = c.publicURLs.firstOrNull!;
|
||||
final Uri publicUrl = Uri.parse(url.url);
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
import "dart:async";
|
||||
|
||||
import "package:ente_accounts/services/user_service.dart";
|
||||
import "package:ente_ui/components/action_sheet_widget.dart";
|
||||
import 'package:ente_ui/components/buttons/button_widget.dart';
|
||||
import 'package:ente_ui/components/buttons/models/button_type.dart';
|
||||
import "package:ente_ui/components/dialog_widget.dart";
|
||||
import "package:ente_ui/components/progress_dialog.dart";
|
||||
import 'package:ente_ui/utils/dialog_util.dart';
|
||||
import "package:ente_utils/email_util.dart";
|
||||
import "package:ente_utils/share_utils.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:locker/core/errors.dart";
|
||||
import 'package:locker/l10n/l10n.dart';
|
||||
import "package:locker/services/collections/collections_api_client.dart";
|
||||
import 'package:locker/services/collections/collections_service.dart';
|
||||
import 'package:locker/services/collections/models/collection.dart';
|
||||
import "package:locker/services/configuration.dart";
|
||||
import "package:locker/ui/components/user_dialogs.dart";
|
||||
import 'package:locker/utils/snack_bar_utils.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
@@ -274,4 +283,130 @@ class CollectionActions {
|
||||
barrierDismissible: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> doesEmailHaveAccount(
|
||||
BuildContext context,
|
||||
String email, {
|
||||
bool showProgress = false,
|
||||
}) async {
|
||||
ProgressDialog? dialog;
|
||||
String? publicKey;
|
||||
if (showProgress) {
|
||||
dialog = createProgressDialog(
|
||||
context,
|
||||
context.l10n.sharing,
|
||||
isDismissible: true,
|
||||
);
|
||||
await dialog.show();
|
||||
}
|
||||
try {
|
||||
publicKey = await UserService.instance.getPublicKey(email);
|
||||
} catch (e) {
|
||||
await dialog?.hide();
|
||||
_logger.severe("Failed to get public key", e);
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
return false;
|
||||
}
|
||||
// getPublicKey can return null when no user is associated with given
|
||||
// email id
|
||||
if (publicKey == null || publicKey == '') {
|
||||
// todo: neeraj replace this as per the design where a new screen
|
||||
// is used for error. Do this change along with handling of network errors
|
||||
await showInviteDialog(context, email);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// addEmailToCollection returns true if add operation was successful
|
||||
Future<bool> addEmailToCollection(
|
||||
BuildContext context,
|
||||
Collection collection,
|
||||
String email,
|
||||
CollectionParticipantRole role, {
|
||||
bool showProgress = false,
|
||||
}) async {
|
||||
if (!isValidEmail(email)) {
|
||||
await showErrorDialog(
|
||||
context,
|
||||
context.l10n.invalidEmailAddress,
|
||||
context.l10n.enterValidEmail,
|
||||
);
|
||||
return false;
|
||||
} else if (email.trim() == Configuration.instance.getEmail()) {
|
||||
await showErrorDialog(
|
||||
context,
|
||||
context.l10n.oops,
|
||||
context.l10n.youCannotShareWithYourself,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
ProgressDialog? dialog;
|
||||
String? publicKey;
|
||||
if (showProgress) {
|
||||
dialog = createProgressDialog(
|
||||
context,
|
||||
context.l10n.sharing,
|
||||
isDismissible: true,
|
||||
);
|
||||
await dialog.show();
|
||||
}
|
||||
|
||||
try {
|
||||
publicKey = await UserService.instance.getPublicKey(email);
|
||||
} catch (e) {
|
||||
await dialog?.hide();
|
||||
_logger.severe("Failed to get public key", e);
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
return false;
|
||||
}
|
||||
// getPublicKey can return null when no user is associated with given
|
||||
// email id
|
||||
if (publicKey == null || publicKey == '') {
|
||||
// todo: neeraj replace this as per the design where a new screen
|
||||
// is used for error. Do this change along with handling of network errors
|
||||
await showDialogWidget(
|
||||
context: context,
|
||||
title: context.l10n.inviteToEnte,
|
||||
icon: Icons.info_outline,
|
||||
body: context.l10n.emailNoEnteAccount(email),
|
||||
isDismissible: true,
|
||||
buttons: [
|
||||
ButtonWidget(
|
||||
buttonType: ButtonType.neutral,
|
||||
icon: Icons.adaptive.share,
|
||||
labelText: context.l10n.sendInvite,
|
||||
isInAlert: true,
|
||||
onTap: () async {
|
||||
unawaited(
|
||||
shareText(
|
||||
context.l10n.shareTextRecommendUsingEnte,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
return false;
|
||||
} else {
|
||||
try {
|
||||
final newSharees = await CollectionApiClient.instance
|
||||
.share(collection.id, email, publicKey, role);
|
||||
await dialog?.hide();
|
||||
collection.updateSharees(newSharees);
|
||||
return true;
|
||||
} catch (e) {
|
||||
await dialog?.hide();
|
||||
if (e is SharingNotPermittedForFreeAccountsError) {
|
||||
await _showUnSupportedAlert(context);
|
||||
} else {
|
||||
_logger.severe("failed to share collection", e);
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user