tangled
alpha
login
or
join now
adam.tngl.sh
/
photos
0
fork
atom
an attempt at a lightweight photo/album viewer
0
fork
atom
overview
issues
pulls
1
pipelines
scrobbler tags for each segment
adam.tngl.sh
4 months ago
4cfdb9d9
890918c8
+82
-29
3 changed files
expand all
collapse all
unified
split
frontend
dist
scrobbler.css
src
gallery.mjs
scrobbler.ts
+2
-1
frontend/dist/scrobbler.css
···
53
53
cursor: pointer;
54
54
}
55
55
56
56
-
#scrobbler-section-title {
56
56
+
#scrobbler-section-title, .scrobbler-tooltip {
57
57
position: absolute;
58
58
top: 0;
59
59
left: -150px; /* Position to the left of the scrobbler */
···
64
64
border-radius: 5px;
65
65
display: none;
66
66
text-align: right;
67
67
+
transform: translateY(-1em);
67
68
}
+29
-13
frontend/src/gallery.mjs
···
55
55
});
56
56
}
57
57
58
58
+
function getTooltipHtml(contents) {
59
59
+
return `
60
60
+
<div class="scrobbler-tooltip">
61
61
+
${contents}
62
62
+
</div>
63
63
+
`
64
64
+
}
65
65
+
58
66
function getSegmentHtml(segment) {
59
67
const images = segment.images;
60
68
var geometry = justifiedLayout(images, config);
61
69
if (geometry.containerHeight < 0) {
62
70
return `
71
71
+
<h4 style="position:relative;z-index:1000; background:rgba(100,100,100,0.5);">
72
72
+
${segment.header}: an error occured<br>
73
73
+
width: ${config.containerWidth}px;<br>
74
74
+
height: ${geometry.containerHeight}px;<br>
75
75
+
numRows: ${geometry.numRows};
76
76
+
</h4>
63
77
<div>
64
64
-
<h4 style="position:relative;z-index:1000; background:rgba(100,100,100,0.5);">
65
65
-
${segment.header}: an error occured<br>
66
66
-
width: ${config.containerWidth}px;<br>
67
67
-
height: ${geometry.containerHeight}px;<br>
68
68
-
numRows: ${geometry.numRows};
69
69
-
</h4>
70
78
</div>`
71
79
}
72
80
var regions = Array.from(new Set(images.map(i => i.metadata).map(m => m.region)))
···
184
192
).sort((a, b) => new Date(a.images[0].timestamp) - new Date(b.images[0].timestamp));
185
193
minTimestamp = new Date(allSegments[0].images[0].timestamp);
186
194
maxTimestamp = new Date(allSegments[allSegments.length - 1].images[0].timestamp);
187
187
-
setTimeout(() => {
188
188
-
const segments = grid.querySelectorAll('.segment');
189
189
-
segments.forEach(s => {
190
190
-
segmentOffsets.set(s.id, s.offsetTop);
191
191
-
});
192
192
-
scrobblerCtrl.updateGalleryMeta(allSegments, segmentOffsets, minTimestamp, maxTimestamp);
193
193
-
}, 1000);
194
195
});
195
196
grid.addEventListener('section-populated', (e) => {
196
197
const section = e.detail.sectionDiv;
197
198
const segments = section.querySelectorAll('.segment');
199
199
+
200
200
+
segments.forEach(s => {
201
201
+
segmentOffsets.set(s.id, s.offsetTop);
202
202
+
});
203
203
+
scrobblerCtrl.updateGalleryMeta(allSegments, segmentOffsets, minTimestamp, maxTimestamp);
204
204
+
205
205
+
segments.forEach(s => {
206
206
+
const tooltipText = s.dataset.title;
207
207
+
const tooltip = document.createElement('div');
208
208
+
tooltip.innerHTML = tooltipText;
209
209
+
tooltip.setAttribute('class', 'scrobbler-tooltip');
210
210
+
scrobblerContainer.appendChild(tooltip);
211
211
+
scrobblerCtrl.registerTooltip(s, tooltip);
212
212
+
});
213
213
+
198
214
const regionTags = [...segments].flatMap(s => s.dataset.regions).flatMap(rs => rs.split(',')).filter(r => r.length !== 0)
199
215
geo.load(regionTags).then(_ => {
200
216
[...segments].forEach(s => {
+51
-15
frontend/src/scrobbler.ts
···
6
6
private minTimestamp: number | undefined;
7
7
private maxTimestamp: number | undefined;
8
8
9
9
+
private tooltipElements: Array<HTMLElement> = [];
9
10
10
11
constructor(
11
12
private grid: HTMLElement,
···
30
31
});
31
32
}
32
33
34
34
+
registerTooltipByDate(tooltipDate: Date, tooltipElement: HTMLElement) {
35
35
+
if (!this.minTimestamp || !this.maxTimestamp) {
36
36
+
console.log("no timestamp data");
37
37
+
return;
38
38
+
}
39
39
+
const rect = this.scrobblerContainer.getBoundingClientRect();
40
40
+
const range = this.maxTimestamp - this.minTimestamp;
41
41
+
const pos = tooltipDate.getTime() - this.minTimestamp;
42
42
+
const tooltipOffset = pos * rect.height / range;
43
43
+
tooltipElement.style.top = `${tooltipOffset}px`;
44
44
+
this.tooltipElements.push(tooltipElement);
45
45
+
}
46
46
+
47
47
+
registerTooltip(referencedElement: HTMLElement, tooltipElement: HTMLElement) {
48
48
+
const rect = this.scrobblerContainer.getBoundingClientRect();
49
49
+
const range = document.body.scrollHeight;
50
50
+
const pos = this.segmentOffsets.get(referencedElement.id)
51
51
+
const tooltipOffset = pos * rect.height / range;
52
52
+
tooltipElement.style.top = `${tooltipOffset}px`;
53
53
+
this.tooltipElements.push(tooltipElement);
54
54
+
}
55
55
+
33
56
updateGalleryMeta(allSegments: any, segmentOffsets: any, min: Date, max: Date) {
34
57
this.allSegments = allSegments;
35
58
this.segmentOffsets = segmentOffsets;
···
61
84
onDragStart(ev: MouseEvent | TouchEvent) {
62
85
this.isDragging = true;
63
86
this.scrobblerSectionTitle.style.display = 'block';
87
87
+
88
88
+
// rought calc for how many tooltips will fit
89
89
+
// TODO better margin
90
90
+
const margin = 50;
91
91
+
var vis = 0;
92
92
+
this.tooltipElements
93
93
+
.filter((el, i) => {
94
94
+
const rect = el.style;
95
95
+
const top = parseInt(rect.top.substring(0, rect.top.length - 2))
96
96
+
if (top > vis) {
97
97
+
vis = top + margin;
98
98
+
return true;
99
99
+
}
100
100
+
return false;
101
101
+
})
102
102
+
.forEach(t => {
103
103
+
t.style.display = 'block';
104
104
+
})
64
105
document.body.style.userSelect = 'none';
65
106
}
66
107
···
92
133
this.scrobblerHandle.style.top = `${handleTop}px`;
93
134
const scrobblerHeight = this.scrobblerContainer.clientHeight;
94
135
const relativePosition = handleTop / scrobblerHeight;
95
95
-
const targetTimestamp = this.minTimestamp + (relativePosition * (this.maxTimestamp - this.minTimestamp));
96
96
-
let targetSegment;
97
97
-
for (let i = 0; i < this.allSegments.length; i++) {
98
98
-
const segmentTimestamp = new Date(this.allSegments[i].images[0].timestamp).getTime();
99
99
-
if (segmentTimestamp >= targetTimestamp) {
100
100
-
targetSegment = this.allSegments[i];
101
101
-
break;
102
102
-
}
103
103
-
}
104
104
-
if (!targetSegment) {
105
105
-
targetSegment = this.allSegments[this.allSegments.length - 1];
106
106
-
}
107
107
-
this.scrobblerSectionTitle.textContent = targetSegment.header;
108
108
-
this.scrobblerSectionTitle.style.top = `${handleTop}px`;
109
109
-
const scrollTop = this.segmentOffsets.get(targetSegment.segmentId);
136
136
+
const targetOffset = relativePosition * document.body.scrollHeight;
137
137
+
console.log(targetOffset);
138
138
+
const targetSegment = this.segmentOffsets.entries().filter((entry, value) => {
139
139
+
return entry[1] > targetOffset;
140
140
+
}).next();
141
141
+
console.log(targetSegment)
142
142
+
const scrollTop = (targetSegment.value || ["", 0])[1];
110
143
if (scrollTop !== undefined) {
111
144
window.scrollTo({ top: scrollTop, behavior: 'auto' });
112
145
}
···
117
150
if (!this.isDragging) return;
118
151
this.isDragging = false;
119
152
this.scrobblerSectionTitle.style.display = 'none';
153
153
+
this.tooltipElements.forEach(t => {
154
154
+
t.style.display = 'none';
155
155
+
})
120
156
document.body.style.userSelect = '';
121
157
}
122
158