@fy-/fws-vue 2.3.10 → 2.3.12
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.
- package/components/ui/DefaultBreadcrumb.vue +31 -11
- package/components/ui/DefaultConfirm.vue +10 -8
- package/components/ui/DefaultDateSelection.vue +7 -1
- package/components/ui/DefaultDropdown.vue +15 -23
- package/components/ui/DefaultDropdownLink.vue +19 -11
- package/components/ui/DefaultGallery.vue +130 -46
- package/components/ui/DefaultInput.vue +15 -9
- package/components/ui/DefaultLoader.vue +16 -7
- package/components/ui/DefaultModal.vue +46 -44
- package/components/ui/DefaultNotif.vue +77 -76
- package/components/ui/DefaultPaging.vue +88 -57
- package/components/ui/DefaultSidebar.vue +37 -9
- package/components/ui/DefaultTagInput.vue +86 -45
- package/composables/seo.ts +57 -92
- package/composables/templating.ts +75 -45
- package/composables/translations.ts +18 -2
- package/package.json +1 -1
- package/stores/user.ts +83 -59
|
@@ -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
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
61
|
-
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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
|
}
|