search inside folders
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -557,32 +557,49 @@ def download_zip(room_id):
|
|||||||
@login_required
|
@login_required
|
||||||
def search_room_files(room_id):
|
def search_room_files(room_id):
|
||||||
"""
|
"""
|
||||||
Search for files in a room by name.
|
Search for files in a room by name, including files in subfolders.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
room_id (int): ID of the room to search in
|
room_id (int): ID of the room to search in
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
JSON response containing matching files
|
JSON response containing matching files with their full paths
|
||||||
"""
|
"""
|
||||||
room = Room.query.get_or_404(room_id)
|
room = Room.query.get_or_404(room_id)
|
||||||
if not user_has_permission(room, 'can_view'):
|
if not user_has_permission(room, 'can_view'):
|
||||||
abort(403)
|
abort(403)
|
||||||
query = request.args.get('query', '').strip().lower()
|
query = request.args.get('query', '').strip().lower()
|
||||||
|
|
||||||
# Search RoomFile for this room
|
# Search RoomFile for this room
|
||||||
files = RoomFile.query.filter(RoomFile.room_id==room_id).all()
|
files = RoomFile.query.filter(RoomFile.room_id==room_id, RoomFile.deleted==False).all()
|
||||||
matches = []
|
matches = []
|
||||||
|
|
||||||
for f in files:
|
for f in files:
|
||||||
if query in f.name.lower():
|
# Create full path by combining folder path and filename
|
||||||
|
full_path = f"{f.path}/{f.name}" if f.path else f.name
|
||||||
|
|
||||||
|
if query in f.name.lower() or query in full_path.lower():
|
||||||
|
uploader_full_name = None
|
||||||
|
uploader_profile_pic = None
|
||||||
|
if f.uploader:
|
||||||
|
uploader_full_name = f.uploader.username
|
||||||
|
if getattr(f.uploader, 'last_name', None):
|
||||||
|
uploader_full_name += ' ' + f.uploader.last_name
|
||||||
|
uploader_profile_pic = f.uploader.profile_picture if getattr(f.uploader, 'profile_picture', None) else None
|
||||||
|
|
||||||
matches.append({
|
matches.append({
|
||||||
'name': f.name,
|
'name': f.name,
|
||||||
'type': f.type,
|
'type': f.type,
|
||||||
'size': f.size if f.type == 'file' else '-',
|
'size': f.size if f.type == 'file' else '-',
|
||||||
'modified': f.modified,
|
'modified': f.modified,
|
||||||
'uploaded_by': f.uploader.username if f.uploader else None,
|
'uploaded_by': uploader_full_name,
|
||||||
|
'uploader_profile_pic': uploader_profile_pic,
|
||||||
'uploaded_at': f.uploaded_at.isoformat() if f.uploaded_at else None,
|
'uploaded_at': f.uploaded_at.isoformat() if f.uploaded_at else None,
|
||||||
'path': f.path,
|
'path': f.path,
|
||||||
|
'full_path': full_path, # Add full path to the response
|
||||||
|
'starred': current_user in f.starred_by
|
||||||
})
|
})
|
||||||
|
|
||||||
return jsonify(matches)
|
return jsonify(matches)
|
||||||
|
|
||||||
@room_files_bp.route('/<int:room_id>/move', methods=['POST'])
|
@room_files_bp.route('/<int:room_id>/move', methods=['POST'])
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export class SearchManager {
|
|||||||
this.roomManager = roomManager;
|
this.roomManager = roomManager;
|
||||||
this.searchInput = document.getElementById('quickSearchInput');
|
this.searchInput = document.getElementById('quickSearchInput');
|
||||||
this.clearSearchBtn = document.getElementById('clearSearchBtn');
|
this.clearSearchBtn = document.getElementById('clearSearchBtn');
|
||||||
|
this.isSearching = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,9 +36,11 @@ export class SearchManager {
|
|||||||
// Create debounced search function
|
// Create debounced search function
|
||||||
const debouncedSearch = this.debounce((searchTerm) => {
|
const debouncedSearch = this.debounce((searchTerm) => {
|
||||||
if (searchTerm) {
|
if (searchTerm) {
|
||||||
|
this.isSearching = true;
|
||||||
this.performSearch(searchTerm);
|
this.performSearch(searchTerm);
|
||||||
this.clearSearchBtn.style.display = 'block';
|
this.clearSearchBtn.style.display = 'block';
|
||||||
} else {
|
} else {
|
||||||
|
this.isSearching = false;
|
||||||
this.roomManager.fileManager.fetchFiles(); // Reset to show all files
|
this.roomManager.fileManager.fetchFiles(); // Reset to show all files
|
||||||
this.clearSearchBtn.style.display = 'none';
|
this.clearSearchBtn.style.display = 'none';
|
||||||
}
|
}
|
||||||
@@ -53,6 +56,7 @@ export class SearchManager {
|
|||||||
if (this.clearSearchBtn) {
|
if (this.clearSearchBtn) {
|
||||||
this.clearSearchBtn.addEventListener('click', () => {
|
this.clearSearchBtn.addEventListener('click', () => {
|
||||||
this.searchInput.value = '';
|
this.searchInput.value = '';
|
||||||
|
this.isSearching = false;
|
||||||
this.roomManager.fileManager.fetchFiles(); // Reset to show all files
|
this.roomManager.fileManager.fetchFiles(); // Reset to show all files
|
||||||
this.clearSearchBtn.style.display = 'none';
|
this.clearSearchBtn.style.display = 'none';
|
||||||
});
|
});
|
||||||
@@ -60,20 +64,25 @@ export class SearchManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the search operation on the current file list.
|
* Performs the search operation using the server-side search endpoint.
|
||||||
* Filters files based on name and type matching the search term.
|
|
||||||
* @param {string} searchTerm - The term to search for
|
* @param {string} searchTerm - The term to search for
|
||||||
*/
|
*/
|
||||||
performSearch(searchTerm) {
|
performSearch(searchTerm) {
|
||||||
if (!this.roomManager.fileManager.currentFiles) return;
|
// Fetch all files from the server for searching
|
||||||
|
fetch(`/api/rooms/${this.roomManager.roomId}/search?query=${encodeURIComponent(searchTerm)}`)
|
||||||
const filteredFiles = this.roomManager.fileManager.currentFiles.filter(file => {
|
.then(response => response.json())
|
||||||
const searchLower = searchTerm.toLowerCase();
|
.then(files => {
|
||||||
return file.name.toLowerCase().includes(searchLower) ||
|
// Modify the file objects to show full paths in search results
|
||||||
(file.type && file.type.toLowerCase().includes(searchLower));
|
const modifiedFiles = files.map(file => ({
|
||||||
});
|
...file,
|
||||||
|
displayName: this.isSearching ? file.full_path : file.name
|
||||||
this.roomManager.viewManager.renderFiles(filteredFiles);
|
}));
|
||||||
|
|
||||||
|
this.roomManager.viewManager.renderFiles(modifiedFiles);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error performing search:', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -255,6 +255,11 @@ export class ViewManager {
|
|||||||
const size = isFolder ? '-' : this.formatFileSize(file.size);
|
const size = isFolder ? '-' : this.formatFileSize(file.size);
|
||||||
const modified = new Date(file.modified).toLocaleString();
|
const modified = new Date(file.modified).toLocaleString();
|
||||||
|
|
||||||
|
// Create file name element
|
||||||
|
const name = document.createElement('span');
|
||||||
|
name.className = 'file-name';
|
||||||
|
name.textContent = file.displayName || file.name;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<tr data-index="${index}" class="file-row" style="cursor: pointer;"
|
<tr data-index="${index}" class="file-row" style="cursor: pointer;"
|
||||||
onclick="window.roomManager.fileManager.updateSelection(${index}, event)"
|
onclick="window.roomManager.fileManager.updateSelection(${index}, event)"
|
||||||
@@ -268,7 +273,7 @@ export class ViewManager {
|
|||||||
<i class="fas ${icon}" style="font-size:1.5rem;color:${isFolder ? 'var(--primary-color)' : 'var(--secondary-color)'}"></i>
|
<i class="fas ${icon}" style="font-size:1.5rem;color:${isFolder ? 'var(--primary-color)' : 'var(--secondary-color)'}"></i>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="file-name">${file.name}</span>
|
${name.outerHTML}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-muted">${size}</td>
|
<td class="text-muted">${size}</td>
|
||||||
<td class="text-muted">${modified}</td>
|
<td class="text-muted">${modified}</td>
|
||||||
@@ -294,6 +299,11 @@ export class ViewManager {
|
|||||||
const size = isFolder ? '-' : this.formatFileSize(file.size);
|
const size = isFolder ? '-' : this.formatFileSize(file.size);
|
||||||
const modified = new Date(file.modified).toLocaleString();
|
const modified = new Date(file.modified).toLocaleString();
|
||||||
|
|
||||||
|
// Create file name element
|
||||||
|
const name = document.createElement('span');
|
||||||
|
name.className = 'file-name';
|
||||||
|
name.textContent = file.displayName || file.name;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="col-12 col-sm-6 col-md-4 col-lg-3 mb-3">
|
<div class="col-12 col-sm-6 col-md-4 col-lg-3 mb-3">
|
||||||
<div class="card file-card h-100 border-0 shadow-sm position-relative"
|
<div class="card file-card h-100 border-0 shadow-sm position-relative"
|
||||||
@@ -309,7 +319,7 @@ export class ViewManager {
|
|||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<i class="fas ${icon}" style="font-size:2.5rem;color:${isFolder ? 'var(--primary-color)' : 'var(--secondary-color)'};"></i>
|
<i class="fas ${icon}" style="font-size:2.5rem;color:${isFolder ? 'var(--primary-color)' : 'var(--secondary-color)'};"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="fw-bold file-name-ellipsis mb-1" title="${file.name}">${file.name}</div>
|
<div class="fw-bold file-name-ellipsis mb-1" title="${file.name}">${name.outerHTML}</div>
|
||||||
<div class="text-muted" style="font-size:0.85rem;">${modified}</div>
|
<div class="text-muted" style="font-size:0.85rem;">${modified}</div>
|
||||||
<div class="text-muted mb-2" style="font-size:0.85rem;">${size}</div>
|
<div class="text-muted mb-2" style="font-size:0.85rem;">${size}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user