fix: use same button position/style for playground back button
Instead of a separate back button, the Previous button is repurposed as "Back" in playground mode - same position (left), same style. Only the Next button is hidden in playground mode. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
33
src/app.js
33
src/app.js
@@ -149,7 +149,6 @@ const elements = {
|
|||||||
expectedOverlay: document.getElementById("expected-overlay"),
|
expectedOverlay: document.getElementById("expected-overlay"),
|
||||||
previewWrapper: document.querySelector(".preview-wrapper"),
|
previewWrapper: document.querySelector(".preview-wrapper"),
|
||||||
previewSection: document.querySelector(".preview-section"),
|
previewSection: document.querySelector(".preview-section"),
|
||||||
backBtn: document.getElementById("back-btn"),
|
|
||||||
prevBtn: document.getElementById("prev-btn"),
|
prevBtn: document.getElementById("prev-btn"),
|
||||||
nextBtn: document.getElementById("next-btn"),
|
nextBtn: document.getElementById("next-btn"),
|
||||||
gameControls: document.querySelector(".game-controls"),
|
gameControls: document.querySelector(".game-controls"),
|
||||||
@@ -751,13 +750,18 @@ function updateNavigationButtons() {
|
|||||||
const engineState = lessonEngine.getCurrentState();
|
const engineState = lessonEngine.getCurrentState();
|
||||||
const isPlayground = engineState.lesson?.mode === "playground";
|
const isPlayground = engineState.lesson?.mode === "playground";
|
||||||
|
|
||||||
// Hide nav buttons and center controls in playground mode, show back button
|
// In playground mode: hide next button, repurpose prev as back button
|
||||||
elements.backBtn?.classList.toggle("hidden", !isPlayground);
|
|
||||||
elements.prevBtn.classList.toggle("hidden", isPlayground);
|
|
||||||
elements.nextBtn.classList.toggle("hidden", isPlayground);
|
elements.nextBtn.classList.toggle("hidden", isPlayground);
|
||||||
elements.gameControls?.classList.toggle("centered", isPlayground);
|
elements.gameControls?.classList.toggle("centered", isPlayground);
|
||||||
|
|
||||||
if (!isPlayground) {
|
if (isPlayground) {
|
||||||
|
// Change prev button to "Back" in playground mode
|
||||||
|
elements.prevBtn.textContent = t("back");
|
||||||
|
elements.prevBtn.disabled = false;
|
||||||
|
elements.prevBtn.classList.remove("btn-disabled");
|
||||||
|
} else {
|
||||||
|
// Normal mode: prev/next navigation
|
||||||
|
elements.prevBtn.textContent = t("previous");
|
||||||
elements.prevBtn.disabled = !engineState.canGoPrev;
|
elements.prevBtn.disabled = !engineState.canGoPrev;
|
||||||
elements.nextBtn.disabled = !engineState.canGoNext;
|
elements.nextBtn.disabled = !engineState.canGoNext;
|
||||||
|
|
||||||
@@ -788,7 +792,16 @@ function nextLesson() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function prevLesson() {
|
function prevLesson() {
|
||||||
const prevModuleId = lessonEngine.getCurrentState().module?.id;
|
const engineState = lessonEngine.getCurrentState();
|
||||||
|
const isPlayground = engineState.lesson?.mode === "playground";
|
||||||
|
|
||||||
|
// In playground mode, "Back" navigates to home
|
||||||
|
if (isPlayground) {
|
||||||
|
navigateTo("#");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevModuleId = engineState.module?.id;
|
||||||
const success = lessonEngine.previousLesson();
|
const success = lessonEngine.previousLesson();
|
||||||
if (success) {
|
if (success) {
|
||||||
const newState = lessonEngine.getCurrentState();
|
const newState = lessonEngine.getCurrentState();
|
||||||
@@ -2455,12 +2468,12 @@ function init() {
|
|||||||
// Set timeout to show fallback if loading takes too long
|
// Set timeout to show fallback if loading takes too long
|
||||||
loadingTimeout = setTimeout(showLoadingFallback, 3000);
|
loadingTimeout = setTimeout(showLoadingFallback, 3000);
|
||||||
|
|
||||||
// Load modules after editor is ready
|
// Handle OAuth callback FIRST (tokens are in URL hash, must run before router)
|
||||||
|
handleOAuthCallback().then(() => {
|
||||||
|
// Load modules (this also calls handleRoute inside)
|
||||||
initializeModules();
|
initializeModules();
|
||||||
|
|
||||||
// Handle OAuth callback BEFORE router (tokens are in URL hash)
|
// Initialize URL router for browser back/forward
|
||||||
handleOAuthCallback().then(() => {
|
|
||||||
// Initialize URL router for shareable links
|
|
||||||
initRouter();
|
initRouter();
|
||||||
|
|
||||||
// Initialize authentication
|
// Initialize authentication
|
||||||
|
|||||||
22
src/auth.js
22
src/auth.js
@@ -17,6 +17,7 @@ export async function handleOAuthCallback() {
|
|||||||
|
|
||||||
// Check if hash contains OAuth tokens (access_token, error, etc.)
|
// Check if hash contains OAuth tokens (access_token, error, etc.)
|
||||||
if (!hash.includes("access_token") && !hash.includes("error_description") && !hash.includes("refresh_token")) {
|
if (!hash.includes("access_token") && !hash.includes("error_description") && !hash.includes("refresh_token")) {
|
||||||
|
console.log("[Auth] No OAuth tokens in hash");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,20 +26,33 @@ export async function handleOAuthCallback() {
|
|||||||
try {
|
try {
|
||||||
const supabaseModule = await import("./supabase.js");
|
const supabaseModule = await import("./supabase.js");
|
||||||
if (!supabaseModule.isConfigured) {
|
if (!supabaseModule.isConfigured) {
|
||||||
|
console.log("[Auth] Supabase not configured");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let Supabase process the OAuth tokens
|
// Parse tokens from hash
|
||||||
const { data, error } = await supabaseModule.auth.getSession();
|
const params = new URLSearchParams(hash.substring(1));
|
||||||
|
const accessToken = params.get("access_token");
|
||||||
|
const refreshToken = params.get("refresh_token");
|
||||||
|
|
||||||
|
console.log("[Auth] Tokens found:", { accessToken: !!accessToken, refreshToken: !!refreshToken });
|
||||||
|
|
||||||
|
if (accessToken && refreshToken) {
|
||||||
|
// Explicitly set the session with tokens from URL
|
||||||
|
const { data, error } = await supabaseModule.auth.setSession({
|
||||||
|
access_token: accessToken,
|
||||||
|
refresh_token: refreshToken
|
||||||
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error("[Auth] OAuth callback error:", error.message);
|
console.error("[Auth] OAuth setSession error:", error.message);
|
||||||
} else if (data?.session) {
|
} else if (data?.session) {
|
||||||
console.log("[Auth] OAuth login successful:", data.session.user?.email);
|
console.log("[Auth] OAuth login successful:", data.session.user?.email);
|
||||||
oauthHandled = true;
|
oauthHandled = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Clear the hash after processing (will be replaced by router)
|
// Clear the hash after processing
|
||||||
window.history.replaceState(null, "", window.location.pathname);
|
window.history.replaceState(null, "", window.location.pathname);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -434,7 +434,6 @@
|
|||||||
<!-- Right Panel: Preview + Navigation -->
|
<!-- Right Panel: Preview + Navigation -->
|
||||||
<div class="right-panel">
|
<div class="right-panel">
|
||||||
<div class="game-controls">
|
<div class="game-controls">
|
||||||
<a id="back-btn" href="#" class="btn hidden" data-i18n="back">Back</a>
|
|
||||||
<button id="prev-btn" class="btn" data-i18n="previous">Previous</button>
|
<button id="prev-btn" class="btn" data-i18n="previous">Previous</button>
|
||||||
<span class="module-pill" id="module-pill">
|
<span class="module-pill" id="module-pill">
|
||||||
<span class="module-name"></span>
|
<span class="module-name"></span>
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ export const auth = {
|
|||||||
supabase?.auth.getSession() ??
|
supabase?.auth.getSession() ??
|
||||||
Promise.resolve({ data: { session: null }, error: null }),
|
Promise.resolve({ data: { session: null }, error: null }),
|
||||||
|
|
||||||
|
setSession: ({ access_token, refresh_token }) =>
|
||||||
|
supabase?.auth.setSession({ access_token, refresh_token }) ??
|
||||||
|
Promise.resolve({ data: { session: null }, error: { message: "Not configured" } }),
|
||||||
|
|
||||||
onAuthStateChange: (callback) =>
|
onAuthStateChange: (callback) =>
|
||||||
supabase?.auth.onAuthStateChange(callback) ?? { data: { subscription: { unsubscribe: () => {} } } },
|
supabase?.auth.onAuthStateChange(callback) ?? { data: { subscription: { unsubscribe: () => {} } } },
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user