Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit9d7a803unknown_key

fix: break circular imports in mobile navigation by extracting types

fix: break circular imports in mobile navigation by extracting types

Extract MainStackParamList to navigation/types.ts and AuthContextValue
+ AuthContext to navigation/AuthContext.ts so screens can import from
leaf files without creating a cycle through the navigator files.

https://claude.ai/code/session_01DzJMTFASjMHt2f5ze4cNLR
Claude committed on June 7, 2026Parent: c53cf00
15 files changed+60529d7a803ea303412c4927151752c55e3458811727
15 changed files+60−52
Addedmobile/src/navigation/AuthContext.ts+16−0View fileUnifiedSplit
1import { createContext } from 'react';
2import { type User } from '../api/client';
3
4export interface AuthContextValue {
5 user: User | null;
6 token: string | null;
7 login: (tokenOrUsername: string, password?: string, host?: string) => Promise<void>;
8 logout: () => Promise<void>;
9}
10
11export const AuthContext = createContext<AuthContextValue>({
12 user: null,
13 token: null,
14 login: async () => {},
15 logout: async () => {},
16});
Modifiedmobile/src/navigation/MainTabNavigator.tsx+5−14View fileUnifiedSplit
11import React from 'react';
22import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
33import { createNativeStackNavigator } from '@react-navigation/native-stack';
4import { Text, View } from 'react-native';
4import { Text } from 'react-native';
55import { colors } from '../theme/colors';
66import { fontSizes } from '../theme/typography';
7import { type MainStackParamList } from './types';
8
9// Re-export for convenience (navigators that don't import screens can still get the type)
10export type { MainStackParamList };
711
812import { DashboardScreen } from '../screens/DashboardScreen';
913import { RepoListScreen } from '../screens/RepoListScreen';
1620import { NotificationsScreen } from '../screens/NotificationsScreen';
1721import { SettingsScreen } from '../screens/SettingsScreen';
1822
19// ─── Stack param types ───────────────────────────────────────────────────────
20
21export type MainStackParamList = {
22 Dashboard: undefined;
23 RepoList: undefined;
24 RepoDetail: { owner: string; repo: string };
25 FileViewer: { owner: string; repo: string; path: string; ref: string };
26 IssueList: { owner: string; repo: string };
27 IssueDetail: { owner: string; repo: string; number: number };
28 PullList: { owner: string; repo: string };
29 PullDetail: { owner: string; repo: string; number: number };
30};
31
3223const Stack = createNativeStackNavigator<MainStackParamList>();
3324const Tab = createBottomTabNavigator();
3425
Modifiedmobile/src/navigation/RootNavigator.tsx+13−25View fileUnifiedSplit
1import React, { createContext, useState, useEffect } from 'react';
1import React from 'react';
22import { NavigationContainer } from '@react-navigation/native';
33import { createNativeStackNavigator } from '@react-navigation/native-stack';
44import { View, ActivityIndicator } from 'react-native';
66import { AuthScreen } from '../screens/AuthScreen';
77import { MainTabNavigator } from './MainTabNavigator';
88import { useAuth } from '../hooks/useAuth';
9import { type User } from '../api/client';
9import { AuthContext } from './AuthContext';
1010
11// ─── Auth context — shared across the whole app ───────────────────────────────
12
13export interface AuthContextValue {
14 user: User | null;
15 token: string | null;
16 login: (tokenOrUsername: string, password?: string, host?: string) => Promise<void>;
17 logout: () => Promise<void>;
18}
19
20export const AuthContext = createContext<AuthContextValue>({
21 user: null,
22 token: null,
23 login: async () => {},
24 logout: async () => {},
25});
11// Re-export AuthContext so callers can do: import { AuthContext } from '../navigation/RootNavigator'
12export { AuthContext };
13export type { AuthContextValue } from './AuthContext';
2614
2715// ─── Root param list ──────────────────────────────────────────────────────────
2816
3826export function RootNavigator() {
3927 const auth = useAuth();
4028
41 const contextValue: AuthContextValue = {
42 user: auth.user,
43 token: auth.token,
44 login: auth.login,
45 logout: auth.logout,
46 };
47
4829 if (auth.loading) {
4930 return (
5031 <View style={{ flex: 1, backgroundColor: colors.bg, alignItems: 'center', justifyContent: 'center' }}>
5435 }
5536
5637 return (
57 <AuthContext.Provider value={contextValue}>
38 <AuthContext.Provider
39 value={{
40 user: auth.user,
41 token: auth.token,
42 login: auth.login,
43 logout: auth.logout,
44 }}
45 >
5846 <NavigationContainer
5947 theme={{
6048 dark: true,
Addedmobile/src/navigation/types.ts+13−0View fileUnifiedSplit
1// Navigation param types — extracted to a separate file to break circular imports.
2// Screens import from here; navigators import from here.
3
4export type MainStackParamList = {
5 Dashboard: undefined;
6 RepoList: undefined;
7 RepoDetail: { owner: string; repo: string };
8 FileViewer: { owner: string; repo: string; path: string; ref: string };
9 IssueList: { owner: string; repo: string };
10 IssueDetail: { owner: string; repo: string; number: number };
11 PullList: { owner: string; repo: string };
12 PullDetail: { owner: string; repo: string; number: number };
13};
Modifiedmobile/src/screens/AuthScreen.tsx+1−1View fileUnifiedSplit
1414import { SafeAreaView } from 'react-native-safe-area-context';
1515import { colors } from '../theme/colors';
1616import { fontSizes, fontWeights } from '../theme/typography';
17import { AuthContext } from '../navigation/RootNavigator';
17import { AuthContext } from '../navigation/AuthContext';
1818
1919export function AuthScreen() {
2020 const { login } = useContext(AuthContext);
Modifiedmobile/src/screens/DashboardScreen.tsx+2−2View fileUnifiedSplit
1111import { type NativeStackNavigationProp } from '@react-navigation/native-stack';
1212import { colors } from '../theme/colors';
1313import { fontSizes, fontWeights } from '../theme/typography';
14import { AuthContext } from '../navigation/RootNavigator';
14import { AuthContext } from '../navigation/AuthContext';
1515import { useUserRepos } from '../hooks/useRepo';
1616import { RepoCard } from '../components/RepoCard';
1717import { LoadingSpinner } from '../components/LoadingSpinner';
18import { type MainStackParamList } from '../navigation/MainTabNavigator';
18import { type MainStackParamList } from '../navigation/types';
1919
2020interface Props {
2121 navigation: NativeStackNavigationProp<MainStackParamList>;
Modifiedmobile/src/screens/FileViewerScreen.tsx+1−1View fileUnifiedSplit
1212import { useFileContent } from '../hooks/useRepo';
1313import { LoadingSpinner } from '../components/LoadingSpinner';
1414import { ErrorState } from '../components/ErrorState';
15import { type MainStackParamList } from '../navigation/MainTabNavigator';
15import { type MainStackParamList } from '../navigation/types';
1616
1717type Props = {
1818 route: RouteProp<MainStackParamList, 'FileViewer'>;
Modifiedmobile/src/screens/IssueDetailScreen.tsx+1−1View fileUnifiedSplit
1818import { useIssue } from '../hooks/useIssues';
1919import { LoadingSpinner } from '../components/LoadingSpinner';
2020import { ErrorState } from '../components/ErrorState';
21import { type MainStackParamList } from '../navigation/MainTabNavigator';
21import { type MainStackParamList } from '../navigation/types';
2222
2323type Props = {
2424 route: RouteProp<MainStackParamList, 'IssueDetail'>;
Modifiedmobile/src/screens/IssueListScreen.tsx+1−1View fileUnifiedSplit
1717import { LoadingSpinner } from '../components/LoadingSpinner';
1818import { ErrorState } from '../components/ErrorState';
1919import { EmptyState } from '../components/EmptyState';
20import { type MainStackParamList } from '../navigation/MainTabNavigator';
20import { type MainStackParamList } from '../navigation/types';
2121
2222type Props = {
2323 navigation: NativeStackNavigationProp<MainStackParamList, 'IssueList'>;
Modifiedmobile/src/screens/NotificationsScreen.tsx+1−1View fileUnifiedSplit
1010import { SafeAreaView } from 'react-native-safe-area-context';
1111import { colors } from '../theme/colors';
1212import { fontSizes, fontWeights } from '../theme/typography';
13import { AuthContext } from '../navigation/RootNavigator';
13import { AuthContext } from '../navigation/AuthContext';
1414import { useUserRepos } from '../hooks/useRepo';
1515import { useNotifications } from '../hooks/useNotifications';
1616import { NotifRow } from '../components/NotifRow';
Modifiedmobile/src/screens/PullDetailScreen.tsx+1−1View fileUnifiedSplit
1212import { usePull } from '../hooks/usePulls';
1313import { LoadingSpinner } from '../components/LoadingSpinner';
1414import { ErrorState } from '../components/ErrorState';
15import { type MainStackParamList } from '../navigation/MainTabNavigator';
15import { type MainStackParamList } from '../navigation/types';
1616
1717type Props = {
1818 route: RouteProp<MainStackParamList, 'PullDetail'>;
Modifiedmobile/src/screens/PullListScreen.tsx+1−1View fileUnifiedSplit
1717import { LoadingSpinner } from '../components/LoadingSpinner';
1818import { ErrorState } from '../components/ErrorState';
1919import { EmptyState } from '../components/EmptyState';
20import { type MainStackParamList } from '../navigation/MainTabNavigator';
20import { type MainStackParamList } from '../navigation/types';
2121
2222type PullState = 'open' | 'closed' | 'merged';
2323
Modifiedmobile/src/screens/RepoDetailScreen.tsx+1−1View fileUnifiedSplit
2020import { CommitRow } from '../components/CommitRow';
2121import { IssueRow } from '../components/IssueRow';
2222import { PullRow } from '../components/PullRow';
23import { type MainStackParamList } from '../navigation/MainTabNavigator';
23import { type MainStackParamList } from '../navigation/types';
2424
2525type Props = {
2626 navigation: NativeStackNavigationProp<MainStackParamList, 'RepoDetail'>;
Modifiedmobile/src/screens/RepoListScreen.tsx+2−2View fileUnifiedSplit
1111import { type NativeStackNavigationProp } from '@react-navigation/native-stack';
1212import { colors } from '../theme/colors';
1313import { fontSizes, fontWeights } from '../theme/typography';
14import { AuthContext } from '../navigation/RootNavigator';
14import { AuthContext } from '../navigation/AuthContext';
1515import { useUserRepos } from '../hooks/useRepo';
1616import { RepoCard } from '../components/RepoCard';
1717import { LoadingSpinner } from '../components/LoadingSpinner';
1818import { ErrorState } from '../components/ErrorState';
1919import { EmptyState } from '../components/EmptyState';
20import { type MainStackParamList } from '../navigation/MainTabNavigator';
20import { type MainStackParamList } from '../navigation/types';
2121
2222interface Props {
2323 navigation: NativeStackNavigationProp<MainStackParamList>;
Modifiedmobile/src/screens/SettingsScreen.tsx+1−1View fileUnifiedSplit
1111import { SafeAreaView } from 'react-native-safe-area-context';
1212import { colors } from '../theme/colors';
1313import { fontSizes, fontWeights } from '../theme/typography';
14import { AuthContext } from '../navigation/RootNavigator';
14import { AuthContext } from '../navigation/AuthContext';
1515import { getBaseUrl, setBaseUrl } from '../api/client';
1616import { saveHost } from '../store/auth';
1717
1818