@things-factory/notification 6.1.17 → 6.1.22

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.
Files changed (108) hide show
  1. package/client/{bootstrap.js → bootstrap.ts} +13 -9
  2. package/client/pages/notification/notification-list-page.ts +267 -0
  3. package/client/pages/notification-rule/notification-rule-importer.ts +93 -0
  4. package/client/pages/notification-rule/notification-rule-list-page.ts +395 -0
  5. package/client/route.ts +10 -0
  6. package/client/tsconfig.json +13 -0
  7. package/client/viewparts/notification-badge.ts +58 -0
  8. package/client/viewparts/notification-item.ts +242 -0
  9. package/client/viewparts/notification-list.ts +171 -0
  10. package/client/viewparts/notification-sender.ts +126 -0
  11. package/client/viewparts/{notification-setting-let.js → notification-setting-let.ts} +48 -52
  12. package/dist-client/actions/notification-fcm.d.ts +7 -0
  13. package/dist-client/actions/notification-fcm.js +125 -0
  14. package/dist-client/actions/notification-fcm.js.map +1 -0
  15. package/dist-client/bootstrap.d.ts +1 -0
  16. package/dist-client/bootstrap.js +112 -0
  17. package/dist-client/bootstrap.js.map +1 -0
  18. package/dist-client/index.d.ts +5 -0
  19. package/dist-client/index.js +6 -0
  20. package/dist-client/index.js.map +1 -0
  21. package/dist-client/pages/notification/notification-list-page.d.ts +49 -0
  22. package/dist-client/pages/notification/notification-list-page.js +262 -0
  23. package/dist-client/pages/notification/notification-list-page.js.map +1 -0
  24. package/dist-client/pages/notification-rule/notification-rule-importer.d.ts +22 -0
  25. package/dist-client/pages/notification-rule/notification-rule-importer.js +100 -0
  26. package/dist-client/pages/notification-rule/notification-rule-importer.js.map +1 -0
  27. package/dist-client/pages/notification-rule/notification-rule-list-page.d.ts +62 -0
  28. package/dist-client/pages/notification-rule/notification-rule-list-page.js +380 -0
  29. package/dist-client/pages/notification-rule/notification-rule-list-page.js.map +1 -0
  30. package/dist-client/reducers/notification.d.ts +8 -0
  31. package/dist-client/reducers/notification.js +22 -0
  32. package/dist-client/reducers/notification.js.map +1 -0
  33. package/dist-client/route.d.ts +1 -0
  34. package/dist-client/route.js +11 -0
  35. package/dist-client/route.js.map +1 -0
  36. package/dist-client/tsconfig.tsbuildinfo +1 -0
  37. package/dist-client/viewparts/notification-badge.d.ts +15 -0
  38. package/dist-client/viewparts/notification-badge.js +61 -0
  39. package/dist-client/viewparts/notification-badge.js.map +1 -0
  40. package/dist-client/viewparts/notification-item.d.ts +16 -0
  41. package/dist-client/viewparts/notification-item.js +249 -0
  42. package/dist-client/viewparts/notification-item.js.map +1 -0
  43. package/dist-client/viewparts/notification-list.d.ts +20 -0
  44. package/dist-client/viewparts/notification-list.js +160 -0
  45. package/dist-client/viewparts/notification-list.js.map +1 -0
  46. package/dist-client/viewparts/notification-sender.d.ts +13 -0
  47. package/dist-client/viewparts/notification-sender.js +122 -0
  48. package/dist-client/viewparts/notification-sender.js.map +1 -0
  49. package/dist-client/viewparts/notification-setting-let.d.ts +20 -0
  50. package/dist-client/viewparts/notification-setting-let.js +193 -0
  51. package/dist-client/viewparts/notification-setting-let.js.map +1 -0
  52. package/dist-server/service/index.js +10 -1
  53. package/dist-server/service/index.js.map +1 -1
  54. package/dist-server/service/notification/index.js +6 -3
  55. package/dist-server/service/notification/index.js.map +1 -1
  56. package/dist-server/service/notification/notification-mutation.js +110 -0
  57. package/dist-server/service/notification/notification-mutation.js.map +1 -0
  58. package/dist-server/service/notification/notification-query.js +109 -0
  59. package/dist-server/service/notification/notification-query.js.map +1 -0
  60. package/dist-server/service/notification/notification-subscription.js +45 -0
  61. package/dist-server/service/notification/notification-subscription.js.map +1 -0
  62. package/dist-server/service/notification/notification-type.js +82 -0
  63. package/dist-server/service/notification/notification-type.js.map +1 -0
  64. package/dist-server/service/notification/notification.js +81 -8
  65. package/dist-server/service/notification/notification.js.map +1 -1
  66. package/dist-server/service/notification-rule/event-subscriber.js +21 -0
  67. package/dist-server/service/notification-rule/event-subscriber.js.map +1 -0
  68. package/dist-server/service/notification-rule/index.js +12 -0
  69. package/dist-server/service/notification-rule/index.js.map +1 -0
  70. package/dist-server/service/notification-rule/notification-rule-history.js +146 -0
  71. package/dist-server/service/notification-rule/notification-rule-history.js.map +1 -0
  72. package/dist-server/service/notification-rule/notification-rule-mutation.js +170 -0
  73. package/dist-server/service/notification-rule/notification-rule-mutation.js.map +1 -0
  74. package/dist-server/service/notification-rule/notification-rule-query.js +97 -0
  75. package/dist-server/service/notification-rule/notification-rule-query.js.map +1 -0
  76. package/dist-server/service/notification-rule/notification-rule-type.js +102 -0
  77. package/dist-server/service/notification-rule/notification-rule-type.js.map +1 -0
  78. package/dist-server/service/notification-rule/notification-rule.js +146 -0
  79. package/dist-server/service/notification-rule/notification-rule.js.map +1 -0
  80. package/dist-server/tsconfig.tsbuildinfo +1 -1
  81. package/helps/notification/noti-box.md +160 -0
  82. package/helps/notification/noti-rule.md +160 -0
  83. package/helps/notification/notibox.md +160 -0
  84. package/package.json +15 -12
  85. package/server/service/index.ts +14 -0
  86. package/server/service/notification/index.ts +6 -3
  87. package/server/service/notification/notification-mutation.ts +119 -0
  88. package/server/service/notification/notification-query.ts +70 -0
  89. package/server/service/notification/{notification-resolver.ts → notification-subscription.ts} +4 -4
  90. package/server/service/notification/notification-type.ts +55 -0
  91. package/server/service/notification/notification.ts +87 -14
  92. package/server/service/notification-rule/event-subscriber.ts +20 -0
  93. package/server/service/notification-rule/index.ts +9 -0
  94. package/server/service/notification-rule/notification-rule-history.ts +130 -0
  95. package/server/service/notification-rule/notification-rule-mutation.ts +203 -0
  96. package/server/service/notification-rule/notification-rule-query.ts +62 -0
  97. package/server/service/notification-rule/notification-rule-type.ts +71 -0
  98. package/server/service/notification-rule/notification-rule.ts +125 -0
  99. package/server/tsconfig.json +9 -0
  100. package/things-factory.config.js +7 -1
  101. package/client/viewparts/notification-badge.js +0 -63
  102. package/client/viewparts/notification-item.js +0 -250
  103. package/client/viewparts/notification-list.js +0 -177
  104. package/client/viewparts/notification-sender.js +0 -128
  105. package/tsconfig.json +0 -9
  106. /package/client/actions/{notification-fcm.js → notification-fcm.ts} +0 -0
  107. /package/client/{index.js → index.ts} +0 -0
  108. /package/client/reducers/{notification.js → notification.ts} +0 -0
@@ -0,0 +1,395 @@
1
+ import '@operato/data-grist'
2
+
3
+ import { CommonButtonStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
4
+ import { PageView, store } from '@operato/shell'
5
+ import { css, html } from 'lit'
6
+ import { customElement, property, query } from 'lit/decorators.js'
7
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements'
8
+ import { ColumnConfig, DataGrist, FetchOption, SortersControl } from '@operato/data-grist'
9
+ import { client } from '@operato/graphql'
10
+ import { i18next, localize } from '@operato/i18n'
11
+ import { notify, openPopup } from '@operato/layout'
12
+ import { OxPopup } from '@operato/popup'
13
+ import { isMobileDevice } from '@operato/utils'
14
+
15
+ import { connect } from 'pwa-helpers/connect-mixin'
16
+ import gql from 'graphql-tag'
17
+
18
+ import { NotificationRuleImporter } from './notification-rule-importer'
19
+
20
+ @customElement('notification-rule-list-page')
21
+ export class NotificationRuleListPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
22
+ static styles = [
23
+ ScrollbarStyles,
24
+ CommonGristStyles,
25
+ css`
26
+ :host {
27
+ display: flex;
28
+
29
+ width: 100%;
30
+
31
+ --grid-record-emphasized-background-color: red;
32
+ --grid-record-emphasized-color: yellow;
33
+ }
34
+ `
35
+ ]
36
+
37
+ static get scopedElements() {
38
+ return {
39
+ 'notification-rule-importer': NotificationRuleImporter
40
+ }
41
+ }
42
+
43
+ @property({ type: Object }) gristConfig: any
44
+ @property({ type: String }) mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
45
+
46
+ @query('ox-grist') private grist!: DataGrist
47
+ @query('#sorter-control') private sortersControl!: OxPopup
48
+
49
+ get context() {
50
+ return {
51
+ search: {
52
+ handler: (search: string) => {
53
+ this.grist.searchText = search
54
+ },
55
+ placeholder: i18next.t('title.notification-rule list'),
56
+ value: this.grist.searchText
57
+ },
58
+ filter: {
59
+ handler: () => {
60
+ this.grist.toggleHeadroom()
61
+ }
62
+ },
63
+ help: 'notification/notification-rule',
64
+ actions: [
65
+ {
66
+ title: i18next.t('button.save'),
67
+ action: this._updateNotificationRule.bind(this),
68
+ ...CommonButtonStyles.save
69
+ },
70
+ {
71
+ title: i18next.t('button.delete'),
72
+ action: this._deleteNotificationRule.bind(this),
73
+ ...CommonButtonStyles.delete
74
+ }
75
+ ],
76
+ exportable: {
77
+ name: i18next.t('title.notification-rule list'),
78
+ data: this.exportHandler.bind(this)
79
+ },
80
+ importable: {
81
+ handler: this.importHandler.bind(this)
82
+ }
83
+ }
84
+ }
85
+
86
+ render() {
87
+ const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
88
+
89
+ return html`
90
+ <ox-grist .mode=${mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>
91
+ <div slot="headroom">
92
+ <div id="filters">
93
+ <ox-filters-form autofocus></ox-filters-form>
94
+ </div>
95
+
96
+ <div id="sorters">
97
+ Sort
98
+ <mwc-icon
99
+ @click=${e => {
100
+ const target = e.currentTarget
101
+ this.sortersControl.open({
102
+ right: 0,
103
+ top: target.offsetTop + target.offsetHeight
104
+ })
105
+ }}
106
+ >expand_more</mwc-icon
107
+ >
108
+ <ox-popup id="sorter-control">
109
+ <ox-sorters-control> </ox-sorters-control>
110
+ </ox-popup>
111
+ </div>
112
+
113
+ <div id="modes">
114
+ <mwc-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</mwc-icon>
115
+ <mwc-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</mwc-icon>
116
+ <mwc-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</mwc-icon>
117
+ </div>
118
+ </div>
119
+ </ox-grist>
120
+ `
121
+ }
122
+
123
+ async pageInitialized(lifecycle: any) {
124
+ this.gristConfig = {
125
+ list: {
126
+ thumbnail: 'thumbnail',
127
+ fields: ['name', 'description'],
128
+ details: ['title', 'updatedAt']
129
+ },
130
+ columns: [
131
+ { type: 'gutter', gutterName: 'sequence' },
132
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
133
+ {
134
+ type: 'string',
135
+ name: 'name',
136
+ header: i18next.t('field.name'),
137
+ record: {
138
+ editable: true
139
+ },
140
+ filter: 'search',
141
+ sortable: true,
142
+ width: 150
143
+ },
144
+ {
145
+ type: 'string',
146
+ name: 'description',
147
+ header: i18next.t('field.description'),
148
+ record: {
149
+ editable: true
150
+ },
151
+ filter: 'search',
152
+ width: 200
153
+ },
154
+ {
155
+ type: 'string',
156
+ name: 'title',
157
+ header: i18next.t('field.title'),
158
+ record: {
159
+ editable: true
160
+ },
161
+ filter: 'search',
162
+ sortable: true,
163
+ width: 150
164
+ },
165
+ {
166
+ type: 'text',
167
+ name: 'body',
168
+ header: i18next.t('field.body'),
169
+ record: {
170
+ editable: true
171
+ },
172
+ filter: 'search',
173
+ sortable: true,
174
+ width: 150
175
+ },
176
+ {
177
+ type: 'string',
178
+ name: 'url',
179
+ header: i18next.t('field.url'),
180
+ record: {
181
+ editable: true
182
+ },
183
+ sortable: true,
184
+ width: 150
185
+ },
186
+ {
187
+ type: 'image',
188
+ name: 'thumbnail',
189
+ header: i18next.t('field.thumbnail'),
190
+ record: { editable: true },
191
+ width: 120
192
+ },
193
+ {
194
+ type: 'select',
195
+ name: 'state',
196
+ label: true,
197
+ header: i18next.t('field.state'),
198
+ record: {
199
+ options: [
200
+ {},
201
+ {
202
+ display: i18next.t('text.draft'),
203
+ value: 'DRAFT'
204
+ },
205
+ {
206
+ display: i18next.t('text.released'),
207
+ value: 'RELEASED'
208
+ }
209
+ ]
210
+ },
211
+ sortable: true,
212
+ filter: true,
213
+ width: 60
214
+ },
215
+ {
216
+ type: 'recipients',
217
+ name: 'recipients',
218
+ header: i18next.t('field.recipients'),
219
+ record: {
220
+ editable: true
221
+ },
222
+ width: 50
223
+ },
224
+ {
225
+ type: 'resource-object',
226
+ name: 'updater',
227
+ header: i18next.t('field.updater'),
228
+ record: {
229
+ editable: false
230
+ },
231
+ sortable: true,
232
+ width: 120
233
+ },
234
+ {
235
+ type: 'datetime',
236
+ name: 'updatedAt',
237
+ header: i18next.t('field.updated_at'),
238
+ record: {
239
+ editable: false
240
+ },
241
+ sortable: true,
242
+ width: 180
243
+ }
244
+ ],
245
+ rows: {
246
+ selectable: {
247
+ multiple: true
248
+ }
249
+ },
250
+ sorters: [
251
+ {
252
+ name: 'name'
253
+ }
254
+ ]
255
+ }
256
+ }
257
+
258
+ async pageUpdated(changes: any, lifecycle: any) {
259
+ if (this.active) {
260
+ // do something here when this page just became as active
261
+ }
262
+ }
263
+
264
+ async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
265
+ const response = await client.query({
266
+ query: gql`
267
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
268
+ responses: notificationRules(filters: $filters, pagination: $pagination, sortings: $sortings) {
269
+ items {
270
+ id
271
+ name
272
+ description
273
+ title
274
+ body
275
+ url
276
+ thumbnail
277
+ state
278
+ updater {
279
+ id
280
+ name
281
+ }
282
+ updatedAt
283
+ }
284
+ total
285
+ }
286
+ }
287
+ `,
288
+ variables: {
289
+ filters,
290
+ pagination: { page, limit },
291
+ sortings
292
+ }
293
+ })
294
+
295
+ return {
296
+ total: response.data.responses.total || 0,
297
+ records: response.data.responses.items || []
298
+ }
299
+ }
300
+
301
+ async _deleteNotificationRule() {
302
+ if (confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) {
303
+ const ids = this.grist.selected.map(record => record.id)
304
+ if (ids && ids.length > 0) {
305
+ const response = await client.mutate({
306
+ mutation: gql`
307
+ mutation ($ids: [String!]!) {
308
+ deleteNotificationRules(ids: $ids)
309
+ }
310
+ `,
311
+ variables: {
312
+ ids
313
+ }
314
+ })
315
+
316
+ if (!response.errors) {
317
+ this.grist.fetch()
318
+ notify({
319
+ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
320
+ })
321
+ }
322
+ }
323
+ }
324
+ }
325
+
326
+ async _updateNotificationRule() {
327
+ let patches = this.grist.dirtyRecords
328
+ if (patches && patches.length) {
329
+ patches = patches.map(patch => {
330
+ let patchField: any = patch.id ? { id: patch.id } : {}
331
+ const dirtyFields = patch.__dirtyfields__
332
+ for (let key in dirtyFields) {
333
+ patchField[key] = dirtyFields[key].after
334
+ }
335
+ patchField.cuFlag = patch.__dirty__
336
+
337
+ return patchField
338
+ })
339
+
340
+ const response = await client.mutate({
341
+ mutation: gql`
342
+ mutation ($patches: [NotificationRulePatch!]!) {
343
+ updateMultipleNotificationRule(patches: $patches) {
344
+ name
345
+ }
346
+ }
347
+ `,
348
+ variables: {
349
+ patches
350
+ }
351
+ })
352
+
353
+ if (!response.errors) {
354
+ this.grist.fetch()
355
+ }
356
+ }
357
+ }
358
+
359
+ async exportHandler() {
360
+ const exportTargets = this.grist.selected.length ? this.grist.selected : this.grist.dirtyData.records
361
+ const targetFieldSet = new Set(['id', 'name', 'description', 'active'])
362
+
363
+ return exportTargets.map(notificationRule => {
364
+ let tempObj = {}
365
+ for (const field of targetFieldSet) {
366
+ tempObj[field] = notificationRule[field]
367
+ }
368
+
369
+ return tempObj
370
+ })
371
+ }
372
+
373
+ async importHandler(records) {
374
+ const popup = openPopup(
375
+ html`
376
+ <notification-rule-importer
377
+ .notificationRules=${records}
378
+ @imported=${() => {
379
+ history.back()
380
+ this.grist.fetch()
381
+ }}
382
+ ></notification-rule-importer>
383
+ `,
384
+ {
385
+ backdrop: true,
386
+ size: 'large',
387
+ title: i18next.t('title.import notification-rule')
388
+ }
389
+ )
390
+
391
+ popup.onclosed = () => {
392
+ this.grist.fetch()
393
+ }
394
+ }
395
+ }
@@ -0,0 +1,10 @@
1
+ export default function route(page: string) {
2
+ switch (page) {
3
+ case 'notification-list':
4
+ import('./pages/notification/notification-list-page')
5
+ return page
6
+ case 'notification-rule-list':
7
+ import('./pages/notification-rule/notification-rule-list-page')
8
+ return page
9
+ }
10
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../tsconfig-base.json",
3
+ "compilerOptions": {
4
+ "experimentalDecorators": true,
5
+ "skipLibCheck": true,
6
+ "strict": true,
7
+ "declaration": true,
8
+ "module": "esnext",
9
+ "outDir": "../dist-client",
10
+ "baseUrl": "./"
11
+ },
12
+ "include": ["./**/*"]
13
+ }
@@ -0,0 +1,58 @@
1
+ import { LitElement, html, css } from 'lit'
2
+ import { customElement, property } from 'lit/decorators.js'
3
+
4
+ import { connect } from 'pwa-helpers/connect-mixin.js'
5
+ import { store } from '@operato/shell'
6
+
7
+ @customElement('notification-badge')
8
+ export class NotificationBadge extends connect(store)(LitElement) {
9
+ static styles = [
10
+ css`
11
+ :host {
12
+ font-size: 2em;
13
+ margin: 0 5px 0 0;
14
+ --badge-size: 14px;
15
+ }
16
+
17
+ mwc-icon {
18
+ display: block;
19
+ }
20
+
21
+ [data-badge] {
22
+ position: relative;
23
+ }
24
+
25
+ [data-badge]::after {
26
+ content: attr(data-badge);
27
+ position: absolute;
28
+ top: 0px;
29
+ right: -6px;
30
+ font-family: var(--theme-font);
31
+ font-size: 0.4em;
32
+ background: var(--paper-deep-orange-a400);
33
+ color: var(--theme-white-color);
34
+ width: var(--badge-size);
35
+ height: var(--badge-size);
36
+ text-align: center;
37
+ line-height: var(--badge-size);
38
+ border-radius: 50%;
39
+ box-shadow: var(--box-shadow);
40
+ }
41
+
42
+ [data-badge='0']::after {
43
+ display: none;
44
+ }
45
+ `
46
+ ]
47
+
48
+ @property({ type: Number }) badge?: number
49
+
50
+ render() {
51
+ var badge = this.badge || 0
52
+ return html` <mwc-icon data-badge=${badge} class="badge">notifications_none</mwc-icon> `
53
+ }
54
+
55
+ stateChanged(state) {
56
+ this.badge = state.notification.badge
57
+ }
58
+ }
@@ -0,0 +1,242 @@
1
+ import gql from 'graphql-tag'
2
+ import { LitElement, html, css } from 'lit'
3
+ import { customElement, property } from 'lit/decorators.js'
4
+ import '@material/mwc-icon'
5
+ import { client } from '@operato/graphql'
6
+
7
+ function isOriginSameAsLocation(url) {
8
+ function defaultPort(protocol) {
9
+ return { 'http:': 80, 'https:': 443 }[protocol]
10
+ }
11
+
12
+ function portOf(location) {
13
+ return location.port || defaultPort(location.protocol || pageLocation.protocol)
14
+ }
15
+
16
+ try {
17
+ var pageLocation = window.location
18
+ var urlparts = new URL(url, pageLocation.href)
19
+
20
+ return !!(
21
+ urlparts.protocol &&
22
+ urlparts.protocol == pageLocation.protocol &&
23
+ urlparts.host &&
24
+ urlparts.host == pageLocation.host &&
25
+ urlparts.host &&
26
+ portOf(urlparts) == portOf(pageLocation)
27
+ )
28
+ } catch (err) {
29
+ console.warn(err, url)
30
+ }
31
+ }
32
+
33
+ @customElement('notification-item')
34
+ export class NotificationItem extends LitElement {
35
+ static styles = [
36
+ css`
37
+ :host {
38
+ position: relative;
39
+ background-color: var(--theme-white-color);
40
+ padding: var(--padding-default);
41
+ border-left: 0 solid transparent;
42
+ border-bottom: var(--border-dark-color) !important;
43
+ transition: border-left 300ms ease-in-out, padding-left 300ms ease-in-out;
44
+ color: var(--scondary-color);
45
+
46
+ --type-dot-size: 9px;
47
+ }
48
+
49
+ :host([newbie]) {
50
+ background-color: var(--paper-light-green-50);
51
+ }
52
+
53
+ :host(:hover) {
54
+ padding-left: 0.5rem;
55
+ border-left: 0.5rem solid var(--status-info-color);
56
+ }
57
+
58
+ [titler] {
59
+ white-space: nowrap;
60
+ overflow: hidden;
61
+ text-overflow: ellipsis;
62
+ font-size: var(--fontsize-large);
63
+ font-weight: bold;
64
+ color: var(--secondary-color);
65
+ }
66
+
67
+ [titler] span {
68
+ display: inline-block;
69
+ width: var(--type-dot-size);
70
+ height: var(--type-dot-size);
71
+ border-radius: 50%;
72
+ margin-right: var(--margin-narrow);
73
+ }
74
+ [titler] * {
75
+ vertical-align: middle;
76
+ }
77
+
78
+ [close] {
79
+ position: absolute;
80
+ top: 13px;
81
+ right: 0;
82
+ font-size: var(--fontsize-large);
83
+ opacity: 0.5;
84
+ cursor: pointer;
85
+ }
86
+ [close]:hover {
87
+ opacity: 1;
88
+ }
89
+ [detail] {
90
+ padding: var(--padding-narrow) var(--padding-default);
91
+ border-radius: var(--border-radius);
92
+ font-size: 0.85em;
93
+ }
94
+ span.more {
95
+ margin-left: 16px;
96
+ float: right;
97
+ padding: 0px var(--padding-narrow) 2px var(--padding-narrow);
98
+ border-radius: var(--border-radius);
99
+ font-size: 0.8em;
100
+ color: var(--theme-white-color);
101
+ --mdc-icon-size: 14px;
102
+ }
103
+ span.more * {
104
+ vertical-align: middle;
105
+ }
106
+
107
+ :host([type='ERROR']) span {
108
+ background-color: var(--status-danger-color);
109
+ }
110
+ :host([type='ERROR']) {
111
+ border-color: var(--status-danger-color);
112
+ }
113
+ :host([type='ERROR']) [detail] {
114
+ border: 1px solid rgba(241, 53, 63, 0.5);
115
+ background-color: rgba(241, 53, 63, 0.1);
116
+ }
117
+ :host([type='WARN']) span {
118
+ background-color: var(--status-warning-color);
119
+ }
120
+ :host([type='WARN']) {
121
+ border-color: var(--status-warning-color);
122
+ }
123
+ :host([type='SUCCESS']) span {
124
+ background-color: var(--status-success-color);
125
+ }
126
+ :host([type='SUCCESS']) {
127
+ border-color: var(--status-success-color);
128
+ }
129
+ :host([type='INFO']) span,
130
+ :host span {
131
+ background-color: var(--status-info-color);
132
+ }
133
+ :host([type='INFO']) {
134
+ border-color: var(--status-info-color);
135
+ }
136
+
137
+ img {
138
+ display: block;
139
+ max-width: 100%;
140
+ margin: auto;
141
+ }
142
+
143
+ [message] {
144
+ font-size: var(--fontsize-default);
145
+ color: var(--secondary-color);
146
+ }
147
+
148
+ [timestamp] {
149
+ white-space: nowrap;
150
+ font-size: var(--fontsize-small);
151
+ color: var(--secondary-text-color);
152
+ }
153
+ `
154
+ ]
155
+
156
+ @property({ type: String, reflect: true }) type?: string
157
+ @property({ type: Object }) notification: any
158
+ @property({ type: String }) target?: string
159
+ @property({ type: Object }) detail?: { message: string; [key: string]: any }
160
+ @property({ type: Boolean }) newbie?: boolean
161
+
162
+ render() {
163
+ const { title, url, image, timestamp, body } = this.notification
164
+ const target = this.target
165
+ const detail = this.detail
166
+ const type = this.type
167
+
168
+ return html`
169
+ <div titler>
170
+ <span></span>
171
+ ${url
172
+ ? target
173
+ ? html`<a href=${url} target=${target}>${title}</a>`
174
+ : html`<a href=${url}>${title}</a>`
175
+ : title}
176
+ </div>
177
+ <mwc-icon close @click=${e => this.dispatchEvent(new CustomEvent('close'))}>close</mwc-icon>
178
+
179
+ ${image
180
+ ? url
181
+ ? target
182
+ ? html`<a href=${url} target=${target}><img src=${image} /></a>`
183
+ : html`<a href=${url}><img src=${image} /></a>`
184
+ : html`<img src=${image} />`
185
+ : html``}
186
+ <div message>${body}</div>
187
+ ${type == 'ERROR'
188
+ ? html`<div
189
+ @click=${() => {
190
+ this.decipherErrorCode()
191
+ }}
192
+ >
193
+ <span class="more"><mwc-icon>expand_circle_down</mwc-icon> more</span>
194
+ </div>`
195
+ : html``}
196
+ ${detail ? html`<div detail>${detail.message}</div>` : html``}
197
+
198
+ <div timestamp>${new Date(Number(timestamp)).toLocaleString()}</div>
199
+ `
200
+ }
201
+
202
+ updated(changed) {
203
+ if (!this.notification?.confirmed) {
204
+ this.setAttribute('newbie', '')
205
+ } else {
206
+ this.removeAttribute('newbie')
207
+ }
208
+
209
+ const url = this.notification?.url
210
+ this.target = url && !isOriginSameAsLocation(url) && '_blank'
211
+ }
212
+
213
+ async decipherErrorCode() {
214
+ if (this.type != 'ERROR' || !this.notification) {
215
+ return
216
+ }
217
+
218
+ const { title, body } = this.notification
219
+ var code = `${title}`
220
+ if (body) {
221
+ code += `: ${body}`
222
+ }
223
+
224
+ const response = await client.query({
225
+ query: gql`
226
+ query ($input: CodeDecipherInput!) {
227
+ decipherErrorCode(input: $input) {
228
+ message
229
+ }
230
+ }
231
+ `,
232
+ variables: {
233
+ input: {
234
+ code,
235
+ system: ''
236
+ }
237
+ }
238
+ })
239
+
240
+ this.detail = response.data.decipherErrorCode
241
+ }
242
+ }