Skip to content

Commit f58a9ed

Browse files
fix: github graph reactivity
1 parent f62ad05 commit f58a9ed

File tree

5 files changed

+123
-118
lines changed

5 files changed

+123
-118
lines changed
Lines changed: 111 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<script lang="ts">
22
import '$lib/app.css';
33
import { debounce } from '$lib/utils';
4-
import { Theme, theme } from '@immich/ui';
4+
import { LoadingSpinner, Theme, theme } from '@immich/ui';
55
import { DateTime } from 'luxon';
6-
import { onMount } from 'svelte';
6+
import { onMount, untrack } from 'svelte';
77
import uPlot, { type Axis } from 'uplot';
88
99
type DataRecord = [DateTime, number];
@@ -12,7 +12,7 @@
1212
type Props = {
1313
id: string;
1414
label: string;
15-
data: DataRecord[];
15+
data?: DataRecord[];
1616
cursorOpts: uPlot.Cursor;
1717
color: Colors;
1818
};
@@ -28,13 +28,16 @@
2828
let tooltipValue = $state('');
2929
let mousePosition = $state<{ x: number; y: number }>({ x: 0, y: 0 });
3030
31-
const xAxis: number[] = [];
32-
const yAxis: number[] = [];
31+
const { xAxis, yAxis } = $derived.by(() => {
32+
const xAxis: number[] = [];
33+
const yAxis: number[] = [];
3334
34-
for (const [date, value] of data) {
35-
xAxis.push(date.toMillis() / 1000);
36-
yAxis.push(value);
37-
}
35+
for (const [date, value] of data ?? []) {
36+
xAxis.push(date.toMillis() / 1000);
37+
yAxis.push(value);
38+
}
39+
return { xAxis, yAxis };
40+
});
3841
3942
let colors = {
4043
yellow: 'rgb(255, 197, 0)',
@@ -50,8 +53,6 @@
5053
blue: 'rgba(63, 106, 222, 0.1)',
5154
};
5255
53-
let plot: uPlot;
54-
5556
let isDark = $derived(theme.value === Theme.Dark);
5657
5758
const hideTooltipElement = () => {
@@ -68,85 +69,89 @@
6869
}
6970
};
7071
71-
onMount(() => {
72-
if (!chartElement) {
73-
return;
74-
}
75-
76-
const formatData: uPlot.AlignedData = [new Float64Array(xAxis), new Float64Array(yAxis)];
72+
const axis: Axis = $derived({
73+
stroke: () => (isDark ? '#ccc' : 'black'),
74+
ticks: {
75+
stroke: () => (isDark ? '#444' : '#ddd'),
76+
},
77+
grid: {
78+
show: false,
79+
},
80+
});
7781
78-
const axis: Axis = {
79-
stroke: () => (isDark ? '#ccc' : 'black'),
80-
ticks: {
81-
stroke: () => (isDark ? '#444' : '#ddd'),
82-
},
83-
grid: {
84-
show: false,
82+
const opts: uPlot.Options = $derived({
83+
id,
84+
padding: [16, 16, 16, 16],
85+
cursor: cursorOpts,
86+
width: chartWidth,
87+
height: chartHeight,
88+
scales: {},
89+
legend: {
90+
show: true,
91+
},
92+
series: [
93+
{
94+
value: '{YYYY}-{MM}-{DD}',
95+
label: 'Date',
8596
},
86-
};
87-
88-
const opts: uPlot.Options = {
89-
id,
90-
padding: [16, 16, 16, 16],
91-
cursor: cursorOpts,
92-
width: 500,
93-
height: 500,
94-
scales: {},
95-
legend: {
97+
{
9698
show: true,
99+
spanGaps: false,
100+
stroke: colors[color],
101+
width: 2,
102+
fill: areaColor[color],
103+
label,
97104
},
98-
series: [
99-
{
100-
value: '{YYYY}-{MM}-{DD}',
101-
label: 'Date',
102-
},
103-
{
104-
show: true,
105-
spanGaps: false,
106-
stroke: colors[color],
107-
width: 2,
108-
fill: areaColor[color],
109-
label,
110-
},
111-
],
105+
],
112106
113-
axes: [axis, axis],
107+
axes: [axis, axis],
114108
115-
hooks: {
116-
setCursor: [
117-
(u) => {
118-
if (u.root.id != chartId || !tooltipElement) {
119-
return;
120-
}
109+
hooks: {
110+
setCursor: [
111+
(u) => {
112+
if (u.root.id != chartId || !tooltipElement) {
113+
return;
114+
}
121115
122-
const { idx } = u.cursor;
116+
const { idx } = u.cursor;
123117
124-
if (idx == null) {
125-
hideTooltipElement();
126-
return;
127-
}
118+
if (idx == null) {
119+
hideTooltipElement();
120+
return;
121+
}
128122
129-
const date = new Date(u.data[0][idx] * 1000).toLocaleDateString();
130-
const value = u.data[1][idx];
123+
const date = new Date(u.data[0][idx] * 1000).toLocaleDateString();
124+
const value = u.data[1][idx];
131125
132-
tooltipDate = date;
133-
tooltipValue = value?.toLocaleString() || '';
126+
tooltipDate = date;
127+
tooltipValue = value?.toLocaleString() || '';
134128
135-
showTooltipElement();
136-
},
137-
],
138-
setSeries: [() => hideTooltipElement()],
139-
},
140-
};
129+
showTooltipElement();
130+
},
131+
],
132+
setSeries: [() => hideTooltipElement()],
133+
},
134+
});
135+
136+
const formatData: uPlot.AlignedData = $derived([new Float64Array(xAxis), new Float64Array(yAxis)]);
141137
138+
let plot: uPlot;
139+
onMount(() => {
142140
plot = new uPlot(opts, formatData, chartElement);
143-
plot.setSize({ width: chartWidth, height: chartHeight });
144141
});
145142
146143
$effect(() => {
147-
if (plot && theme.value) {
148-
plot.redraw(false);
144+
plot.setData(formatData);
145+
});
146+
147+
$effect(() => {
148+
// really this is just to make this reactive when opts updates.
149+
if (!opts) {
150+
return;
149151
}
152+
153+
plot.destroy();
154+
untrack(() => (plot = new uPlot(opts, formatData, chartElement)));
150155
});
151156
152157
const onResize = debounce(() => {
@@ -161,30 +166,36 @@
161166

162167
<svelte:window onresize={onResize} onmousemove={onMouseMove} />
163168

164-
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
165-
<!-- svelte-ignore a11y_no_static_element_interactions -->
166-
<div
167-
class="h-[275px] mb-10 w-full relative"
168-
bind:clientWidth={chartWidth}
169-
bind:clientHeight={chartHeight}
170-
bind:this={chartElement}
171-
onmouseover={() => {
172-
showTooltipElement();
173-
chartId = id;
174-
}}
175-
onmouseleave={() => {
176-
hideTooltipElement();
177-
chartId = '';
178-
}}
179-
></div>
180-
<div
181-
bind:this={tooltipElement}
182-
style="top: {mousePosition.y - 32}px; left: {mousePosition.x - 112}px"
183-
class="absolute border shadow-md text-xs w-[100px] rounded-lg bg-light py-2 px-3 text-center font-mono {chartId &&
184-
tooltipValue
185-
? ''
186-
: 'hidden'}"
187-
>
188-
<p>{tooltipDate}</p>
189-
<p class="font-bold">{tooltipValue}</p>
190-
</div>
169+
{#if data === undefined}
170+
<div class="flex h-[275px] justify-center items-center">
171+
<LoadingSpinner size="giant" />
172+
</div>
173+
{:else}
174+
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
175+
<!-- svelte-ignore a11y_no_static_element_interactions -->
176+
<div
177+
class="h-[275px] mb-10 w-full relative"
178+
bind:clientWidth={chartWidth}
179+
bind:clientHeight={chartHeight}
180+
bind:this={chartElement}
181+
onmouseover={() => {
182+
showTooltipElement();
183+
chartId = id;
184+
}}
185+
onmouseleave={() => {
186+
hideTooltipElement();
187+
chartId = '';
188+
}}
189+
></div>
190+
<div
191+
bind:this={tooltipElement}
192+
style="top: {mousePosition.y - 32}px; left: {mousePosition.x - 112}px"
193+
class="absolute border shadow-md text-xs w-[100px] rounded-lg bg-light py-2 px-3 text-center font-mono {chartId &&
194+
tooltipValue
195+
? ''
196+
: 'hidden'}"
197+
>
198+
<p>{tooltipDate}</p>
199+
<p class="font-bold">{tooltipValue}</p>
200+
</div>
201+
{/if}

frontend/src/lib/services/api.svelte.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ type GithubReportData = Array<[number, number]>;
1010
type GithubDataRecord = [DateTime, number];
1111

1212
type GithubData = {
13-
issues: GithubDataRecord[];
14-
pullRequests: GithubDataRecord[];
15-
stars: GithubDataRecord[];
16-
discussions: GithubDataRecord[];
13+
issues?: GithubDataRecord[];
14+
pullRequests?: GithubDataRecord[];
15+
stars?: GithubDataRecord[];
16+
discussions?: GithubDataRecord[];
1717
};
1818

1919
export const githubData = $state<GithubData>({
20-
stars: [],
21-
issues: [],
22-
pullRequests: [],
23-
discussions: [],
20+
stars: undefined,
21+
issues: undefined,
22+
pullRequests: undefined,
23+
discussions: undefined,
2424
});
2525

2626
const asTimestamp = ([timestamp, value]: [number, number]) =>

frontend/src/routes/+layout.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const ssr = false;

frontend/src/routes/+page.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import '$lib/app.css';
33
import GithubGraph from '$lib/components/graphs/github-graph.svelte';
4-
import { githubData } from '$lib/services/api.svelte';
4+
import { githubData, loadGithubData } from '$lib/services/api.svelte';
55
import { Card, CardBody, CardHeader, CardTitle, Container, Heading, HStack, Icon, Text } from '@immich/ui';
66
import { mdiBugOutline, mdiMessageOutline, mdiSourceBranch, mdiStarOutline } from '@mdi/js';
77
import uPlot from 'uplot';
@@ -17,6 +17,8 @@
1717
key: 'sync',
1818
},
1919
};
20+
21+
void loadGithubData();
2022
</script>
2123

2224
<Container size="giant" center>

frontend/src/routes/+page.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)