feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
import { locale } from '$lib/stores/preferences.store' ;
2024-02-14 08:09:49 -05:00
import { getKey } from '$lib/utils' ;
2025-03-18 10:14:46 -04:00
import { CancellableTask } from '$lib/utils/cancellable-task' ;
import {
getJustifiedLayoutFromAssets ,
getPosition ,
type CommonLayoutOptions ,
type CommonPosition ,
} from '$lib/utils/layout-utils' ;
import { formatDateGroupTitle , fromLocalDateTime } from '$lib/utils/timeline-util' ;
import { TUNABLES } from '$lib/utils/tunables' ;
import { getAssetInfo , getTimeBucket , getTimeBuckets , TimeBucketSize , type AssetResponseDto } from '@immich/sdk' ;
import { debounce , isEqual , throttle } from 'lodash-es' ;
2023-10-06 15:48:11 -05:00
import { DateTime } from 'luxon' ;
2024-07-01 00:29:10 +02:00
import { t } from 'svelte-i18n' ;
2025-03-18 10:14:46 -04:00
2025-03-04 21:34:53 -05:00
import { SvelteSet } from 'svelte/reactivity' ;
2024-07-01 00:29:10 +02:00
import { get , writable , type Unsubscriber } from 'svelte/store' ;
2023-08-03 11:44:12 -04:00
import { handleError } from '../utils/handle-error' ;
2024-02-16 21:43:40 +01:00
import { websocketEvents } from './websocket' ;
2025-03-04 21:34:53 -05:00
2025-03-18 10:14:46 -04:00
const {
TIMELINE : { INTERSECTION_EXPAND_TOP , INTERSECTION_EXPAND_BOTTOM } ,
} = TUNABLES ;
const THUMBNAIL_HEIGHT = 235 ;
const GAP = 12 ;
const HEADER = 49 ; //(1.5rem)
2025-03-04 21:34:53 -05:00
2024-02-14 06:38:57 -08:00
type AssetApiGetTimeBucketsRequest = Parameters < typeof getTimeBuckets > [ 0 ] ;
2025-03-18 10:14:46 -04:00
export type AssetStoreOptions = Omit < AssetApiGetTimeBucketsRequest , 'size' > & {
timelineAlbumId? : string ;
deferInit? : boolean ;
} ;
2025-03-19 11:57:44 -04:00
export type AssetStoreLayoutOptions = {
rowHeight : number ;
} ;
2025-03-18 10:14:46 -04:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function updateObject ( target : any , source : any ) : boolean {
if ( ! target ) {
return false ;
}
let updated = false ;
for ( const key in source ) {
// eslint-disable-next-line no-prototype-builtins
if ( ! source . hasOwnProperty ( key ) ) {
continue ;
}
if ( typeof target [ key ] === 'object' ) {
updated = updated || updateObject ( target [ key ] , source [ key ] ) ;
} else {
// Otherwise, directly copy the value
if ( target [ key ] !== source [ key ] ) {
target [ key ] = source [ key ] ;
updated = true ;
}
}
}
return updated ;
}
2025-03-19 11:55:50 -04:00
export function assetSnapshot ( asset : AssetResponseDto ) {
return $state . snapshot ( asset ) ;
}
export function assetsSnapshot ( assets : AssetResponseDto [ ] ) {
return assets . map ( ( a ) = > $state . snapshot ( a ) ) ;
}
2025-03-18 10:14:46 -04:00
class IntersectingAsset {
// --- public ---
readonly # group : AssetDateGroup ;
intersecting = $derived . by ( ( ) = > {
if ( ! this . position ) {
return false ;
}
const store = this . # group . bucket . store ;
const topWindow = store . visibleWindow . top + HEADER - INTERSECTION_EXPAND_TOP ;
const bottomWindow = store . visibleWindow . bottom + HEADER + INTERSECTION_EXPAND_BOTTOM ;
const positionTop = this . # group . absoluteDateGroupTop + this . position . top ;
const positionBottom = positionTop + this . position . height ;
const intersecting =
( positionTop >= topWindow && positionTop < bottomWindow ) ||
( positionBottom >= topWindow && positionBottom < bottomWindow ) ||
( positionTop < topWindow && positionBottom >= bottomWindow ) ;
return intersecting ;
} ) ;
position : CommonPosition | undefined = $state ( ) ;
asset : AssetResponseDto | undefined = $state ( ) ;
id : string = $derived . by ( ( ) = > this . asset ! . id ) ;
constructor ( group : AssetDateGroup , asset : AssetResponseDto ) {
this . # group = group ;
this . asset = asset ;
}
}
type AssetOperation = ( asset : AssetResponseDto ) = > { remove : boolean } ;
type MoveAsset = { asset : AssetResponseDto ; year : number ; month : number } ;
export class AssetDateGroup {
// --- public
readonly bucket : AssetBucket ;
readonly index : number ;
readonly date : DateTime ;
readonly dayOfMonth : number ;
intersetingAssets : IntersectingAsset [ ] = $state ( [ ] ) ;
dodo : IntersectingAsset [ ] = $state ( [ ] ) ;
height = $state ( 0 ) ;
width = $state ( 0 ) ;
intersecting = $derived . by ( ( ) = > this . intersetingAssets . some ( ( asset ) = > asset . intersecting ) ) ;
// --- private
top : number = $state ( 0 ) ;
left : number = $state ( 0 ) ;
row = $state ( 0 ) ;
col = $state ( 0 ) ;
constructor ( bucket : AssetBucket , index : number , date : DateTime , dayOfMonth : number ) {
this . index = index ;
this . bucket = bucket ;
this . date = date ;
this . dayOfMonth = dayOfMonth ;
}
sortAssets() {
this . intersetingAssets . sort ( ( a , b ) = > {
const aDate = DateTime . fromISO ( a . asset ! . fileCreatedAt ) . toUTC ( ) ;
const bDate = DateTime . fromISO ( b . asset ! . fileCreatedAt ) . toUTC ( ) ;
return bDate . diff ( aDate ) . milliseconds ;
} ) ;
}
getFirstAsset() {
return this . intersetingAssets [ 0 ] ? . asset ;
}
getRandomAsset() {
const random = Math . floor ( Math . random ( ) * this . intersetingAssets . length ) ;
return this . intersetingAssets [ random ] ;
}
getAssets() {
return this . intersetingAssets . map ( ( intersetingAsset ) = > intersetingAsset . asset ! ) ;
}
runAssetOperation ( ids : Set < string > , operation : AssetOperation ) {
if ( ids . size === 0 ) {
return {
moveAssets : [ ] as MoveAsset [ ] ,
processedIds : new Set < string > ( ) ,
unprocessedIds : ids ,
changedGeometry : false ,
} ;
}
const unprocessedIds = new Set < string > ( ids ) ;
const processedIds = new Set < string > ( ) ;
const moveAssets : MoveAsset [ ] = [ ] ;
let changedGeometry = false ;
for ( const assetId of unprocessedIds ) {
const index = this . intersetingAssets . findIndex ( ( ia ) = > ia . id == assetId ) ;
if ( index !== - 1 ) {
const asset = this . intersetingAssets [ index ] . asset ! ;
const oldTime = asset . localDateTime ;
let { remove } = operation ( asset ) ;
const newTime = asset . localDateTime ;
if ( oldTime !== newTime ) {
const utc = DateTime . fromISO ( asset . localDateTime ) . toUTC ( ) . startOf ( 'month' ) ;
const year = utc . get ( 'year' ) ;
const month = utc . get ( 'month' ) ;
if ( this . bucket . year !== year || this . bucket . month !== month ) {
remove = true ;
moveAssets . push ( { asset , year , month } ) ;
}
}
unprocessedIds . delete ( assetId ) ;
processedIds . add ( assetId ) ;
if ( remove || this . bucket . store . isExcluded ( asset ) ) {
this . intersetingAssets . splice ( index , 1 ) ;
changedGeometry = true ;
}
}
}
return { moveAssets , processedIds , unprocessedIds , changedGeometry } ;
}
layout ( options : CommonLayoutOptions ) {
const assets = this . intersetingAssets . map ( ( intersetingAsset ) = > intersetingAsset . asset ! ) ;
const geometry = getJustifiedLayoutFromAssets ( assets , options ) ;
this . width = geometry . containerWidth ;
this . height = assets . length === 0 ? 0 : geometry.containerHeight ;
for ( let i = 0 ; i < this . intersetingAssets . length ; i ++ ) {
const position = getPosition ( geometry , i ) ;
this . intersetingAssets [ i ] . position = position ;
}
}
get absoluteDateGroupTop() {
return this . bucket . top + this . top ;
}
get groupTitle() {
return formatDateGroupTitle ( this . date ) ;
}
}
2023-08-02 21:57:11 -04:00
2023-08-03 11:44:12 -04:00
export interface Viewport {
width : number ;
height : number ;
}
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
export type ViewportXY = Viewport & {
x : number ;
y : number ;
} ;
2023-08-02 21:57:11 -04:00
2023-08-03 11:44:12 -04:00
export class AssetBucket {
2025-03-18 10:14:46 -04:00
// --- public ---
# intersecting : boolean = $state ( false ) ;
isLoaded : boolean = $state ( false ) ;
dateGroups : AssetDateGroup [ ] = $state ( [ ] ) ;
readonly store : AssetStore ;
// --- private ---
2023-08-03 11:44:12 -04:00
/ * *
* The DOM height of the bucket in pixel
* This value is first estimated by the number of asset and later is corrected as the user scroll
2025-03-04 21:34:53 -05:00
* Do not derive this height , it is important for it to be updated at specific times , so that
* calculateing a delta between estimated and actual ( when measured ) is correct .
2023-08-03 11:44:12 -04:00
* /
2025-03-18 10:14:46 -04:00
# bucketHeight : number = $state ( 0 ) ;
# top : number = $state ( 0 ) ;
# initialCount : number = 0 ;
// --- should be private, but is used by AssetStore ---
bucketCount : number = $derived (
this . isLoaded
? this . dateGroups . reduce ( ( accumulator , g ) = > accumulator + g . intersetingAssets . length , 0 )
: this . # initialCount ,
) ;
loader : CancellableTask | undefined ;
2025-03-04 21:34:53 -05:00
isBucketHeightActual : boolean = $state ( false ) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
2025-03-18 10:14:46 -04:00
readonly bucketDateFormatted : string ;
readonly bucketDate : string ;
readonly month : number ;
readonly year : number ;
constructor ( store : AssetStore , utcDate : DateTime , initialCount : number ) {
this . store = store ;
this . # initialCount = initialCount ;
const year = utcDate . get ( 'year' ) ;
const month = utcDate . get ( 'month' ) ;
const bucketDateFormatted = utcDate . toJSDate ( ) . toLocaleString ( get ( locale ) , {
month : 'short' ,
year : 'numeric' ,
timeZone : 'UTC' ,
} ) ;
this . bucketDate = utcDate . toISO ( ) ! . toString ( ) ;
this . bucketDateFormatted = bucketDateFormatted ;
this . month = month ;
this . year = year ;
this . loader = new CancellableTask (
( ) = > {
this . isLoaded = true ;
} ,
( ) = > {
this . isLoaded = false ;
} ,
this . handleLoadError ,
) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
set intersecting ( newValue : boolean ) {
const old = this . # intersecting ;
if ( old !== newValue ) {
this . # intersecting = newValue ;
if ( newValue ) {
void this . store . loadBucket ( this . bucketDate ) ;
} else {
this . cancel ( ) ;
}
}
}
get intersecting() {
return this . # intersecting ;
}
get lastDateGroup() {
return this . dateGroups . at ( - 1 ) ;
}
2025-03-19 11:55:50 -04:00
2025-03-18 10:14:46 -04:00
getFirstAsset() {
return this . dateGroups [ 0 ] ? . getFirstAsset ( ) ;
}
2025-03-19 11:55:50 -04:00
2025-03-18 10:14:46 -04:00
getAssets() {
// eslint-disable-next-line unicorn/no-array-reduce
return this . dateGroups . reduce (
( accumulator : AssetResponseDto [ ] , g : AssetDateGroup ) = > accumulator . concat ( g . getAssets ( ) ) ,
[ ] ,
2025-03-04 21:34:53 -05:00
) ;
2025-03-18 10:14:46 -04:00
}
2025-03-04 21:34:53 -05:00
2025-03-18 10:14:46 -04:00
containsAssetId ( id : string ) {
for ( const group of this . dateGroups ) {
const index = group . intersetingAssets . findIndex ( ( a ) = > a . id == id ) ;
if ( index !== - 1 ) {
return true ;
}
}
return false ;
}
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
2025-03-18 10:14:46 -04:00
sortDateGroups() {
this . dateGroups . sort ( ( a , b ) = > b . date . diff ( a . date ) . milliseconds ) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
runAssetOperation ( ids : Set < string > , operation : AssetOperation ) {
if ( ids . size === 0 ) {
return {
moveAssets : [ ] as MoveAsset [ ] ,
processedIds : new Set < string > ( ) ,
unprocessedIds : ids ,
changedGeometry : false ,
} ;
}
const { dateGroups } = this ;
let combinedChangedGeometry = false ;
let idsToProcess = new Set ( ids ) ;
const idsProcessed = new Set < string > ( ) ;
const combinedMoveAssets : MoveAsset [ ] [ ] = [ ] ;
let index = dateGroups . length ;
while ( index -- ) {
if ( idsToProcess . size > 0 ) {
const group = dateGroups [ index ] ;
const { moveAssets , processedIds , changedGeometry } = group . runAssetOperation ( ids , operation ) ;
if ( moveAssets . length > 0 ) {
combinedMoveAssets . push ( moveAssets ) ;
}
idsToProcess = idsToProcess . difference ( processedIds ) ;
for ( const id of processedIds ) {
idsProcessed . add ( id ) ;
}
combinedChangedGeometry = combinedChangedGeometry || changedGeometry ;
if ( group . intersetingAssets . length === 0 ) {
dateGroups . splice ( index , 1 ) ;
combinedChangedGeometry = true ;
}
}
}
return {
moveAssets : combinedMoveAssets.flat ( ) ,
unprocessedIds : idsToProcess ,
processedIds : idsProcessed ,
changedGeometry : combinedChangedGeometry ,
} ;
}
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
2025-03-18 10:14:46 -04:00
// note - if the assets are not part of this bucket, they will not be added
addAssets ( assets : AssetResponseDto [ ] ) {
const lookupCache : {
[ dayOfMonth : number ] : AssetDateGroup ;
} = { } ;
const unprocessedAssets : AssetResponseDto [ ] = [ ] ;
const changedDateGroups = new Set < AssetDateGroup > ( ) ;
const newDateGroups = new Set < AssetDateGroup > ( ) ;
for ( const asset of assets ) {
const date = DateTime . fromISO ( asset . localDateTime ) . toUTC ( ) ;
const month = date . get ( 'month' ) ;
const year = date . get ( 'year' ) ;
if ( this . month === month && this . year === year ) {
const day = date . get ( 'day' ) ;
let dateGroup : AssetDateGroup | undefined = lookupCache [ day ] ;
if ( ! dateGroup ) {
dateGroup = this . findDateGroupByDay ( day ) ;
if ( dateGroup ) {
lookupCache [ day ] = dateGroup ;
}
}
if ( dateGroup ) {
const intersectingAsset = new IntersectingAsset ( dateGroup , asset ) ;
dateGroup . intersetingAssets . push ( intersectingAsset ) ;
changedDateGroups . add ( dateGroup ) ;
} else {
dateGroup = new AssetDateGroup ( this , this . dateGroups . length , date , day ) ;
dateGroup . intersetingAssets . push ( new IntersectingAsset ( dateGroup , asset ) ) ;
this . dateGroups . push ( dateGroup ) ;
lookupCache [ day ] = dateGroup ;
newDateGroups . add ( dateGroup ) ;
}
} else {
unprocessedAssets . push ( asset ) ;
}
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
for ( const group of changedDateGroups ) {
group . sortAssets ( ) ;
}
for ( const group of newDateGroups ) {
group . sortAssets ( ) ;
}
if ( newDateGroups . size > 0 ) {
this . sortDateGroups ( ) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
return unprocessedAssets ;
}
getRandomDateGroup() {
const random = Math . floor ( Math . random ( ) * this . dateGroups . length ) ;
return this . dateGroups [ random ] ;
}
getRandomAsset() {
return this . getRandomDateGroup ( ) ? . getRandomAsset ( ) ? . asset ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
/** The svelte key for this view model object */
get viewId() {
return this . bucketDate ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
set bucketHeight ( height : number ) {
const { store } = this ;
const index = store . buckets . indexOf ( this ) ;
const bucketHeightDelta = height - this . # bucketHeight ;
const prevBucket = store . buckets [ index - 1 ] ;
if ( prevBucket ) {
this . # top = prevBucket . # top + prevBucket . # bucketHeight ;
}
if ( bucketHeightDelta ) {
let cursor = index + 1 ;
while ( cursor < store . buckets . length ) {
const nextBucket = this . store . buckets [ cursor ] ;
nextBucket . # top += bucketHeightDelta ;
cursor ++ ;
}
}
this . # bucketHeight = height ;
if ( store . topIntersectingBucket ) {
const currentIndex = store . buckets . indexOf ( store . topIntersectingBucket ) ;
// if the bucket is 'before' the last intersecting bucket in the sliding window
// then adjust the scroll position by the delta, to compensate for the bucket
// size adjustment
if ( currentIndex > 0 && index <= currentIndex ) {
store . compensateScrollCallback ? . ( bucketHeightDelta ) ;
}
}
}
get bucketHeight() {
return this . # bucketHeight ;
}
set top ( top : number ) {
this . # top = top ;
}
get top() {
return this . # top + this . store . topSectionHeight ;
}
handleLoadError ( error : unknown ) {
const _ $t = get ( t ) ;
handleError ( error , _ $t ( 'errors.failed_to_load_assets' ) ) ;
}
findDateGroupByDay ( dayOfMonth : number ) {
return this . dateGroups . find ( ( group ) = > group . dayOfMonth === dayOfMonth ) ;
}
findAssetAbsolutePosition ( assetId : string ) {
for ( const group of this . dateGroups ) {
const intersectingAsset = group . intersetingAssets . find ( ( asset ) = > asset . id === assetId ) ;
if ( intersectingAsset ) {
return this . top + group . top + intersectingAsset . position ! . top + HEADER ;
}
}
return - 1 ;
}
cancel() {
this . loader ? . cancel ( ) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2023-08-01 04:27:56 +03:00
}
2022-09-04 08:34:39 -05:00
2023-10-06 15:48:11 -05:00
const isMismatched = ( option : boolean | undefined , value : boolean ) : boolean = >
option === undefined ? false : option !== value ;
interface AddAsset {
type : 'add' ;
2024-03-15 17:11:29 +01:00
values : AssetResponseDto [ ] ;
2023-10-06 15:48:11 -05:00
}
2024-02-29 18:44:30 +01:00
interface UpdateAsset {
type : 'update' ;
2024-03-15 17:11:29 +01:00
values : AssetResponseDto [ ] ;
2024-02-29 18:44:30 +01:00
}
2023-10-06 15:48:11 -05:00
interface DeleteAsset {
type : 'delete' ;
2024-03-15 17:11:29 +01:00
values : string [ ] ;
2023-10-06 15:48:11 -05:00
}
2024-03-02 01:49:31 +01:00
interface TrashAssets {
2023-10-06 15:48:11 -05:00
type : 'trash' ;
2024-03-15 17:11:29 +01:00
values : string [ ] ;
2023-10-06 15:48:11 -05:00
}
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
interface UpdateStackAssets {
type : 'update_stack_assets' ;
values : string [ ] ;
}
2023-10-06 15:48:11 -05:00
2025-02-21 09:58:25 -06:00
export const photoViewerImgElement = writable < HTMLImageElement | null > ( null ) ;
2023-12-05 16:43:15 +01:00
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
type PendingChange = AddAsset | UpdateAsset | DeleteAsset | TrashAssets | UpdateStackAssets ;
2025-03-18 10:14:46 -04:00
export type LiteBucket = {
bucketHeight : number ;
assetCount : number ;
bucketDate : string ;
bucketDateFormattted : string ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
} ;
2023-10-06 15:48:11 -05:00
2023-08-03 11:44:12 -04:00
export class AssetStore {
2025-03-18 10:14:46 -04:00
// --- public ----
isInitialized = $state ( false ) ;
2025-03-04 21:34:53 -05:00
buckets : AssetBucket [ ] = $state ( [ ] ) ;
2025-03-18 10:14:46 -04:00
topSectionHeight = $state ( 0 ) ;
timelineHeight = $derived (
this . buckets . reduce ( ( accumulator , b ) = > accumulator + b . bucketHeight , 0 ) + this . topSectionHeight ,
) ;
// todo - name this better
2025-03-04 21:34:53 -05:00
albumAssets : Set < string > = new SvelteSet ( ) ;
2025-03-18 10:14:46 -04:00
// -- for scrubber only
scrubberBuckets : LiteBucket [ ] = $state ( [ ] ) ;
scrubberTimelineHeight : number = $state ( 0 ) ;
// -- should be private, but used by AssetBucket
compensateScrollCallback : ( ( delta : number ) = > void ) | undefined ;
topIntersectingBucket : AssetBucket | undefined = $state ( ) ;
visibleWindow = $derived . by ( ( ) = > ( {
top : this. # scrollTop ,
bottom : this. # scrollTop + this . viewportHeight ,
} ) ) ;
initTask = new CancellableTask (
( ) = > {
this . isInitialized = true ;
this . connect ( ) ;
} ,
( ) = > {
this . disconnect ( ) ;
this . isInitialized = false ;
} ,
( ) = > void 0 ,
) ;
// --- private
static # INIT_OPTIONS = { } ;
2025-03-19 11:57:44 -04:00
# rowHeight = 235 ;
2025-03-18 10:14:46 -04:00
# viewportHeight = $state ( 0 ) ;
# viewportWidth = $state ( 0 ) ;
# scrollTop = $state ( 0 ) ;
# pendingChanges : PendingChange [ ] = [ ] ;
# unsubscribers : Unsubscriber [ ] = [ ] ;
# options : AssetStoreOptions = AssetStore . # INIT_OPTIONS ;
# scrolling = $state ( false ) ;
# suspendTransitions = $state ( false ) ;
# resetScrolling = debounce ( ( ) = > ( this . # scrolling = false ) , 1000 ) ;
# resetSuspendTransitions = debounce ( ( ) = > ( this . suspendTransitions = false ) , 1000 ) ;
constructor ( ) { }
set scrolling ( value : boolean ) {
this . # scrolling = value ;
if ( value ) {
this . suspendTransitions = true ;
this . # resetScrolling ( ) ;
}
}
2023-08-03 11:44:12 -04:00
2025-03-18 10:14:46 -04:00
get scrolling() {
return this . # scrolling ;
2024-09-05 07:29:07 -07:00
}
2025-03-18 10:14:46 -04:00
set suspendTransitions ( value : boolean ) {
this . # suspendTransitions = value ;
if ( value ) {
this . # resetSuspendTransitions ( ) ;
}
2024-09-05 07:29:07 -07:00
}
2025-03-18 10:14:46 -04:00
get suspendTransitions() {
return this . # suspendTransitions ;
2023-08-03 11:44:12 -04:00
}
2025-03-18 10:14:46 -04:00
set viewportWidth ( value : number ) {
const changed = value !== this . # viewportWidth ;
this . # viewportWidth = value ;
this . suspendTransitions = true ;
2025-03-19 11:57:44 -04:00
this . # rowHeight = value < 850 ? 100 : 235 ;
2025-03-18 10:14:46 -04:00
// side-effect - its ok!
void this . # updateViewportGeometry ( changed ) ;
2023-12-04 20:29:35 -05:00
}
2025-03-18 10:14:46 -04:00
get viewportWidth() {
return this . # viewportWidth ;
2023-10-06 15:48:11 -05:00
}
2025-03-18 10:14:46 -04:00
set viewportHeight ( value : number ) {
this . # viewportHeight = value ;
this . # suspendTransitions = true ;
// side-effect - its ok!
void this . # updateViewportGeometry ( false ) ;
2023-10-06 15:48:11 -05:00
}
2025-03-18 10:14:46 -04:00
get viewportHeight() {
return this . # viewportHeight ;
}
2024-03-15 17:11:29 +01:00
2025-03-18 10:14:46 -04:00
getAssets() {
return this . buckets . flatMap ( ( bucket ) = > bucket . getAssets ( ) ) ;
}
2024-03-15 17:11:29 +01:00
2025-03-18 10:14:46 -04:00
# addPendingChanges ( . . . changes : PendingChange [ ] ) {
this . # pendingChanges . push ( . . . changes ) ;
this . # processPendingChanges ( ) ;
}
2024-03-15 17:11:29 +01:00
2025-03-18 10:14:46 -04:00
connect() {
this . # unsubscribers . push (
websocketEvents . on ( 'on_upload_success' , ( asset ) = > this . # addPendingChanges ( { type : 'add' , values : [ asset ] } ) ) ,
websocketEvents . on ( 'on_asset_trash' , ( ids ) = > this . # addPendingChanges ( { type : 'trash' , values : ids } ) ) ,
websocketEvents . on ( 'on_asset_update' , ( asset ) = > this . # addPendingChanges ( { type : 'update' , values : [ asset ] } ) ) ,
websocketEvents . on ( 'on_asset_delete' , ( id : string ) = > this . # addPendingChanges ( { type : 'delete' , values : [ id ] } ) ) ,
) ;
}
2024-03-15 17:11:29 +01:00
2025-03-18 10:14:46 -04:00
disconnect() {
for ( const unsubscribe of this . # unsubscribers ) {
unsubscribe ( ) ;
2024-03-15 17:11:29 +01:00
}
2025-03-18 10:14:46 -04:00
this . # unsubscribers = [ ] ;
2024-03-15 17:11:29 +01:00
}
2025-03-18 10:14:46 -04:00
# getPendingChangeBatches() {
const batch : {
add : AssetResponseDto [ ] ;
update : AssetResponseDto [ ] ;
remove : string [ ] ;
} = {
add : [ ] ,
update : [ ] ,
remove : [ ] ,
} ;
for ( const { type , values } of this . # pendingChanges ) {
2023-10-06 15:48:11 -05:00
switch ( type ) {
2024-02-02 04:18:00 +01:00
case 'add' : {
2025-03-18 10:14:46 -04:00
batch . add . push ( . . . values ) ;
2023-10-06 15:48:11 -05:00
2024-02-29 18:44:30 +01:00
break ;
}
2025-03-18 10:14:46 -04:00
case 'update' : {
batch . update . push ( . . . values ) ;
2024-02-29 18:44:30 +01:00
2023-10-06 15:48:11 -05:00
break ;
2024-02-02 04:18:00 +01:00
}
2025-03-18 10:14:46 -04:00
case 'delete' :
case 'trash' : {
batch . remove . push ( . . . values ) ;
2023-10-06 15:48:11 -05:00
break ;
2024-02-02 04:18:00 +01:00
}
2025-03-18 10:14:46 -04:00
// No default
2023-10-06 15:48:11 -05:00
}
}
2025-03-18 10:14:46 -04:00
return batch ;
}
2023-10-06 15:48:11 -05:00
2025-03-18 10:14:46 -04:00
// todo: this should probably be a method isteat
# findBucketForAsset ( id : string ) {
for ( const bucket of this . buckets ) {
if ( bucket . containsAssetId ( id ) ) {
return bucket ;
}
}
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
updateSlidingWindow ( scrollTop : number ) {
this . # scrollTop = scrollTop ;
this . updateIntersections ( ) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
updateIntersections() {
if ( ! this . isInitialized || this . visibleWindow . bottom === this . visibleWindow . top ) {
return ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
let topIntersectingBucket = undefined ;
for ( const bucket of this . buckets ) {
this . # updateIntersection ( bucket ) ;
if ( ! topIntersectingBucket && bucket . intersecting ) {
topIntersectingBucket = bucket ;
}
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
if ( this . topIntersectingBucket !== topIntersectingBucket ) {
this . topIntersectingBucket = topIntersectingBucket ;
2025-03-04 21:34:53 -05:00
}
2025-03-18 10:14:46 -04:00
}
2025-03-04 21:34:53 -05:00
2025-03-18 10:14:46 -04:00
# updateIntersection ( bucket : AssetBucket ) {
const bucketTop = bucket . top ;
const bucketBottom = bucketTop + bucket . bucketHeight ;
const topWindow = this . visibleWindow . top - INTERSECTION_EXPAND_TOP ;
const bottomWindow = this . visibleWindow . bottom + INTERSECTION_EXPAND_BOTTOM ;
// a bucket intersections if
// 1) bucket's bottom is in the visible range -or-
// 2) bucket's bottom is in the visible range -or-
// 3) bucket's top is above visible range and bottom is below visible range
bucket . intersecting =
( bucketTop >= topWindow && bucketTop < bottomWindow ) ||
( bucketBottom >= topWindow && bucketBottom < bottomWindow ) ||
( bucketTop < topWindow && bucketBottom >= bottomWindow ) ;
2024-09-05 07:29:07 -07:00
}
2025-03-18 10:14:46 -04:00
# processPendingChanges = throttle ( ( ) = > {
const { add , update , remove } = this . # getPendingChangeBatches ( ) ;
if ( add . length > 0 ) {
this . addAssets ( add ) ;
}
if ( update . length > 0 ) {
this . updateAssets ( update ) ;
}
if ( remove . length > 0 ) {
this . removeAssets ( remove ) ;
}
this . # pendingChanges = [ ] ;
} , 2500 ) ;
setCompensateScrollCallback ( compensateScrollCallback ? : ( delta : number ) = > void ) {
this . compensateScrollCallback = compensateScrollCallback ;
}
2023-08-11 12:00:51 -04:00
2025-03-18 10:14:46 -04:00
async # initialiazeTimeBuckets() {
2024-04-24 15:24:19 -04:00
const timebuckets = await getTimeBuckets ( {
2025-03-18 10:14:46 -04:00
. . . this . # options ,
size : TimeBucketSize.Month ,
2024-03-14 17:45:03 +01:00
key : getKey ( ) ,
} ) ;
2025-03-04 21:34:53 -05:00
2025-03-18 10:14:46 -04:00
this . buckets = timebuckets . map ( ( bucket ) = > {
const utcDate = DateTime . fromISO ( bucket . timeBucket ) . toUTC ( ) ;
return new AssetBucket ( this , utcDate , bucket . count ) ;
} ) ;
this . albumAssets . clear ( ) ;
this . # updateViewportGeometry ( false ) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2023-09-01 19:12:09 +02:00
2025-03-18 10:14:46 -04:00
/ * *
* If the timeline query options change ( i . e . albumId , isArchived , isFavorite , etc )
* call this method to recreate all buckets based on the new options .
*
* @param options The query options for time bucket queries .
* /
2024-09-05 07:29:07 -07:00
async updateOptions ( options : AssetStoreOptions ) {
2025-03-18 10:14:46 -04:00
if ( options . deferInit ) {
2024-09-05 07:29:07 -07:00
return ;
}
2025-03-18 10:14:46 -04:00
if ( this . # options !== AssetStore . # INIT_OPTIONS && isEqual ( this . # options , options ) ) {
2025-03-05 13:36:56 -06:00
return ;
}
2025-03-18 10:14:46 -04:00
await this . initTask . reset ( ) ;
await this . # init ( options ) ;
this . # updateViewportGeometry ( false ) ;
2024-09-05 07:29:07 -07:00
}
2025-03-19 11:57:44 -04:00
updateLayoutOptions ( options : AssetStoreLayoutOptions ) {
this . # rowHeight = options . rowHeight ;
this . refreshLayout ( ) ;
}
2025-03-18 10:14:46 -04:00
async # init ( options : AssetStoreOptions ) {
// doing the following outside of the task reduces flickr
this . isInitialized = false ;
this . buckets = [ ] ;
this . albumAssets . clear ( ) ;
await this . initTask . execute ( async ( ) = > {
this . # options = options ;
await this . # initialiazeTimeBuckets ( ) ;
} , true ) ;
}
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
public destroy() {
2025-03-18 10:14:46 -04:00
this . disconnect ( ) ;
this . isInitialized = false ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2024-04-24 15:24:19 -04:00
2025-03-18 10:14:46 -04:00
async updateViewport ( viewport : Viewport ) {
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
if ( viewport . height === 0 && viewport . width === 0 ) {
return ;
}
2025-03-18 10:14:46 -04:00
if ( this . viewportHeight === viewport . height && this . viewportWidth === viewport . width ) {
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
return ;
}
2024-04-24 15:24:19 -04:00
2025-03-18 10:14:46 -04:00
// special case updateViewport before or soon after call to updateOptions
if ( ! this . initTask . executed ) {
// eslint-disable-next-line unicorn/prefer-ternary
if ( this . initTask . loading ) {
await this . initTask . waitUntilCompletion ( ) ;
} else {
// not executed and not loaded means we should init now, and init will
// also update geometry so just return after
await this . # init ( this . # options ) ;
}
2024-04-24 15:24:19 -04:00
}
2024-03-03 17:12:52 -05:00
2025-03-18 10:14:46 -04:00
// changing width affects the actual height, and needs to re-layout
const changedWidth = viewport . width !== this . viewportWidth ;
this . viewportHeight = viewport . height ;
this . viewportWidth = viewport . width ;
this . # updateViewportGeometry ( changedWidth ) ;
}
# updateViewportGeometry ( changedWidth : boolean ) {
if ( ! this . isInitialized ) {
return ;
}
if ( this . viewportWidth === 0 || this . viewportHeight === 0 ) {
return ;
}
2024-03-05 16:43:24 +01:00
for ( const bucket of this . buckets ) {
2025-03-18 10:14:46 -04:00
this . # updateGeometry ( bucket , changedWidth ) ;
2024-03-05 16:43:24 +01:00
}
2025-03-18 10:14:46 -04:00
this . updateIntersections ( ) ;
this . # createScrubBuckets ( ) ;
2023-08-03 11:44:12 -04:00
}
2025-03-18 10:14:46 -04:00
# createScrubBuckets() {
this . scrubberBuckets = this . buckets . map ( ( bucket ) = > ( {
assetCount : bucket.bucketCount ,
bucketDate : bucket.bucketDate ,
bucketDateFormattted : bucket.bucketDateFormatted ,
bucketHeight : bucket.bucketHeight ,
} ) ) ;
this . scrubberTimelineHeight = this . timelineHeight ;
}
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
2025-03-18 10:14:46 -04:00
createLayoutOptions() {
const viewportWidth = this . viewportWidth ;
2025-03-19 11:57:44 -04:00
2025-03-18 10:14:46 -04:00
return {
2025-03-04 21:34:53 -05:00
spacing : 2 ,
heightTolerance : 0.15 ,
2025-03-19 11:57:44 -04:00
rowHeight : this. # rowHeight ,
2025-03-04 21:34:53 -05:00
rowWidth : Math.floor ( viewportWidth ) ,
} ;
2025-03-18 10:14:46 -04:00
}
# updateGeometry ( bucket : AssetBucket , invalidateHeight : boolean ) {
if ( invalidateHeight ) {
bucket . isBucketHeightActual = false ;
}
if ( ! bucket . isLoaded ) {
// optimize - if bucket already has data, no need to create estimates
const viewportWidth = this . viewportWidth ;
if ( ! bucket . isBucketHeightActual ) {
const unwrappedWidth = ( 3 / 2 ) * bucket . bucketCount * THUMBNAIL_HEIGHT * ( 7 / 10 ) ;
const rows = Math . ceil ( unwrappedWidth / viewportWidth ) ;
const height = 51 + Math . max ( 1 , rows ) * THUMBNAIL_HEIGHT ;
bucket . bucketHeight = height ;
}
return ;
}
this . # layoutBucket ( bucket ) ;
}
# layoutBucket ( bucket : AssetBucket ) {
// these are top offsets, for each row
let cummulativeHeight = 0 ;
// these are left offsets of each group, for each row
let cummulativeWidth = 0 ;
let lastRowHeight = 0 ;
let lastRow = 0 ;
let dateGroupRow = 0 ;
let dateGroupCol = 0 ;
const rowSpaceRemaining : number [ ] = Array . from ( { length : bucket.dateGroups.length } ) ;
rowSpaceRemaining . fill ( this . viewportWidth , 0 , bucket . dateGroups . length ) ;
const options = this . createLayoutOptions ( ) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
for ( const assetGroup of bucket . dateGroups ) {
2025-03-18 10:14:46 -04:00
assetGroup . layout ( options ) ;
rowSpaceRemaining [ dateGroupRow ] -= assetGroup . width - 1 ;
if ( dateGroupCol > 0 ) {
rowSpaceRemaining [ dateGroupRow ] -= GAP ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
if ( rowSpaceRemaining [ dateGroupRow ] >= 0 ) {
assetGroup . row = dateGroupRow ;
assetGroup . col = dateGroupCol ;
assetGroup . left = cummulativeWidth ;
assetGroup . top = cummulativeHeight ;
dateGroupCol ++ ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
2025-03-18 10:14:46 -04:00
cummulativeWidth += assetGroup . width + GAP ;
} else {
// starting a new row, we need to update the last col of the previous row
cummulativeWidth = 0 ;
dateGroupRow ++ ;
dateGroupCol = 0 ;
assetGroup . row = dateGroupRow ;
assetGroup . col = dateGroupCol ;
assetGroup . left = cummulativeWidth ;
rowSpaceRemaining [ dateGroupRow ] -= assetGroup . width ;
dateGroupCol ++ ;
cummulativeHeight += lastRowHeight ;
assetGroup . top = cummulativeHeight ;
cummulativeWidth += assetGroup . width + GAP ;
lastRow = assetGroup . row - 1 ;
}
lastRowHeight = assetGroup . height + HEADER ;
}
if ( lastRow === 0 || lastRow !== bucket . lastDateGroup ? . row ) {
cummulativeHeight += lastRowHeight ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
bucket . bucketHeight = cummulativeHeight ;
bucket . isBucketHeightActual = true ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
async loadBucket ( bucketDate : string , options ? : { cancelable : boolean } ) : Promise < void > {
let cancelable = true ;
if ( options ) {
cancelable = options . cancelable ;
2024-03-20 20:40:41 +01:00
}
2025-03-18 10:14:46 -04:00
const date = DateTime . fromISO ( bucketDate ) . toUTC ( ) ;
const year = date . get ( 'year' ) ;
const month = date . get ( 'month' ) ;
const bucket = this . getBucketByDate ( year , month ) ;
if ( ! bucket ) {
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
return ;
}
2023-08-03 11:44:12 -04:00
2025-03-18 10:14:46 -04:00
if ( bucket . loader ? . executed ) {
2024-03-20 20:40:41 +01:00
return ;
}
2023-08-03 11:44:12 -04:00
2025-03-18 10:14:46 -04:00
const result = await bucket . loader ? . execute ( async ( signal : AbortSignal ) = > {
2024-02-14 08:09:49 -05:00
const assets = await getTimeBucket (
2023-08-25 00:03:28 -04:00
{
2025-03-18 10:14:46 -04:00
. . . this . # options ,
2023-08-25 00:03:28 -04:00
timeBucket : bucketDate ,
2025-03-18 10:14:46 -04:00
size : TimeBucketSize.Month ,
2024-02-14 08:09:49 -05:00
key : getKey ( ) ,
2023-08-25 00:03:28 -04:00
} ,
2025-03-18 10:14:46 -04:00
{ signal } ,
2023-08-03 11:44:12 -04:00
) ;
2025-03-18 10:14:46 -04:00
if ( assets ) {
if ( this . # options . timelineAlbumId ) {
const albumAssets = await getTimeBucket (
{
albumId : this. # options . timelineAlbumId ,
timeBucket : bucketDate ,
size : TimeBucketSize.Month ,
key : getKey ( ) ,
} ,
{ signal } ,
) ;
for ( const asset of albumAssets ) {
this . albumAssets . add ( asset . id ) ;
}
2023-08-11 12:00:51 -04:00
}
2025-03-18 10:14:46 -04:00
const unprocessed = bucket . addAssets ( assets ) ;
if ( unprocessed . length > 0 ) {
console . error (
` Warning: getTimeBucket API returning assets not in requested month: ${ bucket . bucketDate } , ${ JSON . stringify ( unprocessed . map ( ( a ) = > ( { id : a.id , localDateTime : a.localDateTime } )))} ` ,
) ;
}
this . # layoutBucket ( bucket ) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
} , cancelable ) ;
if ( result === 'LOADED' ) {
this . # updateIntersection ( bucket ) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2023-08-03 11:44:12 -04:00
}
2024-03-15 17:11:29 +01:00
addAssets ( assets : AssetResponseDto [ ] ) {
const assetsToUpdate : AssetResponseDto [ ] = [ ] ;
for ( const asset of assets ) {
2025-03-18 10:14:46 -04:00
if ( this . isExcluded ( asset ) ) {
continue ;
2024-03-15 17:11:29 +01:00
}
2025-03-18 10:14:46 -04:00
assetsToUpdate . push ( asset ) ;
2023-10-06 15:48:11 -05:00
}
2025-03-18 10:14:46 -04:00
const notUpdated = this . updateAssets ( assetsToUpdate ) ;
this . # addAssetsToBuckets ( [ . . . notUpdated ] ) ;
2024-02-29 18:44:30 +01:00
}
2025-03-18 10:14:46 -04:00
# addAssetsToBuckets ( assets : AssetResponseDto [ ] ) {
2024-03-15 17:11:29 +01:00
if ( assets . length === 0 ) {
return ;
}
const updatedBuckets = new Set < AssetBucket > ( ) ;
2025-03-18 10:14:46 -04:00
const updatedDateGroups = new Set < AssetDateGroup > ( ) ;
2023-10-06 15:48:11 -05:00
2024-03-15 17:11:29 +01:00
for ( const asset of assets ) {
2025-03-18 10:14:46 -04:00
const utc = DateTime . fromISO ( asset . localDateTime ) . toUTC ( ) . startOf ( 'month' ) ;
const year = utc . get ( 'year' ) ;
const month = utc . get ( 'month' ) ;
let bucket = this . getBucketByDate ( year , month ) ;
2023-10-06 15:48:11 -05:00
2024-03-15 17:11:29 +01:00
if ( ! bucket ) {
2025-03-18 10:14:46 -04:00
bucket = new AssetBucket ( this , utc , 1 ) ;
2024-03-15 17:11:29 +01:00
this . buckets . push ( bucket ) ;
}
2025-03-18 10:14:46 -04:00
bucket . addAssets ( [ asset ] ) ;
2024-03-15 17:11:29 +01:00
updatedBuckets . add ( bucket ) ;
2023-10-06 15:48:11 -05:00
}
2025-03-18 10:14:46 -04:00
this . buckets . sort ( ( a , b ) = > {
return a . year === b . year ? b . month - a.month : b.year - a . year ;
2024-03-05 16:43:24 +01:00
} ) ;
2023-11-04 15:59:21 +03:00
2025-03-18 10:14:46 -04:00
for ( const dateGroup of updatedDateGroups ) {
dateGroup . sortAssets ( ) ;
}
2024-03-15 17:11:29 +01:00
for ( const bucket of updatedBuckets ) {
2025-03-18 10:14:46 -04:00
bucket . sortDateGroups ( ) ;
this . # updateGeometry ( bucket , true ) ;
2024-03-15 17:11:29 +01:00
}
2025-03-18 10:14:46 -04:00
this . updateIntersections ( ) ;
2023-10-06 15:48:11 -05:00
}
2025-03-18 10:14:46 -04:00
getBucketByDate ( year : number , month : number ) : AssetBucket | undefined {
return this . buckets . find ( ( bucket ) = > bucket . year === year && bucket . month === month ) ;
2023-08-03 11:44:12 -04:00
}
2025-03-18 10:14:46 -04:00
async findBucketForAsset ( id : string ) {
await this . initTask . waitUntilCompletion ( ) ;
let bucket = this . # findBucketForAsset ( id ) ;
2024-09-18 15:22:34 -04:00
if ( ! bucket ) {
const asset = await getAssetInfo ( { id } ) ;
if ( ! asset || this . isExcluded ( asset ) ) {
return ;
}
2025-03-18 10:14:46 -04:00
bucket = await this . # loadBucketAtTime ( asset . localDateTime , { cancelable : false } ) ;
2024-09-18 15:22:34 -04:00
}
2025-03-18 10:14:46 -04:00
if ( bucket && bucket ? . containsAssetId ( id ) ) {
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
return bucket ;
2024-04-24 15:24:19 -04:00
}
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
async # loadBucketAtTime ( localDateTime : string , options ? : { cancelable : boolean } ) {
2024-04-24 15:24:19 -04:00
let date = fromLocalDateTime ( localDateTime ) ;
2025-03-18 10:14:46 -04:00
// Only support TimeBucketSize.Month
date = date . set ( { day : 1 , hour : 0 , minute : 0 , second : 0 , millisecond : 0 } ) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
const iso = date . toISO ( ) ! ;
2025-03-18 10:14:46 -04:00
const year = date . get ( 'year' ) ;
const month = date . get ( 'month' ) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
await this . loadBucket ( iso , options ) ;
2025-03-18 10:14:46 -04:00
return this . getBucketByDate ( year , month ) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
}
2025-03-18 10:14:46 -04:00
async # getBucketInfoForAsset ( asset : AssetResponseDto , options ? : { cancelable : boolean } ) {
const bucketInfo = this . # findBucketForAsset ( asset . id ) ;
feat(web): Scroll to asset in gridview; increase gridview perf; reduce memory; scrollbar ticks in fixed position (#10646)
* Squashed
* Change strategy - now pre-measure buckets offscreen, so don't need to worry about sub-bucket scroll preservation
* Reduce jank on scroll, delay DOM updates until after scroll
* css opt, log measure time
* Trickle out queue while scrolling, flush when stopped
* yay
* Cleanup cleanup...
* everybody...
* everywhere...
* Clean up cleanup!
* Everybody do their share
* CLEANUP!
* package-lock ?
* dynamic measure, todo
* Fix web test
* type lint
* fix e2e
* e2e test
* Better scrollbar
* Tuning, and more tunables
* Tunable tweaks, more tunables
* Scrollbar dots and viewport events
* lint
* Tweaked tunnables, use requestIdleCallback for garbage tasks, bug fixes
* New tunables, and don't update url by default
* Bug fixes
* Bug fix, with debug
* Fix flickr, fix graybox bug, reduced debug
* Refactor/cleanup
* Fix
* naming
* Final cleanup
* review comment
* Forgot to update this after naming change
* scrubber works, with debug
* cleanup
* Rename scrollbar to scrubber
* rename to
* left over rename and change to previous album bar
* bugfix addassets, comments
* missing destroy(), cleanup
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-08-21 22:15:21 -04:00
if ( bucketInfo ) {
return bucketInfo ;
}
2025-03-18 10:14:46 -04:00
await this . # loadBucketAtTime ( asset . localDateTime , options ) ;
return this . # findBucketForAsset ( asset . id ) ;
2023-08-03 11:44:12 -04:00
}
getBucketIndexByAssetId ( assetId : string ) {
2025-03-18 10:14:46 -04:00
return this . # findBucketForAsset ( assetId ) ;
2023-08-03 11:44:12 -04:00
}
2025-03-18 10:14:46 -04:00
async getRandomBucket() {
const random = Math . floor ( Math . random ( ) * this . buckets . length ) ;
const bucket = this . buckets [ random ] ;
await this . loadBucket ( bucket . bucketDate , { cancelable : false } ) ;
return bucket ;
}
2023-11-01 21:34:30 -04:00
2025-03-18 10:14:46 -04:00
async getRandomAsset() {
const bucket = await this . getRandomBucket ( ) ;
return bucket ? . getRandomAsset ( ) ;
2023-11-01 21:34:30 -04:00
}
2025-03-18 10:14:46 -04:00
// runs op on assets, returns unprocessed
# runAssetOperation ( ids : Set < string > , operation : AssetOperation ) {
if ( ids . size === 0 ) {
return { processedIds : new Set ( ) , unprocessedIds : ids , changedGeometry : false } ;
2023-08-03 11:44:12 -04:00
}
2025-03-18 10:14:46 -04:00
const changedBuckets = new Set < AssetBucket > ( ) ;
let idsToProcess = new Set ( ids ) ;
const idsProcessed = new Set < string > ( ) ;
const combinedMoveAssets : { asset : AssetResponseDto ; year : number ; month : number } [ ] [ ] = [ ] ;
for ( const bucket of this . buckets ) {
if ( idsToProcess . size > 0 ) {
const { moveAssets , processedIds , changedGeometry } = bucket . runAssetOperation ( idsToProcess , operation ) ;
if ( moveAssets . length > 0 ) {
combinedMoveAssets . push ( moveAssets ) ;
}
idsToProcess = idsToProcess . difference ( processedIds ) ;
for ( const id of processedIds ) {
idsProcessed . add ( id ) ;
}
if ( changedGeometry ) {
changedBuckets . add ( bucket ) ;
break ;
}
2024-03-15 17:11:29 +01:00
}
2024-02-29 18:44:30 +01:00
}
2025-03-18 10:14:46 -04:00
if ( combinedMoveAssets . length > 0 ) {
this . # addAssetsToBuckets ( combinedMoveAssets . flat ( ) . map ( ( a ) = > a . asset ) ) ;
}
const changedGeometry = changedBuckets . size > 0 ;
for ( const bucket of changedBuckets ) {
this . # updateGeometry ( bucket , true ) ;
}
if ( changedGeometry ) {
this . updateIntersections ( ) ;
}
return { unprocessedIds : idsToProcess , processedIds : idsProcessed , changedGeometry } ;
}
/ * *
* Runs a callback on a list of asset ids . The assets in the AssetStore are reactive -
* any change to the asset ( i . e . changing isFavorite , isArchived , etc ) will automatically
* cause the UI to update with no further actions needed . Changing the date of an asset
* will automatically move it to another bucket if needed . Removing the asset will remove
* it from any view that is showing it .
*
* @param ids to run the operation on
* @param operation callback to update the specified asset ids
* /
updateAssetOperation ( ids : string [ ] , operation : AssetOperation ) {
this . # runAssetOperation ( new Set ( ids ) , operation ) ;
}
2023-08-04 23:26:28 -04:00
2025-03-18 10:14:46 -04:00
updateAssets ( assets : AssetResponseDto [ ] ) {
const lookup = new Map < string , AssetResponseDto > ( assets . map ( ( asset ) = > [ asset . id , asset ] ) ) ;
const { unprocessedIds } = this . # runAssetOperation ( new Set ( lookup . keys ( ) ) , ( asset ) = > {
updateObject ( asset , lookup . get ( asset . id ) ) ;
return { remove : false } ;
} ) ;
return unprocessedIds . values ( ) . map ( ( id ) = > lookup . get ( id ) ! ) ;
2023-08-03 11:44:12 -04:00
}
2023-08-16 16:04:55 -04:00
removeAssets ( ids : string [ ] ) {
2025-03-18 10:14:46 -04:00
const { unprocessedIds } = this . # runAssetOperation ( new Set ( ids ) , ( ) = > {
return { remove : true } ;
} ) ;
return [ . . . unprocessedIds ] ;
}
2023-08-03 11:44:12 -04:00
2025-03-18 10:14:46 -04:00
refreshLayout() {
for ( const bucket of this . buckets ) {
this . # updateGeometry ( bucket , true ) ;
2023-08-03 11:44:12 -04:00
}
2025-03-18 10:14:46 -04:00
this . updateIntersections ( ) ;
2023-08-03 11:44:12 -04:00
}
2025-03-18 10:14:46 -04:00
getFirstAsset ( ) : AssetResponseDto | undefined {
return this . buckets [ 0 ] ? . getFirstAsset ( ) ;
2025-03-12 02:18:14 +11:00
}
2025-03-18 10:14:46 -04:00
async getPreviousAsset ( asset : AssetResponseDto ) : Promise < AssetResponseDto | undefined > {
let bucket = await this . # getBucketInfoForAsset ( asset ) ;
if ( ! bucket ) {
return ;
2023-08-03 11:44:12 -04:00
}
2025-03-18 10:14:46 -04:00
for ( const group of bucket . dateGroups ) {
const index = group . intersetingAssets . findIndex ( ( ia ) = > ia . id === asset . id ) ;
if ( index > 0 ) {
return group . intersetingAssets [ index - 1 ] . asset ;
}
2023-08-03 11:44:12 -04:00
}
2025-03-18 10:14:46 -04:00
let bucketIndex = this . buckets . indexOf ( bucket ) - 1 ;
while ( bucketIndex >= 0 ) {
bucket = this . buckets [ bucketIndex ] ;
if ( ! bucket ) {
return ;
}
await this . loadBucket ( bucket . bucketDate ) ;
const previous = bucket . lastDateGroup ? . intersetingAssets . at ( - 1 ) ? . asset ;
if ( previous ) {
return previous ;
}
bucketIndex -- ;
2023-08-03 11:44:12 -04:00
}
}
2025-03-18 10:14:46 -04:00
async getNextAsset ( asset : AssetResponseDto ) : Promise < AssetResponseDto | undefined > {
let bucket = await this . # getBucketInfoForAsset ( asset ) ;
if ( ! bucket ) {
return ;
2023-08-03 11:44:12 -04:00
}
2025-03-18 10:14:46 -04:00
for ( const group of bucket . dateGroups ) {
const index = group . intersetingAssets . findIndex ( ( ia ) = > ia . id === asset . id ) ;
if ( index !== - 1 && index < group . intersetingAssets . length - 1 ) {
return group . intersetingAssets [ index + 1 ] . asset ;
}
2023-08-03 11:44:12 -04:00
}
2025-03-18 10:14:46 -04:00
let bucketIndex = this . buckets . indexOf ( bucket ) + 1 ;
while ( bucketIndex < this . buckets . length - 1 ) {
bucket = this . buckets [ bucketIndex ] ;
await this . loadBucket ( bucket . bucketDate ) ;
const next = bucket . dateGroups [ 0 ] ? . intersetingAssets [ 0 ] ? . asset ;
if ( next ) {
return next ;
}
bucketIndex ++ ;
2023-08-03 11:44:12 -04:00
}
}
2025-03-18 10:14:46 -04:00
isExcluded ( asset : AssetResponseDto ) {
2024-09-18 15:22:34 -04:00
return (
2025-03-18 10:14:46 -04:00
isMismatched ( this . # options . isArchived , asset . isArchived ) ||
isMismatched ( this . # options . isFavorite , asset . isFavorite ) ||
isMismatched ( this . # options . isTrashed , asset . isTrashed )
2024-09-18 15:22:34 -04:00
) ;
}
2022-09-04 08:34:39 -05:00
}
2024-01-01 17:18:22 +01:00
2024-03-21 13:14:13 +01:00
export const isSelectingAllAssets = writable ( false ) ;