1
0
Fork 0
timelinize/frontend/resources/js/timeline.js
Matthew Holt 2b5fd57259
Proper support for mixed timestamps and time zones
This will be a long-time WIP, but we now support full timestamps with local time offsets, absolute ones with UTC times only, and wall times only.

Several other fixes/enhancements. Making an effort to display time zone in time displays throughout the app.

Can now try to infer time zones during import, which is the default setting.

This will take a while to fully implement but it's a good start. Just have to be really careful about date crafting/manipulation/parsing.
2025-09-12 11:17:49 -06:00

223 lines
4.7 KiB
JavaScript

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);
// // }