@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.
- package/content/local-db/worker.js +2 -2
- package/dist/admin/AdminAppIndex.pack.js +944 -210
- package/dist/admin/AdminAppIndex.pack.js.map +1 -1
- package/dist/admin/AdminAppIndex.pack.min.js +28 -3
- package/dist/admin/AdminAppIndex.pack.min.js.map +1 -1
- package/dist/model/model.d.ts +9 -5
- package/dist/model/model.d.ts.map +1 -1
- package/dist/model/model.js +907 -207
- package/dist/model/model.js.map +1 -1
- package/dist/public/channel/ChannelView.pack.js +907 -207
- package/dist/public/channel/ChannelView.pack.js.map +1 -1
- package/dist/public/channel/ChannelView.pack.min.js +1 -1
- package/dist/public/channel/ChannelView.pack.min.js.map +1 -1
- package/dist/public/contact-us/ContactUsApp.d.ts +1 -0
- package/dist/public/contact-us/ContactUsApp.d.ts.map +1 -1
- package/dist/public/contact-us/ContactUsApp.js +100 -14
- package/dist/public/contact-us/ContactUsApp.js.map +1 -1
- package/dist/public/contact-us/ContactUsApp.pack.js +100 -14
- package/dist/public/contact-us/ContactUsApp.pack.js.map +1 -1
- package/dist/public/contact-us/ContactUsApp.pack.min.js +82 -14
- package/dist/public/contact-us/ContactUsApp.pack.min.js.map +1 -1
- package/dist/public/store/Store.pack.js +907 -207
- package/dist/public/store/Store.pack.js.map +1 -1
- package/dist/public/store/Store.pack.min.js +1 -1
- package/dist/public/store/Store.pack.min.js.map +1 -1
- package/dist/public/store/cart/ShoppingCart.pack.js +909 -208
- package/dist/public/store/cart/ShoppingCart.pack.js.map +1 -1
- package/dist/public/store/cart/ShoppingCart.pack.min.js +1 -1
- package/dist/public/store/cart/ShoppingCart.pack.min.js.map +1 -1
- package/dist/services/LoginService.d.ts.map +1 -1
- package/dist/services/LoginService.js +2 -1
- package/dist/services/LoginService.js.map +1 -1
- package/dist/services/local-db/LocalDbService.d.ts +12 -5
- package/dist/services/local-db/LocalDbService.d.ts.map +1 -1
- package/dist/services/local-db/LocalDbService.js +57 -21
- package/dist/services/local-db/LocalDbService.js.map +1 -1
- package/dist/services/local-db/core/LocalTable.d.ts +19 -0
- package/dist/services/local-db/core/LocalTable.d.ts.map +1 -0
- package/dist/services/local-db/core/LocalTable.js +120 -0
- package/dist/services/local-db/core/LocalTable.js.map +1 -0
- package/dist/services/local-db/core/TemplateQuery.d.ts +27 -0
- package/dist/services/local-db/core/TemplateQuery.d.ts.map +1 -0
- package/dist/services/local-db/core/TemplateQuery.js +132 -0
- package/dist/services/local-db/core/TemplateQuery.js.map +1 -0
- package/dist/services/local-db/mailboxes/LocalMailbox.d.ts +13 -0
- package/dist/services/local-db/mailboxes/LocalMailbox.d.ts.map +1 -0
- package/dist/services/local-db/mailboxes/LocalMailbox.js +62 -0
- package/dist/services/local-db/mailboxes/LocalMailbox.js.map +1 -0
- package/dist/services/mime-service/MimeNode.js +1 -1
- package/dist/services/mime-service/MimeNode.js.map +1 -1
- package/dist/site-editor/editor/HtmlPageEditor.d.ts.map +1 -1
- package/dist/site-editor/editor/HtmlPageEditor.js +34 -1
- package/dist/site-editor/editor/HtmlPageEditor.js.map +1 -1
- package/dist/tests/template-query/template-queries.test.d.ts +2 -0
- package/dist/tests/template-query/template-queries.test.d.ts.map +1 -0
- package/dist/tests/template-query/template-queries.test.js +27 -0
- package/dist/tests/template-query/template-queries.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/web/AppIndex.pack.js +944 -210
- package/dist/web/AppIndex.pack.js.map +1 -1
- package/dist/web/AppIndex.pack.min.js +29 -4
- package/dist/web/AppIndex.pack.min.js.map +1 -1
- package/dist/web/page/files/shares/AddSharePage.d.ts +0 -1
- package/dist/web/page/files/shares/AddSharePage.d.ts.map +1 -1
- package/node_modules/@web-atoms/entity/dist/services/BaseEntityService.d.ts +2 -0
- package/node_modules/@web-atoms/entity/dist/services/BaseEntityService.d.ts.map +1 -1
- package/node_modules/@web-atoms/entity/dist/services/BaseEntityService.js.map +1 -1
- package/node_modules/@web-atoms/entity/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@web-atoms/entity/package.json +1 -1
- package/node_modules/@web-atoms/entity/src/services/BaseEntityService.ts +2 -0
- package/package.json +2 -2
- package/pg-worker.js +15 -0
- package/src/model/model.ts +84 -82
- package/src/public/contact-us/ContactUsApp.tsx +102 -17
- package/src/services/LoginService.ts +5 -0
- package/src/services/local-db/LocalDbService.ts +68 -32
- package/src/services/local-db/core/LocalTable.ts +113 -0
- package/src/services/local-db/core/TemplateQuery.ts +135 -0
- package/src/services/local-db/mailboxes/LocalMailbox.ts +60 -0
- package/src/services/mime-service/MimeNode.ts +1 -1
- package/src/site-editor/editor/HtmlPageEditor.tsx +31 -1
- package/src/tests/template-query/template-queries.test.ts +22 -0
- package/dist/services/local-db/SqlDbFactory.d.ts +0 -47
- package/dist/services/local-db/SqlDbFactory.d.ts.map +0 -1
- package/dist/services/local-db/SqlDbFactory.js +0 -179
- package/dist/services/local-db/SqlDbFactory.js.map +0 -1
- 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:
|
|
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
|
|
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
|
|
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
|
-
|
|
2
|
-
import DISingleton from "@web-atoms/core/dist/di/DISingleton";
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
+
}
|
|
@@ -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)}
|
|
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
|
+
}
|