@social-mail/social-mail-client 1.2.4 → 1.2.6

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 (34) hide show
  1. package/dist/admin/AdminAppIndex.pack.js +53 -9
  2. package/dist/admin/AdminAppIndex.pack.js.map +1 -1
  3. package/dist/admin/AdminAppIndex.pack.min.js +10 -6
  4. package/dist/admin/AdminAppIndex.pack.min.js.map +1 -1
  5. package/dist/services/files/LocalFileService.d.ts +2 -0
  6. package/dist/services/files/LocalFileService.d.ts.map +1 -1
  7. package/dist/services/files/LocalFileService.js +32 -8
  8. package/dist/services/files/LocalFileService.js.map +1 -1
  9. package/dist/tsconfig.tsbuildinfo +1 -1
  10. package/dist/web/AppIndex.pack.js +53 -9
  11. package/dist/web/AppIndex.pack.js.map +1 -1
  12. package/dist/web/AppIndex.pack.min.js +10 -6
  13. package/dist/web/AppIndex.pack.min.js.map +1 -1
  14. package/dist/web/page/mails/ConversationPage.d.ts +1 -0
  15. package/dist/web/page/mails/ConversationPage.d.ts.map +1 -1
  16. package/dist/web/page/mails/ConversationPage.js +12 -1
  17. package/dist/web/page/mails/ConversationPage.js.map +1 -1
  18. package/dist/web/page/mails/service/ConversationService.d.ts +1 -0
  19. package/dist/web/page/mails/service/ConversationService.d.ts.map +1 -1
  20. package/dist/web/page/mails/service/ConversationService.js +9 -0
  21. package/dist/web/page/mails/service/ConversationService.js.map +1 -1
  22. package/index.js +0 -1
  23. package/node_modules/@web-atoms/web-controls/dist/dev/DevHost.d.ts +35 -0
  24. package/node_modules/@web-atoms/web-controls/dist/dev/DevHost.d.ts.map +1 -0
  25. package/node_modules/@web-atoms/web-controls/dist/dev/DevHost.js +203 -0
  26. package/node_modules/@web-atoms/web-controls/dist/dev/DevHost.js.map +1 -0
  27. package/node_modules/@web-atoms/web-controls/dist/tsconfig.tsbuildinfo +1 -1
  28. package/node_modules/@web-atoms/web-controls/index.js +89 -0
  29. package/node_modules/@web-atoms/web-controls/package.json +1 -1
  30. package/node_modules/@web-atoms/web-controls/src/dev/DevHost.tsx +222 -0
  31. package/package.json +2 -2
  32. package/src/services/files/LocalFileService.tsx +35 -11
  33. package/src/web/page/mails/ConversationPage.tsx +11 -1
  34. package/src/web/page/mails/service/ConversationService.tsx +6 -0
@@ -0,0 +1,222 @@
1
+ import Bind from "@web-atoms/core/dist/core/Bind"
2
+ import XNode from "@web-atoms/core/dist/core/XNode"
3
+ import { BaseService, Get, Query } from "@web-atoms/core/dist/services/http/RestService";
4
+ import { AtomControl } from "@web-atoms/core/dist/web/controls/AtomControl";
5
+ import styled from "@web-atoms/core/dist/style/styled";
6
+ import InjectProperty from "@web-atoms/core/dist/core/InjectProperty";
7
+
8
+ import "@web-atoms/data-styles/data-styles";
9
+ import { CancelToken } from "@web-atoms/core/dist/core/types";
10
+ import Action from "@web-atoms/core/dist/view-model/Action";
11
+ import DISingleton from "@web-atoms/core/dist/di/DISingleton";
12
+ import AtomRepeater from "../basic/AtomRepeater";
13
+ import ToggleButtonBar from "../basic/ToggleButtonBar";
14
+
15
+ styled.css `
16
+ :root {
17
+ --accent-color: orangered;
18
+ --accent-text-color: white;
19
+ }
20
+ html, body {
21
+ margin: 0;
22
+ padding: 0;
23
+ font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
24
+ }
25
+ `.installGlobal();
26
+
27
+ const css = styled.css `
28
+ position: absolute;
29
+ left: 0;
30
+ top: 0;
31
+ right: 0;
32
+ bottom: 0;
33
+ display: grid;
34
+ grid-template-rows: auto 1fr;
35
+ gap: 5px;
36
+
37
+ & > header {
38
+ grid-row: 1;
39
+ }
40
+ & > div {
41
+ grid-row: 2;
42
+ align-items: stretch;
43
+ justify-self: stretch;
44
+ overflow: auto;
45
+
46
+ & [data-item-index]:hover {
47
+ background-color: lightgreen;
48
+ }
49
+ }
50
+ `.installLocal();
51
+
52
+ const fileTypes = () => [
53
+ { label: "Packed", value: "packed"},
54
+ { label: "All", value: "all"}
55
+ ];
56
+
57
+ const designModes = () => [
58
+ { label: "Design", value: true },
59
+ { label: "Live", value: false }
60
+ ];
61
+
62
+ const replaceSrc = (src: string): string => {
63
+ src = src.split("\\").join("/");
64
+ const tokens = src.split("/");
65
+ if (tokens[0] === "src") {
66
+ tokens[0] = "dist";
67
+ }
68
+ return tokens.join("/");
69
+ }
70
+
71
+ const toAbsoluteUrl = (file: IFilePath, designMode?: boolean) => {
72
+ let url = `/_view/${replaceSrc(file.dir)}/${file.name}`;
73
+ if (designMode) {
74
+ if (url.indexOf("?") === -1) {
75
+ url += "?";
76
+ }
77
+ url += "&designMode=true";
78
+ }
79
+ return `${location.protocol}//${location.host}${url}`;
80
+ };
81
+
82
+ @DISingleton({})
83
+ export class FileService extends BaseService {
84
+
85
+ public async getModules(search: string, packed: boolean, cancelToken: CancelToken) {
86
+ const result = await this.getRemoteModules(search, packed, cancelToken);
87
+ return result.files;
88
+ }
89
+
90
+ @Get("/flat-modules")
91
+ private getRemoteModules(
92
+ @Query("search") search: string,
93
+ @Query("packed") packed: boolean,
94
+ ct?: CancelToken): Promise<IFilePathResult> {
95
+ return null;
96
+ }
97
+
98
+ }
99
+
100
+ export default class AppHost extends AtomControl {
101
+
102
+ public search: string = "";
103
+
104
+ public designMode = false;
105
+
106
+ public fileType = "packed";
107
+
108
+ @InjectProperty
109
+ private fileService: FileService;
110
+
111
+ public async init() {
112
+
113
+ this.render(
114
+ <div
115
+ class={css}>
116
+ <header
117
+ data-layout="row"
118
+ data-padding="auto"
119
+ class="header">
120
+ <input
121
+ type="search"
122
+ value={Bind.twoWaysImmediate(() => this.search)}
123
+ placeholder="Search...">
124
+ </input>
125
+
126
+ <span>Mode</span>
127
+ <ToggleButtonBar
128
+ items={designModes()}
129
+ value={Bind.twoWays(() => this.designMode)}
130
+ />
131
+
132
+ <ToggleButtonBar
133
+ items={fileTypes()}
134
+ value={Bind.twoWays(() => this.fileType)}
135
+ />
136
+ </header>
137
+ <div>
138
+ <AtomRepeater
139
+ class="grid"
140
+ items={Bind.oneWayAsync((x, e, cancelToken) =>
141
+ this.getModules(this.search, this.fileType === "packed", cancelToken))}
142
+ for="table"
143
+ itemRenderer={(item: IFilePath) => <tr
144
+ template="itemTemplate">
145
+ <td>
146
+ <div
147
+ text={item.name}
148
+ title={item.dir}
149
+ style="font-weight: 500;">
150
+ </div>
151
+ <div
152
+ style="font-size: small;"
153
+ text={item.dir}>
154
+ </div>
155
+ </td>
156
+ <td>
157
+ <a
158
+ data-layout="accent-button"
159
+ data-text-decoration="none"
160
+ href={Bind.source(item, (x) => toAbsoluteUrl(x.source, this.designMode))}
161
+ target="_blank"
162
+ text="Open">
163
+ </a>
164
+ <i
165
+ class="fas fa-copy"
166
+ title="Copy Url"
167
+ style="margin: 5px; cursor: pointer;"
168
+ data-click-event="copy-url"
169
+ eventClick={() => this.copyUrl(toAbsoluteUrl(item, this.designMode))}>
170
+ </i>
171
+ </td>
172
+ </tr> } />
173
+ </div>
174
+ </div>
175
+ );
176
+ }
177
+
178
+ async getModules(search: string, packed: boolean, cancelToken: CancelToken) {
179
+ const result = await this.fileService.getModules(search, packed, cancelToken);
180
+ if (!result.length && packed) {
181
+ this.fileType = "all";
182
+ }
183
+ return result;
184
+ }
185
+
186
+ protected preCreate(): void {
187
+ super.preCreate();
188
+ (this.app).installStyleSheet({
189
+ href: "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.4.0/css/all.min.css",
190
+ integrity: "sha256-HtsXJanqjKTc8vVQjO4YMhiqFoXkfBsjBWcX91T1jr8=",
191
+ crossOrigin: "anonymous"
192
+ });
193
+ this.runAfterInit(() => this.app.runAsync(() => this.init()));
194
+ }
195
+
196
+ @Action({ success: "Copied successfully"})
197
+ protected async copyUrl(url: string) {
198
+ await navigator.clipboard.writeText(url);
199
+ }
200
+ }
201
+ export interface IFilePath {
202
+
203
+ name: string;
204
+ base: string;
205
+ dir: string;
206
+ ext: string;
207
+ root: string;
208
+ url: string;
209
+ hostUrl?: string;
210
+ package?: string;
211
+ visible: boolean;
212
+ packed?: boolean;
213
+ module?: string;
214
+
215
+ }
216
+
217
+ export interface IFilePathResult {
218
+
219
+ files: IFilePath[];
220
+
221
+ }
222
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@social-mail/social-mail-client",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -19,7 +19,7 @@
19
19
  "@web-atoms/font-awesome": "^1.0.37",
20
20
  "@web-atoms/hybrid-clr": "^1.0.21",
21
21
  "@web-atoms/module-loader": "^2.1.34",
22
- "@web-atoms/web-controls": "^2.4.6",
22
+ "@web-atoms/web-controls": "^2.4.15",
23
23
  "reflect-metadata": "^0.1.13",
24
24
  "tinycolor2": "^1.6.0"
25
25
  },
@@ -31,7 +31,7 @@ export type UploadedFiles = CustomEvent<{ files: IUploadedFile[], extra: any}>;
31
31
 
32
32
  styled.css `
33
33
  display: inline-grid;
34
- grid-template-rows: auto auto;
34
+ grid-template-rows: auto auto auto;
35
35
 
36
36
  & > [data-element=file-name] {
37
37
  grid-row: 1;
@@ -40,15 +40,20 @@ export type UploadedFiles = CustomEvent<{ files: IUploadedFile[], extra: any}>;
40
40
  text-overflow: ellipsis;
41
41
  }
42
42
 
43
- & > [data-element=progress] {
43
+ & > progress {
44
44
  grid-row: 2;
45
45
  width: 100%;
46
46
  }
47
+ & > [data-element=error] {
48
+ grid-row: 3;
49
+ width: 100%;
50
+ }
47
51
 
48
52
  `.installGlobal("[data-attachment-file]");
49
53
 
50
54
  export interface IFileUpload {
51
55
  name: string;
56
+ error?: string;
52
57
  progress?: number;
53
58
  promise?: Promise<{ appFileID }>
54
59
  }
@@ -164,23 +169,38 @@ export default class LocalFileService extends TaskManager {
164
169
  const upload = {
165
170
  total: 0,
166
171
  tasks,
167
- progress:0
172
+ progress:0,
173
+ error: "",
168
174
  };
169
175
  for (const file of files) {
170
176
  upload.total += file.size;
171
177
  const uploadFile: IFileUpload = {
172
178
  name: file.name,
179
+ error: "",
173
180
  progress: 0
174
181
  };
175
182
  uploadFile.promise = this.run(async () => {
176
- uploadFile.progress = 0.01;
177
-
178
- // we will check if file exists and has same sha256?
179
-
180
- const r = await this.uploadFile(file, extra, ct, (n) => uploadFile.progress = n);
181
- upload.progress += r.file.size;
182
- setTimeout(() => tasks.remove(uploadFile), 500);
183
- return r;
183
+ let lastError: string;
184
+ for(let i=0; i<3; i++) {
185
+ try {
186
+ uploadFile.progress = 0.01;
187
+
188
+ // we will check if file exists and has same sha256?
189
+
190
+ const r = await this.uploadFile(file, extra, ct, (n) => uploadFile.progress = n);
191
+ upload.progress += r.file.size;
192
+ setTimeout(() => tasks.remove(uploadFile), 500);
193
+ upload.error = "";
194
+ lastError = null;
195
+ return r;
196
+ } catch (error) {
197
+ lastError = `${file.webkitRelativePath ?? ""}/${file.name} failed to upload.\n${error.stack ?? error}`;
198
+ uploadFile.error = lastError;
199
+ }
200
+ }
201
+ if (lastError) {
202
+ upload.error += "\n" + lastError;
203
+ }
184
204
  });
185
205
  tasks.push(uploadFile);
186
206
  }
@@ -231,6 +251,10 @@ class FileUploadWindow extends PopupWindow {
231
251
  min={0}
232
252
  max={1}
233
253
  value={Bind.source(file, (x) => x.source.progress)}/>
254
+ <div
255
+ data-element="error"
256
+ data-color="red"
257
+ text={Bind.source(file, (x) => x.source.error)}/>
234
258
  </div>}
235
259
  />
236
260
  </div>);
@@ -31,7 +31,7 @@ const itemCss = styled.css `
31
31
  border-radius: 10px;
32
32
  padding: 10px;
33
33
  margin-right: 20%;
34
- overflow: hidden;
34
+ contain: content;
35
35
  position: relative;
36
36
  background-color: canvas;
37
37
  &[data-sent=true] {
@@ -334,4 +334,14 @@ export default class ConversationPage extends ContentPage {
334
334
  async unblockSender() {
335
335
  await this.conversationService.block(this.parameters);
336
336
  }
337
+
338
+ @Action({
339
+ onEvent: "delete-draft",
340
+ confirm: "Are you sure you want to delete this draft?"
341
+ })
342
+ async deleteDraft(mce: IMailboxContactEmail) {
343
+ await this.conversationService.delete(mce.email);
344
+ this.mails.remove(mce);
345
+ this.draft = null;
346
+ }
337
347
  }
@@ -231,4 +231,10 @@ export default class ConversationService {
231
231
  CommonEvents.onThreadMarkedAsBlocked.dispatchEvent(thread);
232
232
  }
233
233
 
234
+ async delete({ emailID }: IEmail) {
235
+ await this.entityService.delete(Email.create({
236
+ emailID
237
+ }));
238
+ }
239
+
234
240
  }