diff --git a/.github/workflows/react-native-cicd.yml b/.github/workflows/react-native-cicd.yml
index 5ec6ddf..ae6d0bb 100644
--- a/.github/workflows/react-native-cicd.yml
+++ b/.github/workflows/react-native-cicd.yml
@@ -66,6 +66,8 @@ env:
EXPO_APPLE_TEAM_TYPE: ${{ secrets.EXPO_APPLE_TEAM_TYPE }}
UNIT_APTABASE_APP_KEY: ${{ secrets.UNIT_APTABASE_APP_KEY }}
UNIT_APTABASE_URL: ${{ secrets.UNIT_APTABASE_URL }}
+ UNIT_APP_KEY: ${{ secrets.UNIT_APP_KEY }}
+ APP_KEY: ${{ secrets.APP_KEY }}
NODE_OPTIONS: --openssl-legacy-provider
jobs:
diff --git a/src/app/(app)/_layout.tsx b/src/app/(app)/_layout.tsx
index adfb570..caa611c 100644
--- a/src/app/(app)/_layout.tsx
+++ b/src/app/(app)/_layout.tsx
@@ -39,6 +39,7 @@ export default function TabLayout() {
const [isFirstTime, _setIsFirstTime] = useIsFirstTime();
const [isOpen, setIsOpen] = React.useState(false);
const [isNotificationsOpen, setIsNotificationsOpen] = React.useState(false);
+ const [isNotificationSystemReady, setIsNotificationSystemReady] = React.useState(false);
const { width, height } = useWindowDimensions();
const isLandscape = width > height;
const { isActive, appState } = useAppLifecycle();
@@ -215,6 +216,20 @@ export default function TabLayout() {
const activeUnitId = useCoreStore((state) => state.activeUnitId);
const rights = securityStore((state) => state.rights);
+ // Manage notification system readiness
+ useEffect(() => {
+ const isReady = Boolean(activeUnitId && config && config.NovuApplicationId && config.NovuBackendApiUrl && config.NovuSocketUrl && rights?.DepartmentCode);
+
+ if (isReady && !isNotificationSystemReady) {
+ // Add a small delay to ensure the main UI is rendered first
+ setTimeout(() => {
+ setIsNotificationSystemReady(true);
+ }, 1000);
+ } else if (!isReady && isNotificationSystemReady) {
+ setIsNotificationSystemReady(false);
+ }
+ }, [activeUnitId, config, rights?.DepartmentCode, isNotificationSystemReady]);
+
if (isFirstTime) {
//setIsOnboarding();
return ;
@@ -338,7 +353,7 @@ export default function TabLayout() {
{/* NotificationInbox positioned within the tab content area */}
- {activeUnitId && config && rights?.DepartmentCode && setIsNotificationsOpen(false)} />}
+ {isNotificationSystemReady && setIsNotificationsOpen(false)} />}
@@ -346,8 +361,8 @@ export default function TabLayout() {
return (
<>
- {activeUnitId && config && rights?.DepartmentCode ? (
-
+ {isNotificationSystemReady ? (
+
{content}
) : (
@@ -394,11 +409,8 @@ const CreateNotificationButton = ({
return null;
}
- return (
-
- setIsNotificationsOpen(true)} />
-
- );
+ // Only render after notification system is ready to prevent timing issues
+ return setIsNotificationsOpen(true)} />;
};
const styles = StyleSheet.create({
diff --git a/src/components/notifications/NotificationInbox.tsx b/src/components/notifications/NotificationInbox.tsx
index 615f532..3e35ce1 100644
--- a/src/components/notifications/NotificationInbox.tsx
+++ b/src/components/notifications/NotificationInbox.tsx
@@ -235,6 +235,11 @@ export const NotificationInbox = ({ isOpen, onClose }: NotificationInboxProps) =
return null;
}
+ // Additional safety check to prevent rendering overlay without proper config
+ if (!activeUnitId || !config || !config.NovuApplicationId || !config.NovuBackendApiUrl || !config.NovuSocketUrl) {
+ return null;
+ }
+
return (
{/* Backdrop for tapping outside to close */}
diff --git a/src/components/notifications/__tests__/NotificationInbox.test.tsx b/src/components/notifications/__tests__/NotificationInbox.test.tsx
index b28e06d..60a1ddf 100644
--- a/src/components/notifications/__tests__/NotificationInbox.test.tsx
+++ b/src/components/notifications/__tests__/NotificationInbox.test.tsx
@@ -89,7 +89,12 @@ describe('NotificationInbox', () => {
mockUseCoreStore.mockImplementation((selector: any) => {
const state = {
activeUnitId: 'unit-1',
- config: { apiUrl: 'test-url' },
+ config: {
+ apiUrl: 'test-url',
+ NovuApplicationId: 'test-app-id',
+ NovuBackendApiUrl: 'test-backend-url',
+ NovuSocketUrl: 'test-socket-url'
+ },
};
return selector(state);
});
@@ -257,16 +262,18 @@ describe('NotificationInbox', () => {
mockUseCoreStore.mockImplementation((selector: any) => {
const state = {
activeUnitId: null,
- config: null,
+ config: { apiUrl: 'test-url' }, // Missing Novu config properties
};
return selector(state);
});
- const { getByText } = render(
+ const { queryByText } = render(
);
- expect(getByText('Unable to load notifications')).toBeTruthy();
+ // Component should return null when required config is missing
+ expect(queryByText('Notifications')).toBeNull();
+ expect(queryByText('Unable to load notifications')).toBeNull();
});
it('opens notification detail on tap in normal mode', async () => {