@fy-/fws-vue 2.3.11 → 2.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,13 +5,30 @@ import { inject } from 'vue'
5
5
 
6
6
  export type I18nextTranslate = typeof i18next.t
7
7
 
8
+ // Cached translation service instance
9
+ let cachedTranslation: TFunction | null = null
10
+
8
11
  export function useTranslation() {
12
+ // Return cached instance if available
13
+ if (cachedTranslation) return cachedTranslation
14
+
9
15
  const translate = inject<TFunction>('fwsVueTranslate')
10
16
  if (!translate) throw new Error('Did you apply app.use(fwsVue)?')
11
17
 
18
+ // Cache the translation service instance
19
+ cachedTranslation = translate
12
20
  return translate
13
21
  }
14
22
 
23
+ // Default configuration for i18next to avoid object recreation
24
+ const defaultI18nextConfig = {
25
+ ns: ['translation'],
26
+ defaultNS: 'translation',
27
+ debug: false,
28
+ load: 'currentOnly' as const,
29
+ initImmediate: false,
30
+ }
31
+
15
32
  export function i18nextPromise(
16
33
  backend: typeof I18nBackend,
17
34
  locale: string = 'en-US',
@@ -19,11 +36,10 @@ export function i18nextPromise(
19
36
  ns: string = 'translation',
20
37
  ) {
21
38
  return i18next.use(backend).init({
39
+ ...defaultI18nextConfig,
22
40
  ns: [ns],
23
41
  defaultNS: ns,
24
42
  debug,
25
43
  lng: locale,
26
- load: 'currentOnly',
27
- initImmediate: false,
28
44
  })
29
45
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fy-/fws-vue",
3
- "version": "2.3.11",
3
+ "version": "2.3.13",
4
4
  "author": "Florian 'Fy' Gasquez <m@fy.to>",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/fy-to/FWJS#readme",
package/stores/user.ts CHANGED
@@ -3,13 +3,18 @@ import type { RouteLocation } from 'vue-router'
3
3
  import type { APIResult } from '../composables/rest'
4
4
  import { rest } from '@fy-/fws-js'
5
5
  import { defineStore } from 'pinia'
6
- import { computed } from 'vue'
6
+ import { computed, shallowRef } from 'vue'
7
7
  import { useServerRouter } from './serverRouter'
8
8
 
9
9
  export interface UserStore {
10
10
  user: User | null
11
11
  }
12
12
 
13
+ // Create a debounce mechanism for authentication checks
14
+ let refreshPromise: Promise<void> | null = null
15
+ const refreshDebounceTime = 200 // 200ms
16
+ let lastRefreshTime = 0
17
+
13
18
  export const useUserStore = defineStore('userStore', {
14
19
  state: (): UserStore => ({
15
20
  user: null,
@@ -21,33 +26,79 @@ export const useUserStore = defineStore('userStore', {
21
26
  },
22
27
  actions: {
23
28
  async refreshUser() {
24
- const user: APIResult = await rest('User:get', 'GET').catch(() => {
25
- this.setUser(null)
26
- })
27
- if (user.result === 'success') {
28
- this.setUser(user.data)
29
- }
30
- else {
31
- this.setUser(null)
29
+ // Debounce refreshUser calls to prevent multiple concurrent API requests
30
+ const now = Date.now()
31
+ if (refreshPromise && now - lastRefreshTime < refreshDebounceTime) {
32
+ return refreshPromise
32
33
  }
34
+
35
+ lastRefreshTime = now
36
+ refreshPromise = new Promise((resolve) => {
37
+ rest('User:get', 'GET')
38
+ .then((user: APIResult) => {
39
+ if (user.result === 'success') {
40
+ this.setUser(user.data)
41
+ }
42
+ else {
43
+ this.setUser(null)
44
+ }
45
+ })
46
+ .catch(() => {
47
+ this.setUser(null)
48
+ })
49
+ .finally(() => {
50
+ resolve()
51
+ // Clear the promise reference after a delay
52
+ setTimeout(() => {
53
+ refreshPromise = null
54
+ }, refreshDebounceTime)
55
+ })
56
+ })
57
+
58
+ return refreshPromise
33
59
  },
60
+
34
61
  async logout() {
35
- const user: APIResult = await rest('User:logout', 'POST').catch(() => {
36
- this.setUser(null)
37
- })
38
- if (user.result === 'success') {
62
+ try {
63
+ const user: APIResult = await rest('User:logout', 'POST')
64
+ // In all cases, we set the user to null
39
65
  this.setUser(null)
66
+ return user.result === 'success'
40
67
  }
41
- else {
68
+ catch {
42
69
  this.setUser(null)
70
+ return false
43
71
  }
44
72
  },
73
+
45
74
  setUser(user: User | null) {
46
75
  this.user = user
47
76
  },
48
77
  },
49
78
  })
50
79
 
80
+ // Shared implementation for route checking to avoid code duplication
81
+ function createUserChecker(path: string, redirectLink: boolean) {
82
+ // Use shallowRef for router since it doesn't need reactivity
83
+ const router = shallowRef(useServerRouter())
84
+
85
+ return (route: RouteLocation, isAuthenticated: boolean) => {
86
+ if (!route.meta.reqLogin) return false
87
+
88
+ if (!isAuthenticated) {
89
+ if (!redirectLink) {
90
+ router.value.push(path)
91
+ }
92
+ else {
93
+ router.value.status = 307
94
+ router.value.push(`${path}?return_to=${route.path}`)
95
+ }
96
+ return true
97
+ }
98
+ return false
99
+ }
100
+ }
101
+
51
102
  export async function useUserCheckAsyncSimple(
52
103
  path = '/login',
53
104
  redirectLink = false,
@@ -56,25 +107,15 @@ export async function useUserCheckAsyncSimple(
56
107
  await userStore.refreshUser()
57
108
  const isAuth = computed(() => userStore.isAuth)
58
109
  const router = useServerRouter()
110
+ const checkUser = createUserChecker(path, redirectLink)
59
111
 
60
- const checkUser = (route: RouteLocation) => {
61
- if (route.meta.reqLogin) {
62
- if (!isAuth.value) {
63
- if (!redirectLink) {
64
- router.push(path)
65
- }
66
- else {
67
- router.status = 307
68
- router.push(`${path}?return_to=${route.path}`)
69
- }
70
- }
71
- }
72
- }
73
- checkUser(router.currentRoute)
112
+ // Check current route immediately
113
+ checkUser(router.currentRoute, isAuth.value)
74
114
 
115
+ // Setup route guard
75
116
  router._router.beforeEach((to: any) => {
76
117
  if (to.fullPath !== path) {
77
- checkUser(to)
118
+ checkUser(to, isAuth.value)
78
119
  }
79
120
  })
80
121
  }
@@ -84,28 +125,19 @@ export async function useUserCheckAsync(path = '/login', redirectLink = false) {
84
125
  await userStore.refreshUser()
85
126
  const isAuth = computed(() => userStore.isAuth)
86
127
  const router = useServerRouter()
128
+ const checkUser = createUserChecker(path, redirectLink)
87
129
 
88
- const checkUser = (route: RouteLocation) => {
89
- if (route.meta.reqLogin) {
90
- if (!isAuth.value) {
91
- if (!redirectLink) {
92
- router.push(path)
93
- }
94
- else {
95
- router.status = 307
96
- router.push(`${path}?return_to=${route.path}`)
97
- }
98
- }
99
- }
100
- }
101
- checkUser(router.currentRoute)
130
+ // Check current route immediately
131
+ checkUser(router.currentRoute, isAuth.value)
102
132
 
133
+ // Setup route guards
103
134
  router._router.afterEach(async () => {
104
135
  await userStore.refreshUser()
105
136
  })
137
+
106
138
  router._router.beforeEach((to: any) => {
107
139
  if (to.fullPath !== path) {
108
- checkUser(to)
140
+ checkUser(to, isAuth.value)
109
141
  }
110
142
  })
111
143
  }
@@ -114,31 +146,23 @@ export function useUserCheck(path = '/login', redirectLink = false) {
114
146
  const userStore = useUserStore()
115
147
  const isAuth = computed(() => userStore.isAuth)
116
148
  const router = useServerRouter()
149
+ const checkUser = createUserChecker(path, redirectLink)
117
150
 
118
- const checkUser = (route: RouteLocation) => {
119
- if (route.meta.reqLogin) {
120
- if (!isAuth.value) {
121
- if (!redirectLink) {
122
- router.push(path)
123
- }
124
- else {
125
- router.status = 307
126
- router.push(`${path}?return_to=${route.path}`)
127
- }
128
- }
129
- }
130
- }
151
+ // Check current route after refresh
131
152
  userStore.refreshUser().then(() => {
132
153
  if (router.currentRoute) {
133
- checkUser(router.currentRoute)
154
+ checkUser(router.currentRoute, isAuth.value)
134
155
  }
135
156
  })
157
+
158
+ // Setup route guards
136
159
  router._router.afterEach(async () => {
137
160
  await userStore.refreshUser()
138
161
  })
162
+
139
163
  router._router.beforeEach((to: any) => {
140
164
  if (to.fullPath !== path) {
141
- checkUser(to)
165
+ checkUser(to, isAuth.value)
142
166
  }
143
167
  })
144
168
  }