diff --git a/apps/rebreak-native/app/devices.tsx b/apps/rebreak-native/app/devices.tsx index 99dc265..0b4d27a 100644 --- a/apps/rebreak-native/app/devices.tsx +++ b/apps/rebreak-native/app/devices.tsx @@ -577,6 +577,13 @@ export default function DevicesScreen() { label={t('devices.progress_mobile')} /> + = mobileLimit + desktopLimit} label={t('devices.progress_total')} /> - diff --git a/apps/rebreak-native/components/devices/DeviceSlotDonut.tsx b/apps/rebreak-native/components/devices/DeviceSlotDonut.tsx index 6d549b9..db0a6b0 100644 --- a/apps/rebreak-native/components/devices/DeviceSlotDonut.tsx +++ b/apps/rebreak-native/components/devices/DeviceSlotDonut.tsx @@ -7,7 +7,7 @@ const SIZE = 88; const STROKE = 11; const R = (SIZE - STROKE) / 2; -/** Geräte-Kategorie-Farben — auch im Gesamt-Ring (Verteilung) verwendet. */ +/** Geräte-Kategorie-Farben — auch im Gesamt-Half-Donut (Verteilung). */ export const MOBILE_COLOR = '#22c55e'; export const DESKTOP_COLOR = '#2563eb'; @@ -26,29 +26,35 @@ function arcPath(cx: number, cy: number, r: number, startDeg: number, endDeg: nu } /** - * Voller Progress-Ring mit Mehr-Segment-Support (react-native-svg, keine Lib). - * - Einzel-Kategorie: ein Segment (Mobil/Computer). - * - Gesamt: zwei Segmente (Mobil-/Computer-Anteil farblich getrennt). - * Animiert über sweep der Bögen. + * Slot-Anzeige (react-native-svg, keine Lib), gleiche Größe in beiden Modi: + * - `half=false` (Default): voller Progress-Ring — eine Kategorie (Mobil/Computer). + * - `half=true`: Half-Donut für die Gesamt-Verteilung (Mobil-/Computer-Anteil + * als zwei farbige Bögen). */ export function DeviceSlotDonut({ segments, total, label, atLimit, + half = false, }: { segments: SlotSegment[]; total: number; label: string; atLimit: boolean; + half?: boolean; }) { const colors = useColors(); const cx = SIZE / 2; - const cy = SIZE / 2; + // Half-Donut tiefer setzen, damit der Bogen mittig im SIZE×SIZE-Feld sitzt. + const cy = half ? SIZE / 2 + R / 2 : SIZE / 2; const r = R; const safeTotal = Math.max(1, total); const used = segments.reduce((s, x) => s + x.value, 0); + const startAngle = half ? 180 : -90; + const sweep = half ? 180 : 360; + const anim = useRef(new Animated.Value(0)).current; const [progress, setProgress] = useState(0); @@ -64,10 +70,9 @@ export function DeviceSlotDonut({ return () => anim.removeListener(l); }, [used, total, anim]); - // Konsekutive Bögen, Start oben (-90°). - let cum = -90; + let cum = startAngle; const arcs = segments.map((seg) => { - const span = 360 * (seg.value / safeTotal); + const span = sweep * (seg.value / safeTotal); const start = cum; const end = cum + span; cum = end; @@ -78,19 +83,21 @@ export function DeviceSlotDonut({ - + {half ? ( + + ) : ( + + )} {arcs.map((a, i) => { const animEnd = a.start + (a.end - a.start) * progress; if (animEnd <= a.start + 0.5) return null; - // Voll-Kreis vermeiden (start==end wäre degeneriert). - const drawEnd = Math.min(animEnd, a.start + 359.99); + const drawEnd = Math.min(animEnd, a.start + (half ? 179.99 : 359.99)); return (