@mundogamernetwork/shared-ui 1.16.20 → 1.16.22
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/locales/de.json +1 -0
- package/locales/en.json +1 -0
- package/locales/es.json +1 -0
- package/locales/pt-BR.json +1 -0
- package/locales/ro.json +1 -0
- package/package.json +1 -1
- package/pages/key-campaigns/[slug].vue +3 -1
- package/pages/mural/[slug]/pixel/[pixelId].vue +11 -7
- package/plugins/echo.client.ts +49 -9
package/locales/de.json
CHANGED
|
@@ -168,6 +168,7 @@
|
|
|
168
168
|
"status_closed_action_pending": "Geschlossen — eine Aktion ist noch nötig",
|
|
169
169
|
"status_ending": "Ending soon",
|
|
170
170
|
"status_exhausted": "Keys exhausted",
|
|
171
|
+
"status_exhausted_action_pending": "Ausverkauft — eine Aktion ist noch nötig",
|
|
171
172
|
"days": "days",
|
|
172
173
|
"hours": "hours",
|
|
173
174
|
"minutes": "min",
|
package/locales/en.json
CHANGED
|
@@ -168,6 +168,7 @@
|
|
|
168
168
|
"status_closed_action_pending": "Closed — action needed",
|
|
169
169
|
"status_ending": "Ending soon",
|
|
170
170
|
"status_exhausted": "Keys exhausted",
|
|
171
|
+
"status_exhausted_action_pending": "Keys exhausted — action needed",
|
|
171
172
|
"days": "days",
|
|
172
173
|
"hours": "hours",
|
|
173
174
|
"minutes": "min",
|
package/locales/es.json
CHANGED
|
@@ -168,6 +168,7 @@
|
|
|
168
168
|
"status_closed_action_pending": "Cerrada — tienes una acción pendiente",
|
|
169
169
|
"status_ending": "Terminando pronto",
|
|
170
170
|
"status_exhausted": "Agotada",
|
|
171
|
+
"status_exhausted_action_pending": "Agotada — tienes una acción pendiente",
|
|
171
172
|
"days": "días",
|
|
172
173
|
"hours": "horas",
|
|
173
174
|
"minutes": "minutos",
|
package/locales/pt-BR.json
CHANGED
|
@@ -168,6 +168,7 @@
|
|
|
168
168
|
"status_closed_action_pending": "Encerrada — falta uma ação sua",
|
|
169
169
|
"status_ending": "Ending soon",
|
|
170
170
|
"status_exhausted": "Keys exhausted",
|
|
171
|
+
"status_exhausted_action_pending": "Esgotada — falta uma ação sua",
|
|
171
172
|
"days": "days",
|
|
172
173
|
"hours": "hours",
|
|
173
174
|
"minutes": "min",
|
package/locales/ro.json
CHANGED
|
@@ -168,6 +168,7 @@
|
|
|
168
168
|
"status_closed_action_pending": "Închisă — mai ai o acțiune de făcut",
|
|
169
169
|
"status_ending": "Ending soon",
|
|
170
170
|
"status_exhausted": "Keys exhausted",
|
|
171
|
+
"status_exhausted_action_pending": "Epuizată — mai ai o acțiune de făcut",
|
|
171
172
|
"days": "days",
|
|
172
173
|
"hours": "hours",
|
|
173
174
|
"minutes": "min",
|
package/package.json
CHANGED
|
@@ -882,7 +882,9 @@ onMounted(async () => {
|
|
|
882
882
|
? $t("keys.campaigns.campaign.status_closed_action_pending")
|
|
883
883
|
: $t("keys.campaigns.campaign.status_closed"))
|
|
884
884
|
: (campaign?.available_count ?? 0) === 0
|
|
885
|
-
?
|
|
885
|
+
? (hasPendingObligation
|
|
886
|
+
? $t("keys.campaigns.campaign.status_exhausted_action_pending")
|
|
887
|
+
: $t("keys.campaigns.campaign.status_exhausted"))
|
|
886
888
|
: hasNoDateLimit
|
|
887
889
|
? $t("keys.campaigns.campaign.status_active")
|
|
888
890
|
: timeValues.days <= 7
|
|
@@ -72,13 +72,17 @@ const seoDescription = computed(() =>
|
|
|
72
72
|
)
|
|
73
73
|
// share_image_url (auto-generated branded reveal) takes priority, then the
|
|
74
74
|
// pixel's own uploaded image, then the wall's cover/logo as a last resort.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
)
|
|
75
|
+
// indie_wall_pixels.image_url is often a base64 data: URI (the column was
|
|
76
|
+
// widened to `text` specifically for that) — a social crawler can't fetch a
|
|
77
|
+
// data: URI for og:image/twitter:image, so it's skipped here in favor of the
|
|
78
|
+
// wall's own image; the inline <img>/download link elsewhere can still use
|
|
79
|
+
// the raw value since those render in an actual browser.
|
|
80
|
+
const isShareableImageUrl = (url: unknown): url is string => typeof url === 'string' && /^https?:\/\//i.test(url)
|
|
81
|
+
const seoImage = computed(() => {
|
|
82
|
+
if (pixel.value?.share_image_url) return pixel.value.share_image_url
|
|
83
|
+
if (isShareableImageUrl(pixel.value?.image_url)) return pixel.value.image_url
|
|
84
|
+
return wall.value?.cover_image_url || wall.value?.logo_url || ''
|
|
85
|
+
})
|
|
82
86
|
|
|
83
87
|
// Raw useHead meta-array form — same proven pattern as
|
|
84
88
|
// pages/mural/[slug]/index.vue (see the comment there: useSeoMeta silently
|
package/plugins/echo.client.ts
CHANGED
|
@@ -160,6 +160,23 @@ export default defineNuxtPlugin({
|
|
|
160
160
|
let reconnectTimer: number | null = null;
|
|
161
161
|
let reconnectAttempts = 0;
|
|
162
162
|
const MAX_RECONNECT_ATTEMPTS = 5;
|
|
163
|
+
// Once the backoff budget is exhausted we stop the timer-based retries, but the
|
|
164
|
+
// tab isn't necessarily hopeless forever — the failures so far might all be from
|
|
165
|
+
// a dead network or a backgrounded tab throttled by the browser. giveUp tracks
|
|
166
|
+
// that state so the visibility/online listeners below know to give the socket a
|
|
167
|
+
// fresh budget instead of calling connect() on top of an already-scheduled retry.
|
|
168
|
+
let giveUp = false;
|
|
169
|
+
|
|
170
|
+
const scheduleReconnect = () => {
|
|
171
|
+
reconnectAttempts++;
|
|
172
|
+
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts - 1), 30000);
|
|
173
|
+
console.log(`[WebSocket] Reconnecting in ${delay / 1000}s (attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`);
|
|
174
|
+
|
|
175
|
+
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
176
|
+
reconnectTimer = window.setTimeout(() => {
|
|
177
|
+
window.Echo.connector.pusher.connect();
|
|
178
|
+
}, delay);
|
|
179
|
+
};
|
|
163
180
|
|
|
164
181
|
window.Echo.connector.pusher.connection.bind("state_change", (states: any) => {
|
|
165
182
|
const { previous, current } = states;
|
|
@@ -169,7 +186,8 @@ export default defineNuxtPlugin({
|
|
|
169
186
|
// Stop reconnecting after max attempts to avoid infinite loop
|
|
170
187
|
// (e.g. when user is not authenticated)
|
|
171
188
|
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
172
|
-
console.log("[WebSocket] Max reconnect attempts reached, stopping.");
|
|
189
|
+
console.log("[WebSocket] Max reconnect attempts reached, stopping until the tab is visible/online again.");
|
|
190
|
+
giveUp = true;
|
|
173
191
|
if (reconnectTimer) {
|
|
174
192
|
clearTimeout(reconnectTimer);
|
|
175
193
|
reconnectTimer = null;
|
|
@@ -177,17 +195,11 @@ export default defineNuxtPlugin({
|
|
|
177
195
|
return;
|
|
178
196
|
}
|
|
179
197
|
|
|
180
|
-
|
|
181
|
-
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts - 1), 30000);
|
|
182
|
-
console.log(`[WebSocket] Reconnecting in ${delay / 1000}s (attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`);
|
|
183
|
-
|
|
184
|
-
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
185
|
-
reconnectTimer = window.setTimeout(() => {
|
|
186
|
-
window.Echo.connector.pusher.connect();
|
|
187
|
-
}, delay);
|
|
198
|
+
scheduleReconnect();
|
|
188
199
|
}
|
|
189
200
|
|
|
190
201
|
if (current === "connected") {
|
|
202
|
+
giveUp = false;
|
|
191
203
|
reconnectAttempts = 0;
|
|
192
204
|
if (reconnectTimer) {
|
|
193
205
|
clearTimeout(reconnectTimer);
|
|
@@ -196,6 +208,34 @@ export default defineNuxtPlugin({
|
|
|
196
208
|
}
|
|
197
209
|
});
|
|
198
210
|
|
|
211
|
+
// The backoff loop above only reacts to the socket's own state changes, so once
|
|
212
|
+
// it gives up after MAX_RECONNECT_ATTEMPTS nothing ever tries again — a tab left
|
|
213
|
+
// open across a Wi-Fi drop, a phone switching towers, or a background tab the
|
|
214
|
+
// browser froze mid-backoff stays stuck on "disconnected" for the rest of the
|
|
215
|
+
// session (falling back to HTTP polling wherever the app checks connection.state,
|
|
216
|
+
// e.g. community-frontend's chat store). Retry once when the tab regains focus or
|
|
217
|
+
// the browser reports the network is back, giving the connection a fresh budget
|
|
218
|
+
// instead of leaving it permanently given up on.
|
|
219
|
+
const retryIfStalled = () => {
|
|
220
|
+
const state = window.Echo?.connector?.pusher?.connection?.state;
|
|
221
|
+
if (!giveUp && state !== "disconnected" && state !== "failed" && state !== "unavailable") return;
|
|
222
|
+
if (state === "connected" || state === "connecting") return;
|
|
223
|
+
|
|
224
|
+
console.log("[WebSocket] Tab active/online again — retrying connection.");
|
|
225
|
+
giveUp = false;
|
|
226
|
+
reconnectAttempts = 0;
|
|
227
|
+
if (reconnectTimer) {
|
|
228
|
+
clearTimeout(reconnectTimer);
|
|
229
|
+
reconnectTimer = null;
|
|
230
|
+
}
|
|
231
|
+
window.Echo.connector.pusher.connect();
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
document.addEventListener("visibilitychange", () => {
|
|
235
|
+
if (document.visibilityState === "visible") retryIfStalled();
|
|
236
|
+
});
|
|
237
|
+
window.addEventListener("online", retryIfStalled);
|
|
238
|
+
|
|
199
239
|
nuxtApp.provide("echo", window.Echo);
|
|
200
240
|
},
|
|
201
241
|
});
|