@things-factory/announcement 10.1.255 → 10.1.257
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/components/announcement-receipts.ts +31 -36
- package/client/components/announcement-sheet.ts +18 -15
- package/client/components/audience-picker.ts +41 -28
- package/client/pages/announcement-editor.ts +125 -87
- package/client/pages/announcement-view.ts +38 -36
- package/client/utils/announcement-api.ts +3 -50
- package/dist-client/components/announcement-receipts.d.ts +6 -5
- package/dist-client/components/announcement-receipts.js +24 -52
- package/dist-client/components/announcement-receipts.js.map +1 -1
- package/dist-client/components/announcement-sheet.d.ts +2 -1
- package/dist-client/components/announcement-sheet.js +15 -21
- package/dist-client/components/announcement-sheet.js.map +1 -1
- package/dist-client/components/audience-picker.d.ts +8 -4
- package/dist-client/components/audience-picker.js +35 -35
- package/dist-client/components/audience-picker.js.map +1 -1
- package/dist-client/pages/announcement-editor.d.ts +22 -12
- package/dist-client/pages/announcement-editor.js +116 -130
- package/dist-client/pages/announcement-editor.js.map +1 -1
- package/dist-client/pages/announcement-view.d.ts +7 -4
- package/dist-client/pages/announcement-view.js +34 -48
- package/dist-client/pages/announcement-view.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/announcement-api.d.ts +0 -47
- package/dist-client/utils/announcement-api.js +3 -21
- package/dist-client/utils/announcement-api.js.map +1 -1
- package/dist-server/index.d.ts +1 -0
- package/dist-server/index.js +1 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/work-doors.d.ts +1 -0
- package/dist-server/work-doors.js +11 -0
- package/dist-server/work-doors.js.map +1 -0
- package/package.json +11 -11
- package/server/index.ts +1 -0
- package/server/work-doors.ts +9 -0
- package/translations/en.json +2 -1
- package/translations/es.json +2 -1
- package/translations/ja.json +2 -1
- package/translations/ko.json +2 -1
- package/translations/ms.json +2 -1
- package/translations/zh.json +2 -1
|
@@ -2,16 +2,18 @@ import '@operato/list-state/ox-page-state.js'
|
|
|
2
2
|
import '../components/announcement-sheet.js'
|
|
3
3
|
import '../components/announcement-receipts.js'
|
|
4
4
|
|
|
5
|
+
import gql from 'graphql-tag'
|
|
5
6
|
import { css, html, nothing } from 'lit'
|
|
6
7
|
import { customElement, state } from 'lit/decorators.js'
|
|
7
8
|
|
|
9
|
+
import { MutationController, QueryController } from '@operato/graphql'
|
|
8
10
|
import { i18next, localize } from '@operato/i18n'
|
|
9
11
|
import { notify, openPopup } from '@operato/layout'
|
|
10
12
|
import { navigate, PageView } from '@operato/shell'
|
|
11
13
|
import { DocumentPageStyles } from '@operato/styles'
|
|
12
14
|
|
|
13
|
-
import { announcementApi, type AnnouncementItem } from '../utils/announcement-api.js'
|
|
14
|
-
import {
|
|
15
|
+
import { announcementApi, FULL_FIELDS, type AnnouncementItem } from '../utils/announcement-api.js'
|
|
16
|
+
import { statusWord, t, when } from '../utils/announcement-words.js'
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* 공지 보기 — one announcement. Opening it reads it for its audience (ADR-0163 decision 5). Its writers also see whom it
|
|
@@ -49,9 +51,34 @@ export class AnnouncementView extends localize(i18next)(PageView) {
|
|
|
49
51
|
`
|
|
50
52
|
]
|
|
51
53
|
|
|
52
|
-
@state()
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
@state() private announcementId = ''
|
|
55
|
+
|
|
56
|
+
/* The announcement the address names, with its writers' counts (ADR-0190). */
|
|
57
|
+
private read = new QueryController(this, gql`query ($id: String!) { announcement(id: $id) { ${FULL_FIELDS} readCount acknowledgedCount } }`, {
|
|
58
|
+
variables: () => (this.announcementId ? { id: this.announcementId } : undefined)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
private reminder = new MutationController(this, gql`mutation ($id: String!) { remindAnnouncement(id: $id) }`)
|
|
62
|
+
|
|
63
|
+
get announcement(): AnnouncementItem | null {
|
|
64
|
+
return this.read.data?.announcement ?? null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private get error(): unknown {
|
|
68
|
+
if (this.read.error) return this.read.error
|
|
69
|
+
return this.read.data && !this.announcement ? new Error(t('announcement.refusal.unknown-announcement')) : null
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Opening it reads it - asked again after, so its writer's counts include this reading. */
|
|
73
|
+
private readFor?: unknown
|
|
74
|
+
|
|
75
|
+
async updated() {
|
|
76
|
+
const one = this.announcement
|
|
77
|
+
if (!one?.addressed || one.mine?.readAt || this.readFor === this.read.data) return
|
|
78
|
+
this.readFor = this.read.data
|
|
79
|
+
await announcementApi.read(one.id).catch(() => {})
|
|
80
|
+
await this.read.refetch()
|
|
81
|
+
}
|
|
55
82
|
|
|
56
83
|
get context() {
|
|
57
84
|
const one = this.announcement
|
|
@@ -78,19 +105,19 @@ export class AnnouncementView extends localize(i18next)(PageView) {
|
|
|
78
105
|
async pageUpdated(changes: any, lifecycle: any) {
|
|
79
106
|
if (!this.active || !(changes.active || changes.params)) return
|
|
80
107
|
const id = lifecycle?.params?.id
|
|
81
|
-
if (id)
|
|
108
|
+
if (id) this.announcementId = id
|
|
82
109
|
}
|
|
83
110
|
|
|
84
111
|
render() {
|
|
85
112
|
const one = this.announcement
|
|
86
|
-
return html`<ox-page-state .loading=${this.loading} .error=${this.error} @retry=${() =>
|
|
113
|
+
return html`<ox-page-state .loading=${this.read.loading} .error=${this.error} @retry=${() => this.read.refetch()}>
|
|
87
114
|
${one
|
|
88
115
|
? html`<div class="page">
|
|
89
116
|
<section class="page-section">
|
|
90
117
|
<h2>${one.title}</h2>
|
|
91
118
|
${one.status === 'WITHDRAWN' ? html`<p class="notice">${t('announcement.text.withdrawn', { when: when(one.withdrawnAt), reason: one.withdrawReason ?? '' })}</p>` : nothing}
|
|
92
119
|
${one.status === 'SCHEDULED' ? html`<p class="notice">${t('announcement.text.scheduled', { when: when(one.startsAt) })}</p>` : nothing}
|
|
93
|
-
<announcement-sheet .announcement=${one} @announcement-acknowledged=${() => this.
|
|
120
|
+
<announcement-sheet .announcement=${one} @announcement-acknowledged=${() => this.read.refetch()}></announcement-sheet>
|
|
94
121
|
</section>
|
|
95
122
|
${one.editable && one.audiences
|
|
96
123
|
? html`<section class="page-section">
|
|
@@ -123,27 +150,6 @@ export class AnnouncementView extends localize(i18next)(PageView) {
|
|
|
123
150
|
</ox-page-state>`
|
|
124
151
|
}
|
|
125
152
|
|
|
126
|
-
private async load(id: string) {
|
|
127
|
-
this.loading = true
|
|
128
|
-
this.error = null
|
|
129
|
-
try {
|
|
130
|
-
let one = await announcementApi.one(id)
|
|
131
|
-
if (!one) throw new Error(t('announcement.refusal.unknown-announcement'))
|
|
132
|
-
/* Opening it reads it - asked again after, so its writer's counts include this reading. */
|
|
133
|
-
if (one.addressed && !one.mine?.readAt) {
|
|
134
|
-
await announcementApi.read(id)
|
|
135
|
-
one = (await announcementApi.one(id)) ?? one
|
|
136
|
-
}
|
|
137
|
-
this.announcement = one
|
|
138
|
-
} catch (error) {
|
|
139
|
-
this.announcement = null
|
|
140
|
-
this.error = error
|
|
141
|
-
} finally {
|
|
142
|
-
this.loading = false
|
|
143
|
-
this.updateContext()
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
153
|
private openReceipts() {
|
|
148
154
|
const one = this.announcement!
|
|
149
155
|
const popup: any = openPopup(html`<announcement-receipts .announcement=${one} @done=${() => popup.close()}></announcement-receipts>`, {
|
|
@@ -154,18 +160,14 @@ export class AnnouncementView extends localize(i18next)(PageView) {
|
|
|
154
160
|
}
|
|
155
161
|
|
|
156
162
|
private async remind() {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
notify({ message: t('announcement.text.reminded', { count: told }) })
|
|
160
|
-
} catch (error) {
|
|
161
|
-
notify({ level: 'error', message: reasonOf(error) })
|
|
162
|
-
}
|
|
163
|
+
const done = await this.reminder.run({ id: this.announcement!.id })
|
|
164
|
+
if (done) notify({ message: t('announcement.text.reminded', { count: done.remindAnnouncement }) })
|
|
163
165
|
}
|
|
164
166
|
|
|
165
167
|
private withdraw() {
|
|
166
168
|
const one = this.announcement!
|
|
167
169
|
const popup: any = openPopup(
|
|
168
|
-
html`<announcement-withdraw-form .announcement=${one} @announcement-withdrawn=${() => this.
|
|
170
|
+
html`<announcement-withdraw-form .announcement=${one} @announcement-withdrawn=${() => this.read.refetch()} @done=${() => popup.close()}></announcement-withdraw-form>`,
|
|
169
171
|
{ backdrop: true, size: 'small', title: `${t('announcement.button.withdraw')} · ${one.title}` }
|
|
170
172
|
)
|
|
171
173
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* The announcement doors
|
|
2
|
+
* The announcement doors the editor, the badge and the popups call (ADR-0163). Every call throws the server's words when
|
|
3
|
+
* it is refused. The other screens declare their own reads and saves (ADR-0190).
|
|
3
4
|
*/
|
|
4
5
|
import gql from 'graphql-tag'
|
|
5
6
|
|
|
@@ -84,59 +85,11 @@ async function change<T>(mutation: string, variables: Record<string, unknown>, f
|
|
|
84
85
|
export const announcementsChanged = () => window.dispatchEvent(new CustomEvent('announcements-changed'))
|
|
85
86
|
|
|
86
87
|
export const announcementApi = {
|
|
87
|
-
one: (id: string) => ask<AnnouncementItem | null>(`query ($id: String!) { announcement(id: $id) { ${FULL_FIELDS} readCount acknowledgedCount } }`, { id }, 'announcement'),
|
|
88
88
|
summary: () => ask<{ unread: number; toAcknowledge: number; pending: number }>(`query { announcementSummary { unread toAcknowledge pending } }`, {}, 'announcementSummary'),
|
|
89
89
|
popups: () => ask<AnnouncementItem[]>(`query { announcementPopups { ${FULL_FIELDS} } }`, {}, 'announcementPopups'),
|
|
90
90
|
read: async (id: string) => {
|
|
91
91
|
const one = await change<AnnouncementItem>(`mutation ($id: String!) { readAnnouncement(id: $id) { id readerState mine { readAt acknowledgedAt } } }`, { id }, 'readAnnouncement')
|
|
92
92
|
announcementsChanged()
|
|
93
93
|
return one
|
|
94
|
-
}
|
|
95
|
-
acknowledge: async (id: string) => {
|
|
96
|
-
const one = await change<AnnouncementItem>(`mutation ($id: String!) { acknowledgeAnnouncement(id: $id) { id readerState mine { readAt acknowledgedAt } } }`, { id }, 'acknowledgeAnnouncement')
|
|
97
|
-
announcementsChanged()
|
|
98
|
-
return one
|
|
99
|
-
},
|
|
100
|
-
receipts: (id: string) =>
|
|
101
|
-
ask<{ userId: string; name?: string; email?: string; listed: boolean; notifiedAt?: string; readAt?: string; acknowledgedAt?: string }[]>(
|
|
102
|
-
`query ($id: String!) { announcementReceipts(id: $id) { userId name email listed notifiedAt readAt acknowledgedAt } }`,
|
|
103
|
-
{ id },
|
|
104
|
-
'announcementReceipts'
|
|
105
|
-
),
|
|
106
|
-
revisions: (id: string) =>
|
|
107
|
-
ask<{ version: number; title: string; body?: string; rest?: any; createdAt: string; creator?: { name?: string } }[]>(
|
|
108
|
-
`query ($id: String!) { announcementRevisions(id: $id) { version title body rest createdAt creator { name } } }`,
|
|
109
|
-
{ id },
|
|
110
|
-
'announcementRevisions'
|
|
111
|
-
),
|
|
112
|
-
choices: () =>
|
|
113
|
-
ask<{ domainId: string; domainName: string; departments: { id: string; name: string; note?: string }[]; roles: { id: string; name: string; note?: string }[] }>(
|
|
114
|
-
`query { announcementAudienceChoices { domainId domainName departments { id name note } roles { id name note } } }`,
|
|
115
|
-
{},
|
|
116
|
-
'announcementAudienceChoices'
|
|
117
|
-
),
|
|
118
|
-
people: (text: string) => ask<{ id: string; name: string; note?: string }[]>(`query ($text: String!) { announcementPeople(text: $text) { id name note } }`, { text }, 'announcementPeople'),
|
|
119
|
-
audienceSize: (audiences: Audience[]) =>
|
|
120
|
-
ask<number>(`query ($audiences: [AnnouncementAudienceInput!]!) { announcementAudienceSize(audiences: $audiences) }`, { audiences }, 'announcementAudienceSize'),
|
|
121
|
-
create: (input: AnnouncementInput, attach: File[], uploading?: (fraction: number) => void) =>
|
|
122
|
-
change<AnnouncementItem>(
|
|
123
|
-
`mutation ($input: AnnouncementInput!, $attach: [Upload!]) { createAnnouncement(input: $input, attach: $attach) { id } }`,
|
|
124
|
-
{ input, attach },
|
|
125
|
-
'createAnnouncement',
|
|
126
|
-
attach.length ? uploading ?? (() => {}) : undefined
|
|
127
|
-
),
|
|
128
|
-
update: (id: string, input: AnnouncementInput, attach: File[], remove: string[], notifyAgain: boolean, uploading?: (fraction: number) => void) =>
|
|
129
|
-
change<AnnouncementItem>(
|
|
130
|
-
`mutation ($id: String!, $input: AnnouncementInput!, $attach: [Upload!], $remove: [String!], $notifyAgain: Boolean) {
|
|
131
|
-
updateAnnouncement(id: $id, input: $input, attach: $attach, remove: $remove, notifyAgain: $notifyAgain) { id status }
|
|
132
|
-
}`,
|
|
133
|
-
{ id, input, attach, remove, notifyAgain },
|
|
134
|
-
'updateAnnouncement',
|
|
135
|
-
attach.length ? uploading ?? (() => {}) : undefined
|
|
136
|
-
),
|
|
137
|
-
publish: (id: string) => change<AnnouncementItem>(`mutation ($id: String!) { publishAnnouncement(id: $id) { id status startsAt } }`, { id }, 'publishAnnouncement'),
|
|
138
|
-
withdraw: (id: string, reason: string) =>
|
|
139
|
-
change<AnnouncementItem>(`mutation ($id: String!, $reason: String!) { withdrawAnnouncement(id: $id, reason: $reason) { id status } }`, { id, reason }, 'withdrawAnnouncement'),
|
|
140
|
-
remove: (id: string) => change<boolean>(`mutation ($id: String!) { deleteAnnouncement(id: $id) }`, { id }, 'deleteAnnouncement'),
|
|
141
|
-
remind: (id: string) => change<number>(`mutation ($id: String!) { remindAnnouncement(id: $id) }`, { id }, 'remindAnnouncement')
|
|
94
|
+
}
|
|
142
95
|
}
|
|
@@ -7,11 +7,11 @@ declare const AnnouncementReceipts_base: (new (...args: any[]) => LitElement) &
|
|
|
7
7
|
export declare class AnnouncementReceipts extends AnnouncementReceipts_base {
|
|
8
8
|
static styles: import("lit").CSSResult[];
|
|
9
9
|
announcement: AnnouncementItem;
|
|
10
|
-
private rows;
|
|
11
10
|
private which;
|
|
12
|
-
private
|
|
13
|
-
|
|
14
|
-
private
|
|
11
|
+
private receipts;
|
|
12
|
+
private reminder;
|
|
13
|
+
private get rows();
|
|
14
|
+
private get busy();
|
|
15
15
|
render(): import("lit-html").TemplateResult<1>;
|
|
16
16
|
private remind;
|
|
17
17
|
private close;
|
|
@@ -22,7 +22,8 @@ export declare class AnnouncementWithdrawForm extends AnnouncementWithdrawForm_b
|
|
|
22
22
|
static styles: import("lit").CSSResult[];
|
|
23
23
|
announcement: AnnouncementItem;
|
|
24
24
|
private reason;
|
|
25
|
-
private
|
|
25
|
+
private withdrawal;
|
|
26
|
+
private get busy();
|
|
26
27
|
render(): import("lit-html").TemplateResult<1>;
|
|
27
28
|
private withdraw;
|
|
28
29
|
private close;
|
|
@@ -6,13 +6,14 @@ import { __decorate, __metadata } from "tslib";
|
|
|
6
6
|
import '@material/web/button/filled-button.js';
|
|
7
7
|
import '@material/web/button/text-button.js';
|
|
8
8
|
import '@operato/popup/ox-popup-actions.js';
|
|
9
|
+
import gql from 'graphql-tag';
|
|
9
10
|
import { css, html, LitElement, nothing } from 'lit';
|
|
10
11
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
12
|
+
import { MutationController, QueryController } from '@operato/graphql';
|
|
11
13
|
import { i18next, localize } from '@operato/i18n';
|
|
12
14
|
import { notify } from '@operato/layout';
|
|
13
15
|
import { CommonPopupStyles, PopupBodyStyles } from '@operato/styles';
|
|
14
|
-
import {
|
|
15
|
-
import { reasonOf, t, when } from '../utils/announcement-words.js';
|
|
16
|
+
import { t, when } from '../utils/announcement-words.js';
|
|
16
17
|
const FormStyles = css `
|
|
17
18
|
:host {
|
|
18
19
|
height: 100%;
|
|
@@ -67,22 +68,19 @@ const FormStyles = css `
|
|
|
67
68
|
let AnnouncementReceipts = class AnnouncementReceipts extends localize(i18next)(LitElement) {
|
|
68
69
|
constructor() {
|
|
69
70
|
super(...arguments);
|
|
70
|
-
this.rows = [];
|
|
71
71
|
this.which = 'all';
|
|
72
|
-
|
|
72
|
+
/* Whom it reached and what each did with it, read for the announcement shown (ADR-0190). */
|
|
73
|
+
this.receipts = new QueryController(this, gql `query ($id: String!) { announcementReceipts(id: $id) { userId name email listed notifiedAt readAt acknowledgedAt } }`, { variables: () => (this.announcement?.id ? { id: this.announcement.id } : undefined) });
|
|
74
|
+
this.reminder = new MutationController(this, gql `mutation ($id: String!) { remindAnnouncement(id: $id) }`, {
|
|
75
|
+
refetch: () => [this.receipts]
|
|
76
|
+
});
|
|
73
77
|
}
|
|
74
78
|
static { this.styles = [CommonPopupStyles, PopupBodyStyles, FormStyles]; }
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
this.load();
|
|
79
|
+
get rows() {
|
|
80
|
+
return this.receipts.data?.announcementReceipts ?? [];
|
|
78
81
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
this.rows = await announcementApi.receipts(this.announcement.id);
|
|
82
|
-
}
|
|
83
|
-
catch (error) {
|
|
84
|
-
notify({ level: 'error', message: reasonOf(error) });
|
|
85
|
-
}
|
|
82
|
+
get busy() {
|
|
83
|
+
return this.reminder.pending;
|
|
86
84
|
}
|
|
87
85
|
render() {
|
|
88
86
|
const one = this.announcement;
|
|
@@ -127,18 +125,9 @@ let AnnouncementReceipts = class AnnouncementReceipts extends localize(i18next)(
|
|
|
127
125
|
</ox-popup-actions>`;
|
|
128
126
|
}
|
|
129
127
|
async remind() {
|
|
130
|
-
this.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
notify({ message: t('announcement.text.reminded', { count: told }) });
|
|
134
|
-
await this.load();
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
notify({ level: 'error', message: reasonOf(error) });
|
|
138
|
-
}
|
|
139
|
-
finally {
|
|
140
|
-
this.busy = false;
|
|
141
|
-
}
|
|
128
|
+
const done = await this.reminder.run({ id: this.announcement.id });
|
|
129
|
+
if (done)
|
|
130
|
+
notify({ message: t('announcement.text.reminded', { count: done.remindAnnouncement }) });
|
|
142
131
|
}
|
|
143
132
|
close() {
|
|
144
133
|
this.dispatchEvent(new CustomEvent('done', { bubbles: true, composed: true }));
|
|
@@ -148,18 +137,10 @@ __decorate([
|
|
|
148
137
|
property({ attribute: false }),
|
|
149
138
|
__metadata("design:type", Object)
|
|
150
139
|
], AnnouncementReceipts.prototype, "announcement", void 0);
|
|
151
|
-
__decorate([
|
|
152
|
-
state(),
|
|
153
|
-
__metadata("design:type", Array)
|
|
154
|
-
], AnnouncementReceipts.prototype, "rows", void 0);
|
|
155
140
|
__decorate([
|
|
156
141
|
state(),
|
|
157
142
|
__metadata("design:type", String)
|
|
158
143
|
], AnnouncementReceipts.prototype, "which", void 0);
|
|
159
|
-
__decorate([
|
|
160
|
-
state(),
|
|
161
|
-
__metadata("design:type", Object)
|
|
162
|
-
], AnnouncementReceipts.prototype, "busy", void 0);
|
|
163
144
|
AnnouncementReceipts = __decorate([
|
|
164
145
|
customElement('announcement-receipts')
|
|
165
146
|
], AnnouncementReceipts);
|
|
@@ -169,9 +150,12 @@ let AnnouncementWithdrawForm = class AnnouncementWithdrawForm extends localize(i
|
|
|
169
150
|
constructor() {
|
|
170
151
|
super(...arguments);
|
|
171
152
|
this.reason = '';
|
|
172
|
-
this.
|
|
153
|
+
this.withdrawal = new MutationController(this, gql `mutation ($id: String!, $reason: String!) { withdrawAnnouncement(id: $id, reason: $reason) { id status } }`);
|
|
173
154
|
}
|
|
174
155
|
static { this.styles = [CommonPopupStyles, PopupBodyStyles, FormStyles]; }
|
|
156
|
+
get busy() {
|
|
157
|
+
return this.withdrawal.pending;
|
|
158
|
+
}
|
|
175
159
|
render() {
|
|
176
160
|
return html `<div class="body">
|
|
177
161
|
<p>${t('announcement.text.withdraw-lead')}</p>
|
|
@@ -183,19 +167,11 @@ let AnnouncementWithdrawForm = class AnnouncementWithdrawForm extends localize(i
|
|
|
183
167
|
</ox-popup-actions>`;
|
|
184
168
|
}
|
|
185
169
|
async withdraw() {
|
|
186
|
-
this.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
this.close();
|
|
192
|
-
}
|
|
193
|
-
catch (error) {
|
|
194
|
-
notify({ level: 'error', message: reasonOf(error) });
|
|
195
|
-
}
|
|
196
|
-
finally {
|
|
197
|
-
this.busy = false;
|
|
198
|
-
}
|
|
170
|
+
if (!(await this.withdrawal.run({ id: this.announcement.id, reason: this.reason })))
|
|
171
|
+
return;
|
|
172
|
+
notify({ message: t('announcement.text.withdrawn-done') });
|
|
173
|
+
this.dispatchEvent(new CustomEvent('announcement-withdrawn', { bubbles: true, composed: true }));
|
|
174
|
+
this.close();
|
|
199
175
|
}
|
|
200
176
|
close() {
|
|
201
177
|
this.dispatchEvent(new CustomEvent('done', { bubbles: true, composed: true }));
|
|
@@ -209,10 +185,6 @@ __decorate([
|
|
|
209
185
|
state(),
|
|
210
186
|
__metadata("design:type", Object)
|
|
211
187
|
], AnnouncementWithdrawForm.prototype, "reason", void 0);
|
|
212
|
-
__decorate([
|
|
213
|
-
state(),
|
|
214
|
-
__metadata("design:type", Object)
|
|
215
|
-
], AnnouncementWithdrawForm.prototype, "busy", void 0);
|
|
216
188
|
AnnouncementWithdrawForm = __decorate([
|
|
217
189
|
customElement('announcement-withdraw-form')
|
|
218
190
|
], AnnouncementWithdrawForm);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"announcement-receipts.js","sourceRoot":"","sources":["../../client/components/announcement-receipts.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The small forms a writer opens from an announcement (ADR-0163 decision 4): who read it and who acknowledged it, with\n * a reminder to those who have not; and the reason it is withdrawn.\n */\nimport '@material/web/button/filled-button.js'\nimport '@material/web/button/text-button.js'\nimport '@operato/popup/ox-popup-actions.js'\n\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { CommonPopupStyles, PopupBodyStyles } from '@operato/styles'\n\nimport { announcementApi, type AnnouncementItem } from '../utils/announcement-api.js'\nimport { reasonOf, t, when } from '../utils/announcement-words.js'\n\ntype Row = Awaited<ReturnType<typeof announcementApi.receipts>>[number]\n\nconst FormStyles = css`\n :host {\n height: 100%;\n line-height: 1.5;\n }\n .tabs {\n display: flex;\n gap: var(--spacing-small, 4px);\n margin-bottom: var(--spacing-medium, 8px);\n }\n .tabs button {\n border: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));\n background: none;\n border-radius: 999px;\n padding: 2px var(--spacing-medium, 8px);\n font: inherit;\n font-size: var(--ui-t-small, 12px);\n cursor: pointer;\n color: inherit;\n }\n .tabs button[aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n border-color: transparent;\n }\n table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--ui-t-body, 14px);\n font-variant-numeric: tabular-nums;\n }\n th,\n td {\n text-align: left;\n padding: var(--spacing-small, 4px) var(--spacing-medium, 8px);\n border-bottom: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));\n }\n th {\n font-weight: 600;\n font-size: var(--ui-t-small, 12px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n .muted {\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n textarea {\n width: 100%;\n min-height: 96px;\n box-sizing: border-box;\n font: inherit;\n }\n`\n\n@customElement('announcement-receipts')\nexport class AnnouncementReceipts extends localize(i18next)(LitElement) {\n static styles = [CommonPopupStyles, PopupBodyStyles, FormStyles]\n @property({ attribute: false }) announcement!: AnnouncementItem\n @state() private rows: Row[] = []\n @state() private which: 'all' | 'unread' | 'unacknowledged' | 'later' = 'all'\n @state() private busy = false\n\n connectedCallback() {\n super.connectedCallback()\n this.load()\n }\n\n private async load() {\n try {\n this.rows = await announcementApi.receipts(this.announcement.id)\n } catch (error) {\n notify({ level: 'error', message: reasonOf(error) })\n }\n }\n\n render() {\n const one = this.announcement\n const listed = this.rows.filter(row => row.listed)\n const counts = {\n all: listed.length,\n unread: listed.filter(row => !row.readAt).length,\n unacknowledged: listed.filter(row => !row.acknowledgedAt).length,\n later: this.rows.filter(row => !row.listed).length\n }\n const tabs = (['all', 'unread', ...(one.requiresAcknowledgement ? ['unacknowledged'] : []), ...(counts.later ? ['later'] : [])] as (keyof typeof counts)[]).map(\n key => html`<button aria-pressed=${String(this.which === key)} @click=${() => (this.which = key)}>${t(`announcement.receipts.${key}`)} ${counts[key]}</button>`\n )\n const shown =\n this.which === 'later'\n ? this.rows.filter(row => !row.listed)\n : listed.filter(row => (this.which === 'unread' ? !row.readAt : this.which === 'unacknowledged' ? !row.acknowledgedAt : true))\n const owed = one.requiresAcknowledgement ? counts.unacknowledged : counts.unread\n return html`<div class=\"body\">\n <div class=\"tabs\">${tabs}</div>\n <table>\n <thead>\n <tr>\n <th>${t('announcement.receipts.name')}</th>\n <th>${t('announcement.receipts.read-at')}</th>\n ${one.requiresAcknowledgement ? html`<th>${t('announcement.receipts.acknowledged-at')}</th>` : nothing}\n <th>${t('announcement.receipts.notified-at')}</th>\n </tr>\n </thead>\n <tbody>\n ${shown.map(\n row => html`<tr>\n <td>${row.name ?? row.email ?? row.userId}</td>\n <td class=${row.readAt ? '' : 'muted'}>${row.readAt ? when(row.readAt) : t('announcement.reader-state.unread')}</td>\n ${one.requiresAcknowledgement ? html`<td class=${row.acknowledgedAt ? '' : 'muted'}>${row.acknowledgedAt ? when(row.acknowledgedAt) : '—'}</td>` : nothing}\n <td class=\"muted\">${when(row.notifiedAt)}</td>\n </tr>`\n )}\n </tbody>\n </table>\n </div>\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => this.close()}>${t('announcement.button.close')}</md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${this.busy || !owed || one.status !== 'PUBLISHED'} @click=${() => this.remind()}\n >${t('announcement.button.remind-count', { count: owed })}</md-filled-button\n >\n </ox-popup-actions>`\n }\n\n private async remind() {\n this.busy = true\n try {\n const told = await announcementApi.remind(this.announcement.id)\n notify({ message: t('announcement.text.reminded', { count: told }) })\n await this.load()\n } catch (error) {\n notify({ level: 'error', message: reasonOf(error) })\n } finally {\n this.busy = false\n }\n }\n\n private close() {\n this.dispatchEvent(new CustomEvent('done', { bubbles: true, composed: true }))\n }\n}\n\n/** Why it is withdrawn - the readers' copy goes, the reason stays with its record. */\n@customElement('announcement-withdraw-form')\nexport class AnnouncementWithdrawForm extends localize(i18next)(LitElement) {\n static styles = [CommonPopupStyles, PopupBodyStyles, FormStyles]\n @property({ attribute: false }) announcement!: AnnouncementItem\n @state() private reason = ''\n @state() private busy = false\n\n render() {\n return html`<div class=\"body\">\n <p>${t('announcement.text.withdraw-lead')}</p>\n <textarea .value=${this.reason} maxlength=\"500\" placeholder=${t('announcement.text.withdraw-reason')} @input=${(e: any) => (this.reason = e.target.value)}></textarea>\n </div>\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => this.close()}>${t('announcement.button.cancel')}</md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${this.busy || !this.reason.trim()} @click=${() => this.withdraw()}>${t('announcement.button.withdraw')}</md-filled-button>\n </ox-popup-actions>`\n }\n\n private async withdraw() {\n this.busy = true\n try {\n await announcementApi.withdraw(this.announcement.id, this.reason)\n notify({ message: t('announcement.text.withdrawn-done') })\n this.dispatchEvent(new CustomEvent('announcement-withdrawn', { bubbles: true, composed: true }))\n this.close()\n } catch (error) {\n notify({ level: 'error', message: reasonOf(error) })\n } finally {\n this.busy = false\n }\n }\n\n private close() {\n this.dispatchEvent(new CustomEvent('done', { bubbles: true, composed: true }))\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"announcement-receipts.js","sourceRoot":"","sources":["../../client/components/announcement-receipts.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The small forms a writer opens from an announcement (ADR-0163 decision 4): who read it and who acknowledged it, with\n * a reminder to those who have not; and the reason it is withdrawn.\n */\nimport '@material/web/button/filled-button.js'\nimport '@material/web/button/text-button.js'\nimport '@operato/popup/ox-popup-actions.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { MutationController, QueryController } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { CommonPopupStyles, PopupBodyStyles } from '@operato/styles'\n\nimport { type AnnouncementItem } from '../utils/announcement-api.js'\nimport { t, when } from '../utils/announcement-words.js'\n\ntype Row = { userId: string; name?: string; email?: string; listed: boolean; notifiedAt?: string; readAt?: string; acknowledgedAt?: string }\n\nconst FormStyles = css`\n :host {\n height: 100%;\n line-height: 1.5;\n }\n .tabs {\n display: flex;\n gap: var(--spacing-small, 4px);\n margin-bottom: var(--spacing-medium, 8px);\n }\n .tabs button {\n border: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));\n background: none;\n border-radius: 999px;\n padding: 2px var(--spacing-medium, 8px);\n font: inherit;\n font-size: var(--ui-t-small, 12px);\n cursor: pointer;\n color: inherit;\n }\n .tabs button[aria-pressed='true'] {\n background: var(--md-sys-color-secondary-container);\n border-color: transparent;\n }\n table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--ui-t-body, 14px);\n font-variant-numeric: tabular-nums;\n }\n th,\n td {\n text-align: left;\n padding: var(--spacing-small, 4px) var(--spacing-medium, 8px);\n border-bottom: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));\n }\n th {\n font-weight: 600;\n font-size: var(--ui-t-small, 12px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n .muted {\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n textarea {\n width: 100%;\n min-height: 96px;\n box-sizing: border-box;\n font: inherit;\n }\n`\n\n@customElement('announcement-receipts')\nexport class AnnouncementReceipts extends localize(i18next)(LitElement) {\n static styles = [CommonPopupStyles, PopupBodyStyles, FormStyles]\n @property({ attribute: false }) announcement!: AnnouncementItem\n @state() private which: 'all' | 'unread' | 'unacknowledged' | 'later' = 'all'\n\n /* Whom it reached and what each did with it, read for the announcement shown (ADR-0190). */\n private receipts = new QueryController(\n this,\n gql`query ($id: String!) { announcementReceipts(id: $id) { userId name email listed notifiedAt readAt acknowledgedAt } }`,\n { variables: () => (this.announcement?.id ? { id: this.announcement.id } : undefined) }\n )\n\n private reminder = new MutationController(this, gql`mutation ($id: String!) { remindAnnouncement(id: $id) }`, {\n refetch: () => [this.receipts]\n })\n\n private get rows(): Row[] {\n return this.receipts.data?.announcementReceipts ?? []\n }\n\n private get busy() {\n return this.reminder.pending\n }\n\n render() {\n const one = this.announcement\n const listed = this.rows.filter(row => row.listed)\n const counts = {\n all: listed.length,\n unread: listed.filter(row => !row.readAt).length,\n unacknowledged: listed.filter(row => !row.acknowledgedAt).length,\n later: this.rows.filter(row => !row.listed).length\n }\n const tabs = (['all', 'unread', ...(one.requiresAcknowledgement ? ['unacknowledged'] : []), ...(counts.later ? ['later'] : [])] as (keyof typeof counts)[]).map(\n key => html`<button aria-pressed=${String(this.which === key)} @click=${() => (this.which = key)}>${t(`announcement.receipts.${key}`)} ${counts[key]}</button>`\n )\n const shown =\n this.which === 'later'\n ? this.rows.filter(row => !row.listed)\n : listed.filter(row => (this.which === 'unread' ? !row.readAt : this.which === 'unacknowledged' ? !row.acknowledgedAt : true))\n const owed = one.requiresAcknowledgement ? counts.unacknowledged : counts.unread\n return html`<div class=\"body\">\n <div class=\"tabs\">${tabs}</div>\n <table>\n <thead>\n <tr>\n <th>${t('announcement.receipts.name')}</th>\n <th>${t('announcement.receipts.read-at')}</th>\n ${one.requiresAcknowledgement ? html`<th>${t('announcement.receipts.acknowledged-at')}</th>` : nothing}\n <th>${t('announcement.receipts.notified-at')}</th>\n </tr>\n </thead>\n <tbody>\n ${shown.map(\n row => html`<tr>\n <td>${row.name ?? row.email ?? row.userId}</td>\n <td class=${row.readAt ? '' : 'muted'}>${row.readAt ? when(row.readAt) : t('announcement.reader-state.unread')}</td>\n ${one.requiresAcknowledgement ? html`<td class=${row.acknowledgedAt ? '' : 'muted'}>${row.acknowledgedAt ? when(row.acknowledgedAt) : '—'}</td>` : nothing}\n <td class=\"muted\">${when(row.notifiedAt)}</td>\n </tr>`\n )}\n </tbody>\n </table>\n </div>\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => this.close()}>${t('announcement.button.close')}</md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${this.busy || !owed || one.status !== 'PUBLISHED'} @click=${() => this.remind()}\n >${t('announcement.button.remind-count', { count: owed })}</md-filled-button\n >\n </ox-popup-actions>`\n }\n\n private async remind() {\n const done = await this.reminder.run({ id: this.announcement.id })\n if (done) notify({ message: t('announcement.text.reminded', { count: done.remindAnnouncement }) })\n }\n\n private close() {\n this.dispatchEvent(new CustomEvent('done', { bubbles: true, composed: true }))\n }\n}\n\n/** Why it is withdrawn - the readers' copy goes, the reason stays with its record. */\n@customElement('announcement-withdraw-form')\nexport class AnnouncementWithdrawForm extends localize(i18next)(LitElement) {\n static styles = [CommonPopupStyles, PopupBodyStyles, FormStyles]\n @property({ attribute: false }) announcement!: AnnouncementItem\n @state() private reason = ''\n private withdrawal = new MutationController(this, gql`mutation ($id: String!, $reason: String!) { withdrawAnnouncement(id: $id, reason: $reason) { id status } }`)\n\n private get busy() {\n return this.withdrawal.pending\n }\n\n render() {\n return html`<div class=\"body\">\n <p>${t('announcement.text.withdraw-lead')}</p>\n <textarea .value=${this.reason} maxlength=\"500\" placeholder=${t('announcement.text.withdraw-reason')} @input=${(e: any) => (this.reason = e.target.value)}></textarea>\n </div>\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => this.close()}>${t('announcement.button.cancel')}</md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${this.busy || !this.reason.trim()} @click=${() => this.withdraw()}>${t('announcement.button.withdraw')}</md-filled-button>\n </ox-popup-actions>`\n }\n\n private async withdraw() {\n if (!(await this.withdrawal.run({ id: this.announcement.id, reason: this.reason }))) return\n notify({ message: t('announcement.text.withdrawn-done') })\n this.dispatchEvent(new CustomEvent('announcement-withdrawn', { bubbles: true, composed: true }))\n this.close()\n }\n\n private close() {\n this.dispatchEvent(new CustomEvent('done', { bubbles: true, composed: true }))\n }\n}\n"]}
|
|
@@ -8,7 +8,8 @@ declare const AnnouncementSheet_base: (new (...args: any[]) => LitElement) & typ
|
|
|
8
8
|
export declare class AnnouncementSheet extends AnnouncementSheet_base {
|
|
9
9
|
static styles: import("lit").CSSResult;
|
|
10
10
|
announcement: AnnouncementItem;
|
|
11
|
-
private
|
|
11
|
+
private acknowledgement;
|
|
12
|
+
private get busy();
|
|
12
13
|
render(): typeof nothing | import("lit-html").TemplateResult<1>;
|
|
13
14
|
private renderAcknowledge;
|
|
14
15
|
private follow;
|
|
@@ -5,15 +5,16 @@ import { __decorate, __metadata } from "tslib";
|
|
|
5
5
|
*/
|
|
6
6
|
import '@material/web/icon/icon.js';
|
|
7
7
|
import '@material/web/button/filled-button.js';
|
|
8
|
+
import gql from 'graphql-tag';
|
|
8
9
|
import { css, html, LitElement, nothing } from 'lit';
|
|
9
|
-
import { customElement, property
|
|
10
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
10
11
|
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
|
12
|
+
import { MutationController } from '@operato/graphql';
|
|
11
13
|
import { i18next, localize } from '@operato/i18n';
|
|
12
|
-
import { notify } from '@operato/layout';
|
|
13
14
|
import { navigate } from '@operato/shell';
|
|
14
15
|
import { renderMarkdown } from '@things-factory/ai-assistant/dist-client/index.js';
|
|
15
|
-
import {
|
|
16
|
-
import { categoryWord, importanceWord,
|
|
16
|
+
import { announcementsChanged } from '../utils/announcement-api.js';
|
|
17
|
+
import { categoryWord, importanceWord, t, when } from '../utils/announcement-words.js';
|
|
17
18
|
/** A screen of the application opens in place; an https page opens apart. */
|
|
18
19
|
export function followLink(url) {
|
|
19
20
|
if (/^https:\/\//i.test(url))
|
|
@@ -24,7 +25,7 @@ export function followLink(url) {
|
|
|
24
25
|
let AnnouncementSheet = class AnnouncementSheet extends localize(i18next)(LitElement) {
|
|
25
26
|
constructor() {
|
|
26
27
|
super(...arguments);
|
|
27
|
-
this.
|
|
28
|
+
this.acknowledgement = new MutationController(this, gql `mutation ($id: String!) { acknowledgeAnnouncement(id: $id) { id readerState mine { readAt acknowledgedAt } } }`);
|
|
28
29
|
}
|
|
29
30
|
static { this.styles = css `
|
|
30
31
|
:host {
|
|
@@ -142,6 +143,9 @@ let AnnouncementSheet = class AnnouncementSheet extends localize(i18next)(LitEle
|
|
|
142
143
|
color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
|
|
143
144
|
}
|
|
144
145
|
`; }
|
|
146
|
+
get busy() {
|
|
147
|
+
return this.acknowledgement.pending;
|
|
148
|
+
}
|
|
145
149
|
render() {
|
|
146
150
|
const one = this.announcement;
|
|
147
151
|
if (!one)
|
|
@@ -195,28 +199,18 @@ let AnnouncementSheet = class AnnouncementSheet extends localize(i18next)(LitEle
|
|
|
195
199
|
followLink(url);
|
|
196
200
|
}
|
|
197
201
|
async acknowledge() {
|
|
198
|
-
this.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
catch (error) {
|
|
205
|
-
notify({ level: 'error', message: reasonOf(error) });
|
|
206
|
-
}
|
|
207
|
-
finally {
|
|
208
|
-
this.busy = false;
|
|
209
|
-
}
|
|
202
|
+
const done = await this.acknowledgement.run({ id: this.announcement.id });
|
|
203
|
+
if (!done)
|
|
204
|
+
return;
|
|
205
|
+
announcementsChanged();
|
|
206
|
+
this.announcement = { ...this.announcement, ...done.acknowledgeAnnouncement };
|
|
207
|
+
this.dispatchEvent(new CustomEvent('announcement-acknowledged', { bubbles: true, composed: true, detail: this.announcement }));
|
|
210
208
|
}
|
|
211
209
|
};
|
|
212
210
|
__decorate([
|
|
213
211
|
property({ attribute: false }),
|
|
214
212
|
__metadata("design:type", Object)
|
|
215
213
|
], AnnouncementSheet.prototype, "announcement", void 0);
|
|
216
|
-
__decorate([
|
|
217
|
-
state(),
|
|
218
|
-
__metadata("design:type", Object)
|
|
219
|
-
], AnnouncementSheet.prototype, "busy", void 0);
|
|
220
214
|
AnnouncementSheet = __decorate([
|
|
221
215
|
customElement('announcement-sheet')
|
|
222
216
|
], AnnouncementSheet);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"announcement-sheet.js","sourceRoot":"","sources":["../../client/components/announcement-sheet.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * One announcement as its reader sees it - what kind it is and from whom, its body, its files, the screens it points to,\n * and the acknowledgement it asks for (ADR-0163 decisions 3, 5). The screen and the popup both draw it.\n */\nimport '@material/web/icon/icon.js'\nimport '@material/web/button/filled-button.js'\n\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { navigate } from '@operato/shell'\nimport { renderMarkdown } from '@things-factory/ai-assistant/dist-client/index.js'\n\nimport { announcementApi, type AnnouncementItem } from '../utils/announcement-api.js'\nimport { categoryWord, importanceWord, reasonOf, t, when } from '../utils/announcement-words.js'\n\n/** A screen of the application opens in place; an https page opens apart. */\nexport function followLink(url: string) {\n if (/^https:\\/\\//i.test(url)) window.open(url, '_blank', 'noopener')\n else navigate(url)\n}\n\n@customElement('announcement-sheet')\nexport class AnnouncementSheet extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium, 8px);\n color: var(--md-sys-color-on-surface);\n }\n .meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--spacing-small, 4px) var(--spacing-medium, 8px);\n font-size: var(--ui-t-small, 12px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n .chip {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 0 var(--spacing-small, 4px);\n border-radius: var(--border-radius, 4px);\n border: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));\n line-height: 1.6;\n }\n .chip.URGENT {\n color: var(--md-sys-color-error);\n border-color: currentColor;\n }\n .chip.IMPORTANT {\n color: var(--md-sys-color-primary);\n border-color: currentColor;\n }\n .chip md-icon {\n --md-icon-size: 14px;\n }\n .markdown {\n font-size: var(--ui-t-body, 14px);\n line-height: 1.6;\n overflow-wrap: anywhere;\n }\n .markdown :is(h1, h2, h3, h4) {\n margin: var(--spacing-medium, 8px) 0 var(--spacing-small, 4px);\n font-size: var(--ui-t-title, 16px);\n }\n .markdown :is(p, ul, ol, blockquote, pre, table) {\n margin: 0 0 var(--spacing-medium, 8px);\n }\n .markdown :is(ul, ol) {\n padding-inline-start: 1.4em;\n list-style: revert;\n display: block;\n }\n .markdown a {\n color: var(--md-sys-color-primary);\n }\n .markdown table {\n border-collapse: collapse;\n }\n .markdown :is(th, td) {\n border: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));\n padding: 2px var(--spacing-small, 4px);\n }\n h4 {\n margin: var(--spacing-medium, 8px) 0 var(--spacing-small, 4px);\n font-size: var(--ui-t-small, 12px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n font-weight: 600;\n }\n ul.files {\n margin: 0;\n padding: 0;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n a,\n .link {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-small, 4px);\n color: var(--md-sys-color-primary);\n text-decoration: none;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 0;\n font: inherit;\n }\n a:hover,\n .link:hover {\n text-decoration: underline;\n }\n a md-icon,\n .link md-icon {\n --md-icon-size: 16px;\n }\n .size {\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n font-size: var(--ui-t-small, 12px);\n }\n .acknowledge {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border-radius: var(--border-radius, 4px);\n background: var(--md-sys-color-surface-container, rgba(0, 0, 0, 0.04));\n font-size: var(--ui-t-body, 14px);\n }\n .acknowledge.done {\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n `\n\n @property({ attribute: false }) announcement!: AnnouncementItem\n @state() private busy = false\n\n render() {\n const one = this.announcement\n if (!one) return nothing\n const files = one.attachments ?? []\n const links = one.links ?? []\n return html`<div class=\"meta\">\n ${one.pinned ? html`<span class=\"chip\"><md-icon>push_pin</md-icon>${t('announcement.field.pinned')}</span>` : nothing}\n ${one.importance !== 'NORMAL' ? html`<span class=\"chip ${one.importance}\">${importanceWord(one.importance)}</span>` : nothing}\n <span class=\"chip\">${categoryWord(one.category)}</span>\n <span>${one.publisher?.name ?? one.creator?.name ?? ''}</span>\n <span>${when(one.publishedAt ?? one.startsAt)}</span>\n ${one.endsAt ? html`<span>${t('announcement.text.until', { when: when(one.endsAt) })}</span>` : nothing}\n ${one.version > 1 ? html`<span>${t('announcement.text.version', { version: one.version })}</span>` : nothing}\n </div>\n ${one.body ? html`<div class=\"markdown\">${unsafeHTML(renderMarkdown(one.body))}</div>` : nothing}\n ${files.length\n ? html`<h4>${t('announcement.field.attachments')}</h4>\n <ul class=\"files\">\n ${files.map(\n file =>\n html`<li>\n <a href=${file.fullpath} target=\"_blank\" rel=\"noopener\" download=${file.name}><md-icon>attach_file</md-icon>${file.name}</a>\n ${file.size ? html`<span class=\"size\">${Math.max(1, Math.round(file.size / 1024))} KB</span>` : nothing}\n </li>`\n )}\n </ul>`\n : nothing}\n ${links.length\n ? html`<h4>${t('announcement.field.links')}</h4>\n <ul class=\"files\">\n ${links.map(link => html`<li><button class=\"link\" @click=${() => this.follow(link.url)}><md-icon>open_in_new</md-icon>${link.label}</button></li>`)}\n </ul>`\n : nothing}\n ${this.renderAcknowledge()}`\n }\n\n private renderAcknowledge() {\n const one = this.announcement\n if (!one.requiresAcknowledgement || one.status !== 'PUBLISHED') return nothing\n const by = one.acknowledgeBy ? t('announcement.text.acknowledge-by', { when: when(one.acknowledgeBy, false) }) : ''\n if (one.mine?.acknowledgedAt) return html`<div class=\"acknowledge done\">${t('announcement.text.acknowledged-at', { when: when(one.mine.acknowledgedAt) })}</div>`\n /* Only its audience acknowledges; a writer looking at one addressed to others sees what is asked. */\n if (!one.addressed) return html`<div class=\"acknowledge\">${t('announcement.text.acknowledge-asked')} ${by}</div>`\n return html`<div class=\"acknowledge\">\n <span>${t('announcement.text.acknowledge-asked')} ${by}</span>\n <md-filled-button ?disabled=${this.busy} @click=${() => this.acknowledge()}>${t('announcement.button.acknowledge')}</md-filled-button>\n </div>`\n }\n\n private follow(url: string) {\n this.dispatchEvent(new CustomEvent('announcement-follow', { bubbles: true, composed: true }))\n followLink(url)\n }\n\n private async acknowledge() {\n this.busy = true\n try {\n const done = await announcementApi.acknowledge(this.announcement.id)\n this.announcement = { ...this.announcement, ...done }\n this.dispatchEvent(new CustomEvent('announcement-acknowledged', { bubbles: true, composed: true, detail: this.announcement }))\n } catch (error) {\n notify({ level: 'error', message: reasonOf(error) })\n } finally {\n this.busy = false\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"announcement-sheet.js","sourceRoot":"","sources":["../../client/components/announcement-sheet.ts"],"names":[],"mappings":";AAAA;;;GAGG;AACH,OAAO,4BAA4B,CAAA;AACnC,OAAO,uCAAuC,CAAA;AAE9C,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,mDAAmD,CAAA;AAElF,OAAO,EAAE,oBAAoB,EAAyB,MAAM,8BAA8B,CAAA;AAC1F,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,gCAAgC,CAAA;AAEtF,6EAA6E;AAC7E,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;;QAC/D,QAAQ,CAAC,GAAG,CAAC,CAAA;AACpB,CAAC;AAGM,IAAM,iBAAiB,GAAvB,MAAM,iBAAkB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;IAA7D;;QAuHG,oBAAe,GAAG,IAAI,kBAAkB,CAC9C,IAAI,EACJ,GAAG,CAAA,gHAAgH,CACpH,CAAA;IAmEH,CAAC;aA5LQ,WAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmHlB,AAnHY,CAmHZ;IAQD,IAAY,IAAI;QACd,OAAO,IAAI,CAAC,eAAe,CAAC,OAAO,CAAA;IACrC,CAAC;IAED,MAAM;QACJ,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAA;QAC7B,IAAI,CAAC,GAAG;YAAE,OAAO,OAAO,CAAA;QACxB,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,IAAI,EAAE,CAAA;QACnC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,EAAE,CAAA;QAC7B,OAAO,IAAI,CAAA;UACL,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA,iDAAiD,CAAC,CAAC,2BAA2B,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;UACnH,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAA,qBAAqB,GAAG,CAAC,UAAU,KAAK,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;6BACxG,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC;gBACvC,GAAG,CAAC,SAAS,EAAE,IAAI,IAAI,GAAG,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE;gBAC9C,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,QAAQ,CAAC;UAC3C,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA,SAAS,CAAC,CAAC,yBAAyB,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;UACrG,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA,SAAS,CAAC,CAAC,2BAA2B,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;;QAE5G,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA,yBAAyB,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO;QAC9F,KAAK,CAAC,MAAM;YACZ,CAAC,CAAC,IAAI,CAAA,OAAO,CAAC,CAAC,gCAAgC,CAAC;;gBAExC,KAAK,CAAC,GAAG,CACT,IAAI,CAAC,EAAE,CACL,IAAI,CAAA;8BACQ,IAAI,CAAC,QAAQ,4CAA4C,IAAI,CAAC,IAAI,kCAAkC,IAAI,CAAC,IAAI;sBACrH,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA,sBAAsB,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO;wBACnG,CACT;kBACG;YACV,CAAC,CAAC,OAAO;QACT,KAAK,CAAC,MAAM;YACZ,CAAC,CAAC,IAAI,CAAA,OAAO,CAAC,CAAC,0BAA0B,CAAC;;gBAElC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAA,mCAAmC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,kCAAkC,IAAI,CAAC,KAAK,gBAAgB,CAAC;kBAC/I;YACV,CAAC,CAAC,OAAO;QACT,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAA;IAChC,CAAC;IAEO,iBAAiB;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAA;QAC7B,IAAI,CAAC,GAAG,CAAC,uBAAuB,IAAI,GAAG,CAAC,MAAM,KAAK,WAAW;YAAE,OAAO,OAAO,CAAA;QAC9E,MAAM,EAAE,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,kCAAkC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QACnH,IAAI,GAAG,CAAC,IAAI,EAAE,cAAc;YAAE,OAAO,IAAI,CAAA,iCAAiC,CAAC,CAAC,mCAAmC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,QAAQ,CAAA;QACjK,qGAAqG;QACrG,IAAI,CAAC,GAAG,CAAC,SAAS;YAAE,OAAO,IAAI,CAAA,4BAA4B,CAAC,CAAC,qCAAqC,CAAC,IAAI,EAAE,QAAQ,CAAA;QACjH,OAAO,IAAI,CAAA;cACD,CAAC,CAAC,qCAAqC,CAAC,IAAI,EAAE;oCACxB,IAAI,CAAC,IAAI,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,iCAAiC,CAAC;WAC7G,CAAA;IACT,CAAC;IAEO,MAAM,CAAC,GAAW;QACxB,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,qBAAqB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;QAC7F,UAAU,CAAC,GAAG,CAAC,CAAA;IACjB,CAAC;IAEO,KAAK,CAAC,WAAW;QACvB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAA;QACzE,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,oBAAoB,EAAE,CAAA;QACtB,IAAI,CAAC,YAAY,GAAG,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAA;QAC7E,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,2BAA2B,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAA;IAChI,CAAC;;AAtE+B;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;uDAAgC;AAtHpD,iBAAiB;IAD7B,aAAa,CAAC,oBAAoB,CAAC;GACvB,iBAAiB,CA6L7B","sourcesContent":["/*\n * One announcement as its reader sees it - what kind it is and from whom, its body, its files, the screens it points to,\n * and the acknowledgement it asks for (ADR-0163 decisions 3, 5). The screen and the popup both draw it.\n */\nimport '@material/web/icon/icon.js'\nimport '@material/web/button/filled-button.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js'\n\nimport { MutationController } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { navigate } from '@operato/shell'\nimport { renderMarkdown } from '@things-factory/ai-assistant/dist-client/index.js'\n\nimport { announcementsChanged, type AnnouncementItem } from '../utils/announcement-api.js'\nimport { categoryWord, importanceWord, t, when } from '../utils/announcement-words.js'\n\n/** A screen of the application opens in place; an https page opens apart. */\nexport function followLink(url: string) {\n if (/^https:\\/\\//i.test(url)) window.open(url, '_blank', 'noopener')\n else navigate(url)\n}\n\n@customElement('announcement-sheet')\nexport class AnnouncementSheet extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium, 8px);\n color: var(--md-sys-color-on-surface);\n }\n .meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--spacing-small, 4px) var(--spacing-medium, 8px);\n font-size: var(--ui-t-small, 12px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n .chip {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 0 var(--spacing-small, 4px);\n border-radius: var(--border-radius, 4px);\n border: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));\n line-height: 1.6;\n }\n .chip.URGENT {\n color: var(--md-sys-color-error);\n border-color: currentColor;\n }\n .chip.IMPORTANT {\n color: var(--md-sys-color-primary);\n border-color: currentColor;\n }\n .chip md-icon {\n --md-icon-size: 14px;\n }\n .markdown {\n font-size: var(--ui-t-body, 14px);\n line-height: 1.6;\n overflow-wrap: anywhere;\n }\n .markdown :is(h1, h2, h3, h4) {\n margin: var(--spacing-medium, 8px) 0 var(--spacing-small, 4px);\n font-size: var(--ui-t-title, 16px);\n }\n .markdown :is(p, ul, ol, blockquote, pre, table) {\n margin: 0 0 var(--spacing-medium, 8px);\n }\n .markdown :is(ul, ol) {\n padding-inline-start: 1.4em;\n list-style: revert;\n display: block;\n }\n .markdown a {\n color: var(--md-sys-color-primary);\n }\n .markdown table {\n border-collapse: collapse;\n }\n .markdown :is(th, td) {\n border: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));\n padding: 2px var(--spacing-small, 4px);\n }\n h4 {\n margin: var(--spacing-medium, 8px) 0 var(--spacing-small, 4px);\n font-size: var(--ui-t-small, 12px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n font-weight: 600;\n }\n ul.files {\n margin: 0;\n padding: 0;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n a,\n .link {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-small, 4px);\n color: var(--md-sys-color-primary);\n text-decoration: none;\n cursor: pointer;\n background: none;\n border: 0;\n padding: 0;\n font: inherit;\n }\n a:hover,\n .link:hover {\n text-decoration: underline;\n }\n a md-icon,\n .link md-icon {\n --md-icon-size: 16px;\n }\n .size {\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n font-size: var(--ui-t-small, 12px);\n }\n .acknowledge {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border-radius: var(--border-radius, 4px);\n background: var(--md-sys-color-surface-container, rgba(0, 0, 0, 0.04));\n font-size: var(--ui-t-body, 14px);\n }\n .acknowledge.done {\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n `\n\n @property({ attribute: false }) announcement!: AnnouncementItem\n private acknowledgement = new MutationController(\n this,\n gql`mutation ($id: String!) { acknowledgeAnnouncement(id: $id) { id readerState mine { readAt acknowledgedAt } } }`\n )\n\n private get busy() {\n return this.acknowledgement.pending\n }\n\n render() {\n const one = this.announcement\n if (!one) return nothing\n const files = one.attachments ?? []\n const links = one.links ?? []\n return html`<div class=\"meta\">\n ${one.pinned ? html`<span class=\"chip\"><md-icon>push_pin</md-icon>${t('announcement.field.pinned')}</span>` : nothing}\n ${one.importance !== 'NORMAL' ? html`<span class=\"chip ${one.importance}\">${importanceWord(one.importance)}</span>` : nothing}\n <span class=\"chip\">${categoryWord(one.category)}</span>\n <span>${one.publisher?.name ?? one.creator?.name ?? ''}</span>\n <span>${when(one.publishedAt ?? one.startsAt)}</span>\n ${one.endsAt ? html`<span>${t('announcement.text.until', { when: when(one.endsAt) })}</span>` : nothing}\n ${one.version > 1 ? html`<span>${t('announcement.text.version', { version: one.version })}</span>` : nothing}\n </div>\n ${one.body ? html`<div class=\"markdown\">${unsafeHTML(renderMarkdown(one.body))}</div>` : nothing}\n ${files.length\n ? html`<h4>${t('announcement.field.attachments')}</h4>\n <ul class=\"files\">\n ${files.map(\n file =>\n html`<li>\n <a href=${file.fullpath} target=\"_blank\" rel=\"noopener\" download=${file.name}><md-icon>attach_file</md-icon>${file.name}</a>\n ${file.size ? html`<span class=\"size\">${Math.max(1, Math.round(file.size / 1024))} KB</span>` : nothing}\n </li>`\n )}\n </ul>`\n : nothing}\n ${links.length\n ? html`<h4>${t('announcement.field.links')}</h4>\n <ul class=\"files\">\n ${links.map(link => html`<li><button class=\"link\" @click=${() => this.follow(link.url)}><md-icon>open_in_new</md-icon>${link.label}</button></li>`)}\n </ul>`\n : nothing}\n ${this.renderAcknowledge()}`\n }\n\n private renderAcknowledge() {\n const one = this.announcement\n if (!one.requiresAcknowledgement || one.status !== 'PUBLISHED') return nothing\n const by = one.acknowledgeBy ? t('announcement.text.acknowledge-by', { when: when(one.acknowledgeBy, false) }) : ''\n if (one.mine?.acknowledgedAt) return html`<div class=\"acknowledge done\">${t('announcement.text.acknowledged-at', { when: when(one.mine.acknowledgedAt) })}</div>`\n /* Only its audience acknowledges; a writer looking at one addressed to others sees what is asked. */\n if (!one.addressed) return html`<div class=\"acknowledge\">${t('announcement.text.acknowledge-asked')} ${by}</div>`\n return html`<div class=\"acknowledge\">\n <span>${t('announcement.text.acknowledge-asked')} ${by}</span>\n <md-filled-button ?disabled=${this.busy} @click=${() => this.acknowledge()}>${t('announcement.button.acknowledge')}</md-filled-button>\n </div>`\n }\n\n private follow(url: string) {\n this.dispatchEvent(new CustomEvent('announcement-follow', { bubbles: true, composed: true }))\n followLink(url)\n }\n\n private async acknowledge() {\n const done = await this.acknowledgement.run({ id: this.announcement.id })\n if (!done) return\n announcementsChanged()\n this.announcement = { ...this.announcement, ...done.acknowledgeAnnouncement }\n this.dispatchEvent(new CustomEvent('announcement-acknowledged', { bubbles: true, composed: true, detail: this.announcement }))\n }\n}\n"]}
|
|
@@ -13,13 +13,17 @@ export declare class AudiencePicker extends AudiencePicker_base {
|
|
|
13
13
|
static styles: import("lit").CSSResult;
|
|
14
14
|
value: Audience[];
|
|
15
15
|
disabled: boolean;
|
|
16
|
-
private choices?;
|
|
17
16
|
private text;
|
|
18
|
-
private
|
|
19
|
-
private
|
|
17
|
+
private searched;
|
|
18
|
+
private counted;
|
|
20
19
|
private searching?;
|
|
21
20
|
private counting?;
|
|
22
|
-
|
|
21
|
+
private choicesRead;
|
|
22
|
+
private people;
|
|
23
|
+
private sizeRead;
|
|
24
|
+
private get choices();
|
|
25
|
+
private get found();
|
|
26
|
+
private get size();
|
|
23
27
|
updated(changes: Map<string, unknown>): void;
|
|
24
28
|
render(): import("lit-html").TemplateResult<1>;
|
|
25
29
|
private give;
|