async function loadAllGroups(maxGroups = 50) { const params = { related: 1, relations: [ // TODO: Is this still necessary? // don't show motion pictures / live photos, since they are not // considered their own item in a gallery sense, and perhaps // more importantly, we don't want to have to generate a thumbnail // for them (literally no need for a thumbnail of those, just // wasted CPU time and storage space) { "not": true, "relation_label": "motion" } ], // offset: limit * (currentPageNum()-1), // TODO: figure out how to paginate the timeline limit: 2000 }; commonFilterSearchParams(params); const results = await app.SearchItems(params); console.log("BATCH PARAMS, RESULTS:", params, results); const groups = timelineGroups(results.items); return groups; } /* This old code would load the timeline in batches so as not to exceed too many groups. It did not work well. */ // async function loadAllGroups(maxGroups = 50) { // async function loadBatch(params) { // const results = await app.SearchItems(params); // console.log("BATCH PARAMS, RESULTS:", params, results); // return results.items; // } // // TODO: reinstate slicing? // function finished(groups) { // // groups.sort((a, b) => a[0].timestamp > b[0].timestamp ? 1 : -1 ); // // // keep last groups (newest items) because we truncate oldest items // // return groups.slice(-(Math.min(groups.length, maxGroups) - groups.length)); // return groups; // } // let items = []; // const start = DateTime.now(); // do { // const params = timelineFilterParams(items?.[items.length-1]); // const batch = await loadBatch(params); // if (!batch) break; // items = items.concat(batch); // const groups = timelineGroups(items); // if (groups.length > maxGroups || batch?.length < params.limit) { // return finished(groups); // } // } while (-start.diffNow().as('seconds') < 10); // return finished(timelineGroups(items)); // } // function timelineFilterParams(lastItem) { // const params = { // related: 1, // relations: [ // // TODO: Is this still necessary? // // don't show motion pictures / live photos, since they are not // // considered their own item in a gallery sense, and perhaps // // more importantly, we don't want to have to generate a thumbnail // // for them (literally no need for a thumbnail of those, just // // wasted CPU time and storage space) // { // "not": true, // "relation_label": "motion" // } // ], // // offset: limit * (currentPageNum()-1), // TODO: figure out how to paginate the timeline // limit: 100 // }; // commonFilterSearchParams(params); // if (lastItem) { // params.start_timestamp = lastItem?.timestamp; // } // console.log("PARAMS:", params) // return params; // } /* This REALLY old code would do some fancy statistics to try to compute clusters/groups. */ // // // median returns the median of array. *NOTE:* array must be sorted! // // // (TODO: If needed, implement "median of medians" algorithm to get median from unsorted array in O(n) time) // // function median(array) { // // if (array.length == 0) { // // return 0; // // } // // if (array.length == 1) { // // return array[0]; // // } // // if (array.length % 2 == 0) { // // return array[Math.floor(array.length/2)]; // // } // // const middex = Math.floor(array.length / 2); // // return (array[middex] + array[middex+1]) / 2; // // } // // function standardDeviation(array) { // // if (!array?.length) return 0; // // const n = array.length; // // const mean = array.reduce((a, b) => a + b) / n; // // return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n); // // } // // function renderGroup(group, prevGroup) { // // if (!group.length) return; // // const tpl = cloneTemplate('#tpl-tl-item-card'); // // let base, word; // // if (prevGroup?.length) { // // base = DateTime.fromISO(prevGroup[0].timestamp); // // word = "earlier"; // // } // // let relativeTime = DateTime.fromISO(group[0].timestamp).toRelative({ // // base: base // // }); // // if (relativeTime) { // // // TODO: this only works for English, sigh... // // if (relativeTime.startsWith("in ")) { // // relativeTime = relativeTime.replace("in ", "") + " later"; // // } // // $('.list-timeline-time', tpl).innerText = relativeTime; // // } // // const display = itemMiniDisplay(tlz.openRepos[0], group); // // $('.list-timeline-icon', tpl).innerHTML = display.icon; // // $('.list-timeline-icon', tpl).classList.add(`bg-${display.iconColor}`); // // $('.list-timeline-content-container', tpl).append(display.element); // // $('#timeline').append(tpl); // // }