mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 17:21:38 -05:00
Add loading spinner and checkmark
This commit is contained in:
parent
428e927432
commit
3c6f2e5ce3
4 changed files with 101 additions and 7 deletions
|
|
@ -72,3 +72,38 @@
|
|||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.leaflet-loading-control {
|
||||
padding: 5px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.2);
|
||||
margin: 10px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 18px;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.loading-spinner::before {
|
||||
content: '🔵';
|
||||
font-size: 18px;
|
||||
animation: spinner 1s linear infinite;
|
||||
}
|
||||
|
||||
.loading-spinner.done::before {
|
||||
content: '✅';
|
||||
animation: none;
|
||||
}
|
||||
|
||||
@keyframes spinner {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,12 @@
|
|||
class Api::V1::PhotosController < ApiController
|
||||
def index
|
||||
@photos = Rails.cache.fetch("photos_#{params[:start_date]}_#{params[:end_date]}", expires_in: 1.day) do
|
||||
Immich::RequestPhotos.new(current_api_user, start_date: params[:start_date], end_date: params[:end_date]).call
|
||||
end.reject { |photo| photo['type'].downcase == 'video' }
|
||||
Immich::RequestPhotos.new(
|
||||
current_api_user,
|
||||
start_date: params[:start_date],
|
||||
end_date: params[:end_date]
|
||||
).call.reject { |asset| asset['type'].downcase == 'video' }
|
||||
end
|
||||
|
||||
render json: @photos, status: :ok
|
||||
end
|
||||
|
|
|
|||
|
|
@ -786,6 +786,18 @@ export default class extends Controller {
|
|||
const MAX_RETRIES = 3;
|
||||
const RETRY_DELAY = 3000; // 3 seconds
|
||||
|
||||
// Create loading control
|
||||
const LoadingControl = L.Control.extend({
|
||||
onAdd: (map) => {
|
||||
const container = L.DomUtil.create('div', 'leaflet-loading-control');
|
||||
container.innerHTML = '<div class="loading-spinner"></div>';
|
||||
return container;
|
||||
}
|
||||
});
|
||||
|
||||
const loadingControl = new LoadingControl({ position: 'topleft' });
|
||||
this.map.addControl(loadingControl);
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
api_key: this.apiKey,
|
||||
|
|
@ -801,14 +813,42 @@ export default class extends Controller {
|
|||
const photos = await response.json();
|
||||
this.photoMarkers.clearLayers();
|
||||
|
||||
photos.forEach(photo => this.createPhotoMarker(photo));
|
||||
// Create a promise for each photo to track when it's fully loaded
|
||||
const photoLoadPromises = photos.map(photo => {
|
||||
return new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
const thumbnailUrl = `/api/v1/photos/${photo.id}/thumbnail.jpg?api_key=${this.apiKey}`;
|
||||
|
||||
img.onload = () => {
|
||||
this.createPhotoMarker(photo);
|
||||
resolve();
|
||||
};
|
||||
|
||||
img.onerror = () => {
|
||||
console.error(`Failed to load photo ${photo.id}`);
|
||||
resolve(); // Resolve anyway to not block other photos
|
||||
};
|
||||
|
||||
img.src = thumbnailUrl;
|
||||
});
|
||||
});
|
||||
|
||||
// Wait for all photos to be loaded and rendered
|
||||
await Promise.all(photoLoadPromises);
|
||||
|
||||
if (!this.map.hasLayer(this.photoMarkers)) {
|
||||
this.photoMarkers.addTo(this.map);
|
||||
}
|
||||
|
||||
// Show checkmark for 1 second before removing
|
||||
const loadingSpinner = document.querySelector('.loading-spinner');
|
||||
loadingSpinner.classList.add('done');
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching photos:', error);
|
||||
showFlashMessage('error', 'Failed to fetch photos');
|
||||
|
||||
if (retryCount < MAX_RETRIES) {
|
||||
console.log(`Retrying in ${RETRY_DELAY/1000} seconds... (Attempt ${retryCount + 1}/${MAX_RETRIES})`);
|
||||
|
|
@ -818,6 +858,9 @@ export default class extends Controller {
|
|||
} else {
|
||||
showFlashMessage('error', 'Failed to fetch photos after multiple attempts');
|
||||
}
|
||||
} finally {
|
||||
// Remove loading control after the delay
|
||||
this.map.removeControl(loadingControl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ class Immich::RequestPhotos
|
|||
end
|
||||
|
||||
def call
|
||||
retrieve_immich_data
|
||||
data = retrieve_immich_data
|
||||
|
||||
time_framed_data(data)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -20,11 +22,14 @@ class Immich::RequestPhotos
|
|||
def retrieve_immich_data
|
||||
page = 1
|
||||
data = []
|
||||
max_pages = 100_000 # Prevent infinite loop
|
||||
max_pages = 10_000 # Prevent infinite loop
|
||||
|
||||
while page <= max_pages
|
||||
body = request_body(page)
|
||||
response = JSON.parse(HTTParty.post(immich_api_base_url, headers: headers, body: body).body)
|
||||
response = JSON.parse(
|
||||
HTTParty.post(
|
||||
immich_api_base_url, headers: headers, body: request_body(page)
|
||||
).body
|
||||
)
|
||||
|
||||
items = response.dig('assets', 'items')
|
||||
|
||||
|
|
@ -58,4 +63,11 @@ class Immich::RequestPhotos
|
|||
|
||||
body.merge(createdBefore: end_date)
|
||||
end
|
||||
|
||||
def time_framed_data(data)
|
||||
data.select do |photo|
|
||||
photo['localDateTime'] >= start_date &&
|
||||
(end_date.nil? || photo['localDateTime'] <= end_date)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue