@social-mail/social-mail-client 1.2.86 → 1.2.88

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 (50) hide show
  1. package/dist/admin/AdminAppIndex.pack.js +189 -79
  2. package/dist/admin/AdminAppIndex.pack.js.map +1 -1
  3. package/dist/admin/AdminAppIndex.pack.min.js +8 -8
  4. package/dist/admin/AdminAppIndex.pack.min.js.map +1 -1
  5. package/dist/common/controls/html/InlineHtmlEditorWithToolbar.d.ts +3 -0
  6. package/dist/common/controls/html/InlineHtmlEditorWithToolbar.d.ts.map +1 -1
  7. package/dist/common/controls/html/InlineHtmlEditorWithToolbar.js +15 -1
  8. package/dist/common/controls/html/InlineHtmlEditorWithToolbar.js.map +1 -1
  9. package/dist/public/channel/ChannelView.pack.js +10 -2
  10. package/dist/public/channel/ChannelView.pack.js.map +1 -1
  11. package/dist/public/channel/ChannelView.pack.min.js +1 -1
  12. package/dist/public/channel/ChannelView.pack.min.js.map +1 -1
  13. package/dist/services/files/LocalFileService.js +1 -1
  14. package/dist/services/files/LocalFileService.js.map +1 -1
  15. package/dist/tsconfig.tsbuildinfo +1 -1
  16. package/dist/web/AppIndex.pack.js +189 -79
  17. package/dist/web/AppIndex.pack.js.map +1 -1
  18. package/dist/web/AppIndex.pack.min.js +8 -8
  19. package/dist/web/AppIndex.pack.min.js.map +1 -1
  20. package/dist/web/page/mails/ComposeMailPage.d.ts.map +1 -1
  21. package/dist/web/page/mails/ComposeMailPage.js +5 -2
  22. package/dist/web/page/mails/ComposeMailPage.js.map +1 -1
  23. package/dist/web/page/mails/mini-compose/Atttach.d.ts +4 -0
  24. package/dist/web/page/mails/mini-compose/Atttach.d.ts.map +1 -0
  25. package/dist/web/page/mails/mini-compose/Atttach.js +64 -0
  26. package/dist/web/page/mails/mini-compose/Atttach.js.map +1 -0
  27. package/dist/web/page/mails/mini-compose/Insert.d.ts.map +1 -1
  28. package/dist/web/page/mails/mini-compose/Insert.js +13 -13
  29. package/dist/web/page/mails/mini-compose/Insert.js.map +1 -1
  30. package/dist/web/page/mails/mini-compose/MiniComposeBox.d.ts +9 -2
  31. package/dist/web/page/mails/mini-compose/MiniComposeBox.d.ts.map +1 -1
  32. package/dist/web/page/mails/mini-compose/MiniComposeBox.js +93 -35
  33. package/dist/web/page/mails/mini-compose/MiniComposeBox.js.map +1 -1
  34. package/dist/web/page/mails/service/ConversationService.d.ts.map +1 -1
  35. package/dist/web/page/mails/service/ConversationService.js +10 -2
  36. package/dist/web/page/mails/service/ConversationService.js.map +1 -1
  37. package/node_modules/@web-atoms/web-controls/dist/html-editor/InlineHtmlEditor.d.ts.map +1 -1
  38. package/node_modules/@web-atoms/web-controls/dist/html-editor/InlineHtmlEditor.js +3 -5
  39. package/node_modules/@web-atoms/web-controls/dist/html-editor/InlineHtmlEditor.js.map +1 -1
  40. package/node_modules/@web-atoms/web-controls/dist/tsconfig.tsbuildinfo +1 -1
  41. package/node_modules/@web-atoms/web-controls/package.json +1 -1
  42. package/node_modules/@web-atoms/web-controls/src/html-editor/InlineHtmlEditor.tsx +3 -9
  43. package/package.json +2 -2
  44. package/src/common/controls/html/InlineHtmlEditorWithToolbar.tsx +18 -2
  45. package/src/services/files/LocalFileService.tsx +1 -1
  46. package/src/web/page/mails/ComposeMailPage.tsx +3 -0
  47. package/src/web/page/mails/mini-compose/Atttach.tsx +68 -0
  48. package/src/web/page/mails/mini-compose/Insert.tsx +13 -15
  49. package/src/web/page/mails/mini-compose/MiniComposeBox.tsx +85 -20
  50. package/src/web/page/mails/service/ConversationService.tsx +10 -2
@@ -27,6 +27,7 @@ import InjectProperty from "@web-atoms/core/dist/core/InjectProperty";
27
27
  import { StringHelper } from "@web-atoms/core/dist/core/StringHelper";
28
28
  import styled from "@web-atoms/core/dist/style/styled";
29
29
  import { descendentElementIterator, descendentIterator } from "@web-atoms/core/dist/web/core/AtomUI";
30
+ import { IAppFile } from "../../../model/model";
30
31
 
31
32
  const link = document.createElement("link");
32
33
  link.href = "https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css";
@@ -134,13 +135,28 @@ const css = styled.css `
134
135
 
135
136
  export class MailHtmlEditor extends InlineHtmlEditor {
136
137
 
137
- public files: IUploadedFile[];
138
+ public files: IUploadedFile[] = [];
139
+
140
+ public attachments: IAppFile[] = [];
138
141
 
139
142
  public autofocus = false;
140
143
 
141
144
  @InjectProperty
142
145
  private localFileService: LocalFileService;
143
146
 
147
+ protected onContentSet(): void {
148
+ super.onContentSet();
149
+ const files = this.attachments;
150
+ const items = Array.from(this.editor.querySelectorAll(`*[data-content-id]`)) as HTMLElement[];
151
+ for (const iterator of items) {
152
+ const file = files.find((x) => x.contentID === iterator.getAttribute("data-content-id"));
153
+ if (!file) {
154
+ continue;
155
+ }
156
+ iterator.setAttribute("src", file.url);
157
+ }
158
+ }
159
+
144
160
  protected onPasteEvent(e: ClipboardEvent): void {
145
161
  if (e.clipboardData.files.length) {
146
162
  e.preventDefault();
@@ -161,7 +177,7 @@ export class MailHtmlEditor extends InlineHtmlEditor {
161
177
  super.preCreate();
162
178
  this.element.className = css;
163
179
  this.sanitizeHtml = (node) => {
164
- const images = Array.from(node.querySelectorAll(`img[data-content-id]`));
180
+ const images = Array.from(node.querySelectorAll(`*[data-content-id]`));
165
181
  for (const image of images) {
166
182
  image.setAttribute("src", `cid:${image.getAttribute("data-content-id")}`);
167
183
  }
@@ -171,7 +171,7 @@ export default class LocalFileService extends TaskManager {
171
171
 
172
172
  const sha256 = await this.sha256(file);
173
173
 
174
- if (extra.parentID) {
174
+ if (extra?.parentID) {
175
175
  const existing = await this.entityService.query(AppFile, "children", extra.parentID, folder ? folder + "/" + file.name : file.name, sha256)
176
176
  .firstOrDefault();
177
177
  if (existing) {
@@ -68,6 +68,8 @@ export default class ComposeMailPage extends PopupWindowPage {
68
68
  attachments: []
69
69
  });
70
70
 
71
+ this.model.attachments ??= [];
72
+
71
73
  if (this.parameters.social) {
72
74
  // load social template....
73
75
  const senderName = this.parameters.mailbox.emailAddress.name;
@@ -102,6 +104,7 @@ export default class ComposeMailPage extends PopupWindowPage {
102
104
  data-html-editor="email-editor"
103
105
  presenter={Bind.presenter((c) => this.editor = c )}
104
106
  content={this.model.htmlBody}
107
+ attachments={this.model.attachments}
105
108
  style-min-height="200px"
106
109
  />
107
110
  <AtomRepeater
@@ -0,0 +1,68 @@
1
+ import Bind from "@web-atoms/core/dist/core/Bind";
2
+ import XNode from "@web-atoms/core/dist/core/XNode";
3
+ import { IPopupOptions } from "@web-atoms/core/dist/web/services/PopupService";
4
+ import PageNavigator from "@web-atoms/web-controls/dist/PageNavigator";
5
+ import PopupButton, { MenuItem } from "@web-atoms/web-controls/dist/basic/PopupButton";
6
+ import UploadEvent from "@web-atoms/web-controls/dist/basic/UploadEvent";
7
+ import CodeSnippetPage from "../../../../code-editor/CodeSnippetPage";
8
+ import type { MailHtmlEditor } from "../../../../common/controls/html/InlineHtmlEditorWithToolbar";
9
+
10
+ export default function Attach({ alignment = "above" as IPopupOptions["alignment"]}) {
11
+ return <PopupButton
12
+ class="command"
13
+ data-layout="toolbar-button"
14
+ icon="fa-light fa-file-plus"
15
+ title="Attach"
16
+ alignment={alignment}>
17
+ <MenuItem
18
+ { ... UploadEvent.AttachUploadAction({
19
+ accept: "image/*",
20
+ multiple: true,
21
+ extra: {
22
+ }
23
+ })}
24
+ icon="fas fa-image"
25
+ label="Attach Photos"
26
+ />
27
+ <MenuItem
28
+ { ... UploadEvent.AttachUploadAction({
29
+ accept: "image/*",
30
+ capture: "user",
31
+ extra: {
32
+ }
33
+ })}
34
+ icon="fa-duotone fa-camera-retro"
35
+ label="Take Photo"
36
+ />
37
+ <MenuItem
38
+ { ... UploadEvent.AttachUploadAction({
39
+ accept: "video/*",
40
+ multiple: false,
41
+ extra: {
42
+ }
43
+ })}
44
+ icon="fas fa-file-video"
45
+ label="Attach Video"
46
+ />
47
+ <MenuItem
48
+ { ... UploadEvent.AttachUploadAction({
49
+ accept: "video/*",
50
+ capture: "user",
51
+ extra: {
52
+ }
53
+ })}
54
+ icon="fa-duotone fa-video-plus"
55
+ label="Record Video"
56
+ />
57
+ <MenuItem
58
+ { ... UploadEvent.AttachUploadAction({
59
+ accept: "*/*",
60
+ extra: {
61
+ }
62
+ })}
63
+ icon="fa-duotone fa-file"
64
+ label="Attach Documents"
65
+ style-min-width="150px"
66
+ />
67
+ </PopupButton>;
68
+ }
@@ -12,7 +12,7 @@ export default function Insert({ alignment = "above" as IPopupOptions["alignment
12
12
  class="command"
13
13
  data-layout="toolbar-button"
14
14
  icon="fas fa-plus"
15
- title="Change Alignment"
15
+ title="Insert"
16
16
  alignment={alignment}>
17
17
  <MenuItem
18
18
  icon="ri-list-unordered"
@@ -36,7 +36,9 @@ export default function Insert({ alignment = "above" as IPopupOptions["alignment
36
36
  { ... UploadEvent.AttachUploadAction({
37
37
  accept: "image/*",
38
38
  multiple: true,
39
- extra: {}
39
+ extra: {
40
+ insert: true
41
+ }
40
42
  })}
41
43
  data-click-event="send-photos"
42
44
  icon="fas fa-image"
@@ -46,7 +48,9 @@ export default function Insert({ alignment = "above" as IPopupOptions["alignment
46
48
  { ... UploadEvent.AttachUploadAction({
47
49
  accept: "image/*",
48
50
  capture: "user",
49
- extra: {}
51
+ extra: {
52
+ insert: true
53
+ }
50
54
  })}
51
55
  data-click-event="take-photo"
52
56
  icon="fa-duotone fa-camera-retro"
@@ -56,27 +60,21 @@ export default function Insert({ alignment = "above" as IPopupOptions["alignment
56
60
  { ... UploadEvent.AttachUploadAction({
57
61
  accept: "video/*",
58
62
  multiple: false,
59
- extra: {}
63
+ extra: {
64
+ insert: true
65
+ }
60
66
  })}
61
67
  data-click-event="send-video"
62
68
  icon="fas fa-file-video"
63
69
  label="Add Video"
64
70
  />
65
- <MenuItem
66
- { ... UploadEvent.AttachUploadAction({
67
- accept: "*/*",
68
- multiple: false,
69
- extra: {}
70
- })}
71
- data-click-event="send-document"
72
- icon="fas fa-file"
73
- label="Add Document"
74
- />
75
71
  <MenuItem
76
72
  { ... UploadEvent.AttachUploadAction({
77
73
  accept: "video/*",
78
74
  capture: "user",
79
- extra: {}
75
+ extra: {
76
+ insert: true
77
+ }
80
78
  })}
81
79
  data-click-event="record-video"
82
80
  icon="fa-duotone fa-video-plus"
@@ -28,6 +28,8 @@ import Separator from "@web-atoms/web-controls/dist/html-editor/commands/Separat
28
28
  import Insert from "./Insert";
29
29
  import TimeSpan from "@web-atoms/date-time/dist/TimeSpan";
30
30
  import ToggleEmoji from "./ToggleEmoji";
31
+ import Attach from "./Atttach";
32
+ import PopupService from "@web-atoms/core/dist/web/services/PopupService";
31
33
 
32
34
  const css = styled.css `
33
35
  display: grid;
@@ -115,6 +117,8 @@ export default class MiniComposeBox extends AtomControl {
115
117
  <Toolbar>
116
118
  <Insert/>
117
119
  <Separator/>
120
+ <Attach/>
121
+ <Separator/>
118
122
  <Bold/>
119
123
  <Italic/>
120
124
  <Underline/>
@@ -123,8 +127,6 @@ export default class MiniComposeBox extends AtomControl {
123
127
  <Separator/>
124
128
  <AddLink/>
125
129
  <Unlink/>
126
- <Separator/>
127
- <ToggleEmoji/>
128
130
  </Toolbar>
129
131
  <input
130
132
  style-place-self="stretch"
@@ -142,6 +144,7 @@ export default class MiniComposeBox extends AtomControl {
142
144
  presenter={Bind.presenter((c) => this.editor = c )}
143
145
  content={this.typedMessage}
144
146
  style-min-height="50px"
147
+ attachments={Bind.oneWay(() => this.draft.attachments)}
145
148
  />
146
149
  <i
147
150
  data-element="send"
@@ -173,8 +176,57 @@ export default class MiniComposeBox extends AtomControl {
173
176
  }
174
177
 
175
178
  @Action({ onEvent: "filesAvailable"})
176
- async filesAvailable({ files, extra }) {
177
- this.addFiles(... files);
179
+ async filesAvailable({ files, extra }: { files: IUploadedFile[], extra: any }) {
180
+
181
+ if (extra.stream) {
182
+ await this.streamFiles({ files, extra });
183
+ return;
184
+ }
185
+
186
+ const size = files.reduce((pv, cv) => pv + cv.file.size , 0);
187
+ if (size > 20*1024*1024) {
188
+ if (!extra.stream) {
189
+ throw new Error(`Total size of attachments cannot be more than 20MB.`);
190
+ }
191
+ }
192
+
193
+ if (extra.insert) {
194
+ this.ensureDraft();
195
+ // insert into the email..
196
+ const firstChild = this.editor.editor.firstElementChild;
197
+ for (const iterator of files) {
198
+ if (/^image\//.test(iterator.contentType)) {
199
+ const af = this.createAppFile(iterator);
200
+ const img = firstChild.ownerDocument.createElement("img");
201
+ img.src = iterator.url;
202
+ img.setAttribute("data-content-id", af.contentID);
203
+ this.draft.attachments.add(af);
204
+ firstChild.appendChild(img);
205
+ const newLine = document.createElement("div");
206
+ newLine.appendChild(document.createElement("br"));
207
+ firstChild.appendChild(newLine);
208
+ continue;
209
+ }
210
+ if (/^(video|audio)\//.test(iterator.contentType)) {
211
+ const af = this.createAppFile(iterator);
212
+ const img = firstChild.ownerDocument.createElement("video");
213
+ img.src = iterator.url;
214
+ img.setAttribute("data-content-id", af.contentID);
215
+ this.draft.attachments.add(af);
216
+ firstChild.appendChild(img);
217
+ const newLine = document.createElement("div");
218
+ newLine.appendChild(document.createElement("br"));
219
+ firstChild.appendChild(newLine);
220
+ continue;
221
+ }
222
+ }
223
+ return;
224
+ }
225
+ await this.addFiles(... files);
226
+ }
227
+
228
+ async streamFiles({ files, extra }: { files: IUploadedFile[], extra: any }) {
229
+ // do nothing...
178
230
  }
179
231
 
180
232
  @Action({
@@ -187,10 +239,13 @@ export default class MiniComposeBox extends AtomControl {
187
239
  }
188
240
 
189
241
  @Action({ onEvent: CommonEvents.onDraftDeleted })
190
- async refreshDraft() {
242
+ async recreateDraft() {
243
+ this.draft = null;
191
244
  this.draft ??= Email.create({
245
+ subject: "",
192
246
  attachments: []
193
247
  });
248
+ this.draft.attachments ??= [];
194
249
  }
195
250
 
196
251
  @Action({ onEvent: "input", defer: 500, blockMultipleExecution: true })
@@ -246,28 +301,38 @@ export default class MiniComposeBox extends AtomControl {
246
301
  @Action({})
247
302
  async sendAudioMessage() {
248
303
  const uploadedFile = await AudioRecorder.show<IUploadedFile>();
249
- this.draft ??= Email.create({
250
- attachments: []
251
- });
304
+ this.ensureDraft();
252
305
  this.draft.mdBody = this.typedMessage;
253
- this.addFiles(uploadedFile);
306
+ await this.addFiles(uploadedFile);
254
307
  await this.save();
255
308
  }
256
309
 
257
- private addFiles(... files: IUploadedFile[]) {
258
- const attachments = this.draft.attachments ??= [];
259
- AtomBinder.refreshValue(this, "draft");
260
- for (const { appFileID, url, file } of files) {
261
- attachments.add(AppFile.create({
262
- appFileID,
263
- contentType: file.type,
264
- name: file.name,
265
- contentID: appFileID.toString(36),
266
- url
267
- }));
310
+ private async addFiles(... files: IUploadedFile[]) {
311
+ this.ensureDraft();
312
+ const attachments = this.draft.attachments;
313
+ for (const file of files) {
314
+ attachments.add(this.createAppFile(file));
268
315
  }
269
316
  }
270
317
 
318
+ private createAppFile(file: IUploadedFile): IAppFile {
319
+ return AppFile.create({
320
+ appFileID: file.appFileID,
321
+ contentType: file.file.type,
322
+ name: file.file.name,
323
+ contentID: file.appFileID.toString(36),
324
+ url: file.url
325
+ });
326
+ }
327
+
328
+ private ensureDraft() {
329
+ this.draft ??= Email.create({
330
+ subject: "",
331
+ attachments: []
332
+ });
333
+ this.draft.attachments ??= [];
334
+ }
335
+
271
336
  private async save() {
272
337
  this.draft = await this.conversationService.draft({ ... this.mailboxContact, email: this.draft }, true);
273
338
  }
@@ -34,11 +34,19 @@ export const renderMail = AtomControl.registerProperty("data-html", "html", (c,
34
34
  }
35
35
  msg.innerHTML = html;
36
36
  for (const iterator of descendentElementIterator(msg)) {
37
- if(iterator.tagName === "IMG") {
37
+ let cid = iterator.getAttribute("data-content-id");
38
+ if (cid) {
39
+ const a = mail.attachments.find((x) => x.contentID === cid);
40
+ if (a) {
41
+ iterator.setAttribute("src", a.url);
42
+ continue;
43
+ }
44
+ }
45
+ if(/^(IMG|VIDEO)$/.test(iterator.tagName)) {
38
46
  const src = iterator.getAttribute("src");
39
47
  if (src) {
40
48
  if (src.startsWith("cid:")) {
41
- const cid = src.substring(4);
49
+ cid = src.substring(4);
42
50
  const a = mail.attachments.find((x) => x.contentID === cid);
43
51
  if (a) {
44
52
  iterator.setAttribute("src", `/api/files/${a.appFileID}/${a.fileContentID}/${a.name}`);