PageSourceSearch

https://newbridge.ie/scripts/community_skills_row.js?v=20260923a

js newbridge.ie collected 2026-10-02 22:48:12 UTC 5,127 bytes, 117 lines download raw bytes

1/**
2 * community_skills_row.js — Skill Board row on community.html.
3 *
4 * Loads up to 6 recent open posts from the current community via the
5 * listSkillPosts callable and renders compact cards. The callable is for
6 * signed-in members, so the row waits for the restored sign-in and stays
7 * hidden for visitors (calling it signed out only produced a 401 in the
8 * console, and calling it before auth restored hid the row for members
9 * too). Clicking a card
10 * jumps to /skill_board.html?cid=…&post=…; the "Browse all" button
11 * carries the cid through too.
12 *
13 * Community id resolution mirrors what other community.html rows do:
14 *   1. URL param ?community=<cid>
15 *   2. URL param ?cid=<cid>
16 *   3. window.__COMMUNITY_ID__ if set by the page bootstrap
17 *   4. single-town mode via window.COMMUNITY_CONTEXT if exposed
18 */
19
20(function () {
21    "use strict";
22
23    function esc(s) {
24        return String(s == null ? "" : s).replace(/[&<>"']/g, function (c) {
25            return { "&": "&amp;", "<": "&lt;", ">": "&gt;", "\"": "&quot;", "'": "&#39;" }[c];
26        });
27    }
28    function human(k) { return String(k || "").replace(/_/g, " ").trim(); }
29
30    function resolveCid() {
31        try {
32            var u = new URL(window.location.href);
33            var cid = u.searchParams.get("community") || u.searchParams.get("cid");
34            if (cid) return cid;
35        } catch (e) { /* ignore */ }
36        if (window.__COMMUNITY_ID__) return window.__COMMUNITY_ID__;
37        if (window.COMMUNITY_CONTEXT && window.COMMUNITY_CONTEXT.id) return window.COMMUNITY_CONTEXT.id;
38        return null;
39    }
40
41    function cardHtml(p, cid) {
42        var typeTone = p.type === "request" ? "request" : "offer";
43        var tags = (p.skill_keys || []).slice(0, 3).map(function (k) {
44            return '<span class="skills-tag">' + esc(human(k)) + '</span>';
45        }).join("");
46        var href = "/skill_board.html?cid=" + encodeURIComponent(cid || "") + "&post=" + encodeURIComponent(p.id);
47        return '' +
48            '<a class="skills-community-card skills-community-card--' + typeTone + '" href="' + href + '">' +
49                '<span class="skills-community-card__label">' + (p.type === "request" ? "NEED" : "OFFER") + '</span>' +
50                '<h3 class="skills-community-card__title">' + esc(p.title || "(untitled)") + '</h3>' +
51                '<p class="skills-community-card__desc">' + esc((p.description || "").slice(0, 140)) + '</p>' +
52                '<div class="skills-community-card__tags">' + tags + '</div>' +
53                (p.response_count ? '<div class="skills-community-card__meta">' + p.response_count + ' responses</div>' : '') +
54            '</a>';
55    }
56
57    /** Resolves the signed-in user once Firebase has restored the session (null if none). */
58    function signedInUser() {
59        return new Promise(function (resolve) {
60            try {
61                var unsub = firebase.auth().onAuthStateChanged(function (user) {
62                    unsub();
63                    resolve(user || null);
64                });
65            } catch (e) {
66                resolve(null);
67            }
68        });
69    }
70
71    async function load() {
72        var feed = document.getElementById("community_skill_feed");
73        var empty = document.getElementById("community_skill_empty");
74        var btn = document.getElementById("skill_board_all_btn");
75        if (!feed) return;
76        var cid = resolveCid();
77        if (btn) btn.href = "/skill_board.html" + (cid ? ("?cid=" + encodeURIComponent(cid)) : "");
78        if (!cid) {
79            // No community context — hide the whole row so we don't show
80            // a confusing empty state on the homepage.
81            var sec = document.getElementById("row_skill_board");
82            if (sec) sec.style.display = "none";
83            return;
84        }
85        if (!window.firebase || typeof window.firebase.functions !== "function") return;
86        var user = await signedInUser();
87        if (!user) {
88            var hidden = document.getElementById("row_skill_board");
89            if (hidden) hidden.style.display = "none";
90            return;
91        }
92        try {
93            var res = await firebase.functions().httpsCallable("listSkillPosts")({
94                communityId: cid, status: "open", limit: 6,
95            });
96            var posts = (res && res.data && res.data.posts) || [];
97            if (posts.length === 0) {
98                feed.innerHTML = "";
99                if (empty) empty.style.display = "";
100                return;
101            }
102            if (empty) empty.style.display = "none";
103            feed.innerHTML = posts.map(function (p) { return cardHtml(p, cid); }).join("");
104        } catch (e) {
105            // Stay quiet on the public homepage — just hide the row.
106            console.warn("[community-skill-row] load failed", e);
107            var sec2 = document.getElementById("row_skill_board");
108            if (sec2) sec2.style.display = "none";
109        }
110    }
111
112    if (document.readyState === "loading") {
113        document.addEventListener("DOMContentLoaded", load);
114    } else {
115        load();
116    }
117})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.