Skip to content

Commit 8d692cc

Browse files
committed
feat(app): cumulative statistics
Signed-off-by: Eric Hegnes <eric@hegnes.com>
1 parent 7532686 commit 8d692cc

File tree

4 files changed

+80
-26
lines changed

4 files changed

+80
-26
lines changed

app2/src/lib/components/model/BarChart.svelte

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import ErrorComponent from "$lib/components/model/ErrorComponent.svelte"
33
import type { FetchDecodeGraphqlError } from "$lib/utils/queries"
44
import type { DailyTransfer } from "@unionlabs/sdk/schema"
5-
import { Option } from "effect"
5+
import { Array as A, Option } from "effect"
66
import type { TimeoutException } from "effect/Cause"
77
import { constVoid } from "effect/Function"
88
@@ -14,16 +14,16 @@ type Props = {
1414
hoveredDate?: Option.Option<DailyTransfer>
1515
}
1616
17-
const { data, error, class: className = "", onHoverChange = constVoid, hoveredDate }: Props =
18-
$props()
19-
20-
// Format large numbers with commas (used for chart tooltips)
21-
function formatNumber(num: string | number): string {
22-
return Number(num).toLocaleString()
23-
}
17+
const {
18+
data: _data,
19+
error,
20+
class: className = "",
21+
onHoverChange = constVoid,
22+
hoveredDate,
23+
}: Props = $props()
2424
2525
// Derived values for chart data
26-
const reversedDailyTransfers = $derived(Option.isSome(data) ? [...data.value].reverse() : [])
26+
const data = $derived(Option.getOrElse(_data, A.empty<DailyTransfer>))
2727
2828
// Track which bar should be highlighted based on the external hover
2929
const highlightedDate = $derived.by(() => {
@@ -34,18 +34,29 @@ const highlightedDate = $derived.by(() => {
3434
return Option.some(String(hoveredDate.value.day_date))
3535
})
3636
37-
const maxCount = $derived(Option.isSome(data) ? Math.max(...data.value.map(d => d.count)) : 0)
37+
const maxCount = $derived(
38+
Option.match(
39+
_data,
40+
{
41+
onSome: (xs) => Math.max(...xs.map(d => d.count)),
42+
onNone: () => 0,
43+
},
44+
),
45+
)
3846
3947
// Track the currently hovered day for display
4048
let hoveredDay = $state<Option.Option<DailyTransfer>>(Option.none())
4149
4250
// Find the day with the highest count
4351
const highestDay = $derived.by(() => {
44-
if (!Option.isSome(data) || data.value.length === 0) {
52+
if (!Option.isSome(_data) || _data.value.length === 0) {
4553
return Option.none()
4654
}
4755
return Option.some(
48-
data.value.reduce((max, current) => (current.count > max.count ? current : max), data.value[0]),
56+
_data.value.reduce(
57+
(max, current) => (current.count > max.count ? current : max),
58+
_data.value[0],
59+
),
4960
)
5061
})
5162
@@ -67,14 +78,14 @@ const displayDate = $derived(() =>
6778
6879
// Calculate bar heights as percentages
6980
const barHeights = $derived(
70-
reversedDailyTransfers.map(day => ({
81+
data.map(day => ({
7182
...day,
7283
heightPercent: maxCount > 0 ? Math.max((day.count / maxCount) * 100, 1) : 1,
7384
})),
7485
)
7586
</script>
7687

77-
{#if Option.isSome(data) && maxCount > 0}
88+
{#if Option.isSome(_data) && maxCount > 0}
7889
<!-- Chart container -->
7990
<div class="h-full relative chart-container {className} {Option.isSome(highlightedDate) ? 'has-hover' : ''}">
8091
<!-- Grid lines -->

app2/src/lib/queries/statistics.svelte.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import { dailyPackets, dailyTransfers, statistics } from "$lib/stores/statistics.svelte"
22
import { createQueryGraphql } from "$lib/utils/queries"
3-
import { DailyTransfers, Statistics } from "@unionlabs/sdk/schema"
3+
import { DailyTransfer, DailyTransfers, Statistics } from "@unionlabs/sdk/schema"
44
import { Option, Schema, Struct } from "effect"
5+
import * as A from "effect/Array"
6+
import * as DateTime from "effect/DateTime"
7+
import { flow, pipe } from "effect/Function"
8+
import * as Order from "effect/Order"
59
import { graphql } from "gql.tada"
610

11+
const orderDailyTransfer = Order.mapInput<Date, DailyTransfer>(
12+
Order.Date,
13+
({ day_date }) => DateTime.toDate(day_date),
14+
)
15+
716
export const statisticsQuery = createQueryGraphql({
817
schema: Schema.Struct({ v2_stats_count: Statistics }),
918
document: graphql(`
@@ -39,8 +48,12 @@ export const dailyTransfersQuery = (limit = 60) =>
3948
refetchInterval: "60 seconds",
4049
writeData: data => {
4150
// Only show testnet 10 transfers
42-
dailyTransfers.data = data.pipe(
43-
Option.map(Struct.get("v2_stats_transfers_daily_count")),
51+
dailyTransfers.data = pipe(
52+
data,
53+
Option.map(flow(
54+
Struct.get("v2_stats_transfers_daily_count"),
55+
A.sortBy(orderDailyTransfer),
56+
)),
4457
)
4558
},
4659
writeError: error => {
@@ -62,8 +75,12 @@ export const dailyPacketsQuery = (limit = 60) =>
6275
variables: { limit },
6376
refetchInterval: "60 seconds",
6477
writeData: data => {
65-
dailyPackets.data = data.pipe(
66-
Option.map(Struct.get("v2_stats_packets_daily_count")),
78+
dailyPackets.data = pipe(
79+
data,
80+
Option.map(flow(
81+
Struct.get("v2_stats_packets_daily_count"),
82+
A.sortBy(orderDailyTransfer),
83+
)),
6784
)
6885
},
6986
writeError: error => {

app2/src/lib/stores/statistics.svelte.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { type AppContext, runFork, runPromise } from "$lib/runtime"
22
import type { FetchDecodeGraphqlError } from "$lib/utils/queries"
33
import type { DailyTransfers, Statistics } from "@unionlabs/sdk/schema"
4-
import { Effect, Fiber, Option } from "effect"
4+
import { Effect, Fiber, Option, pipe } from "effect"
5+
import * as A from "effect/Array"
56
import type { TimeoutException } from "effect/Cause"
7+
import * as I from "effect/Iterable"
68

79
class StatisticsStore {
810
data = $state(Option.none<Statistics>())
@@ -50,6 +52,30 @@ class DailyPacketsStore {
5052
data = $state(Option.none<DailyTransfers>())
5153
error = $state(Option.none<FetchDecodeGraphqlError | TimeoutException>())
5254
fiber = $state(Option.none<Fiber.RuntimeFiber<any, never>>())
55+
cumData = $derived(
56+
pipe(
57+
Option.all([this.data, statistics.data]),
58+
Option.map(([xs, stats]) => {
59+
const base = stats[0].value
60+
61+
return pipe(
62+
xs,
63+
I.scan(base, (acc, { count }) => acc + Number(count)),
64+
I.zip(xs),
65+
I.drop(1),
66+
I.map(([count, { day_date }]) => ({
67+
count,
68+
day_date,
69+
})),
70+
A.fromIterable,
71+
x => {
72+
console.log({ x })
73+
return x
74+
},
75+
)
76+
}),
77+
),
78+
)
5379

5480
async runEffect<A>(effect: Effect.Effect<A, never, AppContext>) {
5581
this.data = Option.none()

app2/src/routes/explorer/+page.svelte

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ const highestTransferDay = $derived.by(() => {
3535
3636
// Find the day with the highest count for packets
3737
const highestPacketDay = $derived.by(() => {
38-
if (!Option.isSome(dailyPackets.data) || dailyPackets.data.value.length === 0) {
38+
if (!Option.isSome(dailyPackets.cumData) || dailyPackets.cumData.value.length === 0) {
3939
return Option.none()
4040
}
4141
return Option.some(
42-
dailyPackets.data.value.reduce(
42+
dailyPackets.cumData.value.reduce(
4343
(max, current) => (current.count > max.count ? current : max),
44-
dailyPackets.data.value[0],
44+
dailyPackets.cumData.value[0],
4545
),
4646
)
4747
})
@@ -62,9 +62,9 @@ const displayTransferDay = $derived.by(() => {
6262
6363
// Find the packet data for the hovered day
6464
const displayPacketDay = $derived.by(() => {
65-
if (Option.isSome(hoveredDay) && Option.isSome(dailyPackets.data)) {
65+
if (Option.isSome(hoveredDay) && Option.isSome(dailyPackets.cumData)) {
6666
const hoveredDateString = String(hoveredDay.value.day_date)
67-
const packet = dailyPackets.data.value.find(
67+
const packet = dailyPackets.cumData.value.find(
6868
p => String(p.day_date) === hoveredDateString,
6969
)
7070
if (packet) {
@@ -160,7 +160,7 @@ onMount(() => {
160160
</div>
161161

162162
<BarChart
163-
data={dailyPackets.data}
163+
data={dailyPackets.cumData}
164164
error={dailyPackets.error}
165165
onHoverChange={(day) => hoveredDay = day}
166166
hoveredDate={hoveredDay}

0 commit comments

Comments
 (0)