diff --git a/App.tsx b/App.tsx index a45808e0..93d8edfc 100644 --- a/App.tsx +++ b/App.tsx @@ -36,6 +36,7 @@ import { useAuthStore } from './src/stores/auth-store'; import { useCalendarStore } from './src/stores/calendar-store'; import { useContactsStore } from './src/stores/contacts-store'; import { useEmailStore } from './src/stores/email-store'; +import { useHasCalendar, useHasContacts, useHasFiles } from './src/lib/capabilities'; import { useSettingsStore } from './src/stores/settings-store'; import { useLocaleStore } from './src/stores/locale-store'; import { useNetworkStore } from './src/stores/network-store'; @@ -88,6 +89,10 @@ function MainTabsNavigator({ navigation }: NativeStackScreenProps state.mailboxes); const logout = useAuthStore((state) => state.logout); const inboxUnreadCount = mailboxes.find((mailbox) => mailbox.role === 'inbox')?.unreadEmails ?? 0; + const hasCalendar = useHasCalendar(); + const hasContacts = useHasContacts(); + const hasFiles = useHasFiles(); + const disabledTabStyle = { opacity: 0.4 } as const; return ( @@ -149,6 +154,13 @@ function MainTabsNavigator({ navigation }: NativeStackScreenProps , + tabBarItemStyle: hasCalendar ? undefined : disabledTabStyle, + tabBarAccessibilityLabel: hasCalendar ? 'Calendar' : 'Calendar (unavailable)', + }} + listeners={{ + tabPress: (e) => { + if (!hasCalendar) e.preventDefault(); + }, }} /> , + tabBarItemStyle: hasContacts ? undefined : disabledTabStyle, + tabBarAccessibilityLabel: hasContacts ? 'Contacts' : 'Contacts (unavailable)', + }} + listeners={{ + tabPress: (e) => { + if (!hasContacts) e.preventDefault(); + }, }} /> , + tabBarItemStyle: hasFiles ? undefined : disabledTabStyle, + tabBarAccessibilityLabel: hasFiles ? 'Files' : 'Files (unavailable)', + }} + listeners={{ + tabPress: (e) => { + if (!hasFiles) e.preventDefault(); + }, }} /> (s.session ? CAPABILITIES.CALENDARS in s.session.capabilities : true)); +} + +export function useHasContacts(): boolean { + return useAuthStore((s) => (s.session ? CAPABILITIES.CONTACTS in s.session.capabilities : true)); +} + +export function useHasFiles(): boolean { + return useAuthStore((s) => (s.session ? CAPABILITIES.FILES in s.session.capabilities : true)); +} diff --git a/src/screens/SettingsScreen.tsx b/src/screens/SettingsScreen.tsx index 3d45777b..bcbe3f33 100644 --- a/src/screens/SettingsScreen.tsx +++ b/src/screens/SettingsScreen.tsx @@ -36,6 +36,7 @@ import { LanguageSettings } from '../components/settings/LanguageSettings'; import { ComposingSettings } from '../components/settings/ComposingSettings'; import { LayoutSettings } from '../components/settings/LayoutSettings'; import { useLocaleStore } from '../stores/locale-store'; +import { useHasCalendar, useHasContacts, useHasFiles } from '../lib/capabilities'; type Tab = | 'account' | 'language' | 'notifications' @@ -155,6 +156,16 @@ export default function SettingsScreen({ onLogout, onBack, onTabSelect }: Settin // Subscribe to locale so labels re-render when the user picks a different language. const locale = useLocaleStore((s) => s.locale); const t = useLocaleStore((s) => s.t); + const hasCalendar = useHasCalendar(); + const hasContacts = useHasContacts(); + const hasFiles = useHasFiles(); + const unavailableTabs = React.useMemo(() => { + const set = new Set(); + if (!hasCalendar) set.add('calendar'); + if (!hasContacts) set.add('contacts'); + if (!hasFiles) set.add('files'); + return set; + }, [hasCalendar, hasContacts, hasFiles]); const groupedTabs = React.useMemo(() => { void locale; // dependency: re-translate on locale change return groupTabs().map((g) => ({ @@ -178,6 +189,7 @@ export default function SettingsScreen({ onLogout, onBack, onTabSelect }: Settin const handleTabPress = (tab: TabDef) => { if (!tab.implemented) return; + if (unavailableTabs.has(tab.id)) return; setSelectedTab(tab.id); onTabSelect?.(tab.id); }; @@ -241,7 +253,13 @@ export default function SettingsScreen({ onLogout, onBack, onTabSelect }: Settin {group.items.map((tab) => { const Icon = tab.icon; - const disabled = !tab.implemented; + const unavailable = unavailableTabs.has(tab.id); + const disabled = !tab.implemented || unavailable; + const badgeLabel = !tab.implemented + ? 'Not implemented' + : unavailable + ? 'Unavailable' + : null; return ( )} - {disabled ? ( + {badgeLabel ? ( - Not implemented + {badgeLabel} ) : (