Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions apps/mobile/src/features/usage/UsageDailyChart.ios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,40 @@ import { useMemo } from "react";
import type { DailyTotals } from "@t3tools/shared/usageMerge";

import { buildChartDays, type UsageChartMetric } from "./usageChartData";
import { useProviderColors } from "./usageProviders";
import type { UsageSeries } from "./usageProviders";

export interface UsageDailyChartProps {
readonly days: readonly string[];
readonly daily: readonly DailyTotals[];
/** Stacked bottom first: harnesses or model families. */
readonly series: readonly UsageSeries[];
readonly metric: UsageChartMetric;
readonly height: number;
}

/**
* Native Swift Charts daily bars. Points sharing an x value stack, so emitting
* one point per provider per day yields per-provider bands whose stack height
* one point per series per day yields per-series bands whose stack height
* is the day's total; changes animate natively.
*
* Axes are hidden: 30-90 categorical day labels cannot fit on a phone, so the
* screen renders its own edge labels under the chart instead.
*/
export function UsageDailyChart({ days, daily, metric, height }: UsageDailyChartProps) {
const colors = useProviderColors();

export function UsageDailyChart({ days, daily, series, metric, height }: UsageDailyChartProps) {
const data = useMemo((): ChartDataPoint[] => {
return buildChartDays(days, daily, metric).flatMap((day) =>
day.values.map((entry) => ({
return buildChartDays(
days,
daily,
metric,
series.map((entry) => entry.key),
).flatMap((day) =>
series.map((entry, index) => ({
x: day.day,
y: entry.value,
color: colors[entry.provider],
y: day.values[index]?.value ?? 0,
color: entry.color,
})),
);
}, [days, daily, metric, colors]);
}, [days, daily, metric, series]);

return (
<Host style={{ height, width: "100%" }}>
Expand Down
28 changes: 19 additions & 9 deletions apps/mobile/src/features/usage/UsageDailyChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { View } from "react-native";
import type { DailyTotals } from "@t3tools/shared/usageMerge";

import { buildChartDays, type UsageChartMetric } from "./usageChartData";
import { useProviderColors } from "./usageProviders";
import type { UsageSeries } from "./usageProviders";

export interface UsageDailyChartProps {
readonly days: readonly string[];
readonly daily: readonly DailyTotals[];
/** Stacked bottom first: harnesses or model families. */
readonly series: readonly UsageSeries[];
readonly metric: UsageChartMetric;
readonly height: number;
}
Expand All @@ -17,23 +19,31 @@ export interface UsageDailyChartProps {
* Stacked daily bars drawn with plain views. Android and any platform without
* Swift Charts land here; iOS resolves `UsageDailyChart.ios.tsx` instead.
*/
export function UsageDailyChart({ days, daily, metric, height }: UsageDailyChartProps) {
const colors = useProviderColors();
const chartDays = useMemo(() => buildChartDays(days, daily, metric), [days, daily, metric]);
export function UsageDailyChart({ days, daily, series, metric, height }: UsageDailyChartProps) {
const chartDays = useMemo(
() =>
buildChartDays(
days,
daily,
metric,
series.map((entry) => entry.key),
),
[days, daily, metric, series],
);
const max = chartDays.reduce((peak, day) => Math.max(peak, day.total), 0);

return (
<View style={{ height }} className="flex-row items-end gap-px">
{/* column-reverse stacks the bottom-first provider values upward
{/* column-reverse stacks the bottom-first series values upward
without reversing the array (Hermes lacks Array#toReversed). */}
{chartDays.map((day) => (
<View key={day.day} className="h-full flex-1 flex-col-reverse overflow-hidden rounded-sm">
{day.values.map((entry) => (
{series.map((entry, index) => (
<View
key={entry.provider}
key={entry.key}
style={{
height: max === 0 ? 0 : (entry.value / max) * height,
backgroundColor: colors[entry.provider],
height: max === 0 ? 0 : ((day.values[index]?.value ?? 0) / max) * height,
backgroundColor: entry.color,
}}
/>
))}
Expand Down
Loading
Loading