Commit d0cfc2ff authored by Yousef Sameh's avatar Yousef Sameh

inconnectivity loop

parent 8d528dbd
...@@ -6,6 +6,7 @@ using Supabase.Gotrue; ...@@ -6,6 +6,7 @@ using Supabase.Gotrue;
using Supabase.Gotrue.Interfaces; using Supabase.Gotrue.Interfaces;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using TMPro;
public class AppRouter : MonoBehaviour public class AppRouter : MonoBehaviour
{ {
...@@ -15,9 +16,14 @@ public class AppRouter : MonoBehaviour ...@@ -15,9 +16,14 @@ public class AppRouter : MonoBehaviour
[Header("Splash UI (Boot Scene Only)")] [Header("Splash UI (Boot Scene Only)")]
[SerializeField] private GameObject splashScreen; [SerializeField] private GameObject splashScreen;
[SerializeField] private GameObject errorPanel; [SerializeField] private GameObject errorPanel;
[SerializeField] private TextMeshProUGUI statusText;
[Header("Settings")]
public TransitionSettings transitionSettings; public TransitionSettings transitionSettings;
private bool _booted; [Header("Debug Testing")]
[Tooltip("Check this in the Inspector to simulate network loss and test the 'Waiting' loop.")]
[SerializeField] private bool simulateNetworkFailure;
private void Awake() private void Awake()
{ {
...@@ -30,6 +36,7 @@ public class AppRouter : MonoBehaviour ...@@ -30,6 +36,7 @@ public class AppRouter : MonoBehaviour
_instance = this; _instance = this;
DontDestroyOnLoad(gameObject); DontDestroyOnLoad(gameObject);
// Standard mobile optimizations
Application.targetFrameRate = Screen.currentResolution.refreshRate; Application.targetFrameRate = Screen.currentResolution.refreshRate;
Screen.orientation = ScreenOrientation.Portrait; Screen.orientation = ScreenOrientation.Portrait;
} }
...@@ -42,7 +49,7 @@ public class AppRouter : MonoBehaviour ...@@ -42,7 +49,7 @@ public class AppRouter : MonoBehaviour
private void HandleBackButton() private void HandleBackButton()
{ {
// Don't interrupt active challenges // Safety: Don't let the back button break an active game session
if (ChallengeManager.Instance != null && ChallengeManager.Instance.IsChallengeRunning) if (ChallengeManager.Instance != null && ChallengeManager.Instance.IsChallengeRunning)
return; return;
...@@ -66,74 +73,119 @@ public class AppRouter : MonoBehaviour ...@@ -66,74 +73,119 @@ public class AppRouter : MonoBehaviour
var cached = UserService.Instance.LoadFromCache(); var cached = UserService.Instance.LoadFromCache();
TransitionManager.Instance().onTransitionCutPointReached += HideSplash; TransitionManager.Instance().onTransitionCutPointReached += HideSplash;
bool ready = await SupabaseManager.Instance.Initialize(); // --- STEP 1: Network Connection Loop ---
if (!ready) // Execution halts here until isConnected is true
{ await EnsureNetworkConnection();
if (cached != null) { GoToHome(); return; }
ShowError("Failed to connect");
return;
}
// --- STEP 2: Authentication Session ---
UpdateStatus("Checking session...");
var authResult = await SupabaseAuthentication.Instance.EnsureSession(); var authResult = await SupabaseAuthentication.Instance.EnsureSession();
if (authResult.IsT1)
if (authResult.IsT1) // No valid session found
{ {
if (cached != null) { GoToHome(); return; } if (cached != null)
{
Debug.Log("[Boot] Offline/No session, using cache.");
GoToHome();
return;
}
GoToLogin(); GoToLogin();
return; return;
} }
// --- STEP 3: Load User Profile ---
UpdateStatus("Fetching profile...");
var prof = await UserService.Instance.GetCurrentUser(); var prof = await UserService.Instance.GetCurrentUser();
prof.Switch( prof.Switch(
async success => async success =>
{ {
TransitionManager.Instance().Transition("MainMenu", transitionSettings, 0f); UpdateStatus("Welcome back!");
UserService.Instance.StartListening(); TransitionManager.Instance().Transition("MainMenu", transitionSettings, 0.5f);
}, UserService.Instance.StartListening();
error => },
{ error =>
if (cached != null) {
{ // If profile fetch fails but we have a cache, let them in
Debug.LogWarning($"[Boot] Network profile fetch failed, using cache"); if (cached != null)
GoToHome(); {
} Debug.LogWarning($"[Boot] Profile fetch failed, falling back to cache.");
else GoToHome();
{ }
Debug.LogError($"[Boot] Failed to load user profile: {error}"); else
GoToLogin(); {
} Debug.LogError($"[Boot] Critical load error: {error}");
} GoToLogin();
); }
}
);
}
private async UniTask EnsureNetworkConnection()
{
bool isConnected = false;
int attempts = 0;
while (!isConnected)
{
attempts++;
UpdateStatus(attempts == 1 ? "Connecting..." : "Waiting for network...");
if (simulateNetworkFailure)
{
Debug.LogWarning("[TEST] simulateNetworkFailure is ON. Mocking connection failure.");
isConnected = false;
}
else
{
isConnected = await SupabaseManager.Instance.Initialize();
}
if (!isConnected)
{
// Wait 3 seconds before the next heartbeat
// Uses the object's token to stop if the user quits the app
await UniTask.Delay(TimeSpan.FromSeconds(3), cancellationToken: this.GetCancellationTokenOnDestroy());
}
}
UpdateStatus("Connected!");
await UniTask.Delay(500); // Visual polish
} }
// ─── Auth State Listener (Safety Net Only) ─────────────────────── private void UpdateStatus(string message)
{
if (statusText != null)
statusText.text = message;
Debug.Log($"[AppRouter] {message}");
}
// ─── Auth State Listener (Safety Net) ───────────────────────────
private void OnAuthStateChanged(IGotrueClient<Supabase.Gotrue.User, Supabase.Gotrue.Session> sender, Constants.AuthState newState) private void OnAuthStateChanged(IGotrueClient<Supabase.Gotrue.User, Supabase.Gotrue.Session> sender, Constants.AuthState newState)
{ {
switch (newState) switch (newState)
{ {
case Constants.AuthState.SignedOut: case Constants.AuthState.SignedOut:
// Only react if WE didn't trigger the sign-out
if (!SupabaseAuthentication.Instance.IsLoading) if (!SupabaseAuthentication.Instance.IsLoading)
{ {
Debug.LogWarning("[Auth] Unexpected sign-out detected");
UserService.Instance.ClearUser(); UserService.Instance.ClearUser();
GoToLogin(); GoToLogin();
} }
break; break;
case Constants.AuthState.TokenRefreshed: case Constants.AuthState.TokenRefreshed:
Debug.Log("[Auth] Token refreshed"); Debug.Log("[Auth] Session token refreshed.");
break; break;
} }
} }
// ─── Navigation ────────────────────────────────────────────────── // ─── Navigation Logic ───────────────────────────────────────────
public async static void GoToLogin() public static void GoToLogin()
{ {
TransitionManager.Instance().Transition("Login", _instance.transitionSettings, 0f); TransitionManager.Instance().Transition("Login", _instance.transitionSettings, 0f);
} }
public async static void GoToHome() public static void GoToHome()
{ {
TransitionManager.Instance().Transition("MainMenu", _instance.transitionSettings, 0f); TransitionManager.Instance().Transition("MainMenu", _instance.transitionSettings, 0f);
UserService.Instance.StartListening(); UserService.Instance.StartListening();
...@@ -151,7 +203,7 @@ public class AppRouter : MonoBehaviour ...@@ -151,7 +203,7 @@ public class AppRouter : MonoBehaviour
try { SSAudioManager.Instance?.StopMusic(); } catch { } try { SSAudioManager.Instance?.StopMusic(); } catch { }
} }
// ─── Helpers ───────────────────────────────────────────────────── // ─── UI Helpers ──────────────────────────────────────────────────
private static void HideSplash() private static void HideSplash()
{ {
if (_instance == null) return; if (_instance == null) return;
...@@ -163,6 +215,6 @@ public class AppRouter : MonoBehaviour ...@@ -163,6 +215,6 @@ public class AppRouter : MonoBehaviour
{ {
if (splashScreen != null) splashScreen.SetActive(false); if (splashScreen != null) splashScreen.SetActive(false);
if (errorPanel != null) errorPanel.SetActive(true); if (errorPanel != null) errorPanel.SetActive(true);
Debug.LogError($"[Boot] {message}"); Debug.LogError($"[Boot Error] {message}");
} }
} }
\ No newline at end of file
...@@ -50,23 +50,23 @@ public class SupabaseManager ...@@ -50,23 +50,23 @@ public class SupabaseManager
client.Auth.SetPersistence(new UnitySession()); client.Auth.SetPersistence(new UnitySession());
client.Auth.Options.AllowUnconfirmedUserSessions = true; client.Auth.Options.AllowUnconfirmedUserSessions = true;
// string url = $"{SupabaseSettings.SupabaseURL}/auth/v1/settings?apikey={SupabaseSettings.SupabaseAnonKey}"; string url = $"{SupabaseSettings.SupabaseURL}/auth/v1/settings?apikey={SupabaseSettings.SupabaseAnonKey}";
// await client.InitializeAsync(); // await client.InitializeAsync();
// try try
// { {
// client.Auth.Online = await _networkStatus.StartAsync(url); client.Auth.Online = await _networkStatus.StartAsync(url);
// } }
// catch (NotSupportedException) catch (NotSupportedException)
// { {
// client.Auth.Online = true; client.Auth.Online = true;
// } }
// catch (Exception e) catch (Exception e)
// { {
// Debug.LogWarning($"Network check failed: {e.Message}"); Debug.LogWarning($"Network check failed: {e.Message}");
// client.Auth.Online = false; client.Auth.Online = false;
// } }
// if (client.Auth.Online) // if (client.Auth.Online)
// { // {
......
...@@ -95,6 +95,7 @@ public class HomeController : MonoBehaviour ...@@ -95,6 +95,7 @@ public class HomeController : MonoBehaviour
SetPlayButtonsEnabled(enough); SetPlayButtonsEnabled(enough);
}, },
_ => SetPlayButtonsEnabled(false), _ => SetPlayButtonsEnabled(false),
curriculumId: 1,
gradeId: user.Grade, gradeId: user.Grade,
termId: user.Term termId: user.Term
)); ));
......
...@@ -166,7 +166,9 @@ MonoBehaviour: ...@@ -166,7 +166,9 @@ MonoBehaviour:
m_EditorClassIdentifier: Assembly-CSharp::AppRouter m_EditorClassIdentifier: Assembly-CSharp::AppRouter
splashScreen: {fileID: 90795484556287063} splashScreen: {fileID: 90795484556287063}
errorPanel: {fileID: 0} errorPanel: {fileID: 0}
statusText: {fileID: 0}
transitionSettings: {fileID: 11400000, guid: 91673b50629b1ae4f938d0b25135d085, type: 2} transitionSettings: {fileID: 11400000, guid: 91673b50629b1ae4f938d0b25135d085, type: 2}
simulateNetworkFailure: 0
--- !u!1 &787965442 --- !u!1 &787965442
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
......
...@@ -349,6 +349,7 @@ namespace com.al_arcade.shared ...@@ -349,6 +349,7 @@ namespace com.al_arcade.shared
// ═══════════════════════════════════════════════════ // ═══════════════════════════════════════════════════
public IEnumerator GetChapters(Action<ChapterInfo[]> onSuccess, public IEnumerator GetChapters(Action<ChapterInfo[]> onSuccess,
Action<string> onError, Action<string> onError,
int curriculumId = 0,
int subjectId = 0, int subjectId = 0,
int gradeId = 0, int gradeId = 0,
int termId = 0) int termId = 0)
...@@ -357,6 +358,7 @@ namespace com.al_arcade.shared ...@@ -357,6 +358,7 @@ namespace com.al_arcade.shared
if (subjectId > 0) p["subject_id"] = subjectId.ToString(); if (subjectId > 0) p["subject_id"] = subjectId.ToString();
if (gradeId > 0) p["grade_id"] = gradeId.ToString(); if (gradeId > 0) p["grade_id"] = gradeId.ToString();
if (termId > 0) p["term_id"] = termId.ToString(); if (termId > 0) p["term_id"] = termId.ToString();
if (curriculumId > 0) p["curriculum_id"] = curriculumId.ToString();
yield return GetRequest("get_chapters", p, yield return GetRequest("get_chapters", p,
json => json =>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment