I’m working on a mobile app using Ionic with Vue 3 for the frontend, and I’ve set up routing using Vue Router to manage navigation between different pages in the app. My backend API is built with Laravel, handling sanctum authentication based on google token from the frontend, and other server-side logic.
App’s Routing Setup
In my router/index.ts
, I’ve defined several routes:
- Login Page (
LoginPage.vue
): The entry point of the app, where users can log in. - Choose Package Page (
ChoosePackagePage.vue
): A page accessible after logging in, allowing users to select a package. - Game Page and End Game Page (
GamePage.vue
andEndGamePage.vue
): Part of the app’s core functionality, where users can play a game and see their results at the end. - Social Login (
GamePage.vue
reused): An alternative login method.
import {createRouter, createWebHistory} from '@ionic/vue-router';
import {RouteRecordRaw} from 'vue-router';
import {SecureStoragePlugin} from 'capacitor-secure-storage-plugin';
import useAuthStore from "@/store/auth/auth";
import ChoosePackagePage from "@/pages/main/ChoosePackagePage.vue";
import GamePage from "@/pages/game/GamePage.vue";
import LoginPage from "@/pages/auth/LoginPage.vue";
import EndGamePage from "@/pages/game/EndGamePage.vue";
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'login',
component: LoginPage,
meta: {
requiresAuth: false,
},
},
{
path: '/home',
name: 'home',
component: ChoosePackagePage,
meta: {
requiresAuth: true,
},
},
{
path: '/game',
name: 'game',
component: GamePage,
meta: {
requiresAuth: true,
},
},
{
path: '/end-game',
name: 'end-game',
component: EndGamePage,
meta: {
requiresAuth: true,
},
},
// {
// path: '/social-login',
// name: 'social-login',
// component: GamePage,
// meta: {
// requiresAuth: false,
// },
// }
];
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes,
})
router.beforeEach(async (to, from) => {
const authStore = useAuthStore();
if (to.name === 'login' && authStore.isAuthenticated) {
return {name: 'home'};
}
if (to.meta.requiresAuth && !authStore.isAuthenticated) {
try {
console.log('trying to get to protected route, and not authenticated, checking for token in secure storage.')
const result = await SecureStoragePlugin.get({key: 'authToken'});
if (result.value) {
console.log('get the token from secure storage, setting it in the store and redirecting to home page.')
authStore.token = result.value; // Update the token in the store if found
return {path: '/home'};
}
console.log('no token found in secure storage, redirecting to login page.')
return {name: 'login'};
} catch (error) {
if (to.meta.requiresAuth) {
console.log('error getting token from secure storage, redirecting to login page.')
return {name: 'login'};
}
}
}
console.log('nothing returned so just continue to the route.', to.name, from.name)
});
export default router;
The router is configured with a beforeEach
navigation guard to check if the user is authenticated (using a Pinia store authStore.isAuthenticated
) and redirects to the login page if not authenticated, except for routes marked with requiresAuth: false
.
Logout Process
The logout functionality is triggered by a button in my AppLayout.vue
(for now until I get it all working then I will clean it up). When clicked, it:
- Calls the
logout
action fromauthStore
, which clears the user’s authentication token from both the app’s state and secure storage. - Uses
ionRouter
to navigate back to the login page.
const logout = async () => {
await menuController.close();
await authStore.logout();
await ionRouter.push('/');
}
This process works as intended, and upon logout, the app should redirect users back to the LoginPage.vue
.
Issue Encountered
After successfully logging in, I can navigate to the Choose Package Page without any issues, indicating that the login process and subsequent navigation work correctly. However, after logging out, although the user data is gone as it should be, the actual view doesn’t update to show the LoginPage.vue
until I programatically refresh the view - but I can tell I am logged out because I am unable to click through to other parts of the app after I click the logout button.
What Works
- Logging in and navigating to other pages based on authentication state works flawlessly.
- The logout process successfully clears the authentication state and attempts to navigate to the login page because I can see the console log
console.log('nothing returned so just continue to the route.', to.name, from.name)
andto
andfrom
have correct values.
The Problem
- Despite user data being gone after the logout, the view doesn’t update until a programatic page refresh is performed.
I’ve ensured that each page component, especially LoginPage.vue
and ChoosePackagePage.vue
, correctly uses <IonPage>
to wrap the content, adhering to Ionic’s requirements for navigation and page management. They both have access to <IonPage>
via my layouts, SimpleLayout
and AppLayout
.
I’m seeking advice on ensuring that after logging out, the app correctly updates the view to show the LoginPage.vue
immediately, without requiring a manual refresh. I must be doing something silly I just cannot see it.
These are my layouts:
simple:
<script setup lang="ts">
import {IonContent, IonPage} from '@ionic/vue';
</script>
<template>
<ion-page>
<ion-content>
<div class="flex justify-center items-center h-screen">
<slot/>
</div>
</ion-content>
</ion-page>
</template>
AppLayout
<script setup lang="ts">
import {
IonContent,
IonPage,
IonHeader,
IonMenu,
IonIcon,
IonTitle,
IonToolbar,
IonMenuToggle,
IonList,
IonItem,
useIonRouter
} from '@ionic/vue'
import {menu, logOutOutline} from "ionicons/icons";
import {computed} from "vue";
import useAuthStore from "@/store/auth/auth";
import {menuController} from '@ionic/core/components';
const authStore = useAuthStore();
const ionRouter = useIonRouter();
const baseUrl = process.env.NODE_ENV === 'production' ? 'assets/img/' : '/assets/img/';
const backgroundImageUrl = computed(() => `${baseUrl}splash-pattern.svg`);
const logout = async () => {
await menuController.close();
await authStore.logout();
await ionRouter.push('/');
}
</script>
<template>
<ion-menu side="end" content-id="main-content">
<ion-header>
<ion-toolbar>
<ion-title>Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-list class="item-background-color">
<ion-item>
<ion-icon :icon="logOutOutline" class="w-10 h-10 mr-2 text-dark-green"></ion-icon>
<ion-label class="text-xl font-bold text-dark-green" @click="logout">Logout</ion-label>
</ion-item>
</ion-list>
</ion-content>
</ion-menu>
<ion-page id="main-content" class="relative">
<ion-content>
<ion-menu-toggle class="absolute top-3 right-3 z-index-[9999999]">
<ion-icon :icon="menu" class="w-10 h-10"></ion-icon>
</ion-menu-toggle>
<div
class="bg-medium-salmon w-full flex flex-col items-center "
:style="{ backgroundImage: `url(${backgroundImageUrl})`, backgroundSize: '267%' }"
>
<slot/>
</div>
</ion-content>
</ion-page>
</template>
<style scoped>
ion-toolbar {
--background: #e75571;
}
ion-menu ion-content {
--background: #f6a8b7;
}
.item-background-color {
--ion-item-background: none;
--ion-border-color: #f6a8b7;
}
</style>
1 post - 1 participant