Skip to content

Commit

Permalink
Merge pull request #431 from ardriveapp/PE-618/Dont-allow-files-and-f…
Browse files Browse the repository at this point in the history
…olders-with-the-same-name

PE 618/Dont allow files and folders with the same name
  • Loading branch information
javdhu authored Apr 11, 2022
2 parents b12f088 + bf4bd5f commit cdc9a06
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 75 deletions.
11 changes: 5 additions & 6 deletions lib/blocs/folder_create/folder_create_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,11 @@ class FolderCreateCubit extends Cubit<FolderCreateState> {
AbstractControl<dynamic> control) async {
final String folderName = control.value;

// Check that the parent folder does not already have a folder with the input name.
final foldersWithName = await _driveDao
.foldersInFolderWithName(
driveId: driveId, parentFolderId: parentFolderId, name: folderName)
.get();
final nameAlreadyExists = foldersWithName.isNotEmpty;
final nameAlreadyExists = await _driveDao.doesEntityWithNameExist(
name: folderName,
driveId: driveId,
parentFolderId: parentFolderId,
);

if (nameAlreadyExists) {
control.markAsTouched();
Expand Down
31 changes: 12 additions & 19 deletions lib/blocs/fs_entry_move/fs_entry_move_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,6 @@ class FsEntryMoveCubit extends Cubit<FsEntryMoveState> {
);
}

Future<bool> entityNameExists({
required String name,
required String parentFolderId,
}) async {
final foldersWithName = await _driveDao
.foldersInFolderWithName(
driveId: driveId, parentFolderId: parentFolderId, name: name)
.get();
final filesWithName = await _driveDao
.filesInFolderWithName(
driveId: driveId, parentFolderId: parentFolderId, name: name)
.get();
return foldersWithName.isNotEmpty || filesWithName.isNotEmpty;
}

Future<void> submit() async {
try {
final state = this.state as FsEntryMoveFolderLoadSuccess;
Expand All @@ -100,10 +85,14 @@ class FsEntryMoveCubit extends Cubit<FsEntryMoveState> {
.folderById(driveId: driveId, folderId: folderId!)
.getSingle();

if (await entityNameExists(
final entityWithSameNameExists =
await _driveDao.doesEntityWithNameExist(
name: folder.name,
driveId: driveId,
parentFolderId: parentFolder.id,
)) {
);

if (entityWithSameNameExists) {
emit(FsEntryMoveNameConflict(name: folder.name));
return;
}
Expand Down Expand Up @@ -140,10 +129,14 @@ class FsEntryMoveCubit extends Cubit<FsEntryMoveState> {
path: '${parentFolder.path}/${file.name}',
lastUpdated: DateTime.now());

if (await entityNameExists(
final entityWithSameNameExists =
await _driveDao.doesEntityWithNameExist(
name: file.name,
driveId: driveId,
parentFolderId: parentFolder.id,
)) {
);

if (entityWithSameNameExists) {
emit(FsEntryMoveNameConflict(name: file.name));
return;
}
Expand Down
33 changes: 14 additions & 19 deletions lib/blocs/fs_entry_rename/fs_entry_rename_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -154,16 +154,14 @@ class FsEntryRenameCubit extends Cubit<FsEntryRenameState> {
return {AppValidationMessage.fsEntryNameUnchanged: true};
}

// Check that the current folder does not already have a folder with the target file name.
final foldersWithName = await _driveDao
.foldersInFolderWithName(
driveId: driveId,
parentFolderId: folder.parentFolderId,
name: newFolderName)
.get();
final nameAlreadyExists = foldersWithName.isNotEmpty;

if (nameAlreadyExists) {
final entityWithSameNameExists = await _driveDao.doesEntityWithNameExist(
name: newFolderName,
driveId: driveId,
// Will never be null since you can't rename root folder
parentFolderId: folder.parentFolderId!,
);

if (entityWithSameNameExists) {
control.markAsTouched();
return {AppValidationMessage.fsEntryNameAlreadyPresent: true};
}
Expand All @@ -182,16 +180,13 @@ class FsEntryRenameCubit extends Cubit<FsEntryRenameState> {
return {AppValidationMessage.fsEntryNameUnchanged: true};
}

// Check that the current folder does not already have a file with the target file name.
final filesWithName = await _driveDao
.filesInFolderWithName(
driveId: driveId,
parentFolderId: file.parentFolderId,
name: newFileName)
.get();
final nameAlreadyExists = filesWithName.isNotEmpty;
final entityWithSameNameExists = await _driveDao.doesEntityWithNameExist(
name: newFileName,
driveId: driveId,
parentFolderId: file.parentFolderId,
);

if (nameAlreadyExists) {
if (entityWithSameNameExists) {
control.markAsTouched();
return {AppValidationMessage.fsEntryNameAlreadyPresent: true};
}
Expand Down
94 changes: 71 additions & 23 deletions lib/blocs/upload/upload_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,20 @@ class UploadCubit extends Cubit<UploadState> {

/// Map of conflicting file ids keyed by their file names.
final Map<String, String> conflictingFiles = {};
final List<String> conflictingFolders = [];

bool fileSizeWithinBundleLimits(int size) => size < bundleSizeLimit;

UploadCubit(
{required this.driveId,
required this.folderId,
required this.files,
required ProfileCubit profileCubit,
required DriveDao driveDao,
required ArweaveService arweave,
required PstService pst,
required UploadPlanUtils uploadPlanUtils})
: _profileCubit = profileCubit,
UploadCubit({
required this.driveId,
required this.folderId,
required this.files,
required ProfileCubit profileCubit,
required DriveDao driveDao,
required ArweaveService arweave,
required PstService pst,
required UploadPlanUtils uploadPlanUtils,
}) : _profileCubit = profileCubit,
_driveDao = driveDao,
_arweave = arweave,
_pst = pst,
Expand All @@ -69,9 +70,43 @@ class UploadCubit extends Cubit<UploadState> {
///
/// If there's one, prompt the user to upload the file as a version of the existing one.
/// If there isn't one, prepare to upload the file.
Future<void> checkConflictingFolders() async {
emit(UploadPreparationInProgress());

for (final file in files) {
final fileName = file.name;
final existingFolderName = await _driveDao
.foldersInFolderWithName(
driveId: _targetDrive.id,
parentFolderId: _targetFolder.id,
name: fileName,
)
.map((f) => f.name)
.getSingleOrNull();

if (existingFolderName != null) {
conflictingFolders.add(existingFolderName);
}
}

if (conflictingFolders.isNotEmpty) {
emit(
UploadFolderNameConflict(
areAllFilesConflicting: conflictingFolders.length == files.length,
conflictingFileNames: conflictingFolders,
),
);
} else {
await checkConflictingFiles();
}
}

Future<void> checkConflictingFiles() async {
emit(UploadPreparationInProgress());

_removeFilesWithFolderNameConflicts();

for (final file in files) {
final fileName = file.name;
final existingFileId = await _driveDao
Expand All @@ -89,17 +124,21 @@ class UploadCubit extends Cubit<UploadState> {
}

if (conflictingFiles.isNotEmpty) {
emit(UploadFileConflict(
isAllFilesConflicting: conflictingFiles.length == files.length,
conflictingFileNames: conflictingFiles.keys.toList()));
emit(
UploadFileConflict(
areAllFilesConflicting: conflictingFiles.length == files.length,
conflictingFileNames: conflictingFiles.keys.toList(),
),
);
} else {
await prepareUploadPlanAndCostEstimates();
}
}

/// If `conflictingFileAction` is null, means that had no conflict.
Future<void> prepareUploadPlanAndCostEstimates(
{ConflictingFileActions? conflictingFileAction}) async {
Future<void> prepareUploadPlanAndCostEstimates({
ConflictingFileActions? conflictingFileAction,
}) async {
final profile = _profileCubit.state as ProfileLoggedIn;

if (await _profileCubit.checkIfWalletMismatch()) {
Expand All @@ -116,7 +155,7 @@ class UploadCubit extends Cubit<UploadState> {
_targetDrive.isPrivate ? privateFileSizeLimit : publicFileSizeLimit;

if (conflictingFileAction == ConflictingFileActions.Skip) {
_removeConflictingFiles();
_removeFilesWithFileNameConflicts();
}

final tooLargeFiles = [
Expand All @@ -131,23 +170,28 @@ class UploadCubit extends Cubit<UploadState> {
));
return;
}

final uploadPlan = await _uploadPlanUtils.xfilesToUploadPlan(
folderEntry: _targetFolder,
targetDrive: _targetDrive,
files: files,
cipherKey: profile.cipherKey,
wallet: profile.wallet,
conflictingFiles: conflictingFiles);
folderEntry: _targetFolder,
targetDrive: _targetDrive,
files: files,
cipherKey: profile.cipherKey,
wallet: profile.wallet,
conflictingFiles: conflictingFiles,
);

final costEstimate = await CostEstimate.create(
uploadPlan: uploadPlan,
arweaveService: _arweave,
pstService: _pst,
wallet: profile.wallet,
);

if (await _profileCubit.checkIfWalletMismatch()) {
emit(UploadWalletMismatch());
return;
}

emit(
UploadReady(
costEstimate: costEstimate,
Expand Down Expand Up @@ -220,10 +264,14 @@ class UploadCubit extends Cubit<UploadState> {
emit(UploadComplete());
}

void _removeConflictingFiles() {
void _removeFilesWithFileNameConflicts() {
files.removeWhere((element) => conflictingFiles.containsKey(element.name));
}

void _removeFilesWithFolderNameConflicts() {
files.removeWhere((element) => conflictingFolders.contains(element.name));
}

@override
void onError(Object error, StackTrace stackTrace) {
emit(UploadFailure());
Expand Down
14 changes: 12 additions & 2 deletions lib/blocs/upload/upload_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,27 @@ class UploadSigningInProgress extends UploadState {

class UploadFileConflict extends UploadState {
final List<String> conflictingFileNames;
final bool isAllFilesConflicting;
final bool areAllFilesConflicting;

UploadFileConflict({
required this.conflictingFileNames,
required this.isAllFilesConflicting,
required this.areAllFilesConflicting,
});

@override
List<Object> get props => [conflictingFileNames];
}

class UploadFolderNameConflict extends UploadFileConflict {
UploadFolderNameConflict({
required List<String> conflictingFileNames,
required bool areAllFilesConflicting,
}) : super(
conflictingFileNames: conflictingFileNames,
areAllFilesConflicting: areAllFilesConflicting,
);
}

class UploadFileTooLarge extends UploadState {
final List<String> tooLargeFileNames;
final bool isPrivate;
Expand Down
51 changes: 48 additions & 3 deletions lib/components/upload_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,60 @@ class UploadForm extends StatelessWidget {
if (state is UploadComplete || state is UploadWalletMismatch) {
Navigator.pop(context);
} else if (state is UploadPreparationInitialized) {
await context.read<UploadCubit>().checkConflictingFiles();
await context.read<UploadCubit>().checkConflictingFolders();
}
if (state is UploadWalletMismatch) {
Navigator.pop(context);
await context.read<ProfileCubit>().logoutProfile();
}
},
builder: (context, state) {
if (state is UploadFileConflict) {
if (state is UploadFolderNameConflict) {
return AppDialog(
title: appLocalizationsOf(context).duplicateFolders,
content: SizedBox(
width: kMediumDialogWidth,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
appLocalizationsOf(context)
.foldersWithTheSameNameAlreadyExists(
state.conflictingFileNames.length,
),
),
const SizedBox(height: 16),
Text(appLocalizationsOf(context).conflictingFiles),
const SizedBox(height: 8),
ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 320),
child: SingleChildScrollView(
child:
Text(state.conflictingFileNames.join(', ')))),
],
),
),
actions: <Widget>[
if (!state.areAllFilesConflicting)
TextButton(
style: ButtonStyle(
fixedSize:
MaterialStateProperty.all(Size.fromWidth(140))),
onPressed: () =>
context.read<UploadCubit>().checkConflictingFiles(),
child: Text(appLocalizationsOf(context).skipEmphasized),
),
TextButton(
style: ButtonStyle(
fixedSize:
MaterialStateProperty.all(Size.fromWidth(140))),
onPressed: () => Navigator.of(context).pop(false),
child: Text(appLocalizationsOf(context).cancelEmphasized),
),
],
);
} else if (state is UploadFileConflict) {
return AppDialog(
title: appLocalizationsOf(context)
.duplicateFiles(state.conflictingFileNames.length),
Expand Down Expand Up @@ -90,7 +135,7 @@ class UploadForm extends StatelessWidget {
),
),
actions: <Widget>[
if (!state.isAllFilesConflicting)
if (!state.areAllFilesConflicting)
TextButton(
style: ButtonStyle(
fixedSize:
Expand Down
Loading

0 comments on commit cdc9a06

Please sign in to comment.