Backend wirft 403 device_limit_reached für ALLE auth'd endpoints sobald User über
plan-limit ist. Bisheriges Frontend hat silent gefailt → Profile/Notifications/etc
zeigten nichts mehr, User war verwirrt.
Now:
- lib/api.ts: 403 device_limit_reached intercepten, parse error.data.devices,
trigger useDeviceLimitStore.show()
- stores/deviceLimit.ts: Zustand store (visible, devices, max, plan, show/hide)
- components/DeviceLimitReachedSheet.tsx: TrueSheet (UISheetPresentationController)
Auto-präsentiert wenn store visible, zeigt device-list mit trash-button per Eintrag,
DELETE /api/devices/:id mit skipDeviceHeader: true (sonst circular 403)
- app/_layout.tsx: <DeviceLimitReachedSheet /> als globaler overlay vor <Stack>
- i18n: device_limit_* keys DE+EN
UX: User sieht jetzt sofort native bottom-sheet mit erklärung + actionable
device-list statt silent fail. Auto-close wenn devices.length < max nach delete.
TS-fix: detents={['auto', 1] satisfies SheetDetent[]}, onDidDismiss statt onDismiss
(prop heißt anders in TrueSheet API).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
34 lines
808 B
TypeScript
34 lines
808 B
TypeScript
import { create } from 'zustand';
|
|
|
|
export type DeviceLimitDevice = {
|
|
id: string;
|
|
deviceId: string;
|
|
platform: string;
|
|
model: string | null;
|
|
name: string | null;
|
|
lastSeenAt: string;
|
|
createdAt: string;
|
|
};
|
|
|
|
type DeviceLimitState = {
|
|
visible: boolean;
|
|
devices: DeviceLimitDevice[];
|
|
max: number;
|
|
plan: string;
|
|
show: (devices: DeviceLimitDevice[], max: number, plan: string) => void;
|
|
hide: () => void;
|
|
removeDevice: (id: string) => void;
|
|
};
|
|
|
|
export const useDeviceLimitStore = create<DeviceLimitState>((set) => ({
|
|
visible: false,
|
|
devices: [],
|
|
max: 0,
|
|
plan: 'free',
|
|
|
|
show: (devices, max, plan) => set({ visible: true, devices, max, plan }),
|
|
hide: () => set({ visible: false }),
|
|
removeDevice: (id) =>
|
|
set((s) => ({ devices: s.devices.filter((d) => d.id !== id) })),
|
|
}));
|