@social-mail/social-mail-client 1.2.64 → 1.2.66

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 (58) hide show
  1. package/dist/admin/AdminAppIndex.pack.js +299 -42
  2. package/dist/admin/AdminAppIndex.pack.js.map +1 -1
  3. package/dist/admin/AdminAppIndex.pack.min.js +61 -5
  4. package/dist/admin/AdminAppIndex.pack.min.js.map +1 -1
  5. package/dist/model/model.d.ts +120 -2
  6. package/dist/model/model.d.ts.map +1 -1
  7. package/dist/model/model.js +90 -1
  8. package/dist/model/model.js.map +1 -1
  9. package/dist/public/channel/BlogService.d.ts +4 -0
  10. package/dist/public/channel/BlogService.d.ts.map +1 -1
  11. package/dist/public/channel/BlogService.js +13 -2
  12. package/dist/public/channel/BlogService.js.map +1 -1
  13. package/dist/public/channel/ChannelMessage.d.ts.map +1 -1
  14. package/dist/public/channel/ChannelMessage.js +28 -4
  15. package/dist/public/channel/ChannelMessage.js.map +1 -1
  16. package/dist/public/channel/ChannelMessageList.d.ts.map +1 -1
  17. package/dist/public/channel/ChannelMessageList.js +8 -5
  18. package/dist/public/channel/ChannelMessageList.js.map +1 -1
  19. package/dist/public/channel/ChannelView.pack.js +4116 -3964
  20. package/dist/public/channel/ChannelView.pack.js.map +1 -1
  21. package/dist/public/channel/ChannelView.pack.min.js +4 -4
  22. package/dist/public/channel/ChannelView.pack.min.js.map +1 -1
  23. package/dist/public/channel/UrlService.d.ts +4 -0
  24. package/dist/public/channel/UrlService.d.ts.map +1 -0
  25. package/dist/public/channel/UrlService.js +22 -0
  26. package/dist/public/channel/UrlService.js.map +1 -0
  27. package/dist/tsconfig.tsbuildinfo +1 -1
  28. package/dist/web/AppIndex.pack.js +300 -43
  29. package/dist/web/AppIndex.pack.js.map +1 -1
  30. package/dist/web/AppIndex.pack.min.js +62 -6
  31. package/dist/web/AppIndex.pack.min.js.map +1 -1
  32. package/dist/web/drawer/AppDrawer.js +1 -1
  33. package/dist/web/drawer/AppDrawer.js.map +1 -1
  34. package/dist/web/page/channels/detail/ChannelDetailPage.d.ts +2 -0
  35. package/dist/web/page/channels/detail/ChannelDetailPage.d.ts.map +1 -1
  36. package/dist/web/page/channels/detail/ChannelDetailPage.js +128 -2
  37. package/dist/web/page/channels/detail/ChannelDetailPage.js.map +1 -1
  38. package/dist/web/page/channels/services/ChannelService.d.ts +8 -0
  39. package/dist/web/page/channels/services/ChannelService.d.ts.map +1 -1
  40. package/dist/web/page/channels/services/ChannelService.js +37 -1
  41. package/dist/web/page/channels/services/ChannelService.js.map +1 -1
  42. package/dist/web/page/mails/ConversationPage.d.ts.map +1 -1
  43. package/dist/web/page/mails/ConversationPage.js +6 -3
  44. package/dist/web/page/mails/ConversationPage.js.map +1 -1
  45. package/dist/web/page/mails/service/MailService.d.ts.map +1 -1
  46. package/dist/web/page/mails/service/MailService.js +5 -2
  47. package/dist/web/page/mails/service/MailService.js.map +1 -1
  48. package/package.json +1 -1
  49. package/src/model/model.ts +137 -5
  50. package/src/public/channel/BlogService.ts +14 -2
  51. package/src/public/channel/ChannelMessage.tsx +19 -1
  52. package/src/public/channel/ChannelMessageList.tsx +6 -2
  53. package/src/public/channel/UrlService.ts +12 -0
  54. package/src/web/drawer/AppDrawer.tsx +1 -1
  55. package/src/web/page/channels/detail/ChannelDetailPage.tsx +113 -1
  56. package/src/web/page/channels/services/ChannelService.ts +32 -1
  57. package/src/web/page/mails/ConversationPage.tsx +5 -0
  58. package/src/web/page/mails/service/MailService.ts +3 -2
@@ -27,9 +27,9 @@ export interface IAppFile extends IClrEntity {
27
27
  readonly dateCreated?: DateTime;
28
28
  dateUpdated?: DateTime | null;
29
29
  versions?: number | null;
30
+ sourceUrl?: string;
30
31
  url?: string;
31
32
  downloadUrl?: string;
32
- tempKeys?: string[];
33
33
  fileContent?: IFileContent;
34
34
  creator?: IUser;
35
35
  parent?: IAppFile;
@@ -282,11 +282,12 @@ export interface IEmail extends IClrEntity {
282
282
  readonly received?: boolean;
283
283
  headers?: string | null;
284
284
  headerMessageID?: string | null;
285
+ externalEmailID?: string | null;
285
286
  mdBody?: string | null;
286
287
  dateReceived?: DateTime;
287
288
  dateCreated?: DateTime;
288
289
  dateSent?: DateTime | null;
289
- status?: "queued" | "received" | "draft" | "pending-approval" | "scheduled" | "sent" | "sent-failed";
290
+ status?: "queued" | "received" | "draft" | "published" | "pending-approval" | "scheduled" | "sent" | "sent-failed";
290
291
  forwardCount?: number;
291
292
  replyCount?: number;
292
293
  viewCount?: number;
@@ -316,7 +317,7 @@ export interface IEmail extends IClrEntity {
316
317
  subscriberActivities?: ICollection<ISubscriberActivity>;
317
318
  }
318
319
 
319
- export const EmailStatusArray = [{"label":"queued","value":"queued"},{"label":"received","value":"received"},{"label":"draft","value":"draft"},{"label":"pending-approval","value":"pending-approval"},{"label":"scheduled","value":"scheduled"},{"label":"sent","value":"sent"},{"label":"sent-failed","value":"sent-failed"}];
320
+ export const EmailStatusArray = [{"label":"queued","value":"queued"},{"label":"received","value":"received"},{"label":"draft","value":"draft"},{"label":"published","value":"published"},{"label":"pending-approval","value":"pending-approval"},{"label":"scheduled","value":"scheduled"},{"label":"sent","value":"sent"},{"label":"sent-failed","value":"sent-failed"}];
320
321
 
321
322
  export const Email: IModel<IEmail> = new Model<IEmail>("Email", ["emailID"], { senderName: "",received: false,dateReceived: new DefaultFactory(() => new DateTime()),dateCreated: new DefaultFactory(() => new DateTime()),status: "queued",forwardCount: 0,replyCount: 0,viewCount: 0,originalFileSize: 0 });
322
323
 
@@ -413,7 +414,10 @@ export interface IMailbox extends IClrEntity {
413
414
  dateUpdated?: DateTime;
414
415
  isChannel?: boolean;
415
416
  isPrivateChannel?: boolean;
417
+ requireApproval?: boolean;
416
418
  subscriberCount?: number;
419
+ datePublished?: DateTime | null;
420
+ dateEngaged?: DateTime | null;
417
421
  username?: string;
418
422
  name?: string;
419
423
  emailAddress?: IEmailAddress;
@@ -425,12 +429,13 @@ export interface IMailbox extends IClrEntity {
425
429
  mailboxContacts?: ICollection<IMailboxContact>;
426
430
  threadEmails?: ICollection<IMailboxContactEmail>;
427
431
  syncTargets?: ICollection<IMailboxSyncTarget>;
432
+ channelConfig?: IChannelConfig;
428
433
  subscriberActivities?: ICollection<ISubscriberActivity>;
429
434
  userPins?: ICollection<IUserPin>;
430
435
  userMailboxes?: ICollection<IUserMailbox>;
431
436
  }
432
437
 
433
- export const Mailbox: IModel<IMailbox> = new Model<IMailbox>("Mailbox", ["mailboxID"], { storage: 0,maxStorage: 0,indexAttachments: false,indexImageAttachments: false,dateCreated: new DefaultFactory(() => new DateTime()),dateUpdated: new DefaultFactory(() => new DateTime()),isChannel: false,isPrivateChannel: false,subscriberCount: 0 });
438
+ export const Mailbox: IModel<IMailbox> = new Model<IMailbox>("Mailbox", ["mailboxID"], { storage: 0,maxStorage: 0,indexAttachments: false,indexImageAttachments: false,dateCreated: new DefaultFactory(() => new DateTime()),dateUpdated: new DefaultFactory(() => new DateTime()),isChannel: false,isPrivateChannel: false,requireApproval: false,subscriberCount: 0 });
434
439
 
435
440
  export interface IMailboxEmail extends IClrEntity {
436
441
  mailboxID?: number;
@@ -455,6 +460,7 @@ export interface IMailboxContact extends IClrEntity {
455
460
  canContribute?: boolean;
456
461
  priority?: number;
457
462
  blocked?: boolean;
463
+ mute?: boolean;
458
464
  status?: "invited" | "added" | "active" | "unsubscribed" | "blocked";
459
465
  emailAddressText?: string;
460
466
  name?: string;
@@ -466,7 +472,7 @@ export interface IMailboxContact extends IClrEntity {
466
472
 
467
473
  export const MailboxContactStatusArray = [{"label":"invited","value":"invited"},{"label":"added","value":"added"},{"label":"active","value":"active"},{"label":"unsubscribed","value":"unsubscribed"},{"label":"blocked","value":"blocked"}];
468
474
 
469
- export const MailboxContact: IModel<IMailboxContact> = new Model<IMailboxContact>("MailboxContact", ["mailboxID","emailAddressID"], { dateCreated: new DefaultFactory(() => new DateTime()),dateUpdated: new DefaultFactory(() => new DateTime()),unread: 0,total: 0,isOwner: false,canContribute: false,priority: 0,blocked: false,status: "invited" });
475
+ export const MailboxContact: IModel<IMailboxContact> = new Model<IMailboxContact>("MailboxContact", ["mailboxID","emailAddressID"], { dateCreated: new DefaultFactory(() => new DateTime()),dateUpdated: new DefaultFactory(() => new DateTime()),unread: 0,total: 0,isOwner: false,canContribute: false,priority: 0,blocked: false,mute: false,status: "invited" });
470
476
 
471
477
  export interface IMailboxContactEmail extends IClrEntity {
472
478
  mailboxID?: number;
@@ -521,6 +527,19 @@ export const MailboxSyncTargetLastSyncStatusArray = [{"label":"none","value":"no
521
527
 
522
528
  export const MailboxSyncTarget: IModel<IMailboxSyncTarget> = new Model<IMailboxSyncTarget>("MailboxSyncTarget", ["syncTargetID"], { direction: "in",protocol: "imap",username: "",host: "",port: 0,isSecure: false,syncAllFolders: false,syncJunkMails: false,intervalSeconds: 0,active: false });
523
529
 
530
+ export interface IChannelConfig extends IClrEntity {
531
+ mailboxID?: number;
532
+ inviteContent?: string | null;
533
+ welcomeContent?: string | null;
534
+ sendInviteOnEngagement?: boolean;
535
+ memberOnlyEngagement?: boolean;
536
+ notSafeForWork?: boolean;
537
+ postFooter?: string | null;
538
+ mailbox?: IMailbox;
539
+ }
540
+
541
+ export const ChannelConfig: IModel<IChannelConfig> = new Model<IChannelConfig>("ChannelConfig", ["mailboxID"], { sendInviteOnEngagement: false,memberOnlyEngagement: false,notSafeForWork: false });
542
+
524
543
  export interface ISubscriberActivity extends IClrEntity {
525
544
  readonly activityID?: number;
526
545
  mailboxID?: number;
@@ -585,6 +604,7 @@ export interface IUser extends IClrEntity {
585
604
  userMailboxes?: ICollection<IUserMailbox>;
586
605
  authFactors?: ICollection<IUserAuthFactor>;
587
606
  publishedWebSiteVersions?: ICollection<IWebSiteFolderVersion>;
607
+ licenses?: ICollection<IAppLicense>;
588
608
  }
589
609
 
590
610
  export const UserStatusArray = [{"label":"active","value":"active"},{"label":"blocked","value":"blocked"},{"label":"locked","value":"locked"},{"label":"change-password","value":"change-password"},{"label":"external","value":"external"}];
@@ -718,3 +738,115 @@ export interface ITag extends IClrEntity {
718
738
  }
719
739
 
720
740
  export const Tag: IModel<ITag> = new Model<ITag>("Tag", ["tagID"], { name: "",isSystemTag: false });
741
+
742
+ export interface IProduct extends IClrEntity {
743
+ productID?: string;
744
+ name?: string;
745
+ description?: string | null;
746
+ prices?: ICollection<IProductPrice>;
747
+ }
748
+
749
+ export const Product: IModel<IProduct> = new Model<IProduct>("Product", ["productID"], { name: "" });
750
+
751
+ export interface IProductPrice extends IClrEntity {
752
+ priceID?: string;
753
+ productID?: string;
754
+ quantity?: number;
755
+ amount?: number;
756
+ isActive?: boolean;
757
+ currency?: string;
758
+ product?: IProduct;
759
+ licenses?: ICollection<IAppLicense>;
760
+ }
761
+
762
+ export const ProductPrice: IModel<IProductPrice> = new Model<IProductPrice>("ProductPrice", ["priceID"], { quantity: 0,amount: 0,isActive: false,currency: "" });
763
+
764
+ export interface IAppLicense extends IClrEntity {
765
+ readonly licenseID?: number;
766
+ customerID?: number;
767
+ priceID?: string;
768
+ dateCreated?: DateTime;
769
+ dateExpiry?: DateTime;
770
+ dateUpdated?: DateTime;
771
+ maxCores?: number;
772
+ licenseCode?: string;
773
+ customer?: IUser;
774
+ price?: IProductPrice;
775
+ }
776
+
777
+ export const AppLicense: IModel<IAppLicense> = new Model<IAppLicense>("AppLicense", ["licenseID","priceID"], { dateCreated: new DefaultFactory(() => new DateTime()),dateExpiry: new DefaultFactory(() => new DateTime()),dateUpdated: new DefaultFactory(() => new DateTime()),maxCores: 0,licenseCode: "" });
778
+
779
+ export interface IVirtualServer extends IClrEntity {
780
+ readonly serverID?: number;
781
+ serverName?: string;
782
+ ownerEmailAddress?: string | null;
783
+ ownerPhoneNumber?: string | null;
784
+ active?: boolean;
785
+ bucketName?: string | null;
786
+ database?: string | null;
787
+ host?: string;
788
+ forwardHost?: string | null;
789
+ forwardPublicKey?: string | null;
790
+ environmentVariables?: string | null;
791
+ maxStorageGB?: number;
792
+ dnsZones?: ICollection<IDnsZone>;
793
+ }
794
+
795
+ export const VirtualServer: IModel<IVirtualServer> = new Model<IVirtualServer>("VirtualServer", ["serverID"], { serverName: "",active: false,host: "",maxStorageGB: 0 });
796
+
797
+ export interface IDnsZone extends IClrEntity {
798
+ readonly zoneID?: number;
799
+ serverID?: number;
800
+ zone?: string;
801
+ keySigningKeyID?: number;
802
+ zoneSigningKeyID?: number;
803
+ dateCreated?: DateTime;
804
+ dateUpdated?: DateTime;
805
+ signed?: string | null;
806
+ unsigned?: string | null;
807
+ virtualServer?: IVirtualServer;
808
+ ksk?: IDnsSigningKey;
809
+ zsk?: IDnsSigningKey;
810
+ records?: ICollection<IDnsZoneRecord>;
811
+ }
812
+
813
+ export const DnsZone: IModel<IDnsZone> = new Model<IDnsZone>("DnsZone", ["zoneID"], { zone: "",dateCreated: new DefaultFactory(() => new DateTime()),dateUpdated: new DefaultFactory(() => new DateTime()) });
814
+
815
+ export interface IDnsZoneRecord extends IClrEntity {
816
+ readonly recordID?: number;
817
+ zoneID?: number;
818
+ disabled?: boolean;
819
+ name?: string;
820
+ type?: "A" | "AAAA" | "CNAME" | "MX" | "TXT" | "PTR" | "SRV" | "CAA" | "NS" | "SOA";
821
+ isAlias?: boolean;
822
+ value?: string;
823
+ ttl?: number;
824
+ dateCreated?: DateTime;
825
+ dateUpdated?: DateTime;
826
+ notes?: string | null;
827
+ signature?: string | null;
828
+ expiryDate?: DateTime | null;
829
+ readonly fqdn?: string | null;
830
+ hosted?: boolean;
831
+ zone?: IDnsZone;
832
+ }
833
+
834
+ export const DnsZoneRecordTypeArray = [{"label":"A","value":"A"},{"label":"AAAA","value":"AAAA"},{"label":"CNAME","value":"CNAME"},{"label":"MX","value":"MX"},{"label":"TXT","value":"TXT"},{"label":"PTR","value":"PTR"},{"label":"SRV","value":"SRV"},{"label":"CAA","value":"CAA"},{"label":"NS","value":"NS"},{"label":"SOA","value":"SOA"}];
835
+
836
+ export const DnsZoneRecord: IModel<IDnsZoneRecord> = new Model<IDnsZoneRecord>("DnsZoneRecord", ["recordID"], { disabled: false,name: "",type: "A",isAlias: false,value: "",ttl: 0,dateCreated: new DefaultFactory(() => new DateTime()),dateUpdated: new DefaultFactory(() => new DateTime()),hosted: false });
837
+
838
+ export interface IDnsSigningKey extends IClrEntity {
839
+ readonly keyID?: number;
840
+ publicKey?: string;
841
+ digestAlgorithm?: number;
842
+ signingAlgorithm?: number;
843
+ digest?: string;
844
+ flags?: number;
845
+ dateCreated?: DateTime;
846
+ expiryDate?: DateTime;
847
+ fileName?: string;
848
+ kskZones?: ICollection<IDnsZone>;
849
+ zskZones?: ICollection<IDnsZone>;
850
+ }
851
+
852
+ export const DnsSigningKey: IModel<IDnsSigningKey> = new Model<IDnsSigningKey>("DnsSigningKey", ["keyID"], { publicKey: "",digestAlgorithm: 0,signingAlgorithm: 0,digest: "",flags: 0,dateCreated: new DefaultFactory(() => new DateTime()),expiryDate: new DefaultFactory(() => new DateTime()),fileName: "" });
@@ -15,6 +15,16 @@ export default class BlogService {
15
15
  @Inject
16
16
  downloader: EmailContentDownloader;
17
17
 
18
+ async getEmail({
19
+ emailID,
20
+ channelID
21
+ }) {
22
+ const q = this.entityService.query(Email, "channelEmails", channelID)
23
+ .where({ emailID }, (p) => (x) => x.emailID === p.emailID)
24
+ .include((x) => [x.children]);
25
+ return q.firstOrDefault();
26
+ }
27
+
18
28
  async getEmails({
19
29
  channelID = 0,
20
30
  search = "",
@@ -43,12 +53,14 @@ export default class BlogService {
43
53
  }
44
54
 
45
55
  async getChannel(channelID?, emailAddress?: any) {
46
- let q = this.entityService.query(Mailbox, "channels", emailAddress);
56
+ let q = this.entityService.query(Mailbox, "channels", emailAddress)
57
+ .where((x) => x.datePublished !== null);
47
58
  if (channelID) {
48
59
  q = q.where({ channelID}, (p) => (x) => x.mailboxID === p.channelID);
49
60
  }
50
- const c = await this.entityService.query(Mailbox, "channels", emailAddress)
61
+ const c = await q
51
62
  .include((x) => x.emailAddress)
63
+ .orderByDescending((x) => x.datePublished)
52
64
  .firstOrDefault();
53
65
  return c;
54
66
  }
@@ -1,10 +1,16 @@
1
1
  import InjectProperty from "@web-atoms/core/dist/core/InjectProperty";
2
2
  import { AtomControl } from "@web-atoms/core/dist/web/controls/AtomControl";
3
3
  import BlogService from "./BlogService";
4
+ import XNode from "@web-atoms/core/dist/core/XNode";
5
+ import { DateFormats } from "../../common/date/DateFormats";
6
+ import { renderMail } from "../../web/page/mails/service/ConversationService";
4
7
 
5
8
  const url = (window as any).pageUrl as URL;
6
9
 
7
- const {1: channelID = 1, 2: emailID = 1 } = /^\/channels\/(\d+)\/(\d+)\//.exec(url.pathname) ?? {} as any;
10
+ let {1: channelID = 1, 2: emailID = 82 } = /^\/channels\/([^\/]+)\/([^\/]+)\//.exec(url.pathname) ?? {} as any;
11
+
12
+ channelID = parseInt(channelID, 36);
13
+ emailID = parseInt(emailID, 36);
8
14
 
9
15
  export default class ChannelMessage extends AtomControl {
10
16
 
@@ -13,6 +19,18 @@ export default class ChannelMessage extends AtomControl {
13
19
 
14
20
  async init() {
15
21
  const { items: [ email ]} = await this.blogService.getEmails({ channelID, start: 0, size: 1, cancelToken: null});
22
+
23
+ this.renderer = <section data-element="blog-item">
24
+ <h4 data-element="title">
25
+ <label text={email.subject}/>
26
+ </h4>
27
+ <h6 data-element="date" text={DateFormats.relative.short(email.dateSent)}/>
28
+ <h6 text={email.senderName}/>
29
+ <section
30
+ data-element="content">
31
+ <section { ... renderMail({ email })}/>
32
+ </section>
33
+ </section>;
16
34
  }
17
35
 
18
36
  }
@@ -10,6 +10,7 @@ import { IMailbox, IEmail } from "../../model/model";
10
10
  import { renderMail } from "../../web/page/mails/service/ConversationService";
11
11
  import BlogService from "./BlogService";
12
12
  import { channelViewStyle } from "./channelViewStyle";
13
+ import { UrlService } from "./UrlService";
13
14
 
14
15
  export default class ChannelView extends AtomControl {
15
16
 
@@ -29,12 +30,15 @@ export default class ChannelView extends AtomControl {
29
30
 
30
31
  async init() {
31
32
 
32
- const { channelID, emailAddress } = (window as any).socialMail ?? {};
33
+ let { channelID, emailAddress } = (window as any).socialMail ?? {};
33
34
 
34
35
  this.element.setAttribute("data-blog-list", "blog-list");
35
36
 
36
37
  this.channel = await this.blogService.getChannel(emailAddress, channelID);
37
38
 
39
+ channelID = this.channel.mailboxID;
40
+ emailAddress = this.channel.emailAddress.emailAddress;
41
+
38
42
  this.renderer = <div data-layout="flex-center">
39
43
  <span text="..."/>
40
44
  </div>;
@@ -68,7 +72,7 @@ export default class ChannelView extends AtomControl {
68
72
  itemRenderer={(email: IEmail) => {
69
73
  return <section data-element="blog-item">
70
74
  <h4 data-element="title">
71
- <a href={`/channels/${this.channel.mailboxID}/${email.emailID}/${email.subject?.trim() ?? ""}`} text={email.subject}></a>
75
+ <a href={UrlService.toBlogUrl(channelID, email.emailID, email.subject)} text={email.subject}></a>
72
76
  </h4>
73
77
  <h6 data-element="date" text={DateFormats.relative.short(email.dateSent)}/>
74
78
  <h6 text={email.senderName}/>
@@ -0,0 +1,12 @@
1
+ export const UrlService = {
2
+
3
+ toBlogUrl: (channelID: number, emailID: number, subject: string ) => {
4
+ subject ||= "";
5
+ if(subject.length > 400) {
6
+ subject = subject.substring(0, 400);
7
+ }
8
+ subject = subject.replace(/[^\p{L}\p{N}]+/ug, "-");
9
+ subject = encodeURIComponent(subject).replace(/\%20/g, "-");
10
+ return `/channels/${Number(channelID).toString(36)}/${Number(emailID).toString(36)}/${subject}`;
11
+ }
12
+ };
@@ -89,7 +89,7 @@ export default class AppDrawer extends Drawer {
89
89
  commandLink={null}
90
90
  />
91
91
  { ... channels.map((mb) => <MenuItem
92
- commandLink={AppCommands.openMailbox.displayRoute({ id: mb.mailboxID })}
92
+ commandLink={AppCommands.openChannel.displayRoute({ id: mb.mailboxID })}
93
93
  label={mb.emailAddress.name}
94
94
  description={mb.emailAddress.emailAddress}
95
95
  title={mb.emailAddress.emailAddress}
@@ -1,7 +1,76 @@
1
1
  import { ContentPage } from "@web-atoms/web-controls/dist/mobile-app/MobileApp";
2
- import { IMailbox } from "../../../../model/model";
2
+ import { IEmail, IMailbox } from "../../../../model/model";
3
3
  import InjectProperty from "@web-atoms/core/dist/core/InjectProperty";
4
4
  import MailboxService from "../../../../common/pages/mailboxes/services/MailboxService";
5
+ import XNode from "@web-atoms/core/dist/core/XNode";
6
+ import AppCommands from "../../../commands/AppCommands";
7
+ import AtomRepeater from "@web-atoms/web-controls/dist/basic/AtomRepeater";
8
+ import ChannelService from "../services/ChannelService";
9
+ import InfiniteRepeater from "../../../../common/controls/repeater/InfiniteRepeater";
10
+ import Bind from "@web-atoms/core/dist/core/Bind";
11
+ import LimitedText from "@web-atoms/web-controls/dist/basic/LimitedText";
12
+ import { renderMail } from "../../mails/service/ConversationService";
13
+ import { DateFormats } from "../../../../common/date/DateFormats";
14
+ import styled from "@web-atoms/core/dist/style/styled";
15
+
16
+ const css = styled.css `
17
+
18
+ & [data-blog-list=blog-list] {
19
+
20
+
21
+ display: flex;
22
+ flex-direction: column;
23
+ gap: 10px;
24
+
25
+ & > * {
26
+ border: solid 1px lightgray;
27
+ border-radius: 5px;
28
+ display: inline-grid;
29
+ grid-template-columns: 5px 1fr auto auto 5px;
30
+ grid-template-rows: 5px auto auto 1fr auto 5px;
31
+
32
+ & > [data-element=bg] {
33
+ grid-row: 1 / span 3;
34
+ grid-column: 1/ span 5;
35
+ background-color: lightgrey;
36
+ z-index: -1;
37
+ }
38
+
39
+ & > h4, & > h6 {
40
+ margin-block-start: 0;
41
+ margin-block-end: 0;
42
+ }
43
+
44
+ & > [data-element=actions] {
45
+ grid-row: 2;
46
+ grid-column: 4;
47
+ }
48
+
49
+ & > [data-element=title] {
50
+ grid-row: 3;
51
+ grid-column: 2 / span 2;
52
+ }
53
+
54
+ & > [data-element=date] {
55
+ grid-row: 2;
56
+ grid-column: 3;
57
+ }
58
+
59
+ & > [data-element=author] {
60
+ grid-row: 2;
61
+ grid-column: 2;
62
+ }
63
+
64
+ & > [data-element=content] {
65
+ grid-row: 4;
66
+ grid-column: 2 / span 4;
67
+ }
68
+ }
69
+
70
+ }
71
+
72
+ `.installLocal();
73
+
5
74
 
6
75
  export default class ChannelDetailPage extends ContentPage {
7
76
 
@@ -9,12 +78,55 @@ export default class ChannelDetailPage extends ContentPage {
9
78
 
10
79
  public mailbox: IMailbox;
11
80
 
81
+ public search = "";
82
+
12
83
  @InjectProperty
13
84
  private mailboxService: MailboxService;
14
85
 
86
+ @InjectProperty
87
+ private channelService: ChannelService;
88
+
15
89
  async init() {
90
+
16
91
  this.mailbox = await this.mailboxService.get(this.parameters.id);
17
92
  this.title = `${this.mailbox.emailAddress.name || this.mailbox.name} (${this.mailbox.emailAddress.emailAddress})`;
93
+
94
+ this.headerRenderer = () => <div data-layout="header-row">
95
+ <input type="search" value={Bind.twoWaysImmediate(() => this.search)}/>
96
+ <a
97
+ data-click-event="route"
98
+ href={AppCommands.openSubscribers.displayRoute({ id: this.parameters.id})}>Subscribers</a>
99
+ </div>;
100
+
101
+ this.renderer = <section class={css}>
102
+ <AtomRepeater
103
+ data-blog-list="blog-list"
104
+ { ... InfiniteRepeater.pagedItems((start) =>
105
+ (c, e, cancelToken) =>
106
+ this.channelService.getMails({
107
+ channelID: this.parameters.id,
108
+ search: this.search,
109
+ cancelToken
110
+ })
111
+ )}
112
+ itemRenderer={(email: IEmail) => <section data-element="blog-item">
113
+ <div data-element="bg"/>
114
+ <h4 data-element="title"
115
+ text={email.subject}/>
116
+ <h6 data-element="date" text={DateFormats.relative.short(email.dateSent)}/>
117
+ <h6 data-element="author" text={email.senderName}/>
118
+ <i
119
+ data-element="actions"
120
+ class="fa-light fa-square-ellipsis-vertical"/>
121
+ <section
122
+ data-element="content">
123
+ <LimitedText height={200}>
124
+ <section { ... renderMail({ email })}/>
125
+ </LimitedText>
126
+ </section>
127
+ </section>}
128
+ />
129
+ </section>;
18
130
  }
19
131
 
20
132
  }
@@ -1,7 +1,7 @@
1
1
  import DITransient from "@web-atoms/core/dist/di/DITransient";
2
2
  import { Inject } from "@web-atoms/core/dist/di/Inject";
3
3
  import EntityService from "../../../../services/EntityService";
4
- import { IMailboxContact, Mailbox, MailboxContact } from "../../../../model/model";
4
+ import { Email, IMailboxContact, Mailbox, MailboxContact } from "../../../../model/model";
5
5
  import { Sql } from "../../../../common/Sql";
6
6
 
7
7
  @DITransient({})
@@ -10,6 +10,37 @@ export default class ChannelService {
10
10
  @Inject
11
11
  private entityService: EntityService;
12
12
 
13
+ public async getMails({
14
+ channelID,
15
+ published = true,
16
+ start = 0,
17
+ size = 100,
18
+ search = "",
19
+ cancelToken
20
+ }) {
21
+ let q = this.entityService.query(Email);
22
+
23
+ if (channelID) {
24
+ q = q.where({ channelID}, (p) => (x) => x.mailboxes.some((m) => m.mailboxID === p.channelID));
25
+ }
26
+
27
+ if (published) {
28
+ q = q.where((x) => x.status === "published");
29
+ } else {
30
+ q = q.where((x) => x.status !== "published");
31
+ }
32
+
33
+ if (search) {
34
+ if (!/\%/.test(search)) {
35
+ search = search + "%";
36
+ }
37
+ q = q.where({ search }, (p) => (x) => Sql.text.like(x.subject, p.search));
38
+ }
39
+
40
+ return q.orderByDescending((x) => x.emailID)
41
+ .toPagedList({ start, size, cancelToken });
42
+ }
43
+
13
44
  public async getMailbox(id: number) {
14
45
  return this.entityService.query(Mailbox)
15
46
  .include((x) => x.emailAddress)
@@ -288,6 +288,11 @@ export default class ConversationPage extends ContentPage {
288
288
  void InlinePopupControl.showPopup(ce.target as HTMLElement, <div
289
289
  data-click-event="none"
290
290
  data-click-animate="none">
291
+ { thread.email?.status === "published" &&
292
+ <MenuItem
293
+ icon="fas fa-delete"
294
+ data-click-event="un-publish-mail"/>
295
+ }
291
296
  <MenuItem
292
297
  eventClick={() => PageNavigator.pushPage(MailDetailPage, thread.email)}
293
298
  icon="fas fa-eye"
@@ -52,8 +52,9 @@ export default class MailService {
52
52
  }
53
53
 
54
54
  async send(email: IEmail) {
55
- if (email.textBody) {
56
- email.subject ||= email.textBody.substring(0, 80);
55
+ const { textBody } = email;
56
+ if (textBody) {
57
+ email.subject ||= textBody.split("\n")[0].substring(0, 80);
57
58
  }
58
59
  for (const iterator of email.recipients) {
59
60
  if (iterator.emailAddressText) {