main
js 127 lines 2.76 KB
Raw
1 import { createStore } from "/js/AlpineStore.js";
2 import { closeModal } from "/js/modals.js";
3 import { slides } from "/plugins/_whats_new/webui/whats-new-slides.js";
4
5 const NEVER_SHOW_STORAGE_KEY = "a0_whats_new_never_show";
6
7 const emptySlide = {
8 eyebrow: "What's New",
9 title: "No new updates right now",
10 summary: "New highlights will appear here when there are fresh Agent Zero updates.",
11 mediaType: "none",
12 media: "",
13 mediaLabel: "No new updates right now.",
14 bullets: [],
15 };
16
17 function storageValue(key) {
18 try {
19 return globalThis.localStorage?.getItem(key) || "";
20 } catch {
21 return "";
22 }
23 }
24
25 function isNeverShowEnabled() {
26 const value = storageValue(NEVER_SHOW_STORAGE_KEY);
27 if (!value) return false;
28
29 try {
30 const parsed = JSON.parse(value);
31 if (parsed && typeof parsed === "object") return parsed.enabled !== false;
32 return Boolean(parsed);
33 } catch {
34 return !["0", "false", "no", "off"].includes(value.trim().toLowerCase());
35 }
36 }
37
38 function persistNeverShowPreference(enabled) {
39 try {
40 if (enabled) {
41 globalThis.localStorage?.setItem(
42 NEVER_SHOW_STORAGE_KEY,
43 JSON.stringify({
44 enabled: true,
45 updatedAt: new Date().toISOString(),
46 }),
47 );
48 } else {
49 globalThis.localStorage?.removeItem(NEVER_SHOW_STORAGE_KEY);
50 }
51 } catch {
52 // localStorage may be unavailable in private or locked-down browser modes.
53 }
54 }
55
56 export const store = createStore("whatsNew", {
57 slides,
58 currentIndex: 0,
59 neverShowAgain: false,
60
61 onOpen() {
62 this.currentIndex = 0;
63 this.neverShowAgain = isNeverShowEnabled();
64 },
65
66 cleanup() {
67 persistNeverShowPreference(this.neverShowAgain);
68 },
69
70 get currentSlide() {
71 return this.slides[this.currentIndex] || emptySlide;
72 },
73
74 hasSlides() {
75 return this.slides.length > 0;
76 },
77
78 isFirst() {
79 return this.currentIndex <= 0;
80 },
81
82 isLast() {
83 return this.currentIndex >= this.slides.length - 1;
84 },
85
86 progressLabel() {
87 if (!this.hasSlides()) return "";
88 return `${this.currentIndex + 1} of ${this.slides.length}`;
89 },
90
91 dotLabel(index) {
92 const slide = this.slides[index];
93 return slide ? `Show ${slide.title}` : `Show item ${index + 1}`;
94 },
95
96 goTo(index) {
97 const nextIndex = Number(index);
98 if (!Number.isInteger(nextIndex)) return;
99 if (nextIndex < 0 || nextIndex >= this.slides.length) return;
100 this.currentIndex = nextIndex;
101 },
102
103 previous() {
104 if (!this.isFirst()) this.currentIndex -= 1;
105 },
106
107 setNeverShowAgain(value) {
108 this.neverShowAgain = Boolean(value);
109 persistNeverShowPreference(this.neverShowAgain);
110 },
111
112 next() {
113 if (this.isLast()) {
114 this.finish();
115 return;
116 }
117 this.currentIndex += 1;
118 },
119
120 finish() {
121 closeModal();
122 },
123
124 skip() {
125 closeModal();
126 },
127 });