mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-04 21:04:37 +08:00
fix: UTC hour truncation, null event data guard, dedupe animation utils
- metrics/route: use setUTCMinutes for hourly bucket keys to match ClickHouse toStartOfHour (Greptile P2, cubic P1) - analytics batch route: guard against null event.data before object spread (cubic P1) - dashboard: extract easeOutCubic/prefersReducedMotion to shared animation-utils (Greptile P2)
This commit is contained in:
parent
398d4f1a00
commit
f77f775917
@ -144,8 +144,9 @@ export const POST = createSmartRouteHandler({
|
||||
})();
|
||||
|
||||
const rows = body.events.map((event) => {
|
||||
const baseData = (typeof event.data === "object" && !Array.isArray(event.data))
|
||||
? (event.data as Record<string, unknown>)
|
||||
const rawData: unknown = event.data;
|
||||
const baseData = (rawData != null && typeof rawData === "object" && !Array.isArray(rawData))
|
||||
? (rawData as Record<string, unknown>)
|
||||
: {};
|
||||
const existingUa = baseData.user_agent;
|
||||
const mergedData = (existingUa == null || existingUa === "")
|
||||
|
||||
@ -1685,7 +1685,7 @@ async function loadAnalyticsOverview(
|
||||
const hourlyActiveUsers: DataPoints = [];
|
||||
const hourlyVisitors: DataPoints = [];
|
||||
const latestHour = new Date(now);
|
||||
latestHour.setMinutes(0, 0, 0);
|
||||
latestHour.setUTCMinutes(0, 0, 0);
|
||||
for (let i = 23; i >= 0; i--) {
|
||||
const hour = new Date(latestHour.getTime() - i * 60 * 60 * 1000);
|
||||
const key = hour.toISOString().slice(0, 13);
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
export function easeOutCubic(progress: number): number {
|
||||
return 1 - Math.pow(1 - progress, 3);
|
||||
}
|
||||
|
||||
export function prefersReducedMotion(): boolean {
|
||||
return typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
}
|
||||
@ -71,6 +71,7 @@ import {
|
||||
} from "./line-chart";
|
||||
import { MetricsErrorFallback, MetricsLoadingFallback } from "./metrics-loading";
|
||||
import { ReferrersWithAnalyticsCard, TopNamedListCard, TopRegionsCard } from "./top-lists";
|
||||
import { easeOutCubic, prefersReducedMotion } from "./animation-utils";
|
||||
import {
|
||||
ANALYTICS_CHART_METRIC_MODE_ORDER,
|
||||
toggleAnalyticsChartMetricMode,
|
||||
@ -128,10 +129,6 @@ const reducedOverviewHeaderLayoutTransition: Transition = {
|
||||
|
||||
const scrollableOverflowValues = new Set(["auto", "scroll", "overlay"]);
|
||||
|
||||
function easeOutCubic(progress: number): number {
|
||||
return 1 - Math.pow(1 - progress, 3);
|
||||
}
|
||||
|
||||
function findScrollContainer(element: HTMLElement): HTMLElement | null {
|
||||
let current = element.parentElement;
|
||||
while (current != null) {
|
||||
@ -207,10 +204,6 @@ function useDelayedTrue(value: boolean, delayMs: number): boolean {
|
||||
return delayedValue;
|
||||
}
|
||||
|
||||
function prefersReducedMotion(): boolean {
|
||||
return typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
}
|
||||
|
||||
function useAnimatedSeriesValues<T extends { value: number }>(series: T[]): T[] {
|
||||
const [animatedSeries, setAnimatedSeries] = useState(series);
|
||||
const previousSeriesRef = useRef(series);
|
||||
|
||||
@ -9,17 +9,10 @@ import { GlobeIcon } from "@phosphor-icons/react";
|
||||
import type { Icon } from "@phosphor-icons/react";
|
||||
import { stringCompare } from "@hexclave/shared/dist/utils/strings";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { easeOutCubic, prefersReducedMotion } from "./animation-utils";
|
||||
|
||||
const TOP_LIST_ANIMATION_MS = 260;
|
||||
|
||||
function easeOutCubic(progress: number): number {
|
||||
return 1 - Math.pow(1 - progress, 3);
|
||||
}
|
||||
|
||||
function prefersReducedMotion(): boolean {
|
||||
return typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
}
|
||||
|
||||
function useAnimatedBarValues(rows: Array<{ id: string, value: number }>): Map<string, number> {
|
||||
const [animatedValues, setAnimatedValues] = useState(() => new Map(rows.map((row) => [row.id, row.value])));
|
||||
const previousValuesRef = useRef(animatedValues);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user