Skip to content

Commit 8953168

Browse files
committed
fix: comprehensive changes aim to create a smoother and more robust application startup experience, correctly handling the asynchronous nature of authentication and critical configuration fetching. The initial flash of the "App configuration not available" error on the feed page should now be resolved.
1 parent 646b421 commit 8953168

File tree

1 file changed

+50
-150
lines changed

1 file changed

+50
-150
lines changed

lib/router/router.dart

Lines changed: 50 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -75,182 +75,82 @@ GoRouter createRouter({
7575
debugLogDiagnostics: true, // Enable verbose logging for debugging redirects
7676
// --- Redirect Logic ---
7777
redirect: (BuildContext context, GoRouterState state) {
78-
// --- Get Current State ---
79-
// Safely read the AppBloc state. refreshListenable ensures this runs
80-
// within a valid context after AppBloc state changes.
8178
final appStatus = context.read<AppBloc>().state.status;
82-
// The matched route pattern (e.g., '/authentication/email-sign-in').
79+
final appConfig = context.read<AppBloc>().state.appConfig; // Get appConfig
8380
final currentLocation = state.matchedLocation;
84-
// The full URI including query parameters (e.g., '/authentication?context=linking').
8581
final currentUri = state.uri;
8682

87-
// --- Debug Logging ---
88-
// Log current state for easier debugging of redirect behavior.
8983
print(
9084
'GoRouter Redirect Check:\n'
9185
' Current Location (Matched): $currentLocation\n'
9286
' Current URI (Full): $currentUri\n'
93-
' AppStatus: $appStatus',
87+
' AppStatus: $appStatus\n'
88+
' AppConfig isNull: ${appConfig == null}',
9489
);
9590

9691
// --- Define Key Paths ---
97-
// Base paths for major sections.
98-
const authenticationPath = Routes.authentication; // '/authentication'
99-
const feedPath = Routes.feed; // Updated path constant
100-
// Specific authentication sub-routes crucial for the email code verification flow.
101-
const requestCodePath =
102-
'$authenticationPath/${Routes.requestCode}'; // '/authentication/request-code'
103-
const verifyCodePath =
104-
'$authenticationPath/${Routes.verifyCode}'; // '/authentication/verify-code'
105-
106-
// --- Helper Booleans ---
107-
// Check if the navigation target is within the authentication section.
92+
const authenticationPath = Routes.authentication;
93+
const feedPath = Routes.feed;
10894
final isGoingToAuth = currentLocation.startsWith(authenticationPath);
109-
// Check if the navigation target is within the feed section.
110-
final isGoingToFeed = currentLocation.startsWith(
111-
feedPath,
112-
); // Updated path constant
113-
// Check if the navigation target is the *exact* base authentication path.
114-
final isGoingToBaseAuthPath = currentLocation == authenticationPath;
115-
// Check if the 'context=linking' query parameter is present in the URI.
116-
final isLinkingContext =
117-
currentUri.queryParameters['context'] == 'linking';
118-
// Removed isGoingToSplash check
119-
120-
// --- Redirect Logic based on AppStatus ---
12195

122-
// --- Case 0: Initial Loading State ---
123-
// While the app is initializing (status is initial), don't redirect.
124-
// Let the initial navigation attempt proceed. The refreshListenable
125-
// will trigger a redirect check again once the status is known.
126-
if (appStatus == AppStatus.initial) {
96+
// --- Case 0: App is Initializing or Config is being fetched/failed ---
97+
if (appStatus == AppStatus.initial ||
98+
appStatus == AppStatus.configFetching ||
99+
appStatus == AppStatus.configFetchFailed) {
100+
101+
// If AppStatus is initial and trying to go to a non-auth page (e.g. initial /feed)
102+
// redirect to auth immediately to settle auth status first.
103+
if (appStatus == AppStatus.initial && !isGoingToAuth) {
104+
print(
105+
' Redirect Decision: AppStatus is INITIAL and not going to auth. Redirecting to $authenticationPath to settle auth first.',
106+
);
107+
return authenticationPath;
108+
}
109+
// For configFetching or configFetchFailed, or initial going to auth,
110+
// let the App widget's builder handle the UI (loading/error screen).
127111
print(
128-
' Redirect Decision: AppStatus is INITIAL. Allowing navigation.',
112+
' Redirect Decision: AppStatus is $appStatus. Allowing App widget to handle display or navigation to auth.',
129113
);
130-
return null; // Do not redirect during initial phase
114+
return null;
131115
}
132116

133-
// --- Case 1: Unauthenticated User (After Initial Load) ---
134-
// If the user is unauthenticated...
117+
// --- Case 1: Unauthenticated User (after initial phase, config not relevant yet for this decision) ---
135118
if (appStatus == AppStatus.unauthenticated) {
136-
print(' Redirect Decision: User is UNauthenticated (post-initial).');
137-
// If the user is NOT already going to an authentication path...
119+
print(' Redirect Decision: User is UNauthenticated.');
138120
if (!isGoingToAuth) {
139-
// ...redirect them to the main authentication page to sign in or sign up.
140-
print(' Action: Redirecting to $authenticationPath');
121+
print(' Action: Not going to auth. Redirecting to $authenticationPath');
141122
return authenticationPath;
142123
}
143-
// Otherwise, allow them to stay on the authentication path they are navigating to.
144-
print(' Action: Allowing navigation within authentication section.');
145-
return null; // Allow access
124+
print(' Action: Already going to auth. Allowing navigation.');
125+
return null;
146126
}
147-
// --- Case 2: Anonymous User ---
148-
else if (appStatus == AppStatus.anonymous) {
149-
print(' Redirect Decision: User is ANONYMOUS.');
150-
151-
// Define search and account paths for clarity
152-
const searchPath = Routes.search; // '/search'
153-
const accountPath = Routes.account; // '/account'
154127

155-
// Helper booleans for search and account sections
156-
final isGoingToSearch = currentLocation.startsWith(searchPath);
157-
final isGoingToAccount = currentLocation.startsWith(accountPath);
128+
// --- Case 2: Anonymous or Authenticated User ---
129+
// (Covers AppStatus.anonymous and AppStatus.authenticated)
130+
// At this point, AppConfig should be loaded or its loading/error state is handled by App widget.
131+
// The main concern here is preventing authenticated users from re-entering basic auth flows.
132+
if (appStatus == AppStatus.anonymous || appStatus == AppStatus.authenticated) {
133+
print(' Redirect Decision: User is $appStatus.');
134+
135+
final isLinkingContext = currentUri.queryParameters['context'] == 'linking';
158136

159-
// **Sub-Case 2.1: Navigating to the BASE Authentication Path (`/authentication`)**
160-
if (isGoingToBaseAuthPath) {
161-
// Allow access ONLY if they are explicitly starting the linking flow
162-
// (indicated by the 'context=linking' query parameter).
163-
if (isLinkingContext) {
164-
print(
165-
' Action: Allowing navigation to BASE auth for account linking.',
166-
);
167-
return null; // Allow access
168-
} else {
169-
// Prevent anonymous users from accessing the initial sign-in screen again.
170-
// Redirect them to the main content (feed).
171-
print(
172-
' Action: Preventing access to initial sign-in, redirecting to $feedPath', // Updated path constant
173-
);
174-
return feedPath; // Redirect to feed
175-
}
176-
}
177-
// **Sub-Case 2.2: Navigating to Specific Email Code Verification Sub-Routes**
178-
// Explicitly allow access to the necessary pages for the email code verification process,
179-
// even if the 'context=linking' parameter is lost during navigation between these pages.
180-
else if (currentLocation == requestCodePath ||
181-
currentLocation.startsWith(verifyCodePath)) {
182-
// Use startsWith for parameterized path
183-
print(
184-
' Action: Allowing navigation to email code verification sub-route ($currentLocation).',
185-
);
186-
return null; // Allow access
187-
}
188-
// **Sub-Case 2.3: Navigating Within the Main App Sections (Feed, Search, Account) or Details Pages**
189-
// Allow anonymous users to access the main content sections, their sub-routes, and details pages.
190-
else if (isGoingToFeed ||
191-
isGoingToSearch ||
192-
isGoingToAccount ||
193-
currentLocation == Routes.categoryDetails ||
194-
currentLocation == Routes.sourceDetails ||
195-
currentLocation.startsWith(
196-
Routes.globalArticleDetails.split('/:id').first,
197-
) || // Allow global article details
198-
currentLocation.startsWith(
199-
'${Routes.feed}/${Routes.articleDetailsName.split('/:id').first}',
200-
) ||
201-
currentLocation.startsWith(
202-
'${Routes.search}/${Routes.searchArticleDetailsName.split('/:id').first}',
203-
) ||
204-
currentLocation.startsWith(
205-
'${Routes.account}/${Routes.accountSavedHeadlines}/${Routes.accountArticleDetailsName.split('/:id').first}',
206-
)) {
207-
print(
208-
' Action: Allowing navigation to main app section or details page ($currentLocation).',
209-
);
210-
return null; // Allow access
211-
}
212-
// **Sub-Case 2.4: Fallback for Unexpected Paths**
213-
// If an anonymous user tries to navigate anywhere else unexpected,
214-
// redirect them to the main content feed as a safe default.
215-
else {
216-
print(
217-
' Action: Unexpected path ($currentLocation), redirecting to $feedPath',
218-
);
219-
return feedPath; // Redirect to feed
220-
}
221-
}
222-
// --- Case 3: Authenticated User ---
223-
else if (appStatus == AppStatus.authenticated) {
224-
print(' Redirect Decision: User is AUTHENTICATED.');
225-
// If an authenticated user tries to access any part of the authentication flow...
226-
if (isGoingToAuth) {
227-
// ...redirect them away to the main content feed. They don't need to authenticate again.
137+
// If an authenticated/anonymous user tries to access the BASE /authentication path
138+
// AND it's NOT for account linking, redirect them to the feed.
139+
if (currentLocation == authenticationPath && !isLinkingContext) {
228140
print(
229-
' Action: Preventing access to authentication section, redirecting to $feedPath', // Updated path constant
141+
' Action: $appStatus user trying to access base auth path without linking context. Redirecting to $feedPath',
230142
);
231-
return feedPath; // Redirect to feed
143+
return feedPath;
232144
}
233-
// Otherwise, allow authenticated users to access any other part of the app (feed, account, settings, etc.).
234-
print(
235-
' Action: Allowing navigation to non-auth section ($currentLocation).',
236-
);
237-
return null; // Allow access
238-
}
239-
// --- Case 4: Fallback (Should not be reached with initial handling) ---
240-
// This case is less likely now with explicit initial handling.
241-
// If somehow the status is unknown after the initial phase, allow navigation.
242-
else {
243-
print(
244-
' Redirect Decision: AppStatus is UNEXPECTED ($appStatus). Allowing navigation (fallback).',
245-
);
246-
return null; // Allow access as a safe default
145+
146+
// Allow access to other routes (including auth sub-routes if linking, or any other app route)
147+
print(' Action: Allowing navigation to $currentLocation for $appStatus user.');
148+
return null;
247149
}
248150

249-
// --- Default: No Redirect (Should not be reached if logic is exhaustive) ---
250-
// If none of the above conditions triggered an explicit redirect, allow navigation.
251-
// This line should theoretically not be reached if the logic above is exhaustive.
252-
// print(' Redirect Decision: No specific redirect condition met. Allowing navigation.');
253-
// return null; // Allow access (already covered by the final return null below)
151+
// Fallback (should ideally not be reached if all statuses are handled)
152+
print(' Redirect Decision: Fallback, no specific condition met for $appStatus. Allowing navigation.');
153+
return null;
254154
},
255155
// --- Authentication Routes ---
256156
routes: [
@@ -434,14 +334,14 @@ GoRouter createRouter({
434334
),
435335
BlocProvider(
436336
create: (context) {
437-
final feedInjectorService = FeedInjectorService(); // Instantiate
337+
final feedInjectorService =
338+
FeedInjectorService(); // Instantiate
438339
return HeadlinesSearchBloc(
439340
headlinesRepository:
440341
context.read<HtDataRepository<Headline>>(),
441342
categoryRepository:
442343
context.read<HtDataRepository<Category>>(),
443-
sourceRepository:
444-
context.read<HtDataRepository<Source>>(),
344+
sourceRepository: context.read<HtDataRepository<Source>>(),
445345
appBloc: context.read<AppBloc>(), // Provide AppBloc
446346
feedInjectorService: feedInjectorService, // Provide Service
447347
);

0 commit comments

Comments
 (0)