import "./DesktopAnnouncementsFeed.js" import "./DesktopAnnouncementsViewer.js" import server from "/@server/server.js" css(` announcements- { font-family: 'Arial'; scrollbar-width: none; -ms-overflow-style: none; } announcements- input::placeholder { color: var(--headertext); opacity: 0.35; } announcements- textarea::placeholder { color: var(--headertext); opacity: 0.3; } `) class Announcements extends Shadow { announcements = [] selectedId = null searchText = "" get canPost() { return global.currentNetwork.permissions.includes("announcements.add") } get canEdit() { return global.currentNetwork.permissions.includes("announcements.edit") } get canDelete() { return global.currentNetwork.permissions.includes("announcements.delete") } get filtered() { if (!this.searchText) return this.announcements const q = this.searchText.toLowerCase() return this.announcements.filter(a => a.text.toLowerCase().includes(q)) } get selectedAnnouncement() { return this.announcements.find(a => a.id === this.selectedId) || null } render() { HStack(() => { // ── Left: feed list ─────────────────────────────────────── VStack(() => { // Toolbar HStack(() => { p("Announcements") .fontFamily("Laandbrau") .margin(0).fontSize(1.8, em).fontWeight("700").color("var(--headertext)") .flex(1) if (this.canPost) { button("+ New") .paddingHorizontal(0.85, em).paddingVertical(0.42, em) .background("var(--quillred)").border("none") .borderRadius(0.45, em).color("white") .fontSize(0.82, em).fontWeight("600").cursor("pointer") .onClick((done) => { // Tell the viewer to open compose mode if(done) { this.selectedId = null this._openCompose = true this.rerender() } }) } }) .paddingHorizontal(1.1, em).paddingTop(1.1, em).paddingBottom(0.35, em) .alignItems("center").flexShrink(0) DesktopAnnouncementsFeed( this.filtered, this.selectedId, this.searchText, (id) => { this.selectedId = id this._openCompose = false this.rerender() }, (text) => { this.searchText = text this.rerender() } ) .flex(1).minHeight(0).overflow("hidden") }) .width(320, px).minWidth(280, px) .height(100, pct) .borderRight("1px solid var(--divider)") .background("var(--main)") .flexShrink(0).overflow("hidden") // ── Right: viewer / composer ────────────────────────────── VStack(() => { const viewer = DesktopAnnouncementsViewer( this.selectedAnnouncement, this.canPost, this.canEdit, this.canDelete, // onNew (ann) => { this.announcements.unshift(ann) if (global.currentNetwork.data?.announcements) { global.currentNetwork.data.announcements.unshift(ann) } this.selectedId = ann.id this._openCompose = false this.rerender() window.dispatchEvent(new CustomEvent("new-announcement", { detail: { announcement: ann } })) }, // onEdited (ann) => { const i = this.announcements.findIndex(a => a.id === ann.id) if (i !== -1) this.announcements[i] = ann if (global.currentNetwork.data?.announcements) { const gi = global.currentNetwork.data.announcements.findIndex(a => a.id === ann.id) if (gi !== -1) global.currentNetwork.data.announcements[gi] = ann } this.selectedId = ann.id this.rerender() window.dispatchEvent(new CustomEvent("edited-announcement", { detail: ann })) }, // onDeleted (id) => { this.announcements = this.announcements.filter(a => a.id !== id) if (global.currentNetwork.data?.announcements) { global.currentNetwork.data.announcements = global.currentNetwork.data.announcements.filter(a => a.id !== id) } this.selectedId = null this.rerender() window.dispatchEvent(new CustomEvent("deleted-announcement", { detail: { id } })) } ) // If the "New" button was pressed, trigger compose mode immediately if (this._openCompose && viewer) { viewer.composing = true this._openCompose = false } }) .flex(1).height(100, pct).background("var(--main)").overflow("hidden") }) .height(100, pct).width(100, pct).overflow("hidden") .onAppear(async () => { const res = await server.getAnnouncements(global.currentNetwork.id) const data = Array.isArray(res) ? res : (res?.data || []) if (data.length !== this.announcements.length) { this.announcements = data.sort((a, b) => new Date(b.created) - new Date(a.created)) if (global.currentNetwork.data) { global.currentNetwork.data.announcements = this.announcements } if (!this.selectedId && this.announcements.length) { this.selectedId = this.announcements[0].id } this.rerender() } }) .onEvent("new-announcement", (e) => { const ann = e.detail.announcement if (!this.announcements.find(a => a.id === ann.id)) { this.announcements.unshift(ann) this.rerender() } }) .onEvent("deleted-announcement", (e) => { const id = e.detail.id if (this.selectedId === id) this.selectedId = null this.announcements = this.announcements.filter(a => a.id !== id) this.rerender() }) .onEvent("edited-announcement", (e) => { const ann = e.detail const i = this.announcements.findIndex(a => a.id === ann.id) if (i !== -1) { this.announcements[i] = ann; this.rerender() } }) } } register(Announcements)