@@ -1592,6 +1592,10 @@ <h1 class="page-title">EzData Push</h1>
15921592let confirmCallback = null ;
15931593let photoPage = 1 ;
15941594let photoTotalPages = 1 ;
1595+ let photoPageCache = { } ; // { [page]: { total_pages, names: [...] } }
1596+ let photoCache = { } ; // { [name]: { name, url } }
1597+ let photoTotal = 0 ;
1598+ let photoPageLoading = false ;
15951599
15961600// ==================== API Helper ====================
15971601async function apiCall ( method , url , body , isFormData ) {
@@ -1600,15 +1604,23 @@ <h1 class="page-title">EzData Push</h1>
16001604 opts . body = isFormData ? body : JSON . stringify ( body ) ;
16011605 if ( ! isFormData ) opts . headers = { "Content-Type" : "application/json" } ;
16021606 }
1607+ let res ;
16031608 try {
1604- const res = await fetch ( url , opts ) ;
1605- const data = await res . json ( ) ;
1606- if ( ! res . ok ) throw new Error ( data . message || `HTTP ${ res . status } ` ) ;
1607- return data ;
1609+ res = await fetch ( url , opts ) ;
16081610 } catch ( e ) {
1609- if ( e . message && ! e . message . startsWith ( "HTTP" ) ) throw e ;
1611+ throw e ;
1612+ }
1613+
1614+ let data = null ;
1615+ try {
1616+ data = await res . json ( ) ;
1617+ } catch ( e ) {
1618+ if ( ! res . ok ) throw new Error ( `HTTP ${ res . status } ` ) ;
16101619 throw new Error ( "Network request failed" ) ;
16111620 }
1621+
1622+ if ( ! res . ok ) throw new Error ( data . message || `HTTP ${ res . status } ` ) ;
1623+ return data ;
16121624}
16131625
16141626// ==================== Toast ====================
@@ -2654,11 +2666,6 @@ <h1 class="page-title">EzData Push</h1>
26542666 requestAnimationFrame ( ( ) => {
26552667 rerenderCompositionLayout ( false ) ;
26562668 } ) ;
2657-
2658- if ( currentMode === "mode_1" ) {
2659- photoPage = 1 ;
2660- loadPhotoList ( photoPage ) ;
2661- }
26622669}
26632670
26642671// ==================== Unified Settings Modal ====================
@@ -3271,10 +3278,17 @@ <h1 class="page-title">EzData Push</h1>
32713278 formData . append ( "action" , "upload_only" ) ;
32723279 formData . append ( "algorithm" , displayMode ) ;
32733280 showToast ( "Uploading..." , "" ) ;
3274- await apiCall ( "POST" , "/api/photos/upload" , formData , true ) ;
3281+ const resp = await apiCall ( "POST" , "/api/photos/upload" , formData , true ) ;
32753282 showToast ( "Uploaded successfully" , "success" ) ;
3276- photoPage = 9999 ;
3277- loadPhotoList ( photoPage ) ;
3283+ if ( displayMode === "nearest" && resp && resp . name ) {
3284+ // Nearest photo inserts at front — all page boundaries shift, clear page cache
3285+ photoPageCache = { } ;
3286+ const m = resp . name . match ( / ^ i m a g e N 0 * ( \d + ) / i) ;
3287+ if ( m ) photoPage = Math . ceil ( Number ( m [ 1 ] ) / getPhotoPerPage ( ) ) ;
3288+ } else {
3289+ photoPage = 9999 ;
3290+ }
3291+ await loadPhotoList ( photoPage , /* forceRefresh */ true ) ;
32783292 loadStorage ( ) ;
32793293 } catch ( e ) { showToast ( e . message , "error" ) ; }
32803294}
@@ -3300,10 +3314,18 @@ <h1 class="page-title">EzData Push</h1>
33003314 formData . append ( "algorithm" , displayMode ) ;
33013315
33023316 showToast ( "Uploading..." , "" ) ;
3303- await apiCall ( "POST" , "/api/photos/upload" , formData , true ) ;
3317+ const resp = await apiCall ( "POST" , "/api/photos/upload" , formData , true ) ;
33043318 showToast ( "Uploaded & display updated" , "success" ) ;
3305- photoPage = 9999 ;
3306- loadPhotoList ( photoPage ) ;
3319+ // Compute which page the new photo landed on from its name
3320+ if ( displayMode === "nearest" && resp && resp . name ) {
3321+ // Nearest photo inserts at front — all pages shift, clear page cache
3322+ photoPageCache = { } ;
3323+ const m = resp . name . match ( / ^ i m a g e N 0 * ( \d + ) / i) ;
3324+ if ( m ) photoPage = Math . ceil ( Number ( m [ 1 ] ) / getPhotoPerPage ( ) ) ;
3325+ } else {
3326+ photoPage = 9999 ; // Dither appends at end, only last page changes
3327+ }
3328+ await loadPhotoList ( photoPage , /* forceRefresh */ true ) ;
33073329 loadStorage ( ) ;
33083330 } catch ( e ) { showToast ( e . message , "error" ) ; }
33093331}
@@ -3315,20 +3337,43 @@ <h1 class="page-title">EzData Push</h1>
33153337 return 12 ;
33163338}
33173339
3318- async function loadPhotoList ( page ) {
3340+ async function loadPhotoList ( page , forceRefresh = false ) {
33193341 page = page || photoPage ;
33203342 const perPage = getPhotoPerPage ( ) ;
3343+
3344+ // Serve from cache if this page was loaded before
3345+ if ( ! forceRefresh && photoPageCache [ page ] ) {
3346+ photoPage = page ;
3347+ renderPhotoGridFromNames ( photoPageCache [ page ] ) ;
3348+ renderPagination ( ) ;
3349+ return ;
3350+ }
3351+
3352+ photoPageLoading = true ;
33213353 try {
33223354 const data = await apiCall ( "GET" , `/api/photos/list?page=${ page } &per_page=${ perPage } ` ) ;
3323- photoPage = data . page ;
3324- photoTotalPages = data . total_pages ;
3325- renderPhotoGrid ( data . photos , data . total ) ;
3326- renderPagination ( ) ;
3355+ if ( data . page === page || page === photoPage ) {
3356+ // Store per-photo data globally
3357+ const names = [ ] ;
3358+ data . photos . forEach ( p => {
3359+ photoCache [ p . name ] = { name : p . name , url : p . url } ;
3360+ names . push ( p . name ) ;
3361+ } ) ;
3362+ photoPageCache [ data . page ] = names ;
3363+ photoPage = data . page ;
3364+ photoTotalPages = data . total_pages ;
3365+ photoTotal = data . total ;
3366+ renderPhotoGridFromNames ( names ) ;
3367+ renderPagination ( ) ;
3368+ }
33273369 } catch ( e ) { /* silent */ }
3370+ photoPageLoading = false ;
33283371}
33293372
33303373function goPhotoPage ( page ) {
33313374 if ( page < 1 || page > photoTotalPages ) return ;
3375+ photoPage = page ;
3376+ renderPagination ( ) ;
33323377 loadPhotoList ( page ) ;
33333378}
33343379
@@ -3348,25 +3393,26 @@ <h1 class="page-title">EzData Push</h1>
33483393 next . disabled = photoPage >= photoTotalPages ;
33493394}
33503395
3351- function renderPhotoGrid ( photos , total ) {
3396+ function renderPhotoGridFromNames ( names ) {
33523397 const grid = document . getElementById ( "photo-grid" ) ;
33533398 const count = document . getElementById ( "photo-count" ) ;
3354- count . textContent = total > 0 ? `(${ total } )` : "" ;
3399+ count . textContent = photoTotal > 0 ? `(${ photoTotal } )` : "" ;
33553400
3356- if ( photos . length === 0 ) {
3401+ if ( names . length === 0 ) {
33573402 grid . innerHTML = `<div style="color:#999;font-size:14px;text-align:center;grid-column:1/-1;padding:20px;">No photos</div>` ;
33583403 return ;
33593404 }
3360- grid . innerHTML = photos . map ( p =>
3361- `<div class="photo-card">
3405+ grid . innerHTML = names . map ( name => {
3406+ const p = photoCache [ name ] || { name, url : "" } ;
3407+ return `<div class="photo-card">
33623408 <img class="thumb" src="${ escapeHtml ( p . url ) } " alt="${ escapeHtml ( p . name ) } " loading="lazy">
33633409 <div class="photo-info"><div class="photo-name" title="${ escapeHtml ( p . name ) } ">${ escapeHtml ( p . name ) } </div></div>
33643410 <div class="photo-actions">
33653411 <button class="btn btn-outline btn-sm" onclick="displayPhoto('${ escapeAttr ( p . name ) } ')">View</button>
33663412 <button class="btn btn-danger btn-sm" onclick="deletePhoto('${ escapeAttr ( p . name ) } ')">Del</button>
33673413 </div>
3368- </div>`
3369- ) . join ( "" ) ;
3414+ </div>` ;
3415+ } ) . join ( "" ) ;
33703416}
33713417
33723418function escapeAttr ( s ) { return s . replace ( / ' / g, "\\'" ) . replace ( / " / g, """ ) ; }
@@ -3385,13 +3431,23 @@ <h1 class="page-title">EzData Push</h1>
33853431 await apiCall ( "DELETE" , "/api/photos/delete?name=" + encodeURIComponent ( name ) ) ;
33863432 showToast ( "Deleted" , "success" ) ;
33873433 loadStorage ( ) ;
3388- // 如果当前页删空则回退一页
3389- if ( photoPage > 1 ) {
3434+ // Remove only this photo from caches
3435+ delete photoCache [ name ] ;
3436+ photoTotal = Math . max ( 0 , photoTotal - 1 ) ;
3437+ for ( const key of Object . keys ( photoPageCache ) ) {
3438+ photoPageCache [ key ] = photoPageCache [ key ] . filter ( n => n !== name ) ;
3439+ }
3440+ // If the current page is now empty, step back
3441+ const names = photoPageCache [ photoPage ] ;
3442+ if ( names && names . length === 0 && photoPage > 1 ) {
3443+ photoPage -- ;
3444+ } else if ( ! names && photoPage > 1 ) {
3445+ // Page not cached — check server whether it's now empty
33903446 const perPage = getPhotoPerPage ( ) ;
33913447 const data = await apiCall ( "GET" , `/api/photos/list?page=${ photoPage } &per_page=${ perPage } ` ) ;
3392- if ( data . photos . length === 0 && photoPage > 1 ) { photoPage -- ; }
3448+ if ( data . photos . length === 0 ) { photoPage -- ; }
33933449 }
3394- loadPhotoList ( photoPage ) ;
3450+ loadPhotoList ( photoPage , /* forceRefresh */ true ) ;
33953451 } catch ( e ) { showToast ( e . message , "error" ) ; }
33963452 } ) ;
33973453}
0 commit comments