@inboxsdk/core 0.2.28-alpha.3 → 0.2.28-alpha.4

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 (137) hide show
  1. package/inboxsdk.js +1 -1
  2. package/inboxsdk.js.map +1 -1
  3. package/package.json +1 -1
  4. package/src/build/exec.js +26 -0
  5. package/src/build/spawn.js +17 -0
  6. package/src/common/ajax.js +90 -0
  7. package/src/common/cachebust-url.js +10 -0
  8. package/src/common/censorHTMLstring.js +37 -0
  9. package/src/common/censorHTMLtree.js +28 -0
  10. package/src/common/censorJSONTree.js +15 -0
  11. package/src/common/connectivity-test.js +21 -0
  12. package/src/common/defer.js +11 -0
  13. package/src/common/find-parent.js +10 -0
  14. package/src/common/get-extension-id.js +7 -0
  15. package/src/common/get-or-fail.js +8 -0
  16. package/src/common/get-session-id.js +14 -0
  17. package/src/common/get-stack-trace.js +6 -0
  18. package/src/common/html-to-text.js +7 -0
  19. package/src/common/isElementVisible.js +6 -0
  20. package/src/common/load-script.js +127 -0
  21. package/src/common/log-error.js +191 -0
  22. package/src/common/quoted-split.js +18 -0
  23. package/src/common/rate-limit-queuer.js +57 -0
  24. package/src/common/rate-limit.js +14 -0
  25. package/src/common/version.js +6 -0
  26. package/src/inboxsdk-js/loading/platform-implementation-loader.js +27 -0
  27. package/src/injected-js/gmail/constants.js +2 -0
  28. package/src/injected-js/gmail/modify-suggestions.js +106 -0
  29. package/src/injected-js/gmail/setup-gmonkey-handler.js +49 -0
  30. package/src/injected-js/gmail/sync-compose-processor-20220909.js +298 -0
  31. package/src/injected-js/gmail/sync-compose-processor.js +55 -0
  32. package/src/injected-js/gmail/sync-compose-request-processor.js +110 -0
  33. package/src/injected-js/injected-logger.js +50 -0
  34. package/src/injected-js/setupCustomViewEventAssassin.js +90 -0
  35. package/src/platform-implementation-js/constants/nav-item-types.js +19 -0
  36. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-app-menu-item.js +21 -0
  37. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-collapsible-panel.js +28 -0
  38. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-nav-item.js +98 -0
  39. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-support-item.js +4 -0
  40. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/encodeDraftUrlId.js +8 -0
  41. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/get-rfc-message-id-for-gmail-message-id.js +12 -0
  42. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/get-rfc-message-id-for-gmail-thread-id.js +33 -0
  43. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/isIntegratedViewGmail.js +4 -0
  44. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/open-draft-by-message-id.js +44 -0
  45. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/track-gmail-styles.js +95 -0
  46. package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter/get-main-content-element-changed-stream.js +38 -0
  47. package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter/wait-for-gmail-mode-to-settle.js +12 -0
  48. package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter.js +253 -0
  49. package/src/platform-implementation-js/dom-driver/gmail/gmail-page-communicator.js +200 -0
  50. package/src/platform-implementation-js/dom-driver/gmail/gmail-response-processor.js +533 -0
  51. package/src/platform-implementation-js/dom-driver/gmail/gmail-sync-response-processor.js +289 -0
  52. package/src/platform-implementation-js/dom-driver/gmail/is-compose-titlebar-light-color.js +21 -0
  53. package/src/platform-implementation-js/dom-driver/gmail/makePageParserTree.js +28 -0
  54. package/src/platform-implementation-js/dom-driver/gmail/pageParserOptions.js +19 -0
  55. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-app-menu-item-view.js +126 -0
  56. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/add-status-bar.js +101 -0
  57. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/detectClassicRecipientsArea.js +38 -0
  58. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/get-address-information-extractor.js +28 -0
  59. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/get-recipients.js +22 -0
  60. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/gmail-compose-button-view.js +233 -0
  61. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/page-parser.js +223 -0
  62. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/set-recipients.js +157 -0
  63. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/setupLinkPopovers.js +109 -0
  64. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-message-view/attachment-icon.js +5 -0
  65. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-nav-item-view.js +759 -0
  66. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-support-item-view.js +84 -0
  67. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/button-color-classes.js +33 -0
  68. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/button-view.js +325 -0
  69. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/create-accessory-button-view.js +58 -0
  70. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/create-parent-accessory-button-view.js +55 -0
  71. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/modal-button-view.js +96 -0
  72. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/more-dropdown-button-view.js +30 -0
  73. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/more-parent-accessory-button-view.js +10 -0
  74. package/src/platform-implementation-js/dom-driver/gmail/widgets/gmail-dropdown-view.js +22 -0
  75. package/src/platform-implementation-js/driver-common/getAccountUrlPart.js +17 -0
  76. package/src/platform-implementation-js/driver-common/getOriginalMessagePage.js +33 -0
  77. package/src/platform-implementation-js/driver-common/getSyncThreadsForSearch.js +49 -0
  78. package/src/platform-implementation-js/driver-common/gmailAjax.js +65 -0
  79. package/src/platform-implementation-js/driver-common/render-custom-icon.js +16 -0
  80. package/src/platform-implementation-js/driver-common/update-icon.js +90 -0
  81. package/src/platform-implementation-js/lib/ErrorCollector.js +47 -0
  82. package/src/platform-implementation-js/lib/Membrane.js +22 -0
  83. package/src/platform-implementation-js/lib/ScrollableContainByScreen.js +30 -0
  84. package/src/platform-implementation-js/lib/attemptWithRetries.js +16 -0
  85. package/src/platform-implementation-js/lib/common-page-communicator.js +137 -0
  86. package/src/platform-implementation-js/lib/delay-asap.js +12 -0
  87. package/src/platform-implementation-js/lib/delay-idle.js +16 -0
  88. package/src/platform-implementation-js/lib/dom/block-and-reemit-keyboard-events.js +36 -0
  89. package/src/platform-implementation-js/lib/dom/generic-button-view.js +39 -0
  90. package/src/platform-implementation-js/lib/dom/get-insert-before-element.js +39 -0
  91. package/src/platform-implementation-js/lib/dom/insert-element-in-order.js +38 -0
  92. package/src/platform-implementation-js/lib/dom/keyboard-shortcut-stream.js +12 -0
  93. package/src/platform-implementation-js/lib/dom/make-element-child-stream.js +79 -0
  94. package/src/platform-implementation-js/lib/dom/make-element-stream-merger.js +28 -0
  95. package/src/platform-implementation-js/lib/dom/make-element-view-stream.js +15 -0
  96. package/src/platform-implementation-js/lib/dom/make-mutation-observer-chunked-stream.js +15 -0
  97. package/src/platform-implementation-js/lib/dom/make-mutation-observer-stream.js +5 -0
  98. package/src/platform-implementation-js/lib/dom/outsideClicksAndEscape.js +39 -0
  99. package/src/platform-implementation-js/lib/dom/parentNodes.js +8 -0
  100. package/src/platform-implementation-js/lib/dom/querySelectorOrFail.js +6 -0
  101. package/src/platform-implementation-js/lib/dom/reemitClickEventForReact.js +22 -0
  102. package/src/platform-implementation-js/lib/dom/simulate-key.js +46 -0
  103. package/src/platform-implementation-js/lib/dom/simulate-mouse-event.js +16 -0
  104. package/src/platform-implementation-js/lib/dom/trigger-relay-event.js +7 -0
  105. package/src/platform-implementation-js/lib/extract-contact-from-email-contact-string.js +25 -0
  106. package/src/platform-implementation-js/lib/fake-window-resize.js +5 -0
  107. package/src/platform-implementation-js/lib/from-event-target-capture.js +12 -0
  108. package/src/platform-implementation-js/lib/fromEventsWithOptions.js +10 -0
  109. package/src/platform-implementation-js/lib/handler-registry.js +67 -0
  110. package/src/platform-implementation-js/lib/idMap.js +45 -0
  111. package/src/platform-implementation-js/lib/imageRequest.js +8 -0
  112. package/src/platform-implementation-js/lib/incrementName.js +7 -0
  113. package/src/platform-implementation-js/lib/inject-script-EMBEDDED.js +14 -0
  114. package/src/platform-implementation-js/lib/inject-script.js +33 -0
  115. package/src/platform-implementation-js/lib/is-valid-app-id.js +27 -0
  116. package/src/platform-implementation-js/lib/is-valid-email.js +14 -0
  117. package/src/platform-implementation-js/lib/isNotNil.js +5 -0
  118. package/src/platform-implementation-js/lib/isStreakAppId.js +11 -0
  119. package/src/platform-implementation-js/lib/logger.js +441 -0
  120. package/src/platform-implementation-js/lib/persistent-queue.js +107 -0
  121. package/src/platform-implementation-js/lib/safe-event-emitter.js +17 -0
  122. package/src/platform-implementation-js/lib/stopper-pool.js +41 -0
  123. package/src/platform-implementation-js/lib/stream-wait-for.js +30 -0
  124. package/src/platform-implementation-js/lib/wait-for.js +28 -0
  125. package/src/platform-implementation-js/lib/waitForAsync.js +19 -0
  126. package/src/platform-implementation-js/namespaces/app-menu.d.ts.map +1 -1
  127. package/src/platform-implementation-js/namespaces/app-menu.js +31 -0
  128. package/src/platform-implementation-js/namespaces/butter-bar.js +163 -0
  129. package/src/platform-implementation-js/views/SimpleElementView.js +16 -0
  130. package/src/platform-implementation-js/views/app-menu-item-view.js +257 -0
  131. package/src/platform-implementation-js/views/collapsible-panel-view.js +222 -0
  132. package/src/platform-implementation-js/views/compose-button-view.js +45 -0
  133. package/src/platform-implementation-js/views/compose-view.js +312 -0
  134. package/src/platform-implementation-js/views/keyboard-shortcut-handle.js +14 -0
  135. package/src/platform-implementation-js/widgets/buttons/basic-button-view-controller.js +39 -0
  136. package/src/platform-implementation-js/widgets/buttons/dropdown-button-view-controller.js +98 -0
  137. package/src/platform-implementation-js/widgets/buttons/dropdown-view.js +122 -0
@@ -0,0 +1,18 @@
1
+ // Splits a string on spaces, but ignores spaces inside quotes.
2
+ export default function quotedSplit(s) {
3
+ let split = [];
4
+ let lastEnd = 0;
5
+ const quoteRe = /"[^"]*"/g;
6
+ // eslint-disable-next-line no-constant-condition
7
+ while (true) {
8
+ const match = quoteRe.exec(s);
9
+ split = split.concat((match ? s.substring(lastEnd, match.index) : s.substring(lastEnd))
10
+ .split(/ +/)
11
+ .filter(Boolean));
12
+ if (!match)
13
+ break;
14
+ lastEnd = match.index + match[0].length;
15
+ split.push(match[0]);
16
+ }
17
+ return split;
18
+ }
@@ -0,0 +1,57 @@
1
+ // Returns a wrapped version of the function which queues up callTimestamps to the
2
+ // function if it is called more than count times within period amount of time.
3
+ export default function rateLimitQueuer(fn, period, count) {
4
+ let callTimestamps = [];
5
+ const queue = [];
6
+ let runningQueue = false;
7
+ function runJob() {
8
+ const job = queue.shift();
9
+ job();
10
+ if (queue.length) {
11
+ runQueue();
12
+ }
13
+ else {
14
+ runningQueue = false;
15
+ }
16
+ }
17
+ function runQueue() {
18
+ runningQueue = true;
19
+ const timeToWait = getTimeToUnqueueItem();
20
+ if (timeToWait > 0) {
21
+ setTimeout(runJob, timeToWait);
22
+ }
23
+ else {
24
+ runJob();
25
+ }
26
+ }
27
+ function getTimeToUnqueueItem() {
28
+ const now = Date.now();
29
+ const periodAgo = now - period;
30
+ callTimestamps = callTimestamps.filter((time) => time > periodAgo);
31
+ if (callTimestamps.length >= count) {
32
+ return callTimestamps[0] - periodAgo;
33
+ }
34
+ return -1;
35
+ }
36
+ return function attempt(...args) {
37
+ let job;
38
+ const promise = new Promise((resolve, reject) => {
39
+ job = () => {
40
+ callTimestamps.push(Date.now());
41
+ try {
42
+ resolve(fn.apply(this, args));
43
+ }
44
+ catch (err) {
45
+ reject(err);
46
+ }
47
+ };
48
+ });
49
+ if (!job)
50
+ throw new Error('Should not happen');
51
+ queue.push(job);
52
+ if (!runningQueue) {
53
+ runQueue();
54
+ }
55
+ return promise;
56
+ };
57
+ }
@@ -0,0 +1,14 @@
1
+ // Returns a wrapped version of the function which throws an exception if it's
2
+ // called more than count times within period amount of time.
3
+ export default function rateLimit(fn, period, count) {
4
+ let calls = [];
5
+ return function (...args) {
6
+ const now = Date.now();
7
+ calls = calls.filter((time) => time > now - period);
8
+ if (calls.length >= count) {
9
+ throw new Error('Function rate limit exceeded');
10
+ }
11
+ calls.push(now);
12
+ return fn.apply(this, args);
13
+ };
14
+ }
@@ -0,0 +1,6 @@
1
+ // This is in its own file so that updates to the version value don't cause a
2
+ // reload of everything.
3
+ export const BUILD_VERSION = process.env.VERSION;
4
+ if (module.hot) {
5
+ module.hot.accept();
6
+ }
@@ -0,0 +1,27 @@
1
+ import once from 'lodash/once';
2
+ import loadScript from '../../common/load-script';
3
+ const PlatformImplementationLoader = {
4
+ load(appId, opts) {
5
+ return Promise.resolve()
6
+ .then(() => {
7
+ if (!global.__InboxSDKImpLoader) {
8
+ return PlatformImplementationLoader._loadScript().then(() => {
9
+ if (!global.__InboxSDKImpLoader) {
10
+ throw new Error('Implementation file did not load correctly');
11
+ }
12
+ });
13
+ }
14
+ })
15
+ .then(() => global.__InboxSDKImpLoader.load('0.1', appId, opts));
16
+ },
17
+ _loadScript: once(function () {
18
+ return loadScript(process.env.IMPLEMENTATION_URL, {
19
+ nowrap: true, // platform-implementation has no top-level vars so no need for function wrapping
20
+ });
21
+ }),
22
+ preload() {
23
+ // Prime the load by calling it and letting the promise be memoized.
24
+ PlatformImplementationLoader._loadScript();
25
+ },
26
+ };
27
+ export default PlatformImplementationLoader;
@@ -0,0 +1,2 @@
1
+ export const SEND_ACTIONS = ['^pfg'];
2
+ export const DRAFT_SAVING_ACTIONS = ['^r', '^r_bt'];
@@ -0,0 +1,106 @@
1
+ import escape from 'lodash/escape';
2
+ import autoHtml from 'auto-html';
3
+ import { defn } from 'ud';
4
+ import htmlToText from '../../common/html-to-text';
5
+ import * as GRP from '../../platform-implementation-js/dom-driver/gmail/gmail-response-processor';
6
+ /*
7
+ Notes about the Gmail suggestions response:
8
+ The response may be made up of multiple sections. Each section can specify
9
+ results. There are three types of results: search terms/contacts, drive files,
10
+ and emails. Each section may only contain one type of result. The sections can
11
+ be in any order, though Gmail appears to always put the search terms/contacts
12
+ section first.
13
+
14
+ Some fields of a section:
15
+ 0: The constant "aso.srp"
16
+ 1: The user's search query
17
+ 3: Array of search term/contact suggestions.
18
+ 4: Array of email suggestions.
19
+ 5: Array of drive suggestions.
20
+ 6-9: Constants signifying type of section(?)
21
+ search terms/contacts: 1,0,0,1
22
+ drive: 0,0,1,3
23
+ email: 0,1,0,2
24
+ 11: Timestamp in microseconds. Each section should have the same timestamp.
25
+ 12: Typing autocomplete value or empty array.
26
+ 13: The length of the user's search query times 4 then cast to a string.
27
+
28
+ Currently modifySuggestions modifies the first section and adds the
29
+ app-provided suggestions into the search term/contact suggestions array.
30
+ */
31
+ function modifySuggestions(responseText, modifications) {
32
+ const { value: parsed, options } = GRP.deserialize(responseText);
33
+ const query = parsed[0][1];
34
+ for (const modification of modifications) {
35
+ let name, nameHTML;
36
+ if (typeof modification.name === 'string') {
37
+ name = modification.name;
38
+ nameHTML = escape(name);
39
+ }
40
+ else if (typeof modification.nameHTML === 'string') {
41
+ nameHTML = modification.nameHTML;
42
+ name = htmlToText(nameHTML);
43
+ }
44
+ if (name == null || nameHTML == null) {
45
+ throw new Error('name or nameHTML must be provided');
46
+ }
47
+ let description, descriptionHTML;
48
+ if (typeof modification.description === 'string') {
49
+ description = modification.description;
50
+ descriptionHTML = escape(description);
51
+ }
52
+ else if (typeof modification.descriptionHTML === 'string') {
53
+ descriptionHTML = modification.descriptionHTML;
54
+ description = htmlToText(descriptionHTML);
55
+ }
56
+ const data = {
57
+ id: modification.id,
58
+ routeName: modification.routeName,
59
+ routeParams: modification.routeParams,
60
+ externalURL: modification.externalURL,
61
+ };
62
+ nameHTML += autoHtml ` <span style="display:none" data-inboxsdk-suggestion="${JSON.stringify(data)}"></span>`;
63
+ if (modification.iconHTML != null) {
64
+ nameHTML = `<div class="inboxsdk__custom_suggestion_iconHTML">${modification.iconHTML}</div>${nameHTML}`;
65
+ }
66
+ const newItem = [
67
+ 'aso.sug',
68
+ modification.searchTerm || query,
69
+ nameHTML,
70
+ null,
71
+ [],
72
+ // screen height estimate. Currently Gmail bugs out if the screen height
73
+ // estimates add up to above the screen height, so let's avoid making the
74
+ // issue more likely by telling it our entries are zero-height.
75
+ 0,
76
+ null,
77
+ 'asor inboxsdk__custom_suggestion ' +
78
+ modification.providerId +
79
+ ' ' +
80
+ (modification.iconClass || ''),
81
+ 0,
82
+ ];
83
+ if (descriptionHTML != null) {
84
+ newItem[3] = ['aso.eme', description, name, descriptionHTML, nameHTML];
85
+ }
86
+ // Allow iconHtml to be passed, and ignore iconUrl if iconHtml is presents
87
+ if (modification.iconHTML != null) {
88
+ // set empty image
89
+ newItem[6] = [
90
+ 'aso.thn',
91
+ 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==',
92
+ ];
93
+ newItem[7] += ' inboxsdk__no_bg';
94
+ }
95
+ else if (modification.iconUrl) {
96
+ newItem[6] = ['aso.thn', modification.iconUrl];
97
+ newItem[7] += ' inboxsdk__no_bg';
98
+ }
99
+ else {
100
+ newItem[7] += ' asor_i4';
101
+ }
102
+ parsed[0][3].push(newItem);
103
+ }
104
+ return GRP.serialize(parsed, options);
105
+ }
106
+ export default defn(module, modifySuggestions);
@@ -0,0 +1,49 @@
1
+ export default function setupGmonkeyHandler() {
2
+ const gmonkeyPromise = setupGmonkey();
3
+ document.addEventListener('inboxSDKtellMeIsConversationViewDisabled', function () {
4
+ gmonkeyPromise.then((gmonkey) => {
5
+ const answer = gmonkey.isConversationViewDisabled();
6
+ const event = document.createEvent('CustomEvent');
7
+ event.initCustomEvent('inboxSDKgmonkeyResponse', false, false, answer);
8
+ document.dispatchEvent(event);
9
+ });
10
+ });
11
+ document.addEventListener('inboxSDKtellMeCurrentThreadId', function (event) {
12
+ let threadId;
13
+ if (event.detail.isPreviewedThread) {
14
+ const rows = Array.from(document.querySelectorAll('[gh=tl] tr.aps'));
15
+ if (rows.length > 0) {
16
+ const elementWithId = rows
17
+ .map((row) => row.querySelector('[data-thread-id]'))
18
+ .filter(Boolean)[0];
19
+ if (elementWithId) {
20
+ threadId = elementWithId.getAttribute('data-thread-id');
21
+ }
22
+ else {
23
+ threadId = rows[0].getAttribute('data-inboxsdk-threadid');
24
+ }
25
+ }
26
+ }
27
+ else {
28
+ threadId = window.gmonkey?.v2?.getCurrentThread?.()?.getThreadId();
29
+ }
30
+ if (threadId) {
31
+ // hash is included in the sync id route url, so we also need to take it out
32
+ threadId = threadId.replace('#', '');
33
+ event.target.setAttribute('data-inboxsdk-currentthreadid', threadId);
34
+ }
35
+ });
36
+ }
37
+ function setupGmonkey() {
38
+ return new Promise((resolve) => {
39
+ function check() {
40
+ if (!window.gmonkey) {
41
+ setTimeout(check, 500);
42
+ }
43
+ else {
44
+ window.gmonkey.load('2.0', resolve);
45
+ }
46
+ }
47
+ check();
48
+ });
49
+ }
@@ -0,0 +1,298 @@
1
+ import sortBy from 'lodash/sortBy';
2
+ import intersection from 'lodash/intersection';
3
+ import isNotNil from '../../platform-implementation-js/lib/isNotNil';
4
+ import { DRAFT_SAVING_ACTIONS, SEND_ACTIONS, } from './constants';
5
+ const ACTION_TYPE_PRIORITY_RANK = [
6
+ 'SEND',
7
+ 'DRAFT_SAVE',
8
+ ];
9
+ export function parseComposeRequestBody_2022_09_09(request) {
10
+ return parseCreateUpdateSendDraftRequestBody(request);
11
+ }
12
+ export function parseComposeResponseBody_2022_09_09(response) {
13
+ return parseCreateUpdateSendDraftResponseBody(response);
14
+ }
15
+ export function replaceBodyContentInComposeSendRequestBody_2022_09_09(request, newBodyHtmlContent) {
16
+ return replaceBodyContentInSendRequestBody(request, newBodyHtmlContent);
17
+ }
18
+ /**
19
+ * Parses request body when compose window either saves a draft for the first time,
20
+ * updates the draft or sends the draft.
21
+ * NOTE: request could contain multiple threads and messages within it,
22
+ * prioritize SEND to DRAFT_SAVE message.
23
+ */
24
+ function parseCreateUpdateSendDraftRequestBody(request) {
25
+ const updateList = request[1]?.[0];
26
+ if (!Array.isArray(updateList)) {
27
+ // cannot parse
28
+ return null;
29
+ }
30
+ const parsedMessages = updateList.map(parseRequestThread).filter(isNotNil);
31
+ const sorted = sortBy(parsedMessages, (m) => ACTION_TYPE_PRIORITY_RANK.indexOf(m.type));
32
+ return sorted[0] || null;
33
+ }
34
+ /**
35
+ * Parses response body when compose window either saved a draft for the first time,
36
+ * updated the draft, or sent the draft.
37
+ * NOTE: response could contain multiple threads and messages within it
38
+ * even not related to a message/thread in the request body.
39
+ * So the calling code should find needed message manually.
40
+ */
41
+ function parseCreateUpdateSendDraftResponseBody(response) {
42
+ const updateList = response[1]?.[5];
43
+ if (!Array.isArray(updateList)) {
44
+ // cannot parse
45
+ return [];
46
+ }
47
+ return updateList
48
+ .map(parseResponseThread)
49
+ .filter(isNotNil)
50
+ .flatMap((parsedThread) => {
51
+ const { threadId, oldThreadId, parsedMessages } = parsedThread;
52
+ return parsedMessages.map((parsedMessage) => {
53
+ const { messageId, to, cc, bcc, actions, rfcID, oldMessageId } = parsedMessage;
54
+ const actionType = actionsToComposeRequestType(actions);
55
+ if (!actionType) {
56
+ // unsupported actions within a message
57
+ return null;
58
+ }
59
+ return {
60
+ threadId,
61
+ messageId,
62
+ to,
63
+ cc,
64
+ bcc,
65
+ actions,
66
+ rfcID,
67
+ oldMessageId,
68
+ oldThreadId,
69
+ type: actionType,
70
+ };
71
+ });
72
+ })
73
+ .filter(isNotNil);
74
+ }
75
+ function replaceBodyContentInSendRequestBody(request, newBodyHtmlContent) {
76
+ // since draftID is not passed from outside,
77
+ // use parse function to find a message which body needs to be replaced
78
+ const parsed = parseCreateUpdateSendDraftRequestBody(request);
79
+ if (!parsed) {
80
+ return null;
81
+ }
82
+ const replaceBodyInThisMessageId = parsed.messageId;
83
+ const updateList = request[1]?.[0];
84
+ if (!Array.isArray(updateList)) {
85
+ // cannot parse
86
+ return null;
87
+ }
88
+ for (const threadWrapper of updateList) {
89
+ if (!Array.isArray(threadWrapper) || !Array.isArray(threadWrapper[1])) {
90
+ // cannot parse
91
+ return null;
92
+ }
93
+ const thread = threadWrapper[1];
94
+ const threadId = parseThreadId(thread[0]);
95
+ if (!threadId) {
96
+ // cannot parse
97
+ return null;
98
+ }
99
+ const parseResult = findAndParseRequestMessage(thread);
100
+ if (parseResult?.parsedMsg.messageId === replaceBodyInThisMessageId) {
101
+ const actionType = actionsToComposeRequestType(parseResult.parsedMsg.actions);
102
+ if (actionType === 'SEND') {
103
+ // find first message with needed messageId and 'SEND' action and replace the body content
104
+ replaceBodyInRequestMsg(parseResult.originalMsg, newBodyHtmlContent);
105
+ return request;
106
+ }
107
+ }
108
+ }
109
+ return null;
110
+ }
111
+ function parseThreadId(threadId) {
112
+ if (!threadId.startsWith('thread-')) {
113
+ return null;
114
+ }
115
+ if (threadId.includes('|')) {
116
+ return threadId.split('|')[0];
117
+ }
118
+ return threadId;
119
+ }
120
+ function parseMsgId(messageId) {
121
+ if (!messageId.startsWith('msg-')) {
122
+ // cannot parse
123
+ return null;
124
+ }
125
+ return messageId;
126
+ }
127
+ function parseContacts(contacts) {
128
+ if (!Array.isArray(contacts)) {
129
+ // cannot parse
130
+ return null;
131
+ }
132
+ return contacts
133
+ .filter((c) => !!c[1])
134
+ .map((c) => ({ emailAddress: c[1], name: c[2] ?? null }));
135
+ }
136
+ function findAndParseRequestMessage(thread) {
137
+ const originalMsgs = [
138
+ thread[1]?.[2]?.[0]?.[4]?.[0],
139
+ thread[1]?.[1]?.[0],
140
+ thread[1]?.[13]?.[0],
141
+ ];
142
+ for (const originalMsg of originalMsgs) {
143
+ const parsedMsg = parseRequestMsg(originalMsg);
144
+ if (parsedMsg) {
145
+ return { parsedMsg, originalMsg };
146
+ }
147
+ }
148
+ return null;
149
+ }
150
+ function parseRequestThread(threadWrapper) {
151
+ if (!Array.isArray(threadWrapper) || !Array.isArray(threadWrapper[1])) {
152
+ // cannot parse
153
+ return null;
154
+ }
155
+ const thread = threadWrapper[1];
156
+ const threadId = parseThreadId(thread[0]);
157
+ if (!threadId) {
158
+ // cannot parse
159
+ return null;
160
+ }
161
+ const parseResult = findAndParseRequestMessage(thread);
162
+ if (!parseResult) {
163
+ // cannot parse
164
+ return null;
165
+ }
166
+ const { parsedMsg: message, originalMsg } = parseResult;
167
+ const { messageId, to, cc, bcc, subject, body, actions } = message;
168
+ let actionType = actionsToComposeRequestType(actions);
169
+ if (!actionType) {
170
+ // exit if doesn't have required actions
171
+ return null;
172
+ }
173
+ // usually for draft_save action when draft or reply got saved for first time, response could be different
174
+ // from usual update response, so replace draft_save action with first_draft_save in this case.
175
+ if (actionType === 'DRAFT_SAVE' &&
176
+ (originalMsg === thread[1]?.[2]?.[0]?.[4]?.[0] ||
177
+ originalMsg === thread[1]?.[1]?.[0])) {
178
+ actionType = 'FIRST_DRAFT_SAVE';
179
+ }
180
+ return {
181
+ threadId,
182
+ messageId,
183
+ to,
184
+ cc,
185
+ bcc,
186
+ subject,
187
+ body,
188
+ actions,
189
+ type: actionType,
190
+ };
191
+ }
192
+ function parseRequestMsg(msg) {
193
+ if (!Array.isArray(msg)) {
194
+ // cannot parse
195
+ return null;
196
+ }
197
+ const messageId = parseMsgId(msg[0]);
198
+ if (!messageId) {
199
+ // cannot parse
200
+ return null;
201
+ }
202
+ const subject = msg[7];
203
+ const to = parseContacts(msg[2]);
204
+ const cc = parseContacts(msg[3]);
205
+ const bcc = parseContacts(msg[4]);
206
+ const body = msg[8][1][0][1];
207
+ const actions = msg[10];
208
+ const rfcID = msg[13];
209
+ const oldMessageId = msg[55];
210
+ return {
211
+ messageId,
212
+ to,
213
+ cc,
214
+ bcc,
215
+ subject,
216
+ body,
217
+ actions,
218
+ rfcID,
219
+ oldMessageId,
220
+ };
221
+ }
222
+ function replaceBodyInRequestMsg(msg, newBodyHtmlContent) {
223
+ if (!Array.isArray(msg)) {
224
+ // cannot parse
225
+ return null;
226
+ }
227
+ msg[8][1][0][1] = newBodyHtmlContent;
228
+ }
229
+ function parseResponseThread(threadWrapper) {
230
+ if (!Array.isArray(threadWrapper) || !Array.isArray(threadWrapper[0])) {
231
+ // cannot parse
232
+ return null;
233
+ }
234
+ const thread = threadWrapper[0];
235
+ const threadId = parseThreadId(thread[0]);
236
+ if (!threadId) {
237
+ // cannot parse
238
+ return null;
239
+ }
240
+ const threadInner = thread[2]?.[6]?.[0];
241
+ if (!Array.isArray(threadInner)) {
242
+ // cannot parse
243
+ return null;
244
+ }
245
+ const oldThreadId = threadInner[19];
246
+ const parsedMessages = Array.isArray(threadInner[4])
247
+ ? threadInner[4]
248
+ .map((msg) => {
249
+ if (!Array.isArray(msg)) {
250
+ // cannot parse
251
+ return null;
252
+ }
253
+ return parseResponseMsg(msg);
254
+ })
255
+ .filter(isNotNil)
256
+ : [];
257
+ return {
258
+ threadId,
259
+ oldThreadId,
260
+ parsedMessages,
261
+ };
262
+ }
263
+ function parseResponseMsg(msg) {
264
+ if (!Array.isArray(msg)) {
265
+ // cannot parse
266
+ return null;
267
+ }
268
+ const messageId = parseMsgId(msg[0]);
269
+ if (!messageId) {
270
+ // cannot parse
271
+ return null;
272
+ }
273
+ const actions = msg[10];
274
+ const to = parseContacts(msg[2]);
275
+ const cc = parseContacts(msg[3]);
276
+ const bcc = parseContacts(msg[4]);
277
+ const rfcID = msg[13];
278
+ const oldMessageId = msg[55];
279
+ return {
280
+ messageId,
281
+ to,
282
+ cc,
283
+ bcc,
284
+ actions,
285
+ rfcID,
286
+ oldMessageId,
287
+ };
288
+ }
289
+ export function actionsToComposeRequestType(actions) {
290
+ if (intersection(actions, DRAFT_SAVING_ACTIONS).length ===
291
+ DRAFT_SAVING_ACTIONS.length) {
292
+ return 'DRAFT_SAVE';
293
+ }
294
+ if (intersection(actions, SEND_ACTIONS).length === SEND_ACTIONS.length) {
295
+ return 'SEND';
296
+ }
297
+ return null;
298
+ }
@@ -0,0 +1,55 @@
1
+ import { parseComposeRequestBody_2022_09_09, parseComposeResponseBody_2022_09_09, replaceBodyContentInComposeSendRequestBody_2022_09_09, } from './sync-compose-processor-20220909';
2
+ import { getDetailsOfComposeRequest, replaceEmailBodyForSendRequest, } from './sync-compose-request-processor';
3
+ import * as logger from '../injected-logger';
4
+ export function parseComposeRequestBody(request) {
5
+ const requestParsed = JSON.parse(request);
6
+ try {
7
+ if (Array.isArray(requestParsed)) {
8
+ const parsed = parseComposeRequestBody_2022_09_09(requestParsed);
9
+ if (parsed) {
10
+ return {
11
+ type: parsed.type,
12
+ to: parsed.to,
13
+ cc: parsed.cc,
14
+ bcc: parsed.bcc,
15
+ draftID: parsed.messageId.replace('msg-a:', ''),
16
+ subject: parsed.subject,
17
+ body: parsed.body,
18
+ };
19
+ }
20
+ return null;
21
+ }
22
+ }
23
+ catch (err) {
24
+ logger.eventSdkPassive('connection.requestResponseParsingFailed', {
25
+ requestParseError: err,
26
+ });
27
+ }
28
+ return getDetailsOfComposeRequest(requestParsed);
29
+ }
30
+ export function parseComposeResponseBody(response) {
31
+ const responseParsed = JSON.parse(response);
32
+ if (Array.isArray(responseParsed)) {
33
+ return parseComposeResponseBody_2022_09_09(responseParsed);
34
+ }
35
+ return [];
36
+ }
37
+ export function replaceBodyContentInComposeSendRequestBody(request, newBodyHtmlContent) {
38
+ const requestParsed = JSON.parse(request);
39
+ try {
40
+ if (Array.isArray(requestParsed)) {
41
+ const replacedRequestObj = replaceBodyContentInComposeSendRequestBody_2022_09_09(requestParsed, newBodyHtmlContent);
42
+ if (replacedRequestObj) {
43
+ return JSON.stringify(replacedRequestObj);
44
+ }
45
+ // if couldn't parse and replace body content, return original object
46
+ return request;
47
+ }
48
+ }
49
+ catch (err) {
50
+ logger.eventSdkPassive('connection.requestResponseParsingFailed', {
51
+ replaceBodyFailed: err,
52
+ });
53
+ }
54
+ return replaceEmailBodyForSendRequest(request, newBodyHtmlContent);
55
+ }