Skip to content

Commit

Permalink
feat: Add multi-select and delete functionality for duplicates
Browse files Browse the repository at this point in the history
- Added multi-select feature for duplicates
- Added "Delete Selected" button to remove multiple duplicates at once
- Added search and filter functionality for duplicates by file path or name
- Improved error handling for batch deletion of duplicates
  • Loading branch information
eldertek committed Jul 5, 2024
1 parent 3733331 commit cd7cd26
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 14 deletions.
3 changes: 3 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ RUN apt-get update && apt-get install -y \
php-bcmath \
php-gmp \
wget \
make \
unzip \
git \
composer \
npm \
&& apt-get clean

# Supprimer les fichiers de configuration Apache par défaut
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## 1.1.11 - 2024-07-05
### Added
- Multi-select feature for duplicates
- "Delete Selected" button to remove multiple duplicates at once
- Search and filter functionality for duplicates by file path or name

### Fixed
- Improved error handling for batch deletion of duplicates

## 1.1.10 - 2024-05-21
### Added
- Support for Nextcloud 29
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ Normally the detection methods should be used in the order as listed, but if you
## Ignoring Duplicates
To prevent a folder from being scanned for duplicates, place a `.nodupefinder` file inside it. Any files in this folder will be excluded from the duplicate detection process.

## Search and Filter Duplicates
You can search and filter duplicates by file path or name using the search input at the top of the "Unacknowledged" and "Acknowledged" sections.

- To include files containing a specific term, simply type the term (e.g., `.png` to show all PNG files).
- To exclude files containing a specific term, prefix the term with an exclamation mark (e.g., `!.ptx` to exclude all PTX files).

## Command usage

`occ [-v] duplicates:ACTION [options]`
Expand Down
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<summary lang="fr">Économisez de l’espace en trouvant vos fichiers en doublon</summary>
<description>Are you tired of sifting through piles of files and folders, only to discover multiple copies of the same content cluttering your storage space?</description>
<description lang="fr">Vous en avez assez de passer au crible des piles de fichiers et de dossiers pour découvrir que plusieurs copies du même contenu encombrent votre espace de stockage ?</description>
<version>1.1.10</version>
<version>1.1.11</version>
<licence>agpl</licence>
<author mail="andrelauret@eclipse-technology.eu" >André Théo LAURET</author>
<namespace>DuplicateFinder</namespace>
Expand Down
2 changes: 1 addition & 1 deletion js/duplicatefinder-main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/duplicatefinder-settings.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "duplicatefinder",
"description": "Save some space by finding your duplicate files",
"version": "1.1.10",
"version": "1.1.11",
"author": "André Théo LAURET <andrelauret@eclipse-technology.eu>",
"contributors": [],
"bugs": {
Expand Down
20 changes: 19 additions & 1 deletion src/components/DuplicateDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
<a v-else class="acknowledge-link" @click="unOrAcknowledgeDuplicate(duplicate)" href="#">
{{ t('duplicatefinder', 'Acknowledge it') }}
</a>
<button @click="deleteSelectedDuplicates">{{ t('duplicatefinder', 'Delete Selected') }}</button>
</div>
<div v-for="(file, index) in duplicate.files" :key="file.id" class="file-display">
<input type="checkbox" v-model="selectedFiles" :value="file" />
<DuplicateFileDisplay :file="file" :index="index" @fileDeleted="removeFileFromListAndUpdate(file)">
</DuplicateFileDisplay>
</div>
Expand All @@ -29,7 +31,7 @@
</template>

<script>
import { acknowledgeDuplicate, unacknowledgeDuplicate } from '@/tools/api';
import { acknowledgeDuplicate, unacknowledgeDuplicate, deleteFiles } from '@/tools/api';
import { getFormattedSizeOfCurrentDuplicate, openFileInViewer, removeFileFromList } from '@/tools/utils';
import DuplicateFileDisplay from './DuplicateFileDisplay.vue';
Expand All @@ -40,6 +42,11 @@ export default {
props: {
duplicate: Object
},
data() {
return {
selectedFiles: []
};
},
methods: {
unOrAcknowledgeDuplicate(duplicate) {
// Acknowledge or unacknowledge the duplicate
Expand All @@ -62,6 +69,17 @@ export default {
this.$emit('lastFileDeleted', this.duplicate);
}
},
async deleteSelectedDuplicates() {
try {
await deleteFiles(this.selectedFiles);
this.selectedFiles.forEach(file => {
this.removeFileFromListAndUpdate(file);
});
this.selectedFiles = [];
} catch (error) {
console.error('Error deleting selected files:', error);
}
}
}
}
</script>
Expand Down
61 changes: 54 additions & 7 deletions src/components/DuplicateNavigation.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
<template>
<NcAppNavigation>
<template #list>
<!-- Search Input -->
<div class="search-container">
<input type="text" v-model="searchQuery" placeholder="Search by path or name" />
</div>
<!-- Navigation for Unacknowledged Duplicates -->
<NcAppNavigationItem :name="t('duplicatefinder', 'Unacknowledged')" :allowCollapse="true" :open="true">
<template #icon>
<CloseCircle :size="20" />
</template>
<template>
<DuplicateListItem v-for="duplicate in unacknowledgedDuplicates" :key="duplicate.id"
:duplicate="duplicate" :isActive="currentDuplicateId === duplicate.id"
@duplicate-selected="openDuplicate" />
<div v-for="duplicate in filteredUnacknowledgedDuplicates" :key="duplicate.id" class="duplicate-item">
<DuplicateListItem :duplicate="duplicate" :isActive="currentDuplicateId === duplicate.id"
@duplicate-selected="openDuplicate" />
</div>
</template>
</NcAppNavigationItem>
<!-- Navigation for Acknowledged Duplicates -->
Expand All @@ -18,9 +23,10 @@
<CheckCircle :size="20" />
</template>
<template>
<DuplicateListItem v-for="duplicate in acknowledgedDuplicates" :key="duplicate.id"
:duplicate="duplicate" :isActive="currentDuplicateId === duplicate.id"
@duplicate-selected="openDuplicate" />
<div v-for="duplicate in filteredAcknowledgedDuplicates" :key="duplicate.id" class="duplicate-item">
<DuplicateListItem :duplicate="duplicate" :isActive="currentDuplicateId === duplicate.id"
@duplicate-selected="openDuplicate" />
</div>
</template>
</NcAppNavigationItem>
</template>
Expand All @@ -36,10 +42,51 @@ import CheckCircle from 'vue-material-design-icons/CheckCircle';
export default {
components: { DuplicateListItem, NcAppNavigation, NcAppNavigationItem, CheckCircle, CloseCircle },
props: ['acknowledgedDuplicates', 'unacknowledgedDuplicates', 'currentDuplicateId'],
data() {
return {
searchQuery: ''
};
},
computed: {
filteredUnacknowledgedDuplicates() {
return this.unacknowledgedDuplicates.filter(duplicate => {
return this.filterDuplicate(duplicate);
});
},
filteredAcknowledgedDuplicates() {
return this.acknowledgedDuplicates.filter(duplicate => {
return this.filterDuplicate(duplicate);
});
}
},
methods: {
filterDuplicate(duplicate) {
const query = this.searchQuery.toLowerCase();
const filePathMatch = duplicate.files.some(file => file.path && file.path.toLowerCase().includes(query));
const excludeMatch = query.startsWith('!') && duplicate.files.some(file => (file.name && file.name.toLowerCase().includes(query.slice(1))) || (file.path && file.path.toLowerCase().includes(query.slice(1))));
return (filePathMatch || excludeMatch);
},
openDuplicate(duplicate) {
this.$emit('open-duplicate', duplicate);
}
}
};
</script>
</script>

<style scoped>
.search-container {
padding: 10px;
text-align: center;
}
.search-container input {
width: 100%;
padding: 5px;
border-radius: 5px;
border: 1px solid #ccc;
}
.duplicate-item {
width: 100%;
}
</style>
18 changes: 18 additions & 0 deletions src/tools/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,21 @@ export const deleteFile = async (file) => {
showErrorNotification(t('duplicatefinder', 'Error deleting file.'));
}
};

/**
* Deletes multiple files.
* @param {Array} files The files to delete.
* @returns {Promise<void>}
*/
export const deleteFiles = async (files) => {
try {
for (const file of files) {
await deleteFile(file);
}
showSuccessNotification(t('duplicatefinder', 'Selected files deleted successfully.'));
} catch (error) {
console.error('Error deleting selected files:', error);
showErrorNotification(t('duplicatefinder', 'Error deleting selected files.'));
throw error;
}
};

0 comments on commit cd7cd26

Please sign in to comment.