@things-factory/gallery 0.0.0-stage → 10.1.226

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 (102) hide show
  1. package/client/bootstrap.ts +16 -0
  2. package/client/components/gallery-forms.ts +188 -0
  3. package/client/components/gallery-uploader.ts +557 -0
  4. package/client/components/gallery-viewer.ts +397 -0
  5. package/client/index.ts +1 -0
  6. package/client/pages/gallery-page.ts +567 -0
  7. package/client/route.ts +8 -0
  8. package/client/tsconfig.json +13 -0
  9. package/client/utils/gallery-api.ts +157 -0
  10. package/client/utils/gallery-badge.ts +44 -0
  11. package/client/utils/gallery-words.ts +22 -0
  12. package/client/utils/photo-prep.ts +93 -0
  13. package/dist-client/bootstrap.d.ts +1 -0
  14. package/dist-client/bootstrap.js +16 -0
  15. package/dist-client/bootstrap.js.map +1 -0
  16. package/dist-client/components/gallery-forms.d.ts +31 -0
  17. package/dist-client/components/gallery-forms.js +223 -0
  18. package/dist-client/components/gallery-forms.js.map +1 -0
  19. package/dist-client/components/gallery-uploader.d.ts +51 -0
  20. package/dist-client/components/gallery-uploader.js +588 -0
  21. package/dist-client/components/gallery-uploader.js.map +1 -0
  22. package/dist-client/components/gallery-viewer.d.ts +30 -0
  23. package/dist-client/components/gallery-viewer.js +416 -0
  24. package/dist-client/components/gallery-viewer.js.map +1 -0
  25. package/dist-client/index.d.ts +1 -0
  26. package/dist-client/index.js +2 -0
  27. package/dist-client/index.js.map +1 -0
  28. package/dist-client/pages/gallery-page.d.ts +55 -0
  29. package/dist-client/pages/gallery-page.js +586 -0
  30. package/dist-client/pages/gallery-page.js.map +1 -0
  31. package/dist-client/route.d.ts +1 -0
  32. package/dist-client/route.js +9 -0
  33. package/dist-client/route.js.map +1 -0
  34. package/dist-client/tsconfig.tsbuildinfo +1 -0
  35. package/dist-client/utils/gallery-api.d.ts +95 -0
  36. package/dist-client/utils/gallery-api.js +46 -0
  37. package/dist-client/utils/gallery-api.js.map +1 -0
  38. package/dist-client/utils/gallery-badge.d.ts +4 -0
  39. package/dist-client/utils/gallery-badge.js +45 -0
  40. package/dist-client/utils/gallery-badge.js.map +1 -0
  41. package/dist-client/utils/gallery-words.d.ts +7 -0
  42. package/dist-client/utils/gallery-words.js +20 -0
  43. package/dist-client/utils/gallery-words.js.map +1 -0
  44. package/dist-client/utils/photo-prep.d.ts +10 -0
  45. package/dist-client/utils/photo-prep.js +102 -0
  46. package/dist-client/utils/photo-prep.js.map +1 -0
  47. package/dist-server/attachment-reader.d.ts +1 -0
  48. package/dist-server/attachment-reader.js +13 -0
  49. package/dist-server/attachment-reader.js.map +1 -0
  50. package/dist-server/index.d.ts +1 -0
  51. package/dist-server/index.js +11 -0
  52. package/dist-server/index.js.map +1 -0
  53. package/dist-server/role-tasks.d.ts +2 -0
  54. package/dist-server/role-tasks.js +15 -0
  55. package/dist-server/role-tasks.js.map +1 -0
  56. package/dist-server/service/gallery/gallery-resolver.d.ts +29 -0
  57. package/dist-server/service/gallery/gallery-resolver.js +602 -0
  58. package/dist-server/service/gallery/gallery-resolver.js.map +1 -0
  59. package/dist-server/service/gallery/gallery-rules.d.ts +88 -0
  60. package/dist-server/service/gallery/gallery-rules.js +181 -0
  61. package/dist-server/service/gallery/gallery-rules.js.map +1 -0
  62. package/dist-server/service/gallery/gallery-service.d.ts +37 -0
  63. package/dist-server/service/gallery/gallery-service.js +234 -0
  64. package/dist-server/service/gallery/gallery-service.js.map +1 -0
  65. package/dist-server/service/gallery/gallery-types.d.ts +62 -0
  66. package/dist-server/service/gallery/gallery-types.js +227 -0
  67. package/dist-server/service/gallery/gallery-types.js.map +1 -0
  68. package/dist-server/service/gallery/gallery.d.ts +83 -0
  69. package/dist-server/service/gallery/gallery.js +330 -0
  70. package/dist-server/service/gallery/gallery.js.map +1 -0
  71. package/dist-server/service/gallery/index.d.ts +4 -0
  72. package/dist-server/service/gallery/index.js +8 -0
  73. package/dist-server/service/gallery/index.js.map +1 -0
  74. package/dist-server/service/index.d.ts +7 -0
  75. package/dist-server/service/index.js +23 -0
  76. package/dist-server/service/index.js.map +1 -0
  77. package/dist-server/tsconfig.tsbuildinfo +1 -0
  78. package/package.json +48 -4
  79. package/server/attachment-reader.ts +14 -0
  80. package/server/index.ts +9 -0
  81. package/server/role-tasks.ts +14 -0
  82. package/server/service/gallery/gallery-resolver.ts +541 -0
  83. package/server/service/gallery/gallery-rules.ts +187 -0
  84. package/server/service/gallery/gallery-service.ts +254 -0
  85. package/server/service/gallery/gallery-types.ts +151 -0
  86. package/server/service/gallery/gallery.ts +266 -0
  87. package/server/service/gallery/index.ts +5 -0
  88. package/server/service/index.ts +19 -0
  89. package/server/tsconfig.json +11 -0
  90. package/test/gallery-persistence.test.ts +372 -0
  91. package/test/gallery-rules.test.ts +140 -0
  92. package/test/open-doors.test.ts +62 -0
  93. package/test/photo-prep.test.ts +61 -0
  94. package/test/translations.test.ts +68 -0
  95. package/things-factory.config.js +8 -0
  96. package/translations/en.json +91 -0
  97. package/translations/es.json +91 -0
  98. package/translations/ja.json +91 -0
  99. package/translations/ko.json +91 -0
  100. package/translations/ms.json +91 -0
  101. package/translations/zh.json +91 -0
  102. package/README.md +0 -3
@@ -0,0 +1,16 @@
1
+ import { navigate, store } from '@operato/shell'
2
+ import { ADD_USER_MENU } from '@things-factory/apptool-base/client'
3
+
4
+ /* One's own gallery is in the menu the header's avatar opens, right after the profile (ADR-0165 decision 6). */
5
+ export default function bootstrap() {
6
+ store.dispatch({
7
+ type: ADD_USER_MENU,
8
+ item: {
9
+ name: 'my-gallery',
10
+ icon: 'photo_library',
11
+ label: 'gallery.title.my-gallery',
12
+ order: 12,
13
+ action: () => navigate('gallery-page?tab=mine')
14
+ }
15
+ })
16
+ }
@@ -0,0 +1,188 @@
1
+ /*
2
+ * The two small forms of the gallery's moderators (ADR-0165 decision 5): an event made from the gallery, for one nobody
3
+ * put on the calendar yet, and a tag renamed on every post - into one already there, the two become one.
4
+ */
5
+ import '@material/web/button/filled-button.js'
6
+ import '@material/web/button/text-button.js'
7
+ import '@operato/popup/ox-popup-actions.js'
8
+
9
+ import { css, html, LitElement } from 'lit'
10
+ import { customElement, property, state } from 'lit/decorators.js'
11
+
12
+ import { i18next, localize } from '@operato/i18n'
13
+ import { notify } from '@operato/layout'
14
+ import { CommonPopupStyles, PopupBodyStyles } from '@operato/styles'
15
+
16
+ import { galleryApi, galleryChanged } from '../utils/gallery-api.js'
17
+ import { reasonOf, t } from '../utils/gallery-words.js'
18
+
19
+ const FormStyles = css`
20
+ :host {
21
+ height: 100%;
22
+ }
23
+ .body {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: var(--spacing-medium, 8px);
27
+ }
28
+ label {
29
+ display: flex;
30
+ flex-direction: column;
31
+ gap: 2px;
32
+ font-size: var(--ui-t-small, 12px);
33
+ font-weight: 600;
34
+ color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
35
+ }
36
+ input {
37
+ box-sizing: border-box;
38
+ font: inherit;
39
+ font-size: var(--ui-t-body, 14px);
40
+ font-weight: normal;
41
+ padding: var(--spacing-small, 4px) var(--spacing-medium, 8px);
42
+ border: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));
43
+ border-radius: var(--border-radius, 4px);
44
+ background: var(--md-sys-color-surface);
45
+ color: var(--md-sys-color-on-surface);
46
+ }
47
+ .row {
48
+ display: flex;
49
+ flex-wrap: wrap;
50
+ gap: var(--spacing-medium, 8px);
51
+ }
52
+ .note {
53
+ font-size: var(--ui-t-small, 12px);
54
+ color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
55
+ }
56
+ `
57
+
58
+ const local = (value: Date) => {
59
+ const pad = (n: number) => String(n).padStart(2, '0')
60
+ return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}T${pad(value.getHours())}:${pad(value.getMinutes())}`
61
+ }
62
+
63
+ @customElement('gallery-event-form')
64
+ export class GalleryEventForm extends localize(i18next)(LitElement) {
65
+ static styles = [CommonPopupStyles, PopupBodyStyles, FormStyles]
66
+
67
+ @state() private name = ''
68
+ @state() private startAt = local(new Date(new Date().setHours(9, 0, 0, 0)))
69
+ @state() private endAt = local(new Date(new Date().setHours(18, 0, 0, 0)))
70
+ @state() private location = ''
71
+ @state() private busy = false
72
+
73
+ render() {
74
+ const ready = this.name.trim() && this.startAt && this.endAt && this.endAt > this.startAt
75
+ return html`<div class="body">
76
+ <label
77
+ >${t('gallery.field.event-name')}<input
78
+ .value=${this.name}
79
+ @input=${(e: any) => (this.name = e.target.value)}
80
+ /></label>
81
+ <div class="row">
82
+ <label
83
+ >${t('gallery.field.starts')}<input
84
+ type="datetime-local"
85
+ .value=${this.startAt}
86
+ @change=${(e: any) => this.start(e.target.value)}
87
+ /></label>
88
+ <label
89
+ >${t('gallery.field.ends')}<input
90
+ type="datetime-local"
91
+ .value=${this.endAt}
92
+ @change=${(e: any) => (this.endAt = e.target.value)}
93
+ /></label>
94
+ </div>
95
+ <label
96
+ >${t('gallery.field.location')}<input
97
+ .value=${this.location}
98
+ @input=${(e: any) => (this.location = e.target.value)}
99
+ /></label>
100
+ <div class="note">${t('gallery.text.event-form-note')}</div>
101
+ </div>
102
+ <ox-popup-actions>
103
+ <md-text-button slot="cancel" @click=${() => this.close()}>${t('gallery.button.cancel')}</md-text-button>
104
+ <md-filled-button slot="confirm" ?disabled=${this.busy || !ready} @click=${() => this.save()}
105
+ >${t('gallery.button.create-event')}</md-filled-button
106
+ >
107
+ </ox-popup-actions>`
108
+ }
109
+
110
+ /** A later start carries the end with it, the same length. */
111
+ private start(value: string) {
112
+ const length = new Date(this.endAt).getTime() - new Date(this.startAt).getTime()
113
+ this.startAt = value
114
+ if (value && length > 0) this.endAt = local(new Date(new Date(value).getTime() + length))
115
+ }
116
+
117
+ private async save() {
118
+ this.busy = true
119
+ try {
120
+ const event = await galleryApi.createEvent({
121
+ name: this.name.trim(),
122
+ startAt: new Date(this.startAt).toISOString(),
123
+ endAt: new Date(this.endAt).toISOString(),
124
+ location: this.location.trim() || undefined
125
+ })
126
+ galleryChanged()
127
+ this.close(event)
128
+ } catch (error) {
129
+ notify({ level: 'error', message: reasonOf(error) })
130
+ } finally {
131
+ this.busy = false
132
+ }
133
+ }
134
+
135
+ /** What was made goes as the popup's result. */
136
+ private close(result?: unknown) {
137
+ this.dispatchEvent(new CustomEvent('done', { detail: result, bubbles: true, composed: true }))
138
+ }
139
+ }
140
+
141
+ @customElement('gallery-tag-form')
142
+ export class GalleryTagForm extends localize(i18next)(LitElement) {
143
+ static styles = [CommonPopupStyles, PopupBodyStyles, FormStyles]
144
+
145
+ @property() tag = ''
146
+ @state() private to = ''
147
+ @state() private busy = false
148
+
149
+ render() {
150
+ const ready = this.to.trim().replace(/^#+/, '') && this.to.trim().replace(/^#+/, '') !== this.tag
151
+ return html`<div class="body">
152
+ <label
153
+ >${t('gallery.field.new-tag')}<input
154
+ .value=${this.to}
155
+ placeholder=${this.tag}
156
+ maxlength="31"
157
+ @input=${(e: any) => (this.to = e.target.value)}
158
+ /></label>
159
+ <div class="note">${t('gallery.text.tag-form-note', { tag: this.tag })}</div>
160
+ </div>
161
+ <ox-popup-actions>
162
+ <md-text-button slot="cancel" @click=${() => this.close()}>${t('gallery.button.cancel')}</md-text-button>
163
+ <md-filled-button slot="confirm" ?disabled=${this.busy || !ready} @click=${() => this.save()}
164
+ >${t('gallery.button.rename-tag')}</md-filled-button
165
+ >
166
+ </ox-popup-actions>`
167
+ }
168
+
169
+ private async save() {
170
+ this.busy = true
171
+ try {
172
+ const to = this.to.trim().replace(/^#+/, '')
173
+ const count = await galleryApi.renameTag(this.tag, to)
174
+ notify({ message: t('gallery.text.tag-renamed', { count }) })
175
+ galleryChanged()
176
+ this.close(to)
177
+ } catch (error) {
178
+ notify({ level: 'error', message: reasonOf(error) })
179
+ } finally {
180
+ this.busy = false
181
+ }
182
+ }
183
+
184
+ /** What was made goes as the popup's result. */
185
+ private close(result?: unknown) {
186
+ this.dispatchEvent(new CustomEvent('done', { detail: result, bubbles: true, composed: true }))
187
+ }
188
+ }