@social-mail/social-mail-client 1.2.488 → 1.2.490

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 (87) hide show
  1. package/content/local-db/worker.js +2 -2
  2. package/dist/admin/AdminAppIndex.pack.js +944 -210
  3. package/dist/admin/AdminAppIndex.pack.js.map +1 -1
  4. package/dist/admin/AdminAppIndex.pack.min.js +28 -3
  5. package/dist/admin/AdminAppIndex.pack.min.js.map +1 -1
  6. package/dist/model/model.d.ts +9 -5
  7. package/dist/model/model.d.ts.map +1 -1
  8. package/dist/model/model.js +907 -207
  9. package/dist/model/model.js.map +1 -1
  10. package/dist/public/channel/ChannelView.pack.js +907 -207
  11. package/dist/public/channel/ChannelView.pack.js.map +1 -1
  12. package/dist/public/channel/ChannelView.pack.min.js +1 -1
  13. package/dist/public/channel/ChannelView.pack.min.js.map +1 -1
  14. package/dist/public/contact-us/ContactUsApp.d.ts +1 -0
  15. package/dist/public/contact-us/ContactUsApp.d.ts.map +1 -1
  16. package/dist/public/contact-us/ContactUsApp.js +100 -14
  17. package/dist/public/contact-us/ContactUsApp.js.map +1 -1
  18. package/dist/public/contact-us/ContactUsApp.pack.js +100 -14
  19. package/dist/public/contact-us/ContactUsApp.pack.js.map +1 -1
  20. package/dist/public/contact-us/ContactUsApp.pack.min.js +82 -14
  21. package/dist/public/contact-us/ContactUsApp.pack.min.js.map +1 -1
  22. package/dist/public/store/Store.pack.js +907 -207
  23. package/dist/public/store/Store.pack.js.map +1 -1
  24. package/dist/public/store/Store.pack.min.js +1 -1
  25. package/dist/public/store/Store.pack.min.js.map +1 -1
  26. package/dist/public/store/cart/ShoppingCart.pack.js +909 -208
  27. package/dist/public/store/cart/ShoppingCart.pack.js.map +1 -1
  28. package/dist/public/store/cart/ShoppingCart.pack.min.js +1 -1
  29. package/dist/public/store/cart/ShoppingCart.pack.min.js.map +1 -1
  30. package/dist/services/LoginService.d.ts.map +1 -1
  31. package/dist/services/LoginService.js +2 -1
  32. package/dist/services/LoginService.js.map +1 -1
  33. package/dist/services/local-db/LocalDbService.d.ts +12 -5
  34. package/dist/services/local-db/LocalDbService.d.ts.map +1 -1
  35. package/dist/services/local-db/LocalDbService.js +57 -21
  36. package/dist/services/local-db/LocalDbService.js.map +1 -1
  37. package/dist/services/local-db/core/LocalTable.d.ts +19 -0
  38. package/dist/services/local-db/core/LocalTable.d.ts.map +1 -0
  39. package/dist/services/local-db/core/LocalTable.js +120 -0
  40. package/dist/services/local-db/core/LocalTable.js.map +1 -0
  41. package/dist/services/local-db/core/TemplateQuery.d.ts +27 -0
  42. package/dist/services/local-db/core/TemplateQuery.d.ts.map +1 -0
  43. package/dist/services/local-db/core/TemplateQuery.js +132 -0
  44. package/dist/services/local-db/core/TemplateQuery.js.map +1 -0
  45. package/dist/services/local-db/mailboxes/LocalMailbox.d.ts +13 -0
  46. package/dist/services/local-db/mailboxes/LocalMailbox.d.ts.map +1 -0
  47. package/dist/services/local-db/mailboxes/LocalMailbox.js +62 -0
  48. package/dist/services/local-db/mailboxes/LocalMailbox.js.map +1 -0
  49. package/dist/services/mime-service/MimeNode.js +1 -1
  50. package/dist/services/mime-service/MimeNode.js.map +1 -1
  51. package/dist/site-editor/editor/HtmlPageEditor.d.ts.map +1 -1
  52. package/dist/site-editor/editor/HtmlPageEditor.js +34 -1
  53. package/dist/site-editor/editor/HtmlPageEditor.js.map +1 -1
  54. package/dist/tests/template-query/template-queries.test.d.ts +2 -0
  55. package/dist/tests/template-query/template-queries.test.d.ts.map +1 -0
  56. package/dist/tests/template-query/template-queries.test.js +27 -0
  57. package/dist/tests/template-query/template-queries.test.js.map +1 -0
  58. package/dist/tsconfig.tsbuildinfo +1 -1
  59. package/dist/web/AppIndex.pack.js +944 -210
  60. package/dist/web/AppIndex.pack.js.map +1 -1
  61. package/dist/web/AppIndex.pack.min.js +29 -4
  62. package/dist/web/AppIndex.pack.min.js.map +1 -1
  63. package/dist/web/page/files/shares/AddSharePage.d.ts +0 -1
  64. package/dist/web/page/files/shares/AddSharePage.d.ts.map +1 -1
  65. package/node_modules/@web-atoms/entity/dist/services/BaseEntityService.d.ts +2 -0
  66. package/node_modules/@web-atoms/entity/dist/services/BaseEntityService.d.ts.map +1 -1
  67. package/node_modules/@web-atoms/entity/dist/services/BaseEntityService.js.map +1 -1
  68. package/node_modules/@web-atoms/entity/dist/tsconfig.tsbuildinfo +1 -1
  69. package/node_modules/@web-atoms/entity/package.json +1 -1
  70. package/node_modules/@web-atoms/entity/src/services/BaseEntityService.ts +2 -0
  71. package/package.json +2 -2
  72. package/pg-worker.js +15 -0
  73. package/src/model/model.ts +84 -82
  74. package/src/public/contact-us/ContactUsApp.tsx +102 -17
  75. package/src/services/LoginService.ts +5 -0
  76. package/src/services/local-db/LocalDbService.ts +68 -32
  77. package/src/services/local-db/core/LocalTable.ts +113 -0
  78. package/src/services/local-db/core/TemplateQuery.ts +135 -0
  79. package/src/services/local-db/mailboxes/LocalMailbox.ts +60 -0
  80. package/src/services/mime-service/MimeNode.ts +1 -1
  81. package/src/site-editor/editor/HtmlPageEditor.tsx +31 -1
  82. package/src/tests/template-query/template-queries.test.ts +22 -0
  83. package/dist/services/local-db/SqlDbFactory.d.ts +0 -47
  84. package/dist/services/local-db/SqlDbFactory.d.ts.map +0 -1
  85. package/dist/services/local-db/SqlDbFactory.js +0 -179
  86. package/dist/services/local-db/SqlDbFactory.js.map +0 -1
  87. package/src/services/local-db/SqlDbFactory.ts +0 -181
@@ -10,29 +10,97 @@ import Pack from "@web-atoms/core/dist/Pack";
10
10
  import Action from "@web-atoms/core/dist/view-model/Action";
11
11
 
12
12
  styled.css `
13
- display: flex;
14
- justify-content: center;
13
+ display: grid;
15
14
  align-items: center;
15
+ justify-items: stretch;
16
+
17
+ padding: 20px;
18
+
19
+ & [data-form] {
20
+ display: grid;
21
+ grid-template-columns: 1fr 1fr;
22
+ grid-template-rows: auto auto auto auto auto;
23
+
24
+ background-color: transparent;
25
+
26
+ & .field-error, & .error-message {
27
+ background-color: transparent;
28
+ color: red;
29
+ }
30
+
31
+ & > * {
32
+ background-color: #80808015;
33
+ padding: 5px;
34
+ padding-left: 10px;
35
+ padding-right: 10px;
36
+ border-radius: 10px;
37
+ }
38
+
39
+ }
40
+
41
+ & .error-message {
42
+ grid-column: 1 / span 2;
43
+ grid-row: 1;
44
+ }
45
+
46
+ & .name {
47
+ grid-column: 1;
48
+ grid-row: 2;
49
+ }
50
+
51
+ & .contact {
52
+ grid-column: 2;
53
+ grid-row: 2;
54
+ }
55
+
56
+ & .questions {
57
+ grid-column: 1 / span 2;
58
+ grid-row: 3;
59
+ }
60
+
61
+ & .verify {
62
+ grid-column: 1 / span 2;
63
+ grid-row: 4;
64
+ & img {
65
+ max-width: 250px;
66
+ }
67
+ }
68
+
69
+ & .email {
70
+ grid-column: 1 / span 2;
71
+ grid-row: 5;
72
+ background-color: transparent;
73
+ place-self: center;
74
+ & button {
75
+ display: flex;
76
+ gap: 10px;
77
+ background-color: lightgreen;
78
+ font-size: larger;
79
+ border: none;
80
+ outline: none;
81
+ padding: 10px;
82
+ padding-left: 20px;
83
+ padding-right: 20px;
84
+ border-radius: 20px;
85
+ margin-bottom: 20px;
86
+ &:hover {
87
+ box-shadow: 0 0 5px 5px #0000FF50;
88
+ }
89
+ }
90
+ }
91
+
16
92
  & .captcha {
17
93
  display: flex;
18
94
  flex-direction: column;
19
95
  gap: 10px;
20
96
  }
97
+
98
+
99
+
21
100
  & input, &,textarea {
22
101
  border: none;
23
102
  outline: none;
24
103
  background-color: transparent;
25
- border-bottom: solid 1px lightgray;
26
- }
27
- & button {
28
- display: flex;
29
- gap: 10px;
30
- background-color: lightgreen;
31
- padding: 2px;
32
- padding-left: 10px;
33
- padding-right: 10px;
34
- border-radius: 10px;
35
- margin-bottom: 10px;
36
104
  }
37
105
  `.installGlobal("contact-us");
38
106
 
@@ -61,26 +129,30 @@ export default class ContactUsApp extends AtomControl {
61
129
 
62
130
  captcha.code = "";
63
131
 
64
- this.renderer = <div>
132
+ this.renderer = <div event-click={(e) => this.selectInput(e.target as any)}>
65
133
  <Form>
66
- <FormField label="Your Name"
134
+ <FormField class="name" label="Your Name"
67
135
  error={Bind.oneWay(() => (this.model.name) ? "" : "Name is required")}>
68
136
  <input
69
137
  autofocus={true}
70
138
  value={Bind.twoWaysImmediate(() => this.model.name)}/>
71
139
  </FormField>
72
140
  <FormField
141
+ class="contact"
73
142
  label="Your Phone/Email"
74
143
  error={Bind.oneWay(() => (this.model.email || this.model.phone) ? "" : "Email or Phone is required")}>
75
144
  <input
76
145
  value={Bind.twoWaysImmediate(() => this.model.email)}/>
77
146
  </FormField>
78
- <FormField label="Questions">
147
+ <FormField
148
+ class="questions"
149
+ label="Questions">
79
150
  <textarea
80
151
  rows="8"
81
152
  value={Bind.twoWaysImmediate(() => this.model.description)}/>
82
153
  </FormField>
83
154
  <FormField
155
+ class="verify"
84
156
  label="Verify Code"
85
157
  error={Bind.oneWay(() => this.model.captcha.code ? "" : "Code is required")}>
86
158
  <div>
@@ -90,7 +162,9 @@ export default class ContactUsApp extends AtomControl {
90
162
  </div>
91
163
  </div>
92
164
  </FormField>
93
- <FormField label="">
165
+ <FormField
166
+ class="email"
167
+ label="">
94
168
  <div>
95
169
  <EmailIconTextButton
96
170
  data-submit-event="send-email"
@@ -102,6 +176,17 @@ export default class ContactUsApp extends AtomControl {
102
176
  </div>;
103
177
  }
104
178
 
179
+ selectInput(target: HTMLElement) {
180
+ while(target) {
181
+ const input = target.querySelector("input") ?? target.querySelector("textarea");
182
+ if (input) {
183
+ input.focus();
184
+ return;
185
+ }
186
+ target = target.parentElement;
187
+ }
188
+ }
189
+
105
190
  @Action({
106
191
  onEvent: "send-email"
107
192
  })
@@ -8,6 +8,7 @@ import WebPushService from "./WebPushService";
8
8
  import sleep from "@web-atoms/core/dist/core/sleep";
9
9
  import DateTime from "@web-atoms/date-time/dist/DateTime";
10
10
  import { CancelToken } from "@web-atoms/core/dist/core/types";
11
+ // import LocalDbService from "./local-db/LocalDbService";
11
12
 
12
13
  @DISingleton()
13
14
  export default class LoginService {
@@ -23,6 +24,10 @@ export default class LoginService {
23
24
  public async getUser() {
24
25
  this.user ??= (await this.entityService.query(LoginSession, "currentUser").firstOrDefault())?.user;
25
26
  SocialMailApp.user = this.user;
27
+ if (this.user?.userID) {
28
+ // let start syncing...
29
+ // this.app.resolve(LocalDbService).sync();
30
+ }
26
31
  return this.user;
27
32
  }
28
33
 
@@ -1,32 +1,68 @@
1
- import { UMD } from "@web-atoms/core/dist/core/types";
2
- import DISingleton from "@web-atoms/core/dist/di/DISingleton";
3
- import SqlDbFactory from "./SqlDbFactory";
4
-
5
- @DISingleton()
6
- export default class LocalDbService {
7
-
8
- async contacts() {
9
- const db = await this.load();
10
- return db.execute `SELECT * FROM mailbox_thread_emails`;
11
- }
12
-
13
- private load() {
14
- const r = (async () => {
15
- const db = await SqlDbFactory.load("sm1");
16
- // seed db...
17
- await db.query(`
18
- CREATE TABLE IF NOT EXISTS mailboxes (mailbox_id TEXT PRIMARY KEY, name TEXT);
19
- CREATE TABLE IF NOT EXISTS mailbox_threads (mailbox_id TEXT, email_address_id TEXT, PRIMARY KEY (mailbox_id, email_address_id));
20
- CREATE TABLE IF NOT EXISTS mailbox_thread_emails (mailbox_id TEXT, email_address_id TEXT, email_id TEXT, PRIMARY KEY (mailbox_id, email_address_id, email_id));
21
- `);
22
- return db;
23
- })();
24
-
25
- Object.defineProperty(this, "load", {
26
- value: () => r
27
- });
28
-
29
- return r;
30
- }
31
-
32
- }
1
+ /* eslint-disable no-console */
2
+ import DISingleton from "@web-atoms/core/dist/di/DISingleton";
3
+ import LocalMailbox from "./mailboxes/LocalMailbox";
4
+ import { UMD } from "@web-atoms/core/dist/core/types";
5
+ import Modules from "../../common/Modules";
6
+ import { Inject } from "@web-atoms/core/dist/di/Inject";
7
+ import EntityService from "../EntityService";
8
+ import TemplateQuery from "./core/TemplateQuery";
9
+
10
+ const pgWorker = UMD.resolvePath("@social-mail/social-mail-client/pg-worker.js");
11
+
12
+ export interface IResult<T> {
13
+ rows: T[];
14
+ affectedRows: number;
15
+ }
16
+
17
+ @DISingleton()
18
+ export default class LocalDbService {
19
+
20
+ @Inject
21
+ entityService: EntityService;
22
+
23
+ public mailboxes = new LocalMailbox(this);
24
+
25
+ sync() {
26
+ this.ready().catch(console.error);
27
+ }
28
+
29
+ ready() {
30
+ const w = (async () => {
31
+ const { default: pg }= await Modules.import(pgWorker);
32
+
33
+ await pg.waitReady;
34
+
35
+ // start syncing
36
+ for (const key in this) {
37
+ if (Object.prototype.hasOwnProperty.call(this, key)) {
38
+ const element = this[key];
39
+ if (element instanceof LocalMailbox) {
40
+ element.sync().catch(console.error);
41
+ }
42
+ }
43
+ }
44
+
45
+ return pg;
46
+ })();
47
+ Object.defineProperty(this, "ready", { value: () => w });
48
+ return w;
49
+ }
50
+
51
+ async query<T>(a: TemplateStringsArray | TemplateQuery, ... args: any[]): Promise<IResult<T>> {
52
+ let q;
53
+ try {
54
+ const pg = await this.ready();
55
+ if (!(a instanceof TemplateQuery)) {
56
+ a = TemplateQuery.create(a, ... args);
57
+ }
58
+ q = a.toQuery();
59
+ console.log (q);
60
+ return await pg.query(q.text, q.values);
61
+ } catch (error) {
62
+ console.error(`Failed executing ${q.text}`);
63
+ console.error(error);
64
+ }
65
+ }
66
+
67
+ }
68
+
@@ -0,0 +1,113 @@
1
+ import { IModel, IModelSchema, Model } from "@web-atoms/entity/dist/services/BaseEntityService";
2
+ import type LocalDbService from "../LocalDbService";
3
+ import TemplateQuery from "./TemplateQuery";
4
+ import { IResult } from "../LocalDbService";
5
+
6
+ export default abstract class LocalTable<T> {
7
+
8
+ name: TemplateQuery;
9
+ model: IModel<T>;
10
+
11
+ constructor(
12
+ protected readonly service: LocalDbService
13
+ ) {
14
+
15
+ }
16
+
17
+ async sync() {
18
+ const s = (async () => {
19
+ await this.init();
20
+ await this.startSync();
21
+ delete this.sync;
22
+ })();
23
+ Object.defineProperty(this, "sync", { value: () => s});
24
+ return s;
25
+ }
26
+
27
+
28
+ public abstract startSync(): Promise<void>;
29
+
30
+ public async afterInit() {
31
+
32
+ }
33
+
34
+ public async init() {
35
+ let keys = TemplateQuery.fragments(",");
36
+ let pKeys = TemplateQuery.fragments(",");
37
+ for (const { name, dataType, length } of this.model.schema.keys) {
38
+ keys = keys.add `${TemplateQuery.literal(name)} ${TemplateQuery.literal(this.toPgType(dataType, length))}`;
39
+ pKeys = pKeys.add `${TemplateQuery.literal(name)}`;
40
+ }
41
+ await this.query `CREATE TABLE IF NOT EXISTS ${this.name} (${keys}, PRIMARY KEY (${pKeys}))`;
42
+ for (const { name, dataType, length } of this.model.schema.properties) {
43
+ await this.query `ALTER TABLE ${this.name} ADD COLUMN IF NOT EXISTS ${TemplateQuery.literal(name)} ${TemplateQuery.literal(this.toPgColumn(dataType, length))}`;
44
+ }
45
+ await this.afterInit();
46
+ }
47
+
48
+ public query(a: TemplateStringsArray | TemplateQuery, ... args: any[]) {
49
+ return this.service.query(a, ... args) as Promise<IResult<T>>;
50
+ }
51
+
52
+ async save(element: T, retry = 3) {
53
+ try {
54
+ // try to update directly...
55
+ let where = TemplateQuery.fragments(" AND ");
56
+ for(const key of this.model.schema.keys) {
57
+ where = where.add `${TemplateQuery.literal(key.name)} = ${element[key.name]}`;
58
+ }
59
+
60
+ let fields = TemplateQuery.fragments(" , ");
61
+ for (const field of this.model.schema.properties) {
62
+ fields = fields.add `${TemplateQuery.literal(field.name)} = ${element[field.name]}`;
63
+ }
64
+
65
+ const { affectedRows } = await this.query `UPDATE ${this.name} SET ${fields} WHERE ${where}`;
66
+ if (affectedRows === 1) {
67
+ return;
68
+ }
69
+
70
+ // need to insert
71
+ let values = TemplateQuery.fragments(" , ");
72
+ let columns = TemplateQuery.fragments(" , ");
73
+ for(const key of this.model.schema.keys) {
74
+ columns = columns.add `${element[key.name]}`;
75
+ values = values.add `${element[key.name]}`;
76
+ }
77
+ await this.query `INSERT INTO ${this.name} (${columns}) VALUES (${values}) `;
78
+ } catch (error) {
79
+ if (-- retry) {
80
+ await this.save(element, retry);
81
+ return;
82
+ }
83
+ throw error;
84
+ }
85
+
86
+ }
87
+
88
+ private toPgColumn(type: string, length: number) {
89
+ const pgType = this.toPgType(type, length);
90
+ return pgType + " NULL";
91
+ }
92
+
93
+ private toPgType(type: string, length: number): string {
94
+ switch(type) {
95
+ case "Integer":
96
+ return "int";
97
+ case "BigInt":
98
+ return "bigint";
99
+ case "Boolean":
100
+ case "Bool":
101
+ return "bool";
102
+ case "DateTime":
103
+ return "timestamp";
104
+ case "Decimal":
105
+ case "Double":
106
+ return "real";
107
+ }
108
+ if (length) {
109
+ return `varchar (${length})`;
110
+ }
111
+ return "text";
112
+ }
113
+ }
@@ -0,0 +1,135 @@
1
+ export interface IQueryPart {
2
+ literal?: string;
3
+ arg?: any;
4
+ }
5
+
6
+ export class QueryFragments {
7
+
8
+ public readonly fragments: IQueryPart[] = [];
9
+
10
+ public readonly prefix: string;
11
+
12
+ public readonly separator: string;
13
+
14
+ constructor(
15
+ separator: string,
16
+ prefix?: string) {
17
+ this.prefix = prefix;
18
+ this.separator = separator;
19
+ }
20
+
21
+ public add(query: TemplateQuery | QueryFragments): QueryFragments;
22
+ public add(query: TemplateStringsArray, ... args: any[]): QueryFragments;
23
+ public add(query: TemplateQuery | QueryFragments | TemplateStringsArray, ... args: any[]): QueryFragments {
24
+ const { separator } = this;
25
+ if (separator && this.fragments.length) {
26
+ this.fragments.push({ literal: separator });
27
+ }
28
+
29
+ if (query instanceof QueryFragments) {
30
+ query = query.toQuery();
31
+ } else if (!(query instanceof TemplateQuery)) {
32
+ query = TemplateQuery.create(query, ... args);
33
+ }
34
+
35
+ this.fragments.push(... query.fragments);
36
+
37
+ return this;
38
+ }
39
+
40
+ public toQuery(): TemplateQuery {
41
+ const f = [];
42
+ if (this.prefix) {
43
+ f.push({ literal: this.prefix });
44
+ }
45
+ f.push(... this.fragments);
46
+ return new TemplateQuery(f);
47
+ }
48
+
49
+ }
50
+
51
+ export default class TemplateQuery {
52
+
53
+ public static literal(literal: string) {
54
+ return new TemplateQuery([ { literal }]);
55
+ }
56
+
57
+ public static create(query: TemplateStringsArray, ... args: any[]): TemplateQuery {
58
+ const q = new TemplateQuery();
59
+ for (let index = 0; index < query.length; index++) {
60
+ const literal = query[index];
61
+ q.fragments.push({ literal });
62
+ if (index < args.length) {
63
+
64
+ const arg = args[index];
65
+ if (arg instanceof TemplateQuery || arg instanceof QueryFragments) {
66
+ q.fragments.push(... arg.fragments);
67
+ continue;
68
+ }
69
+ q.fragments.push({ arg });
70
+ }
71
+ }
72
+ return q;
73
+ }
74
+
75
+ public static fragments(separator: string): QueryFragments;
76
+ public static fragments(separator: string, prefix?: string): QueryFragments {
77
+ return new QueryFragments(separator, prefix);
78
+ }
79
+
80
+ constructor(public fragments: IQueryPart[] = []) {}
81
+
82
+ public toQuery(prefix?: string, argName = (index) => `$${index + 1}`): IQuery {
83
+ let text = prefix ?? "";
84
+ const values = [];
85
+ const map = new Map();
86
+ for (const { literal, arg } of this.fragments) {
87
+ if (literal !== void 0) {
88
+ text += literal;
89
+ continue;
90
+ }
91
+ const av = arg ?? null;
92
+ if (av === null) {
93
+ text += " NULL ";
94
+ continue;
95
+ }
96
+ let an = map.get(av);
97
+ if (an === void 0) {
98
+ an = argName(values.length);
99
+ map.set(av, an);
100
+ values.push(this.toValue(av));
101
+ }
102
+ text += an;
103
+ }
104
+ return {
105
+ text,
106
+ values
107
+ };
108
+ }
109
+
110
+ toValue(av: any): any {
111
+ if (av === void 0) {
112
+ return null;
113
+ }
114
+ if (av === null) {
115
+ return null;
116
+ }
117
+ if (av instanceof Date) {
118
+ return av.toJSON();
119
+ }
120
+ switch(typeof av) {
121
+ case "boolean":
122
+ return av;
123
+ case "string":
124
+ case "number":
125
+ case "bigint":
126
+ return (av as any).toString();
127
+ }
128
+ return av;
129
+ }
130
+ }
131
+
132
+ export interface IQuery {
133
+ text: string;
134
+ values: any[];
135
+ }
@@ -0,0 +1,60 @@
1
+ import DateTime from "@web-atoms/date-time/dist/DateTime";
2
+ import { IMailbox, Mailbox } from "../../../model/model";
3
+ import LocalTable from "../core/LocalTable";
4
+ import TemplateQuery from "../core/TemplateQuery";
5
+
6
+ export default class LocalMailbox extends LocalTable<IMailbox> {
7
+
8
+ name = TemplateQuery.literal("mailboxes");
9
+
10
+ model = Mailbox;
11
+
12
+ public async startSync() {
13
+
14
+ const es = this.service.entityService;
15
+
16
+ let q = es.query(this.model);
17
+
18
+ let max = null;
19
+
20
+ // get last max updated...
21
+ const { rows:[first] } = await this.query `SELECT * FROM ${this.name} ORDER BY dateUpdated DESC LIMIT 1`;
22
+ if (first) {
23
+ max = first.dateUpdated;
24
+ q = q.where({ max}, (p) => (x) => x.dateUpdated >= p.max);
25
+ }
26
+
27
+ let min = DateTime.now;
28
+
29
+ do {
30
+ const result = await q.where({ min }, (p) => (x) => x.dateUpdated < p.min)
31
+ .orderByDescending((x) => x.dateUpdated)
32
+ .toPagedList({
33
+ start: 0,
34
+ size: 200,
35
+ count: false
36
+ });
37
+
38
+ if (!result.items.length) {
39
+ break;
40
+ }
41
+
42
+ for (const element of result.items) {
43
+ await this.save(element);
44
+ min = element.dateUpdated;
45
+ }
46
+ } while(true);
47
+ }
48
+
49
+ // public async init() {
50
+ // await this.service.query `CREATE TABLE IF NOT EXISTS ${this.name}
51
+ // (
52
+ // mailboxId char (200),
53
+ // name char (200),
54
+ // dateUpdated timestamp,
55
+ // primary key (mailboxId)
56
+ // )
57
+ // `;
58
+ // }
59
+
60
+ }
@@ -393,7 +393,7 @@ export class MimeNode {
393
393
  return;
394
394
  }
395
395
 
396
- let text = this.textContent;
396
+ let text = this.text;
397
397
  if (this.contentTransferEncoding === "quoted-printable") {
398
398
  text = quotedPrintable.encode( RawBuffer.encode(text));
399
399
  }
@@ -121,6 +121,31 @@ import BreadCrum from "./BreadCrum";
121
121
  pointer-events: none;
122
122
  outline: solid 1px green;
123
123
  z-index: 22;
124
+ & > .lt, & > .rt, & > .lb, & > .rb {
125
+ position: absolute;
126
+ width: 10px;
127
+ height: 10px;
128
+ background-color: blue;
129
+ border: solid 1px white;
130
+ display: none;
131
+ border-radius: 5px;
132
+ }
133
+ & > .lt {
134
+ top: -5px;
135
+ left: -5px;
136
+ }
137
+ & > .rt {
138
+ top: -5px;
139
+ right: -5px;
140
+ }
141
+ & > .lb {
142
+ bottom: -5px;
143
+ left: -5px;
144
+ }
145
+ & > .rb {
146
+ bottom: -5px;
147
+ right: -5px;
148
+ }
124
149
  & > div {
125
150
  pointer-events: all;
126
151
  position: absolute;
@@ -345,12 +370,17 @@ export default class HtmlPageEditor extends AtomControl {
345
370
  style-left={Bind.oneWay(() => this.dropTarget.left)}
346
371
  style-top={Bind.oneWay(() => this.dropTarget.top)}
347
372
  style-width={Bind.oneWay(() => this.dropTarget.width)}
348
- style-height={Bind.oneWay(() => this.dropTarget.height)}></div>
373
+ style-height={Bind.oneWay(() => this.dropTarget.height)}>
374
+ </div>
349
375
  <div data-element="selection"
350
376
  style-left={Bind.oneWay(() => this.selection.left)}
351
377
  style-top={Bind.oneWay(() => this.selection.top)}
352
378
  style-width={Bind.oneWay(() => this.selection.width)}
353
379
  style-height={Bind.oneWay(() => this.selection.height)}>
380
+ <i class="lt" />
381
+ <i class="rt" />
382
+ <i class="lb" />
383
+ <i class="rb" />
354
384
  <div>
355
385
  <i
356
386
  class="fa-regular fa-arrow-up"
@@ -0,0 +1,22 @@
1
+ import TemplateQuery from "../../services/local-db/core/TemplateQuery";
2
+ import { Assert } from "../Assert";
3
+
4
+ export default function() {
5
+ let tq = TemplateQuery.fragments(" AND ");
6
+ tq = tq.add `a === ${4}`;
7
+ tq = tq.add `b === ${5}`;
8
+
9
+ let q = tq.toQuery().toQuery();
10
+ Assert.equal("a === $1 AND b === $2", q.text);
11
+
12
+
13
+ let o1 = TemplateQuery.fragments(" OR ");
14
+ o1 = o1.add `(a === ${4}`;
15
+ o1 = o1.add `b === ${5})`;
16
+
17
+ tq = tq.add(o1.toQuery());
18
+
19
+ q = tq.toQuery().toQuery();
20
+ Assert.equal("a === $1 AND b === $2 AND (a === $1 OR b === $2)", q.text);
21
+
22
+ }