@social-mail/social-mail-client 1.2.547 → 1.2.549

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 (74) hide show
  1. package/dist/admin/AdminAppIndex.pack.js +3 -2
  2. package/dist/admin/AdminAppIndex.pack.js.map +1 -1
  3. package/dist/admin/AdminAppIndex.pack.min.js +2 -2
  4. package/dist/admin/AdminAppIndex.pack.min.js.map +1 -1
  5. package/dist/common/cache/CacheTTL.d.ts +1 -0
  6. package/dist/common/cache/CacheTTL.d.ts.map +1 -1
  7. package/dist/common/cache/CacheTTL.js +2 -1
  8. package/dist/common/cache/CacheTTL.js.map +1 -1
  9. package/dist/common/toBase36.d.ts +4 -0
  10. package/dist/common/toBase36.d.ts.map +1 -1
  11. package/dist/common/toBase36.js +33 -1
  12. package/dist/common/toBase36.js.map +1 -1
  13. package/dist/public/channel/BlogService.d.ts +4 -5
  14. package/dist/public/channel/BlogService.d.ts.map +1 -1
  15. package/dist/public/channel/BlogService.js +25 -9
  16. package/dist/public/channel/BlogService.js.map +1 -1
  17. package/dist/public/channel/ChannelMessage.d.ts +0 -12
  18. package/dist/public/channel/ChannelMessage.d.ts.map +1 -1
  19. package/dist/public/channel/ChannelMessage.js +3 -134
  20. package/dist/public/channel/ChannelMessage.js.map +1 -1
  21. package/dist/public/channel/ChannelMessageList.d.ts +0 -12
  22. package/dist/public/channel/ChannelMessageList.d.ts.map +1 -1
  23. package/dist/public/channel/ChannelMessageList.js +3 -119
  24. package/dist/public/channel/ChannelMessageList.js.map +1 -1
  25. package/dist/public/channel/ChannelView.d.ts +16 -3
  26. package/dist/public/channel/ChannelView.d.ts.map +1 -1
  27. package/dist/public/channel/ChannelView.js +174 -11
  28. package/dist/public/channel/ChannelView.js.map +1 -1
  29. package/dist/public/channel/ChannelView.pack.js +776 -831
  30. package/dist/public/channel/ChannelView.pack.js.map +1 -1
  31. package/dist/public/channel/ChannelView.pack.min.js +111 -129
  32. package/dist/public/channel/ChannelView.pack.min.js.map +1 -1
  33. package/dist/public/channel/PostSummary.d.ts +6 -0
  34. package/dist/public/channel/PostSummary.d.ts.map +1 -0
  35. package/dist/public/channel/PostSummary.js +61 -0
  36. package/dist/public/channel/PostSummary.js.map +1 -0
  37. package/dist/public/channel/UrlService.d.ts +2 -2
  38. package/dist/public/channel/UrlService.d.ts.map +1 -1
  39. package/dist/public/channel/UrlService.js +12 -6
  40. package/dist/public/channel/UrlService.js.map +1 -1
  41. package/dist/public/channel/controls/PostList.d.ts.map +1 -1
  42. package/dist/public/channel/controls/PostList.js +9 -4
  43. package/dist/public/channel/controls/PostList.js.map +1 -1
  44. package/dist/public/channel/controls/TagCloud.d.ts.map +1 -1
  45. package/dist/public/channel/controls/TagCloud.js +8 -3
  46. package/dist/public/channel/controls/TagCloud.js.map +1 -1
  47. package/dist/public/channel/styles/channelViewStyle.d.ts.map +1 -1
  48. package/dist/public/channel/styles/channelViewStyle.js +17 -0
  49. package/dist/public/channel/styles/channelViewStyle.js.map +1 -1
  50. package/dist/public/store/Store.pack.js +33 -1
  51. package/dist/public/store/Store.pack.js.map +1 -1
  52. package/dist/public/store/Store.pack.min.js +1 -1
  53. package/dist/public/store/Store.pack.min.js.map +1 -1
  54. package/dist/site-editor/editor/WebSitePageEditor.d.ts.map +1 -1
  55. package/dist/site-editor/editor/WebSitePageEditor.js +1 -1
  56. package/dist/site-editor/editor/WebSitePageEditor.js.map +1 -1
  57. package/dist/tsconfig.tsbuildinfo +1 -1
  58. package/dist/web/AppIndex.pack.js +3 -2
  59. package/dist/web/AppIndex.pack.js.map +1 -1
  60. package/dist/web/AppIndex.pack.min.js +2 -2
  61. package/dist/web/AppIndex.pack.min.js.map +1 -1
  62. package/package.json +1 -1
  63. package/src/common/cache/CacheTTL.ts +2 -1
  64. package/src/common/toBase36.ts +36 -0
  65. package/src/public/channel/BlogService.ts +18 -4
  66. package/src/public/channel/ChannelMessage.tsx +113 -113
  67. package/src/public/channel/ChannelMessageList.tsx +110 -110
  68. package/src/public/channel/ChannelView.tsx +159 -7
  69. package/src/public/channel/PostSummary.tsx +46 -0
  70. package/src/public/channel/UrlService.ts +7 -7
  71. package/src/public/channel/controls/PostList.tsx +7 -2
  72. package/src/public/channel/controls/TagCloud.tsx +7 -1
  73. package/src/public/channel/styles/channelViewStyle.ts +17 -0
  74. package/src/site-editor/editor/WebSitePageEditor.tsx +3 -1
@@ -1,15 +1,167 @@
1
+
2
+
3
+ import InjectProperty from "@web-atoms/core/dist/core/InjectProperty";
4
+ import { AtomControl } from "@web-atoms/core/dist/web/controls/AtomControl";
5
+ import BlogService from "./BlogService";
6
+ import XNode from "@web-atoms/core/dist/core/XNode";
7
+ import Action from "@web-atoms/core/dist/view-model/Action";
8
+ import EmojiPopup from "./emoji/EmojiPopup";
1
9
  import "../../style/GlobalStyle";
10
+ import "./styles/channelViewStyle";
11
+ import PopupService from "@web-atoms/core/dist/web/services/PopupService";
12
+ import { IEmail, IMailbox } from "../../model/model";
13
+ import AtomRepeater from "@web-atoms/web-controls/dist/basic/AtomRepeater";
14
+ import PostView from "./PostView";
15
+ import CopyIcon from "../../common/controls/buttons/CopyIcon";
16
+ import TagCloud from "./controls/TagCloud";
17
+ import PopularPosts from "./controls/PostList";
18
+ import RecentPosts from "./controls/RecentPosts";
19
+ import BlogHeader from "./controls/BlogHeader";
2
20
  import Pack from "@web-atoms/core/dist/Pack";
21
+ import InfiniteRepeater from "../../common/controls/repeater/InfiniteRepeater";
22
+ import Bind from "@web-atoms/core/dist/core/Bind";
23
+ import PostSummary from "./PostSummary";
24
+ import { Base36 } from "../../common/toBase36";
25
+
26
+ const url = (window as any).pageUrl as URL ?? new URL(location.href);
3
27
 
28
+ let {
29
+ 1: channelID = url.searchParams.get("p0") ,
30
+ 2: type = url.searchParams.get("p1"),
31
+ 3: p2 = url.searchParams.get("p2"),
32
+ }
33
+ = /^\/channels\/([^\/]+)\/([^\/]+)\/([^\/]+)\//.exec(url?.pathname) ?? {} as any;
4
34
 
5
- const url = new URL(location.href);
6
- (window as any).pageUrl = url;
7
- const { default: view } = await (/\/channels\//.test(url.pathname) || url.searchParams.has("detail")
8
- ? import("./ChannelMessage")
9
- : import("./ChannelMessageList")) as any;
35
+ if (channelID) {
36
+ channelID = Base36.decode(channelID) as any;
37
+ }
38
+ type ||= "";
39
+ if (p2) {
40
+ p2 = Base36.decode(p2);
41
+ }
42
+ const emailID = type === "p" ? p2 : void 0;
43
+
44
+ const commentIcon = "💬";
10
45
 
11
46
  @Pack
12
- export default class Empty extends view {
47
+ export default class ChannelMessage extends AtomControl {
13
48
 
14
- }
49
+ @InjectProperty
50
+ blogService: BlogService;
51
+
52
+
53
+ private channel: IMailbox;
54
+ private email: IEmail;
55
+ private search = "";
56
+
57
+ private tagID = type === "t" ? p2 : null;
58
+ private authorID = type === "a" ? p2 : null;
59
+
60
+ private emails: IEmail[] = [];
61
+
62
+ constructor(app, e = document.createElement("section")) {
63
+ super(app, e);
64
+ this.pushInit();
65
+ }
66
+
67
+ async init() {
68
+
69
+ const channel = await this.blogService.getChannel(void 0, channelID, void 0);
70
+ this.channel = channel;
71
+
72
+ if (emailID){
73
+ this.email = await this.blogService.getEmail({ emailID, channelID});
74
+ this.email.engagements ??= [];
75
+ this.emails = [this.email];
76
+ }
77
+
78
+ const search = Bind.twoWaysImmediate(() => this.search);
79
+
80
+ this.element.setAttribute("data-blog-list", "blog-list");
81
+ this.element.setAttribute("data-layout", "flex-center");
82
+ this.renderer = <section>
83
+ <BlogHeader
84
+ search={search}
85
+ channel={channel}/>
86
+ <section>
87
+ { emailID
88
+ ? <AtomRepeater
89
+ items={this.emails}
90
+ event-items-updated={() => this.itemsUpdated()}
91
+ itemRenderer={(x) => <PostView email={x} />}
92
+ />
93
+ : <AtomRepeater
94
+ items={this.emails}
95
+ event-items-updated={() => this.itemsUpdated()}
96
+ data-auto-scroll="false"
97
+ { ... InfiniteRepeater.pagedItems((start) =>
98
+ (c, e, cancelToken) =>
99
+ this.blogService.getEmails({
100
+ channelID: this.channel.mailboxID,
101
+ authorID: this.authorID,
102
+ tagID: this.tagID,
103
+ start,
104
+ search: this.search,
105
+ cancelToken
106
+ })
107
+ )}
108
+ itemRenderer={(x) => <PostSummary mailboxEmail={x} />}
109
+ /> }
110
+ </section>
111
+ <aside>
112
+ <TagCloud/>
113
+ <PopularPosts/>
114
+ <RecentPosts/>
115
+ </aside>
116
+ <footer></footer>
117
+ </section>;
118
+ }
119
+
120
+ itemsUpdated() {
121
+ (window as any).pageReady = true;
122
+ }
123
+
124
+ @Action({ onEvent: "react-email"})
125
+ async reactEmail(email, ce: CustomEvent) {
126
+ const { headerMessageID } = email;
127
+ const emoji: string = await EmojiPopup.showControl(ce.target as any, { parentElement: ce.target as any});
128
+ const { channel } = this;
129
+ const subject = `${emoji}:${Number(email.emailID).toString(36)}:${headerMessageID}`;
130
+ await PopupService.alert({
131
+ message: <div data-layout="vertical-flex">
132
+ To add your emoji to this post, email just the emoji {emoji} with given subject to the channel email address.
133
+ <div>To: {channel.emailAddress.emailAddress} <CopyIcon text={channel.emailAddress.emailAddress}/> </div>
134
+ <div>Subject: {subject} <CopyIcon text={subject}/></div>
135
+ <div>Body: {emoji} <CopyIcon text={emoji}/></div>
136
+ <br/>
137
+ <div>If you have email client configured, you can click on following link.</div>
138
+ <div>
139
+ <a href={`mailto:${channel.emailAddress.emailAddress}?subject=${subject}&body=${emoji}&in-reply-to=${headerMessageID}`}>Email Now</a>
140
+ </div>
141
+ </div>,
142
+ title: "Send Email"
143
+ });
144
+ }
145
+
146
+ @Action({ onEvent: "reply-email"})
147
+ async replyEmail(email, ce: CustomEvent) {
148
+ const emoji = commentIcon;
149
+ const { headerMessageID } = email;
150
+ const { channel } = this;
151
+ const subject = `${emoji}:${Number(email.emailID).toString(36)}:${headerMessageID}`;
152
+ await PopupService.alert({
153
+ message: <div data-layout="vertical-flex">
154
+ To send comment to this post, email us your comment with given subject to the channel email address.
155
+ <div>To: {channel.emailAddress.emailAddress} <CopyIcon text={channel.emailAddress.emailAddress}/></div>
156
+ <div>Subject: {subject} <CopyIcon text={subject}/></div>
157
+ <br/>
158
+ <div>If you have email client configured, you can click on following link.</div>
159
+ <div>
160
+ <a href={`mailto:${channel.emailAddress.emailAddress}?subject=${subject}&in-reply-to=${headerMessageID}`}>Email Now</a>
161
+ </div>
162
+ </div>,
163
+ title: "Send Email"
164
+ });
165
+ }
15
166
 
167
+ }
@@ -0,0 +1,46 @@
1
+ import XNode from "@web-atoms/core/dist/core/XNode";
2
+ import { IEmail, IEmailEngagement, IMailboxEmail } from "../../model/model";
3
+ import { DateFormats } from "../../common/date/DateFormats";
4
+ import { UMD } from "@web-atoms/core/dist/core/types";
5
+ import { renderMail } from "../../web/page/mails/service/ConversationService";
6
+ import { UrlService } from "./UrlService";
7
+
8
+ const commentIcon = "💬";
9
+
10
+ export default function PostSummary ({
11
+ mailboxEmail = void 0 as IMailboxEmail,
12
+ itemPath = void 0 as string
13
+ }) {
14
+
15
+ const { mailboxID, email } = mailboxEmail;
16
+ const { engagements = [] } = email;
17
+
18
+ const isComment = email.parentID !== null;
19
+
20
+ return <section
21
+ data-element="blog-item"
22
+ data-comment={isComment}
23
+ data-item-path={itemPath}>
24
+ <h4 data-element="title">
25
+ <a href={UrlService.toBlogUrl(mailboxEmail)} text={ isComment ? email.senderName : email.subject}/>
26
+ </h4>
27
+ <h6 data-element="date" text={DateFormats.relative.short(email.dateSent)}/>
28
+ {!isComment && <h5 text={email.senderName}/> }
29
+ <section
30
+ data-element="content"
31
+ data-fade="bottom">
32
+ { !email.parentID
33
+ ? <section { ... renderMail({ email })}/>
34
+ : <section text={email.textBody}/>
35
+ }
36
+ </section>
37
+ <footer>
38
+ { ... engagements.map((x: IEmailEngagement) =>
39
+ <span
40
+ data-click-event={ x.engagement === commentIcon ? "view-comments" : "none" }
41
+ data-element="engagements"
42
+ title={`View all ${x.engagement} ${x.count}`}
43
+ text={`${x.engagement} ${x.count}`}/>)}
44
+ </footer>
45
+ </section>;
46
+ }
@@ -1,9 +1,9 @@
1
1
  import { AppLocation } from "../../common/TestMode";
2
- import { IMailboxTag } from "../../model/model";
2
+ import { IMailboxEmail, IMailboxTag } from "../../model/model";
3
3
 
4
4
  export const UrlService = {
5
5
 
6
- toBlogUrl (channelID: number, emailID: number, subject: string ) {
6
+ toBlogUrl ({ mailboxID, emailID, email: { subject } = {} as any }: IMailboxEmail) {
7
7
  subject ||= "";
8
8
  if(subject.length > 400) {
9
9
  subject = subject.substring(0, 400);
@@ -11,21 +11,21 @@ export const UrlService = {
11
11
  subject = subject.replace(/[^\p{L}\p{N}]+/ug, "-");
12
12
  subject = encodeURIComponent(subject).replace(/\%20/g, "-");
13
13
 
14
- const cid = BigInt(channelID).toString(36);
14
+ const cid = BigInt(mailboxID).toString(36);
15
15
  const eid = BigInt(emailID).toString(36);
16
16
 
17
17
  if (AppLocation.isLocalTesting) {
18
- return `?detail=true&cid=${cid}&eid=${eid}`;
18
+ return `?detail=true&p0=${cid}&p1=p&p2=${eid}`;
19
19
  }
20
20
 
21
- return `/channels/${cid}/${eid}/${subject}`;
21
+ return `/channels/${cid}/p/${eid}/${subject}`;
22
22
  },
23
23
 
24
24
  toTagUrl ({ mailboxID, tagID, tag: { name } = {} as any }: IMailboxTag) {
25
25
  const cid = BigInt(mailboxID).toString(36);
26
26
  const eid = BigInt(tagID).toString(36);
27
27
  if (AppLocation.isLocalTesting) {
28
- return `?detail=true&cid=${cid}&eid=${eid}`;
28
+ return `?detail=true&p0=${cid}&p1=t&p2=${eid}`;
29
29
  }
30
30
 
31
31
  name ||= "";
@@ -36,6 +36,6 @@ export const UrlService = {
36
36
  name = encodeURIComponent(name).replace(/\%20/g, "-");
37
37
 
38
38
 
39
- return `/channels/${cid}/${eid}/${name}`;
39
+ return `/channels/${cid}/t/${eid}/${name}`;
40
40
  }
41
41
  };
@@ -12,6 +12,9 @@ import Bind from "@web-atoms/core/dist/core/Bind";
12
12
  import loaderImage from "@social-mail/social-mail-client/content/images/gif/loader.gif";
13
13
  import { Sql } from "../../../common/Sql";
14
14
  import BlogImageService from "../BlogImageService";
15
+ import { CacheTTL } from "../../../common/cache/CacheTTL";
16
+
17
+ const cacheVersion = "1a";
15
18
 
16
19
 
17
20
  styled.css `
@@ -79,7 +82,7 @@ export default class PostList extends AtomRepeater {
79
82
  this.itemRenderer = (x: IMailboxEmail) => <div>
80
83
  <img src={Bind.oneTimeAsync(() => this.blogImageService.getImage(x), loaderImage)}/>
81
84
  <a
82
- href={UrlService.toBlogUrl(x.mailboxID, x.emailID, x.email.subject)}
85
+ href={UrlService.toBlogUrl(x)}
83
86
  title={x.email.subject}
84
87
  target="_blank" text={x.email.subject}></a>
85
88
  </div>;
@@ -98,7 +101,9 @@ export default class PostList extends AtomRepeater {
98
101
  return q
99
102
  .orderByDescending((x) => x.engagementCount)
100
103
  .toPagedList({
101
- size: 5
104
+ size: 5,
105
+ cacheVersion,
106
+ cacheSeconds: CacheTTL.seconds.fifteenMinutes
102
107
  });
103
108
  }
104
109
  }
@@ -5,6 +5,10 @@ import { IMailbox, IMailboxTag, MailboxTag } from "../../../model/model";
5
5
  import XNode from "@web-atoms/core/dist/core/XNode";
6
6
  import styled from "@web-atoms/core/dist/style/styled";
7
7
  import { UrlService } from "../UrlService";
8
+ import { CacheTTL } from "../../../common/cache/CacheTTL";
9
+
10
+ const cacheVersion = "1a";
11
+
8
12
 
9
13
  styled.css `
10
14
  display: flex;
@@ -48,7 +52,9 @@ export default class TagCloud extends AtomRepeater {
48
52
  .include((x) => x.tag)
49
53
  .orderByDescending((x) => x.count)
50
54
  .toPagedList({
51
- size: 20
55
+ size: 20,
56
+ cacheVersion,
57
+ cacheSeconds: CacheTTL.seconds.fifteenMinutes
52
58
  });
53
59
 
54
60
  this.itemRenderer = (x: IMailboxTag) => <a href={UrlService.toTagUrl(x)} text={`# ${x.tag.name}`}></a>;
@@ -38,6 +38,23 @@ box-sizing: border-box;
38
38
  color: canvastext;
39
39
  }
40
40
 
41
+ & [data-fade=bottom] {
42
+ position: relative;
43
+ max-height: 200px;
44
+ overflow: hidden;
45
+ &:after {
46
+ position: absolute;
47
+ left: 0;
48
+ right: 0;
49
+ top: 0;
50
+ bottom: 0;
51
+ content: "";
52
+ margin: 0;
53
+ pointer-events: none;
54
+ background: linear-gradient(to top, rgba(255, 255, 255, 1) 1px, rgba(255, 255, 255, 0) 32px);
55
+ }
56
+ }
57
+
41
58
  & > h4 {
42
59
  grid-row: 1;
43
60
  grid-column: 1 / span 2;
@@ -182,7 +182,9 @@ export default class WebSitePageEditor extends HtmlPageEditor {
182
182
  pending.push(async () => {
183
183
  const stylerFile = await this.cloudFileService.children(this.folder.appFileID, "styles/styler.css")
184
184
  .firstOrDefault();
185
- const stylerContent = await FetchBuilder.get(stylerFile.downloadUrl).responseAsText();
185
+ const stylerContent = stylerFile
186
+ ? await FetchBuilder.get(stylerFile.downloadUrl).responseAsText()
187
+ : null;
186
188
  const newStyle = StylerSet.instance.toString();
187
189
  if (stylerContent !== newStyle) {
188
190
  const stylerCSS = await this.createFile("styler.css", newStyle);