diff --git a/apps/rebreak-native/app/devices.tsx b/apps/rebreak-native/app/devices.tsx index 0b4d27a..1db1c73 100644 --- a/apps/rebreak-native/app/devices.tsx +++ b/apps/rebreak-native/app/devices.tsx @@ -33,6 +33,7 @@ import { useUserPlan } from '../hooks/useUserPlan'; import { AppHeader } from '../components/AppHeader'; import { MagicSheet } from '../components/devices/MagicSheet'; import { DeviceSlotDonut, MOBILE_COLOR, DESKTOP_COLOR } from '../components/devices/DeviceSlotDonut'; +import { DeviceDistributionBar } from '../components/devices/DeviceDistributionBar'; import { DeviceStatusPill } from '../components/devices/DeviceStatusPill'; import { DeviceDetailSheet, type DeviceDetail } from '../components/devices/DeviceDetailSheet'; import { deviceImage } from '../components/devices/deviceIcon'; @@ -567,9 +568,9 @@ export default function DevicesScreen() { }} showsVerticalScrollIndicator={false} > - {/* Slot-Ringe: Mobil · Gesamt (Verteilung) · Computer */} - - + {/* Zwei Circles (Mobil/Computer) + animierter Gesamt-Verteilungs-Balken */} + + - = mobileLimit + desktopLimit} - label={t('devices.progress_total')} - /> + {/* Unified devices section: Mobile zuerst, dann Desktop */} diff --git a/apps/rebreak-native/components/devices/DeviceDistributionBar.tsx b/apps/rebreak-native/components/devices/DeviceDistributionBar.tsx new file mode 100644 index 0000000..1aba5fb --- /dev/null +++ b/apps/rebreak-native/components/devices/DeviceDistributionBar.tsx @@ -0,0 +1,108 @@ +import { useEffect, useRef } from 'react'; +import { Animated, Easing, Text, View } from 'react-native'; +import { useTranslation } from 'react-i18next'; +import { useColors } from '../../lib/theme'; +import { MOBILE_COLOR, DESKTOP_COLOR } from './DeviceSlotDonut'; + +/** + * Animierter Verteilungs-Balken (Gesamt) — gleiche Design-Sprache wie die + * Slot-Ringe (grün=Mobil, blau=Computer, gleiche Easing/Dauer). Sitzt unter + * den beiden Circles und zeigt die Aufteilung mobil/stationär. + */ +export function DeviceDistributionBar({ + mobile, + desktop, + total, +}: { + mobile: number; + desktop: number; + total: number; +}) { + const { t } = useTranslation(); + const colors = useColors(); + const safeTotal = Math.max(1, total); + const used = mobile + desktop; + + const anim = useRef(new Animated.Value(0)).current; + useEffect(() => { + anim.setValue(0); + Animated.timing(anim, { + toValue: 1, + duration: 1400, + easing: Easing.out(Easing.cubic), + useNativeDriver: false, + }).start(); + }, [mobile, desktop, total, anim]); + + const mobileW = anim.interpolate({ + inputRange: [0, 1], + outputRange: ['0%', `${(mobile / safeTotal) * 100}%`], + }); + const desktopW = anim.interpolate({ + inputRange: [0, 1], + outputRange: ['0%', `${(desktop / safeTotal) * 100}%`], + }); + + return ( + + + + {t('devices.progress_total')} + + + {used} + + /{total} + + + + + + + + + + + + + + + ); +} + +function Legend({ + color, + label, + count, + colors, +}: { + color: string; + label: string; + count: number; + colors: ReturnType; +}) { + return ( + + + + {label} {count} + + + ); +}