@mundogamernetwork/shared-ui 1.17.7 → 1.17.10
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/MgHeaderAvatarToggle.vue +2 -1
- package/components/ui/MgHeaderComponent.vue +8 -1
- package/components/ui/MgHeaderUIUser.vue +8 -1
- package/components/ui/MgLoginModal.vue +86 -77
- package/package.json +1 -1
- package/pages/key-campaigns/[slug].vue +11 -9
- package/pages/key-campaigns/index.vue +3 -1
- package/pages/key-campaigns/redeem-key-approved.vue +8 -3
- package/pages/key-campaigns/redeem-key.vue +9 -4
|
@@ -42,11 +42,12 @@ const initialized = computed(() => {
|
|
|
42
42
|
<MGIcon v-else-if="!signedIn" icon="profile" />
|
|
43
43
|
<img
|
|
44
44
|
v-else
|
|
45
|
-
:src="user?.avatar_url"
|
|
45
|
+
:src="user?.avatar_url || '/imgs/default-avatar.png'"
|
|
46
46
|
:width="props.size"
|
|
47
47
|
:height="props.size"
|
|
48
48
|
class="mg-avatar-img"
|
|
49
49
|
alt=""
|
|
50
|
+
@error="(e: Event) => { const t = e.target as HTMLImageElement; if (t) { t.onerror = null; t.src = '/imgs/default-avatar.png' } }"
|
|
50
51
|
/>
|
|
51
52
|
<template #fallback>
|
|
52
53
|
<div
|
|
@@ -109,7 +109,14 @@ onMounted(() => {
|
|
|
109
109
|
@click.prevent="show(false, !showUsers)"
|
|
110
110
|
>
|
|
111
111
|
<MGIcon v-if="!signedIn" icon="profile" />
|
|
112
|
-
<img
|
|
112
|
+
<img
|
|
113
|
+
v-else
|
|
114
|
+
:src="user?.avatar_url || '/imgs/default-avatar.png'"
|
|
115
|
+
width="32"
|
|
116
|
+
height="32"
|
|
117
|
+
class="rounded-circle"
|
|
118
|
+
@error="(e: Event) => { const t = e.target as HTMLImageElement; if (t) { t.onerror = null; t.src = '/imgs/default-avatar.png' } }"
|
|
119
|
+
/>
|
|
113
120
|
</a>
|
|
114
121
|
</li>
|
|
115
122
|
|
|
@@ -122,7 +122,14 @@ const handleToggleOnline = () => {
|
|
|
122
122
|
<div v-if="!user?.avatar_url" class="no-avatar-wrapper">
|
|
123
123
|
<MGIcon icon="profile" />
|
|
124
124
|
</div>
|
|
125
|
-
<img
|
|
125
|
+
<img
|
|
126
|
+
v-else
|
|
127
|
+
:src="user.avatar_url"
|
|
128
|
+
width="40"
|
|
129
|
+
height="40"
|
|
130
|
+
class="avatar-img"
|
|
131
|
+
@error="(e: Event) => { const t = e.target as HTMLImageElement; if (t) { t.onerror = null; t.src = '/imgs/default-avatar.png' } }"
|
|
132
|
+
/>
|
|
126
133
|
</div>
|
|
127
134
|
|
|
128
135
|
<!--
|
|
@@ -40,23 +40,23 @@ function close() {
|
|
|
40
40
|
{{ $t("header.login.btn_register") }}
|
|
41
41
|
</a>
|
|
42
42
|
<Teleport to="body">
|
|
43
|
-
<div v-if="open" class="modal">
|
|
44
|
-
<div class="modal-backdrop" @click="close()"></div>
|
|
45
|
-
<div class="
|
|
46
|
-
<div class="title">
|
|
43
|
+
<div v-if="open" class="mg-login-modal">
|
|
44
|
+
<div class="mg-login-modal-backdrop" @click="close()"></div>
|
|
45
|
+
<div class="mg-login-modal-popup">
|
|
46
|
+
<div class="mg-login-modal-title">
|
|
47
47
|
{{ $t("more.login_modal.title") }}
|
|
48
48
|
</div>
|
|
49
|
-
<div class="subtitle">
|
|
49
|
+
<div class="mg-login-modal-subtitle">
|
|
50
50
|
{{ $t("more.login_modal.subtitle") }}
|
|
51
51
|
</div>
|
|
52
|
-
<div class="buttons">
|
|
53
|
-
<a v-if="props.loginUrl" class="btn1" :href="props.loginUrl">
|
|
52
|
+
<div class="mg-login-modal-buttons">
|
|
53
|
+
<a v-if="props.loginUrl" class="mg-login-modal-btn1" :href="props.loginUrl">
|
|
54
54
|
{{ $t("more.login_modal.btn_login") }}
|
|
55
55
|
</a>
|
|
56
|
-
<a v-if="!signedIn" class="btn1" :href="registerUrl">
|
|
56
|
+
<a v-if="!signedIn" class="mg-login-modal-btn1" :href="registerUrl">
|
|
57
57
|
{{ $t("more.login_modal.btn_1") }}
|
|
58
58
|
</a>
|
|
59
|
-
<a class="btn2" @click="close()">
|
|
59
|
+
<a class="mg-login-modal-btn2" @click="close()">
|
|
60
60
|
{{ $t("more.login_modal.btn_2") }}
|
|
61
61
|
</a>
|
|
62
62
|
</div>
|
|
@@ -67,100 +67,109 @@ function close() {
|
|
|
67
67
|
</template>
|
|
68
68
|
|
|
69
69
|
<style lang="scss">
|
|
70
|
+
// #mgLoginModal only wraps the inline "Register" link below — the modal
|
|
71
|
+
// itself is <Teleport to="body">'d out of this element entirely, so it can
|
|
72
|
+
// never be reached by a `#mgLoginModal .foo` selector. It previously used
|
|
73
|
+
// bare, unscoped names (.modal, .pop-up, .title, .buttons, .btn1/2) that
|
|
74
|
+
// silently collided with Bootstrap's own (and other) same-named classes —
|
|
75
|
+
// Bootstrap's `.modal { display: none; z-index: 1055 }` always won since the
|
|
76
|
+
// teleported node is no longer a descendant of #mgLoginModal, so the intended
|
|
77
|
+
// `#mgLoginModal .modal` override never matched it. Every teleported class is
|
|
78
|
+
// now uniquely prefixed and styled independently of #mgLoginModal.
|
|
70
79
|
#mgLoginModal {
|
|
71
80
|
a {
|
|
72
81
|
cursor: pointer;
|
|
73
82
|
margin-left: 5px;
|
|
74
83
|
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.mg-login-modal {
|
|
87
|
+
position: fixed;
|
|
88
|
+
display: block;
|
|
89
|
+
width: 100%;
|
|
90
|
+
height: 100vh;
|
|
91
|
+
z-index: 99999;
|
|
92
|
+
top: 0;
|
|
93
|
+
left: 0;
|
|
75
94
|
|
|
76
|
-
.modal {
|
|
95
|
+
.mg-login-modal-backdrop {
|
|
77
96
|
position: fixed;
|
|
78
|
-
display: block;
|
|
79
97
|
width: 100%;
|
|
98
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
80
99
|
height: 100vh;
|
|
81
|
-
z-index:
|
|
82
|
-
top: 0;
|
|
100
|
+
z-index: 1;
|
|
83
101
|
left: 0;
|
|
102
|
+
top: 0;
|
|
103
|
+
}
|
|
84
104
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
105
|
+
.mg-login-modal-popup {
|
|
106
|
+
max-height: 90vh;
|
|
107
|
+
width: 320px;
|
|
108
|
+
padding: 24px 32px;
|
|
109
|
+
background-color: var(--sidebar-bg);
|
|
110
|
+
border: 0;
|
|
111
|
+
display: flex;
|
|
112
|
+
flex-direction: column;
|
|
113
|
+
gap: 16px;
|
|
114
|
+
justify-content: center;
|
|
115
|
+
align-items: center;
|
|
116
|
+
text-align: center;
|
|
117
|
+
margin: auto;
|
|
118
|
+
margin-top: 15%;
|
|
119
|
+
position: relative;
|
|
120
|
+
z-index: 999;
|
|
121
|
+
|
|
122
|
+
.mg-login-modal-title {
|
|
123
|
+
font-size: 18px;
|
|
124
|
+
font-weight: 600;
|
|
125
|
+
line-height: 26px;
|
|
126
|
+
color: var(--active);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.mg-login-modal-subtitle {
|
|
130
|
+
font-size: 14px;
|
|
131
|
+
font-weight: 400;
|
|
132
|
+
line-height: 20px;
|
|
133
|
+
color: var(--active);
|
|
93
134
|
}
|
|
94
135
|
|
|
95
|
-
.
|
|
96
|
-
max-height: 90vh;
|
|
97
|
-
width: 320px;
|
|
98
|
-
padding: 24px 32px;
|
|
99
|
-
background-color: var(--sidebar-bg);
|
|
100
|
-
border: 0;
|
|
136
|
+
.mg-login-modal-buttons {
|
|
101
137
|
display: flex;
|
|
102
138
|
flex-direction: column;
|
|
103
|
-
gap:
|
|
104
|
-
|
|
105
|
-
align-items: center;
|
|
106
|
-
text-align: center;
|
|
107
|
-
margin: auto;
|
|
108
|
-
margin-top: 15%;
|
|
109
|
-
position: relative;
|
|
110
|
-
z-index: 999;
|
|
111
|
-
|
|
112
|
-
.title {
|
|
113
|
-
font-size: 18px;
|
|
114
|
-
font-weight: 600;
|
|
115
|
-
line-height: 26px;
|
|
116
|
-
color: var(--active);
|
|
117
|
-
}
|
|
139
|
+
gap: 8px;
|
|
140
|
+
width: 100%;
|
|
118
141
|
|
|
119
|
-
.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
142
|
+
.mg-login-modal-btn1 {
|
|
143
|
+
display: flex;
|
|
144
|
+
width: 100%;
|
|
145
|
+
height: 44px;
|
|
146
|
+
padding: 0 12px;
|
|
147
|
+
justify-content: center;
|
|
148
|
+
align-items: center;
|
|
149
|
+
font-size: 16px;
|
|
123
150
|
color: var(--active);
|
|
151
|
+
background-color: var(--sidebar-bg);
|
|
152
|
+
cursor: pointer;
|
|
153
|
+
border: 1px solid var(--chip-text);
|
|
124
154
|
}
|
|
125
155
|
|
|
126
|
-
.
|
|
156
|
+
.mg-login-modal-btn2 {
|
|
127
157
|
display: flex;
|
|
128
|
-
flex-direction: column;
|
|
129
|
-
gap: 8px;
|
|
130
158
|
width: 100%;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
font-size: 16px;
|
|
140
|
-
color: var(--active);
|
|
141
|
-
background-color: var(--sidebar-bg);
|
|
142
|
-
cursor: pointer;
|
|
143
|
-
border: 1px solid var(--chip-text);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
.btn2 {
|
|
147
|
-
display: flex;
|
|
148
|
-
width: 100%;
|
|
149
|
-
height: 44px;
|
|
150
|
-
padding: 0 12px;
|
|
151
|
-
justify-content: center;
|
|
152
|
-
align-items: center;
|
|
153
|
-
border: 0;
|
|
154
|
-
color: var(--chip-text);
|
|
155
|
-
cursor: pointer;
|
|
156
|
-
background-color: var(--search-bar-bg);
|
|
157
|
-
}
|
|
159
|
+
height: 44px;
|
|
160
|
+
padding: 0 12px;
|
|
161
|
+
justify-content: center;
|
|
162
|
+
align-items: center;
|
|
163
|
+
border: 0;
|
|
164
|
+
color: var(--chip-text);
|
|
165
|
+
cursor: pointer;
|
|
166
|
+
background-color: var(--search-bar-bg);
|
|
158
167
|
}
|
|
159
168
|
}
|
|
160
169
|
}
|
|
161
170
|
|
|
162
171
|
@media screen and (max-width: 768px) {
|
|
163
|
-
.modal
|
|
172
|
+
.mg-login-modal-popup {
|
|
164
173
|
width: 100%;
|
|
165
174
|
margin-top: 50%;
|
|
166
175
|
}
|
package/package.json
CHANGED
|
@@ -14,13 +14,17 @@ import {
|
|
|
14
14
|
} from "../../services/campaignService"
|
|
15
15
|
import { fetchDetailGame, fetchDetailGameImage } from "../../services/gamesService"
|
|
16
16
|
import { formatEmbargoDateTime } from "../../utils/embargo"
|
|
17
|
+
import { useLoginStore } from "../../stores/login"
|
|
17
18
|
|
|
18
19
|
// Injected by each front — provide in app.vue / a plugin
|
|
19
20
|
const mgPlatform = inject<string>("mgPlatform", "MGTV")
|
|
20
|
-
const loginUrl = inject<string>("loginUrl", "/")
|
|
21
21
|
const upgradeRoute = inject<string>("upgradeRoute", "/")
|
|
22
22
|
// The consuming front should provide the authenticated user as { id, ... } or null
|
|
23
23
|
const currentUser = inject<any>("currentUser", null)
|
|
24
|
+
const loginStore = useLoginStore()
|
|
25
|
+
// The auth store actually defaults `user` to `{}` (not null) even when signed
|
|
26
|
+
// out, so a raw truthiness check on it is always true — `.id` is the real signal.
|
|
27
|
+
const isSignedIn = () => !!unref(currentUser)?.id
|
|
24
28
|
|
|
25
29
|
const { $i18n } = useNuxtApp()
|
|
26
30
|
const locale = $i18n.locale
|
|
@@ -212,8 +216,9 @@ const following = ref(!!campaign.value?.is_following)
|
|
|
212
216
|
const followBusy = ref(false)
|
|
213
217
|
|
|
214
218
|
async function toggleFollow() {
|
|
215
|
-
if (!
|
|
216
|
-
|
|
219
|
+
if (!isSignedIn()) {
|
|
220
|
+
loginStore.showLoginModalComponent = true
|
|
221
|
+
return
|
|
217
222
|
}
|
|
218
223
|
if (!campaign.value?.id || followBusy.value) return
|
|
219
224
|
|
|
@@ -601,14 +606,11 @@ const tierLabel = computed(() => {
|
|
|
601
606
|
// Auth guard for CTA actions
|
|
602
607
|
// ------------------------------------------------------------------
|
|
603
608
|
function requireAuth(targetPath: string) {
|
|
604
|
-
if (
|
|
609
|
+
if (isSignedIn()) {
|
|
605
610
|
navigateTo(targetPath)
|
|
606
611
|
return
|
|
607
612
|
}
|
|
608
|
-
|
|
609
|
-
? `${window.location.origin}${route.fullPath}`
|
|
610
|
-
: targetPath
|
|
611
|
-
navigateTo(`${loginUrl}?redirect_to=${encodeURIComponent(returnTo)}`, { external: true })
|
|
613
|
+
loginStore.showLoginModalComponent = true
|
|
612
614
|
}
|
|
613
615
|
|
|
614
616
|
// ------------------------------------------------------------------
|
|
@@ -622,7 +624,7 @@ const showUsersModal = ref(false)
|
|
|
622
624
|
onMounted(async () => {
|
|
623
625
|
updateCountdown()
|
|
624
626
|
|
|
625
|
-
if (
|
|
627
|
+
if (isSignedIn() && campaign.value?.id) {
|
|
626
628
|
try {
|
|
627
629
|
const response = await fetchMyRequests({ "filter[key_id]": campaign.value.id })
|
|
628
630
|
const requests = response?.data?.data
|
|
@@ -470,7 +470,9 @@ function truthyFlag(value: unknown) {
|
|
|
470
470
|
}
|
|
471
471
|
|
|
472
472
|
function hasCurrentUser() {
|
|
473
|
-
|
|
473
|
+
// `currentUser` defaults to `{}` (not null) in the auth store even when
|
|
474
|
+
// signed out, so a raw truthiness check on it is always true.
|
|
475
|
+
return !!unref(currentUser)?.id
|
|
474
476
|
}
|
|
475
477
|
|
|
476
478
|
function requestLogin() {
|
|
@@ -14,13 +14,19 @@ import {
|
|
|
14
14
|
isEmbargoActive,
|
|
15
15
|
type EmbargoDisplay,
|
|
16
16
|
} from "../../utils/embargo"
|
|
17
|
+
import { useLoginStore } from "../../stores/login"
|
|
17
18
|
|
|
18
19
|
const route = useRoute()
|
|
19
20
|
const { $i18n } = useNuxtApp()
|
|
20
21
|
const locale = $i18n.locale
|
|
22
|
+
const loginStore = useLoginStore()
|
|
21
23
|
|
|
22
24
|
const currentUser = inject<any>("currentUser", null)
|
|
23
25
|
const loginUrl = inject<string>("loginUrl", "/")
|
|
26
|
+
// `currentUser` defaults to `{}` (never null) in the auth store even when
|
|
27
|
+
// signed out, so a raw truthiness check on it is always true. `.id` is the
|
|
28
|
+
// same "really signed in" signal the store's own getters/persisted state use.
|
|
29
|
+
const isSignedIn = () => !!unref(currentUser)?.id
|
|
24
30
|
|
|
25
31
|
const requestId = Number(route.query.requestId)
|
|
26
32
|
const keyItemsParam = route.query.keyItems as string | undefined
|
|
@@ -74,9 +80,8 @@ const redeemUrl = computed(() => {
|
|
|
74
80
|
})
|
|
75
81
|
|
|
76
82
|
onMounted(async () => {
|
|
77
|
-
if (!
|
|
78
|
-
|
|
79
|
-
navigateTo(`${loginUrl}?redirect_to=${encodeURIComponent(returnTo)}`, { external: true })
|
|
83
|
+
if (!isSignedIn()) {
|
|
84
|
+
loginStore.showLoginModalComponent = true
|
|
80
85
|
return
|
|
81
86
|
}
|
|
82
87
|
if (!requestId) {
|
|
@@ -13,13 +13,19 @@ import {
|
|
|
13
13
|
buildReLoginUrl,
|
|
14
14
|
} from "../../services/campaignService"
|
|
15
15
|
import { formatEmbargoDateTime } from "../../utils/embargo"
|
|
16
|
+
import { useLoginStore } from "../../stores/login"
|
|
16
17
|
|
|
17
18
|
const route = useRoute()
|
|
18
19
|
const { $i18n } = useNuxtApp()
|
|
19
20
|
const locale = $i18n.locale
|
|
21
|
+
const loginStore = useLoginStore()
|
|
20
22
|
|
|
21
23
|
const currentUser = inject<any>("currentUser", null)
|
|
22
24
|
const loginUrl = inject<string>("loginUrl", "/")
|
|
25
|
+
// `currentUser` defaults to `{}` (never null) in the auth store even when
|
|
26
|
+
// signed out, so a raw truthiness check on it is always true. `.id` is the
|
|
27
|
+
// same "really signed in" signal the store's own getters/persisted state use.
|
|
28
|
+
const isSignedIn = () => !!unref(currentUser)?.id
|
|
23
29
|
|
|
24
30
|
const keyId = Number(route.query.keyId)
|
|
25
31
|
|
|
@@ -70,9 +76,8 @@ function clampQuantity() {
|
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
onMounted(async () => {
|
|
73
|
-
if (!
|
|
74
|
-
|
|
75
|
-
navigateTo(`${loginUrl}?redirect_to=${encodeURIComponent(returnTo)}`, { external: true })
|
|
79
|
+
if (!isSignedIn()) {
|
|
80
|
+
loginStore.showLoginModalComponent = true
|
|
76
81
|
return
|
|
77
82
|
}
|
|
78
83
|
try {
|
|
@@ -118,7 +123,7 @@ onMounted(async () => {
|
|
|
118
123
|
watch(
|
|
119
124
|
() => unref(currentUser),
|
|
120
125
|
(user, previous) => {
|
|
121
|
-
if (previous && !user) {
|
|
126
|
+
if (previous?.id && !user?.id) {
|
|
122
127
|
sessionExpired.value = true
|
|
123
128
|
requestError.value = $i18n.t("keys.campaigns.error_session_expired")
|
|
124
129
|
}
|