|
174 | 174 | importFileInput: $("#import-file-input"), |
175 | 175 | btnClearAll: $("#btn-clear-all"), |
176 | 176 | btnResetSettings: $("#btn-reset-settings"), |
| 177 | + messageList: $("#message-list"), |
| 178 | + btnClearMessages: $("#btn-clear-messages"), |
177 | 179 | }; |
178 | 180 |
|
179 | 181 | // ---- Init ---- |
|
184 | 186 | populateUI(); |
185 | 187 | bindEvents(); |
186 | 188 | loadStats(); |
| 189 | + // Trigger a fresh announcement fetch (keeps the history up to date even |
| 190 | + // if the user only ever opens the options page) and render whatever we have. |
| 191 | + if (window.announcement) { |
| 192 | + window.announcement.fetch(false).finally(() => renderMessageHistory()); |
| 193 | + } else { |
| 194 | + renderMessageHistory(); |
| 195 | + } |
187 | 196 | } |
188 | 197 |
|
189 | 198 | function applyLanguage() { |
|
521 | 530 | btn.classList.add("active"); |
522 | 531 | $$(".settings-section").forEach((s) => s.classList.remove("active")); |
523 | 532 | $(`#section-${btn.dataset.section}`).classList.add("active"); |
| 533 | + if (btn.dataset.section === "messages") { |
| 534 | + renderMessageHistory(); |
| 535 | + } |
524 | 536 | }); |
525 | 537 | }); |
526 | 538 |
|
| 539 | + // Clear message history |
| 540 | + if (dom.btnClearMessages) { |
| 541 | + dom.btnClearMessages.addEventListener("click", async () => { |
| 542 | + if (window.announcement) { |
| 543 | + await window.announcement.clearHistory(); |
| 544 | + } |
| 545 | + renderMessageHistory(); |
| 546 | + }); |
| 547 | + } |
| 548 | + |
527 | 549 | // Provider |
528 | 550 | dom.providerSelect.addEventListener("change", () => { |
529 | 551 | settings.provider = dom.providerSelect.value; |
|
794 | 816 | dom.statMessages.textContent = totalMessages; |
795 | 817 | } |
796 | 818 |
|
| 819 | + // ---- Message history (announcements) ---- |
| 820 | + async function renderMessageHistory() { |
| 821 | + if (!dom.messageList) return; |
| 822 | + dom.messageList.innerHTML = ""; |
| 823 | + |
| 824 | + if (!window.announcement) { |
| 825 | + const empty = document.createElement("div"); |
| 826 | + empty.className = "message-empty"; |
| 827 | + empty.textContent = t("messages.empty"); |
| 828 | + dom.messageList.appendChild(empty); |
| 829 | + return; |
| 830 | + } |
| 831 | + |
| 832 | + const history = await window.announcement.getHistory(); |
| 833 | + if (!history.length) { |
| 834 | + const empty = document.createElement("div"); |
| 835 | + empty.className = "message-empty"; |
| 836 | + empty.textContent = t("messages.empty"); |
| 837 | + dom.messageList.appendChild(empty); |
| 838 | + return; |
| 839 | + } |
| 840 | + |
| 841 | + const lang = window.i18n ? window.i18n.i18nGetLang() : "en"; |
| 842 | + const fmt = new Intl.DateTimeFormat(lang === "en" ? undefined : lang, { |
| 843 | + year: "numeric", |
| 844 | + month: "2-digit", |
| 845 | + day: "2-digit", |
| 846 | + hour: "2-digit", |
| 847 | + minute: "2-digit", |
| 848 | + }); |
| 849 | + |
| 850 | + history.forEach((entry) => { |
| 851 | + const type = ["info", "update", "warning"].includes(entry.type) |
| 852 | + ? entry.type |
| 853 | + : "info"; |
| 854 | + |
| 855 | + const card = document.createElement("div"); |
| 856 | + card.className = `message-card message-${type}`; |
| 857 | + |
| 858 | + const header = document.createElement("div"); |
| 859 | + header.className = "message-card-header"; |
| 860 | + |
| 861 | + const icon = document.createElement("span"); |
| 862 | + icon.className = "message-icon"; |
| 863 | + icon.textContent = |
| 864 | + type === "warning" ? "⚠️" : type === "update" ? "⬆️" : "ℹ️"; |
| 865 | + header.appendChild(icon); |
| 866 | + |
| 867 | + const title = document.createElement("div"); |
| 868 | + title.className = "message-card-title"; |
| 869 | + title.textContent = window.announcement.pickLocalized(entry.title, lang); |
| 870 | + header.appendChild(title); |
| 871 | + |
| 872 | + const time = document.createElement("span"); |
| 873 | + time.className = "message-card-time"; |
| 874 | + time.textContent = entry.receivedAt ? fmt.format(entry.receivedAt) : ""; |
| 875 | + header.appendChild(time); |
| 876 | + |
| 877 | + card.appendChild(header); |
| 878 | + |
| 879 | + const body = window.announcement.pickLocalized(entry.body, lang); |
| 880 | + if (body) { |
| 881 | + const bodyEl = document.createElement("div"); |
| 882 | + bodyEl.className = "message-card-body"; |
| 883 | + bodyEl.textContent = body; |
| 884 | + card.appendChild(bodyEl); |
| 885 | + } |
| 886 | + |
| 887 | + if (entry.link) { |
| 888 | + const link = document.createElement("a"); |
| 889 | + link.className = "message-card-link"; |
| 890 | + link.href = entry.link; |
| 891 | + link.target = "_blank"; |
| 892 | + link.rel = "noopener noreferrer"; |
| 893 | + link.textContent = |
| 894 | + window.announcement.pickLocalized(entry.linkLabel, lang) || |
| 895 | + entry.link; |
| 896 | + card.appendChild(link); |
| 897 | + } |
| 898 | + |
| 899 | + dom.messageList.appendChild(card); |
| 900 | + }); |
| 901 | + } |
| 902 | + |
797 | 903 | /** |
798 | 904 | * Full backup: conversations + settings (includes custom models, base URLs, |
799 | 905 | * API keys, model check cache, language, etc). |
|
0 commit comments