Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document AuthFlow and Glitchy Login Fix #245

Merged
merged 1 commit into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions game/lib/api/game_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,52 @@ import 'package:socket_io_client/socket_io_client.dart' as IO;
import 'package:http/http.dart' as http;
import 'package:game/api/geopoint.dart';

/**
* ApiClient Class - Core authentication and API communication manager.
*
* A ChangeNotifier class that manages authentication state, socket connections,
* and API communication for the entire application.
*
* @remarks
* This class serves as the central hub for all API-related functionality:
* - Authentication state management (Google Sign-In, device login)
* - Socket connection handling and management
* - Token management (access and refresh tokens)
* - Stream management through GameClientApi
* - Server API access through GameServerApi
*
* The class uses a multi-layered approach:
* - ApiClient: Top-level manager and state coordinator
* - GameClientApi: Manages streams for UI updates
* - GameServerApi: Handles server-side communication
*
* Key Features:
* - Automatic token refresh
* - Persistent authentication through secure storage
* - Socket connection management
* - Google Sign-In integration
* - Device-based authentication
*
* @example
* ```dart
* final apiClient = Provider.of<ApiClient>(context);
*
* // Listen to connection state
* StreamBuilder(
* stream: apiClient.clientApi.disconnectedStream,
* builder: (context, snapshot) {
* // Handle connection state changes
* }
* );
*
* // Perform authentication
* await apiClient.connectGoogle(googleAccount, ...);
* ```
*
* @see GameClientApi for stream management
* @see GameServerApi for server communication
*/

class ApiClient extends ChangeNotifier {
final FlutterSecureStorage _storage;
final _googleSignIn = GoogleSignIn(
Expand All @@ -20,6 +66,7 @@ class ApiClient extends ChangeNotifier {
],
);

// The ApiClient manages the socket and authentication state while the ClientApi manages the streams that components listen to
final String _apiUrl;
final Uri _googleLoginUrl;
final Uri _deviceLoginUrl;
Expand Down
53 changes: 30 additions & 23 deletions game/lib/gameplay/gameplay_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,28 @@ import 'package:game/model/group_model.dart';
import 'package:game/model/event_model.dart';
import 'package:game/model/challenge_model.dart';

/// GameplayMap Widget
///
/// Displays an interactive map for gameplay, showing the user's location,
/// hint circles, and challenge-related information.
///
/// @remarks
/// This widget is responsible for handling user location updates, displaying
/// hints, and managing the challenge completion process. It uses Google Maps
/// for rendering the map and integrates with various game-related models and
/// APIs to provide a seamless gameplay experience.
///
/// @param challengeId - The unique identifier for the current challenge.
/// @param targetLocation - The GeoPoint representing the target location for the challenge.
/// @param awardingRadius - The radius (in meters) within which the challenge is considered complete.
/// @param points - The number of points awarded for completing this challenge.
/// @param startingHintsUsed - The number of hints already used for this challenge.
///
/// @returns A StatefulWidget that renders the gameplay map and associated UI elements
/*

* GameplayMap Widget
*
* Displays an interactive map for gameplay, showing the user's location,
* hint circles, and challenge-related information.
*
* @remarks
* This widget is responsible for handling user location updates, displaying
* hints, and managing the challenge completion process. It uses Google Maps
* for rendering the map and integrates with various game-related models and
* APIs to provide a seamless gameplay experience.
*
* @param challengeId - The unique identifier for the current challenge.
* @param targetLocation - The GeoPoint representing the target location for the challenge.
* @param awardingRadius - The radius (in meters) within which the challenge is considered complete.
* @param points - The number of points awarded for completing this challenge.
* @param startingHintsUsed - The number of hints already used for this challenge.
*
* @returns A StatefulWidget that renders the gameplay map and associated UI elements

*/

class GameplayMap extends StatefulWidget {
final String challengeId;
Expand Down Expand Up @@ -358,13 +362,16 @@ class _GameplayMapState extends State<GameplayMap> {
StreamBuilder(
stream: client.clientApi.disconnectedStream,
builder: ((context, snapshot) {
// Redirect to login if server api is null
if (client.serverApi == null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => SplashPageWidget()));
displayToast("Lost connection!", Status.success);
// Clear entire navigation stack and push to login screen
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => SplashPageWidget()),
(route) => false,
);
displayToast("Signed out", Status.success);
});
}

Expand Down
45 changes: 39 additions & 6 deletions game/lib/navigation_page/bottom_navbar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,38 @@ import 'package:provider/provider.dart';
import 'home_navbar.dart';
import 'search_filter_home.dart';

/**

* BottomNavBar Widget - Main navigation container for the app.
*
* A stateful widget that manages the primary navigation interface and handles
* authentication state for the main app flow.
*
* @remarks
* This widget serves as the root container for the main app interface, managing:
* - Navigation between main app sections (Home, Leaderboard, Profile)
* - Authentication state monitoring via disconnection stream
* - Automatic redirection to login when authentication is lost
* - Consistent bottom navigation bar UI across all main sections
*
* The widget uses Provider pattern to access the ApiClient and manages its own
* state for the selected navigation index.
*
* @example
* ```dart
* Navigator.pushReplacement(
* context,
* MaterialPageRoute(builder: (context) => BottomNavBar()),
* );
* ```
*
* @param key - Optional widget key for identification and testing.
*
* @returns A StatefulWidget that renders the main navigation interface with
* bottom navigation bar and handles authentication state.

*/

class BottomNavBar extends StatefulWidget {
const BottomNavBar({Key? key}) : super(key: key);

Expand Down Expand Up @@ -41,17 +73,18 @@ class _BottomNavBarState extends State<BottomNavBar> {
child: StreamBuilder(
stream: client.clientApi.disconnectedStream,
builder: (context, snapshot) {
// Redirect to login if server api is null
if (client.serverApi == null) {
print("ServerApi == null");
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => SplashPageWidget()));
// Clear entire navigation stack and push to login screen
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => SplashPageWidget()),
(route) => false,
);
displayToast("Signed out", Status.success);
});
}

// Returning the main content page abvoe the navbar [Home, Leaderboard, Profile]
return _widgetOptions.elementAt(_selectedIndex);
}),
),
Expand Down
Loading