@@ -127,7 +127,7 @@ import { renderElegooCamBanner } from './printers/elegoo/widget_camera.js';
127127import { sdcpWireLive } from './printers/elegoo/sdcp.js';
128128import { elgFanStep } from './printers/elegoo/widget_control.js';
129129import { jobBar, jobBarFill } from './printers/job-bar.js';
130- import { initReport, toggleReport, openReport, isReportOpen, reportDropFiles, reportDragState, syncReports } from './report/index.js';
130+ import { initReport, toggleReport, openReport, isReportOpen, reportDropFiles, reportDragState, syncReports, refreshSquadCard } from './report/index.js';
131131
132132 const API_BASE = "https://cdn.tigertag.io";
133133
@@ -4700,11 +4700,21 @@ import { initReport, toggleReport, openReport, isReportOpen, reportDropFiles, re
47004700 renderSupporterBadge(); renderFriendBanner();
47014701 }, () => {});
47024702 }
4703+ let _lastSquadTier; // what the TigerSquad card last showed (undefined = never rendered)
4704+ let _squadCountCache = null; // { n, at } — the TigerSquad head count (_squadMemberCount)
47034705 function renderSupporterBadge() {
47044706 const host = document.querySelector("#sbUser .sb-avatar-wrap"); if (!host) return;
47054707 // TigerSquad member (any level) → heart and ring in the level's colour on the
47064708 // avatar (the level pill lives on the banner, not here). Not a member → nothing.
47074709 const tier = _ownSquadTier();
4710+ // Joined, left or changed level → the head count is stale, and the requests
4711+ // panel's card flips between "join" and "you're in".
4712+ if (tier !== _lastSquadTier) {
4713+ _lastSquadTier = tier; _squadCountCache = null;
4714+ try { refreshSquadCard(); } catch (_) {}
4715+ const row = _supportMenu?.querySelector(".support-squad");
4716+ if (row) { const tmp = document.createElement("div"); tmp.innerHTML = _supportSquadRowHTML(); tmp.firstElementChild ? row.replaceWith(tmp.firstElementChild) : row.remove(); }
4717+ }
47084718 const av = $("sbAvatar");
47094719 av?.classList.toggle("av-squad", !!tier);
47104720 SQUAD_TIERS.forEach(x => av?.classList.toggle(`sq-${x}`, tier === x));
@@ -4956,7 +4966,6 @@ import { initReport, toggleReport, openReport, isReportOpen, reportDropFiles, re
49564966 // How many accounts are in the TigerSquad — a COUNT aggregation over
49574967 // userProfiles (public read) through Firestore REST: ordering on
49584968 // tigerSquad.since also filters to the profiles that have it. Cached 10 min.
4959- let _squadCountCache = null;
49604969 async function _squadMemberCount() {
49614970 if (_squadCountCache && Date.now() - _squadCountCache.at < 600000) return _squadCountCache.n;
49624971 try {
@@ -5004,6 +5013,30 @@ import { initReport, toggleReport, openReport, isReportOpen, reportDropFiles, re
50045013 // The badge itself arrives with the user doc (server-set); show it right away.
50055014 if (status === "granted") { state.supporter = true; renderSupporterBadge(); }
50065015 }
5016+ // The TigerSquad row of the support menu: "join" (opens the invite) for an
5017+ // account outside, "you're in" (badge, level, head count → the wall) for a member.
5018+ function _supportSquadRowHTML() {
5019+ if (!state.activeAccountId || state.friendView) return "";
5020+ const tier = _ownSquadTier();
5021+ if (!tier) return `
5022+ <button type="button" class="support-squad" data-squad-open>
5023+ <img class="support-squad-badge" src="../assets/img/tigersquad/tigersquad-gold.webp" alt="" aria-hidden="true">
5024+ <span class="support-squad-txt"><b>${esc(t("squadJoin"))}</b><small>${esc(t(state.supporter ? "squadJoinSubMember" : "squadJoinSubFree"))}</small></span>
5025+ <span class="icon icon-arrow-r icon-12 support-squad-go" aria-hidden="true"></span>
5026+ </button>`;
5027+ const n = _squadCountCache?.n;
5028+ if (!(n >= 1)) _squadMemberCount().then(c => {
5029+ const el = _supportMenu?.querySelector(".support-squad-n");
5030+ if (el && c >= 1) { el.textContent = t("squadMembers", { n: c }); el.hidden = false; }
5031+ });
5032+ return `
5033+ <button type="button" class="support-squad support-squad--in sq-${tier}" data-squad-wall>
5034+ <img class="support-squad-badge" src="${esc(SQUAD_ASSETS[tier])}" alt="" aria-hidden="true">
5035+ <span class="support-squad-txt"><b>${esc(t("squadInTitle"))}<span class="support-squad-tier">${esc(squadTierLabel(tier))}</span></b>
5036+ <small class="support-squad-n"${n >= 1 ? "" : " hidden"}>${n >= 1 ? esc(t("squadMembers", { n })) : ""}</small></span>
5037+ <span class="icon icon-arrow-up-right icon-12 support-squad-go" aria-hidden="true"></span>
5038+ </button>`;
5039+ }
50075040 function _openCoffee(e) {
50085041 const anchor = e?.currentTarget || (e instanceof Element ? e : null);
50095042 if (_supportMenu) { _closeSupportMenu(); return; }
@@ -5028,12 +5061,7 @@ import { initReport, toggleReport, openReport, isReportOpen, reportDropFiles, re
50285061 <span>${esc(t("supportCodeHint"))}</span>
50295062 <button type="button" class="support-code-chip${_supporterCodeCached() ? "" : " is-loading"}" data-support-copy="${esc(_supporterCodeCached() || "")}"><span>${esc(_supporterCodeCached() || "TS-····-····")}</span><span class="icon icon-copy icon-12" aria-hidden="true"></span></button>
50305063 </div>` : ""}
5031- ${state.activeAccountId && !state.supportPublicly && !anchor?.closest?.(".squad-invite") ? `
5032- <button type="button" class="support-squad" data-squad-open>
5033- <img class="support-squad-badge" src="../assets/img/tigersquad/tigersquad-gold.webp" alt="" aria-hidden="true">
5034- <span class="support-squad-txt"><b>${esc(t("squadJoin"))}</b><small>${esc(t(state.supporter ? "squadJoinSubMember" : "squadJoinSubFree"))}</small></span>
5035- <span class="icon icon-arrow-r icon-12 support-squad-go" aria-hidden="true"></span>
5036- </button>` : ""}
5064+ ${!anchor?.closest?.(".squad-invite") ? _supportSquadRowHTML() : ""}
50375065 ${!state.supporter && state.activeAccountId ? `
50385066 <details class="support-claim">
50395067 <summary>${esc(t("supportClaimToggle"))}</summary>
@@ -5092,6 +5120,11 @@ import { initReport, toggleReport, openReport, isReportOpen, reportDropFiles, re
50925120 return;
50935121 }
50945122 // TigerSquad → the full invite (perks, levels, the free join), never a silent join.
5123+ if (ev.target.closest("[data-squad-wall]")) {
5124+ _closeSupportMenu();
5125+ window.electronAPI?.openExternal(`https://tigersystem.io/${state.lang || "en"}/tigersquad`);
5126+ return;
5127+ }
50955128 if (ev.target.closest("[data-squad-open]")) {
50965129 _closeSupportMenu();
50975130 if (!document.querySelector(".squad-invite")) setTimeout(_openSquadInvite, 130);
@@ -8442,7 +8475,10 @@ import { initReport, toggleReport, openReport, isReportOpen, reportDropFiles, re
84428475 ? `<span class="card-tl-badge card-tl-badge--twin" title="${t('twinBadge')} — ${t('twinTitle')}"><span class="icon icon-link ${iconSize}"></span></span>` : "";
84438476 const backup = (withBackup && r.isPlus && r.rfidBackup)
84448477 ? `<span class="card-tl-badge card-tl-badge--backup" title="${esc(t('backupBadge'))}"><span class="icon icon-backup ${iconSize}"></span></span>` : "";
8445- return (twin || backup) ? `<div class="card-badges-tl${compact ? " card-badges-tl--sm" : ""}">${twin}${backup}</div>` : "";
8478+ // Refill (info1) — a coil with no spool of its own; the same flag as the "Refill" chip. First in the stack.
8479+ const refill = r.isRefill
8480+ ? `<span class="card-tl-badge card-tl-badge--refill"><span class="icon icon-recycle ${iconSize}" aria-hidden="true"></span>${compact ? "" : "<span class=\"refill-txt\">Refill</span>"}</span>` : "";
8481+ return (twin || backup || refill) ? `<div class="card-badges-tl${compact ? " card-badges-tl--sm" : ""}">${refill}${twin}${backup}</div>` : "";
84468482 }
84478483
84488484 // Tier badge shown next to a row everywhere we display its origin:
@@ -9790,6 +9826,7 @@ import { initReport, toggleReport, openReport, isReportOpen, reportDropFiles, re
97909826 r.isPlus ? 1 : 0,
97919827 r.isCloud ? 1 : 0,
97929828 r.rfidBackup ? 1 : 0,
9829+ r.isRefill ? 1 : 0,
97939830 r.colorHex || "",
97949831 r.colorHex2 || "",
97959832 r.colorHex3 || "",
@@ -18966,9 +19003,14 @@ import { initReport, toggleReport, openReport, isReportOpen, reportDropFiles, re
1896619003 const imgUrl = r.imgUrl || p.label?.imgUrl || "";
1896719004 const _img = imgUrl ? resolvedImg(imgUrl) : null;
1896819005 const onerr = `this.outerHTML='<div class=\\'panel-img-color-placeholder\\'style=\\'background:${colorBg(r)}\\'><img src=\\'${logoSrc(colorBg(r))}\\'class=\\'panel-img-logo\\'></div>'`;
19006+ // Refill pictogram top-right, as on the spool's detail panel — known once the
19007+ // product detail (`product/get` → filament.refill → info1) has arrived.
19008+ const refill = r.isRefill
19009+ ? `<div class="panel-img-badge panel-img-badge--tr panel-img-badge-tr-group"><span class="panel-img-badge-tr-item panel-img-icon-badge panel-img-refill-badge"><span class="icon icon-recycle icon-11" aria-hidden="true"></span><span class="refill-txt">Refill</span></span></div>`
19010+ : "";
1896919011 const imgSection = _img
18970- ? `<div class="panel-img-wrap" style="background:${colorBg(r)}"><img class="panel-img" src="${esc(_img)}" onerror="${esc(onerr)}" /></div>`
18971- : `<div class="panel-img-wrap"><div class="panel-img-color-placeholder" style="background:${colorBg(r)}"><img src="${logoSrc(colorBg(r))}" class="panel-img-logo" /></div></div>`;
19012+ ? `<div class="panel-img-wrap" style="background:${colorBg(r)}"><img class="panel-img" src="${esc(_img)}" onerror="${esc(onerr)}" />${refill} </div>`
19013+ : `<div class="panel-img-wrap"><div class="panel-img-color-placeholder" style="background:${colorBg(r)}"><img src="${logoSrc(colorBg(r))}" class="panel-img-logo" /></div>${refill} </div>`;
1897219014 // Identity — Brand · Series · Material on line 1, colour name on line 2.
1897319015 const brand = (r.brand && r.brand !== "-") ? r.brand : "";
1897419016 const series = (r.series && r.series !== "-") ? r.series : "";
@@ -20242,8 +20284,11 @@ import { initReport, toggleReport, openReport, isReportOpen, reportDropFiles, re
2024220284 const badgeBackup = (r.isPlus && r.rfidBackup)
2024320285 ? `<span class="panel-img-badge-tr-item panel-img-icon-badge panel-img-backup-badge" title="${esc(t("backupBadge"))}"><span class="icon icon-backup icon-11"></span></span>`
2024420286 : "";
20245- const badgeTrGroup = (badgeBackup || badgeTwin || badgeChip)
20246- ? `<div class="panel-img-badge panel-img-badge--tr panel-img-badge-tr-group">${badgeTwin}${badgeBackup}${badgeChip}</div>`
20287+ const badgeRefill = r.isRefill
20288+ ? `<span class="panel-img-badge-tr-item panel-img-icon-badge panel-img-refill-badge"><span class="icon icon-recycle icon-11" aria-hidden="true"></span><span class="refill-txt">Refill</span></span>`
20289+ : "";
20290+ const badgeTrGroup = (badgeBackup || badgeTwin || badgeChip || badgeRefill)
20291+ ? `<div class="panel-img-badge panel-img-badge--tr panel-img-badge-tr-group">${badgeRefill}${badgeTwin}${badgeBackup}${badgeChip}</div>`
2024720292 : "";
2024820293 // Bottom-right ❤/★/🛍 status cluster, same as the grid/table illustrations.
2024920294 const overlays = badgeLeft + badgeTrGroup + badgeTd + _productBadgesHTML(r);
@@ -22157,6 +22202,12 @@ import { initReport, toggleReport, openReport, isReportOpen, reportDropFiles, re
2215722202 openExternal: url => window.electronAPI?.openExternal(url),
2215822203 openCoffee: anchor => _openCoffee(anchor),
2215922204 canJoinSquad: () => !!state.activeAccountId && !state.supportPublicly && !state.friendView,
22205+ // Already in → the card shows the member's badge instead, with the squad's head count.
22206+ squadMember: () => {
22207+ const tier = state.activeAccountId && !state.friendView ? _ownSquadTier() : null;
22208+ return tier ? { tier, label: squadTierLabel(tier), src: SQUAD_ASSETS[tier], wall: `https://tigersystem.io/${state.lang || "en"}/tigersquad` } : null;
22209+ },
22210+ squadMemberCount: () => _squadMemberCount(),
2216022211 openSquad: () => { if (!document.querySelector(".squad-invite")) _openSquadInvite(); },
2216122212 timeAgo: ms => timeAgo(ms),
2216222213 getUid: () => state.activeAccountId || null,
0 commit comments