@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 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",
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mundogamernetwork/shared-ui",
3
- "version": "1.16.20",
3
+ "version": "1.16.22",
4
4
  "description": "Mundo Gamer Network - Shared UI Layer (Nuxt 3)",
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
@@ -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
- ? $t("keys.campaigns.campaign.status_exhausted")
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
- const seoImage = computed(() =>
76
- pixel.value?.share_image_url
77
- || pixel.value?.image_url
78
- || wall.value?.cover_image_url
79
- || wall.value?.logo_url
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
@@ -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
- reconnectAttempts++;
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
  });