@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.
- package/inboxsdk.js +1 -1
- package/inboxsdk.js.map +1 -1
- package/package.json +1 -1
- package/src/build/exec.js +26 -0
- package/src/build/spawn.js +17 -0
- package/src/common/ajax.js +90 -0
- package/src/common/cachebust-url.js +10 -0
- package/src/common/censorHTMLstring.js +37 -0
- package/src/common/censorHTMLtree.js +28 -0
- package/src/common/censorJSONTree.js +15 -0
- package/src/common/connectivity-test.js +21 -0
- package/src/common/defer.js +11 -0
- package/src/common/find-parent.js +10 -0
- package/src/common/get-extension-id.js +7 -0
- package/src/common/get-or-fail.js +8 -0
- package/src/common/get-session-id.js +14 -0
- package/src/common/get-stack-trace.js +6 -0
- package/src/common/html-to-text.js +7 -0
- package/src/common/isElementVisible.js +6 -0
- package/src/common/load-script.js +127 -0
- package/src/common/log-error.js +191 -0
- package/src/common/quoted-split.js +18 -0
- package/src/common/rate-limit-queuer.js +57 -0
- package/src/common/rate-limit.js +14 -0
- package/src/common/version.js +6 -0
- package/src/inboxsdk-js/loading/platform-implementation-loader.js +27 -0
- package/src/injected-js/gmail/constants.js +2 -0
- package/src/injected-js/gmail/modify-suggestions.js +106 -0
- package/src/injected-js/gmail/setup-gmonkey-handler.js +49 -0
- package/src/injected-js/gmail/sync-compose-processor-20220909.js +298 -0
- package/src/injected-js/gmail/sync-compose-processor.js +55 -0
- package/src/injected-js/gmail/sync-compose-request-processor.js +110 -0
- package/src/injected-js/injected-logger.js +50 -0
- package/src/injected-js/setupCustomViewEventAssassin.js +90 -0
- package/src/platform-implementation-js/constants/nav-item-types.js +19 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-app-menu-item.js +21 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-collapsible-panel.js +28 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-nav-item.js +98 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-support-item.js +4 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/encodeDraftUrlId.js +8 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/get-rfc-message-id-for-gmail-message-id.js +12 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/get-rfc-message-id-for-gmail-thread-id.js +33 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/isIntegratedViewGmail.js +4 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/open-draft-by-message-id.js +44 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/track-gmail-styles.js +95 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter/get-main-content-element-changed-stream.js +38 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter/wait-for-gmail-mode-to-settle.js +12 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter.js +253 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-page-communicator.js +200 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-response-processor.js +533 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-sync-response-processor.js +289 -0
- package/src/platform-implementation-js/dom-driver/gmail/is-compose-titlebar-light-color.js +21 -0
- package/src/platform-implementation-js/dom-driver/gmail/makePageParserTree.js +28 -0
- package/src/platform-implementation-js/dom-driver/gmail/pageParserOptions.js +19 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-app-menu-item-view.js +126 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/add-status-bar.js +101 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/detectClassicRecipientsArea.js +38 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/get-address-information-extractor.js +28 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/get-recipients.js +22 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/gmail-compose-button-view.js +233 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/page-parser.js +223 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/set-recipients.js +157 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/setupLinkPopovers.js +109 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-message-view/attachment-icon.js +5 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-nav-item-view.js +759 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-support-item-view.js +84 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/button-color-classes.js +33 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/button-view.js +325 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/create-accessory-button-view.js +58 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/create-parent-accessory-button-view.js +55 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/modal-button-view.js +96 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/more-dropdown-button-view.js +30 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/more-parent-accessory-button-view.js +10 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/gmail-dropdown-view.js +22 -0
- package/src/platform-implementation-js/driver-common/getAccountUrlPart.js +17 -0
- package/src/platform-implementation-js/driver-common/getOriginalMessagePage.js +33 -0
- package/src/platform-implementation-js/driver-common/getSyncThreadsForSearch.js +49 -0
- package/src/platform-implementation-js/driver-common/gmailAjax.js +65 -0
- package/src/platform-implementation-js/driver-common/render-custom-icon.js +16 -0
- package/src/platform-implementation-js/driver-common/update-icon.js +90 -0
- package/src/platform-implementation-js/lib/ErrorCollector.js +47 -0
- package/src/platform-implementation-js/lib/Membrane.js +22 -0
- package/src/platform-implementation-js/lib/ScrollableContainByScreen.js +30 -0
- package/src/platform-implementation-js/lib/attemptWithRetries.js +16 -0
- package/src/platform-implementation-js/lib/common-page-communicator.js +137 -0
- package/src/platform-implementation-js/lib/delay-asap.js +12 -0
- package/src/platform-implementation-js/lib/delay-idle.js +16 -0
- package/src/platform-implementation-js/lib/dom/block-and-reemit-keyboard-events.js +36 -0
- package/src/platform-implementation-js/lib/dom/generic-button-view.js +39 -0
- package/src/platform-implementation-js/lib/dom/get-insert-before-element.js +39 -0
- package/src/platform-implementation-js/lib/dom/insert-element-in-order.js +38 -0
- package/src/platform-implementation-js/lib/dom/keyboard-shortcut-stream.js +12 -0
- package/src/platform-implementation-js/lib/dom/make-element-child-stream.js +79 -0
- package/src/platform-implementation-js/lib/dom/make-element-stream-merger.js +28 -0
- package/src/platform-implementation-js/lib/dom/make-element-view-stream.js +15 -0
- package/src/platform-implementation-js/lib/dom/make-mutation-observer-chunked-stream.js +15 -0
- package/src/platform-implementation-js/lib/dom/make-mutation-observer-stream.js +5 -0
- package/src/platform-implementation-js/lib/dom/outsideClicksAndEscape.js +39 -0
- package/src/platform-implementation-js/lib/dom/parentNodes.js +8 -0
- package/src/platform-implementation-js/lib/dom/querySelectorOrFail.js +6 -0
- package/src/platform-implementation-js/lib/dom/reemitClickEventForReact.js +22 -0
- package/src/platform-implementation-js/lib/dom/simulate-key.js +46 -0
- package/src/platform-implementation-js/lib/dom/simulate-mouse-event.js +16 -0
- package/src/platform-implementation-js/lib/dom/trigger-relay-event.js +7 -0
- package/src/platform-implementation-js/lib/extract-contact-from-email-contact-string.js +25 -0
- package/src/platform-implementation-js/lib/fake-window-resize.js +5 -0
- package/src/platform-implementation-js/lib/from-event-target-capture.js +12 -0
- package/src/platform-implementation-js/lib/fromEventsWithOptions.js +10 -0
- package/src/platform-implementation-js/lib/handler-registry.js +67 -0
- package/src/platform-implementation-js/lib/idMap.js +45 -0
- package/src/platform-implementation-js/lib/imageRequest.js +8 -0
- package/src/platform-implementation-js/lib/incrementName.js +7 -0
- package/src/platform-implementation-js/lib/inject-script-EMBEDDED.js +14 -0
- package/src/platform-implementation-js/lib/inject-script.js +33 -0
- package/src/platform-implementation-js/lib/is-valid-app-id.js +27 -0
- package/src/platform-implementation-js/lib/is-valid-email.js +14 -0
- package/src/platform-implementation-js/lib/isNotNil.js +5 -0
- package/src/platform-implementation-js/lib/isStreakAppId.js +11 -0
- package/src/platform-implementation-js/lib/logger.js +441 -0
- package/src/platform-implementation-js/lib/persistent-queue.js +107 -0
- package/src/platform-implementation-js/lib/safe-event-emitter.js +17 -0
- package/src/platform-implementation-js/lib/stopper-pool.js +41 -0
- package/src/platform-implementation-js/lib/stream-wait-for.js +30 -0
- package/src/platform-implementation-js/lib/wait-for.js +28 -0
- package/src/platform-implementation-js/lib/waitForAsync.js +19 -0
- package/src/platform-implementation-js/namespaces/app-menu.d.ts.map +1 -1
- package/src/platform-implementation-js/namespaces/app-menu.js +31 -0
- package/src/platform-implementation-js/namespaces/butter-bar.js +163 -0
- package/src/platform-implementation-js/views/SimpleElementView.js +16 -0
- package/src/platform-implementation-js/views/app-menu-item-view.js +257 -0
- package/src/platform-implementation-js/views/collapsible-panel-view.js +222 -0
- package/src/platform-implementation-js/views/compose-button-view.js +45 -0
- package/src/platform-implementation-js/views/compose-view.js +312 -0
- package/src/platform-implementation-js/views/keyboard-shortcut-handle.js +14 -0
- package/src/platform-implementation-js/widgets/buttons/basic-button-view-controller.js +39 -0
- package/src/platform-implementation-js/widgets/buttons/dropdown-button-view-controller.js +98 -0
- package/src/platform-implementation-js/widgets/buttons/dropdown-view.js +122 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/* @flow */
|
|
2
|
+
import intersection from 'lodash/intersection';
|
|
3
|
+
import { SEND_ACTIONS } from './constants';
|
|
4
|
+
export function getDetailsOfComposeRequest(parsed) {
|
|
5
|
+
const updateList = parsed[2] && parsed[2][1];
|
|
6
|
+
if (!updateList)
|
|
7
|
+
return null;
|
|
8
|
+
const messageUpdates = updateList.filter((update) => {
|
|
9
|
+
const updateWrapper = update[2] && update[2][2] && (update[2][2][14] || update[2][2][2]);
|
|
10
|
+
return (updateWrapper &&
|
|
11
|
+
updateWrapper[1] &&
|
|
12
|
+
updateWrapper[1][1] &&
|
|
13
|
+
updateWrapper[1][1].indexOf('msg-a:') > -1);
|
|
14
|
+
});
|
|
15
|
+
if (messageUpdates.length) {
|
|
16
|
+
const sendUpdateMatch = messageUpdates.find((update) => {
|
|
17
|
+
const updateWrapper = update[2] && update[2][2] && (update[2][2][14] || update[2][2][2]);
|
|
18
|
+
return (updateWrapper[1][11] &&
|
|
19
|
+
intersection(updateWrapper[1][11], SEND_ACTIONS).length ===
|
|
20
|
+
SEND_ACTIONS.length);
|
|
21
|
+
});
|
|
22
|
+
if (sendUpdateMatch) {
|
|
23
|
+
const sendUpdateWrapper = sendUpdateMatch[2] &&
|
|
24
|
+
sendUpdateMatch[2][2] &&
|
|
25
|
+
(sendUpdateMatch[2][2][14] || sendUpdateMatch[2][2][2]);
|
|
26
|
+
const sendUpdate = sendUpdateWrapper[1];
|
|
27
|
+
return getComposeRequestFromUpdate(sendUpdate, 'SEND');
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// There's a small chance that an update list could contain the
|
|
31
|
+
// draft saves for multiple drafts in some situations — we've never
|
|
32
|
+
// seen this so currently just picking the first update and assuming
|
|
33
|
+
// that if there are multiple updates in the request they are for
|
|
34
|
+
// queued up versions of the same draft.
|
|
35
|
+
const firstMessageUpdate = messageUpdates[0];
|
|
36
|
+
const updateWrapper = firstMessageUpdate[2] &&
|
|
37
|
+
firstMessageUpdate[2][2] &&
|
|
38
|
+
(firstMessageUpdate[2][2][14] || firstMessageUpdate[2][2][2]);
|
|
39
|
+
const update = updateWrapper[1];
|
|
40
|
+
return getComposeRequestFromUpdate(update, 'DRAFT_SAVE');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// the first time a draft is saved it has a different response format
|
|
45
|
+
const messageUpdates = updateList
|
|
46
|
+
.map((update) => update[2] &&
|
|
47
|
+
update[2][2] &&
|
|
48
|
+
update[2][2][3] &&
|
|
49
|
+
update[2][2][3][1] &&
|
|
50
|
+
update[2][2][3][1][5] &&
|
|
51
|
+
update[2][2][3][1][5][0])
|
|
52
|
+
.filter(Boolean);
|
|
53
|
+
if (messageUpdates.length === 0)
|
|
54
|
+
return null;
|
|
55
|
+
const firstMessageUpdate = messageUpdates[0];
|
|
56
|
+
return getComposeRequestFromUpdate(firstMessageUpdate, 'FIRST_DRAFT_SAVE');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function getComposeRequestFromUpdate(update, type) {
|
|
60
|
+
const body = update[9] && update[9][2] && update[9][2][0] && update[9][2][0][2];
|
|
61
|
+
if (body == null)
|
|
62
|
+
return null;
|
|
63
|
+
return {
|
|
64
|
+
body,
|
|
65
|
+
type,
|
|
66
|
+
to: parseContacts(update[3]),
|
|
67
|
+
cc: parseContacts(update[4]),
|
|
68
|
+
bcc: parseContacts(update[5]),
|
|
69
|
+
draftID: update[1].replace('msg-a:', ''),
|
|
70
|
+
subject: update[8],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function parseContacts(contacts) {
|
|
74
|
+
if (!Array.isArray(contacts)) {
|
|
75
|
+
// exit cuz cannot parse
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return contacts.map((c) => ({ emailAddress: c[2], name: c[3] || null }));
|
|
79
|
+
}
|
|
80
|
+
export function replaceEmailBodyForSendRequest(request, newBody) {
|
|
81
|
+
if (!newBody)
|
|
82
|
+
return request;
|
|
83
|
+
const parsed = JSON.parse(request);
|
|
84
|
+
const updateList = parsed[2] && parsed[2][1];
|
|
85
|
+
if (!updateList)
|
|
86
|
+
return request;
|
|
87
|
+
const messageUpdates = updateList.filter((update) => {
|
|
88
|
+
const updateWrapper = update[2] && update[2][2] && (update[2][2][14] || update[2][2][2]);
|
|
89
|
+
return (updateWrapper &&
|
|
90
|
+
updateWrapper[1] &&
|
|
91
|
+
updateWrapper[1][1] &&
|
|
92
|
+
updateWrapper[1][1].indexOf('msg-a:') > -1);
|
|
93
|
+
});
|
|
94
|
+
if (!messageUpdates.length)
|
|
95
|
+
return request;
|
|
96
|
+
const sendUpdateMatch = messageUpdates.find((update) => {
|
|
97
|
+
const updateWrapper = update[2] && update[2][2] && (update[2][2][14] || update[2][2][2]);
|
|
98
|
+
return (updateWrapper[1][11] &&
|
|
99
|
+
intersection(updateWrapper[1][11], SEND_ACTIONS).length ===
|
|
100
|
+
SEND_ACTIONS.length);
|
|
101
|
+
});
|
|
102
|
+
if (!sendUpdateMatch)
|
|
103
|
+
return request;
|
|
104
|
+
const sendUpdateWrapper = sendUpdateMatch[2] &&
|
|
105
|
+
sendUpdateMatch[2][2] &&
|
|
106
|
+
(sendUpdateMatch[2][2][14] || sendUpdateMatch[2][2][2]);
|
|
107
|
+
const sendUpdate = sendUpdateWrapper[1];
|
|
108
|
+
sendUpdate[9][2][0][2] = newBody;
|
|
109
|
+
return JSON.stringify(parsed);
|
|
110
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export function error(err, details) {
|
|
2
|
+
if (!err) {
|
|
3
|
+
err = new Error('No error given');
|
|
4
|
+
}
|
|
5
|
+
console.error('Error in injected script', err, details); //eslint-disable-line no-console
|
|
6
|
+
try {
|
|
7
|
+
JSON.stringify(details);
|
|
8
|
+
}
|
|
9
|
+
catch (e) {
|
|
10
|
+
details = '<failed to jsonify>';
|
|
11
|
+
}
|
|
12
|
+
const errorProperties = {};
|
|
13
|
+
for (const name in err) {
|
|
14
|
+
if (Object.prototype.hasOwnProperty.call(err, name)) {
|
|
15
|
+
try {
|
|
16
|
+
const value = err[name];
|
|
17
|
+
JSON.stringify(value);
|
|
18
|
+
errorProperties[name] = value;
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
// ignore
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (Object.keys(errorProperties).length > 0) {
|
|
26
|
+
details = { errorProperties, details };
|
|
27
|
+
}
|
|
28
|
+
document.dispatchEvent(new CustomEvent('inboxSDKinjectedError', {
|
|
29
|
+
bubbles: false,
|
|
30
|
+
cancelable: false,
|
|
31
|
+
detail: {
|
|
32
|
+
message: err && err.message,
|
|
33
|
+
stack: err && err.stack,
|
|
34
|
+
details,
|
|
35
|
+
},
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
export function eventSdkPassive(name, details, sensitive) {
|
|
39
|
+
try {
|
|
40
|
+
JSON.stringify(details);
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
details = '<failed to jsonify>';
|
|
44
|
+
}
|
|
45
|
+
document.dispatchEvent(new CustomEvent('inboxSDKinjectedEventSdkPassive', {
|
|
46
|
+
bubbles: false,
|
|
47
|
+
cancelable: false,
|
|
48
|
+
detail: { name, details, sensitive },
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { defn } from 'ud';
|
|
2
|
+
import includes from 'lodash/includes';
|
|
3
|
+
import closest from 'closest-ng';
|
|
4
|
+
import * as logger from './injected-logger';
|
|
5
|
+
function md(value) {
|
|
6
|
+
return { value, configurable: true };
|
|
7
|
+
}
|
|
8
|
+
// These are basically all the keys that trigger some action in thread list
|
|
9
|
+
// views and thread views that we don't want to be triggerable while a custom
|
|
10
|
+
// view is open. Key combos which affect things still visible on the screen or
|
|
11
|
+
// navigate to a new view are still allowed.
|
|
12
|
+
const blockedAnyModKeys = [
|
|
13
|
+
'ArrowLeft',
|
|
14
|
+
'ArrowRight',
|
|
15
|
+
'ArrowUp',
|
|
16
|
+
'ArrowDown',
|
|
17
|
+
'PageUp',
|
|
18
|
+
'PageDown',
|
|
19
|
+
'Home',
|
|
20
|
+
'End',
|
|
21
|
+
'Enter',
|
|
22
|
+
];
|
|
23
|
+
// These are only necessary for Safari
|
|
24
|
+
const blockedKeyIdentifiers = ['Left', 'Right', 'Up', 'Down'];
|
|
25
|
+
const blockedAnyModCharacters = '!#[]{}_+=-;:\r\n1234567890`~';
|
|
26
|
+
const blockedNoModCharacters = ',xsyemrafz.ujkpnl';
|
|
27
|
+
const blockedShiftCharacters = 'parfniut';
|
|
28
|
+
function shouldBlockEvent(event) {
|
|
29
|
+
if (!document.body.classList.contains('inboxsdk__custom_view_active')) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const target = event.target;
|
|
33
|
+
const key = event.key || /* safari*/ String.fromCharCode(event.which || event.keyCode);
|
|
34
|
+
// Block all escape key presses inside a custom view, even when an input
|
|
35
|
+
// is focused.
|
|
36
|
+
if (event.key === 'Escape' && closest(target, '.inboxsdk__custom_view')) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if (includes(blockedAnyModKeys, key) ||
|
|
40
|
+
/* safari */ includes(blockedKeyIdentifiers, event.keyIdentifier) ||
|
|
41
|
+
includes(blockedAnyModCharacters, key) ||
|
|
42
|
+
(!event.shiftKey &&
|
|
43
|
+
!event.ctrlKey &&
|
|
44
|
+
!event.metaKey &&
|
|
45
|
+
!event.altKey &&
|
|
46
|
+
includes(blockedNoModCharacters, key)) ||
|
|
47
|
+
(event.shiftKey &&
|
|
48
|
+
!event.ctrlKey &&
|
|
49
|
+
!event.metaKey &&
|
|
50
|
+
!event.altKey &&
|
|
51
|
+
includes(blockedShiftCharacters, key.toLowerCase()))) {
|
|
52
|
+
if (
|
|
53
|
+
// Gmail already ignores events originating in these elements even if
|
|
54
|
+
// they were made by an extension.
|
|
55
|
+
closest(target, 'input, textarea, button, [contenteditable]') ||
|
|
56
|
+
// Gmail ignores events originating in its own interactive elements
|
|
57
|
+
// which tend to have certain role attributes.
|
|
58
|
+
(!closest(target, '.inboxsdk__custom_view') &&
|
|
59
|
+
closest(target, '[role=button], [role=link]'))) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const handler = defn(module, function (event) {
|
|
67
|
+
try {
|
|
68
|
+
// If the key is in a blacklist and it originated while a custom view is
|
|
69
|
+
// present, then maim the event object before Gmail or Inbox sees it.
|
|
70
|
+
if (shouldBlockEvent(event)) {
|
|
71
|
+
Object.defineProperties(event, {
|
|
72
|
+
altKey: md(false),
|
|
73
|
+
ctrlKey: md(false),
|
|
74
|
+
shiftKey: md(false),
|
|
75
|
+
metaKey: md(false),
|
|
76
|
+
charCode: md(92),
|
|
77
|
+
code: md('Backslash'),
|
|
78
|
+
key: md('\\'),
|
|
79
|
+
keyCode: md(92),
|
|
80
|
+
which: md(92),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
logger.error(err);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
export default function setupCustomViewEventAssassin() {
|
|
89
|
+
document.addEventListener('keydown', handler, true);
|
|
90
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The different nav item types that exist
|
|
3
|
+
*/
|
|
4
|
+
const navItemTypes = Object.freeze({
|
|
5
|
+
/**
|
|
6
|
+
* standard nav item for navigating
|
|
7
|
+
*/
|
|
8
|
+
NAVIGATION: 'NAVIGATION',
|
|
9
|
+
/**
|
|
10
|
+
* nav item that looks like a link
|
|
11
|
+
*/
|
|
12
|
+
LINK: 'LINK',
|
|
13
|
+
// nav item for logical grouping. In Gmailv2, the entire nav item can be clicked to expand/collapse its children and it ignores all NavItemDescriptor options other than `name` and `subtitle`.
|
|
14
|
+
// Behaves identically to NAVIGATION when in Gmailv1.
|
|
15
|
+
GROUPER: 'GROUPER',
|
|
16
|
+
// Old alias for LINK
|
|
17
|
+
MANAGE: 'MANAGE',
|
|
18
|
+
});
|
|
19
|
+
export default navItemTypes;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { GmailAppMenuItemView } from '../views/gmail-app-menu-item-view';
|
|
2
|
+
import GmailElementGetter from '../gmail-element-getter';
|
|
3
|
+
import Logger from '../../../lib/logger';
|
|
4
|
+
export async function addAppMenuItem(driver, menuItemDescriptor) {
|
|
5
|
+
const appMenuInjectionContainer = await GmailElementGetter.getAppMenuAsync();
|
|
6
|
+
const gmailAppMenuItemView = new GmailAppMenuItemView(driver);
|
|
7
|
+
gmailAppMenuItemView.setMenuItemDescriptor(menuItemDescriptor);
|
|
8
|
+
try {
|
|
9
|
+
if (!appMenuInjectionContainer || !gmailAppMenuItemView.element)
|
|
10
|
+
return;
|
|
11
|
+
const siblingElement = menuItemDescriptor.insertIndex
|
|
12
|
+
? appMenuInjectionContainer.childNodes[menuItemDescriptor.insertIndex] ??
|
|
13
|
+
null
|
|
14
|
+
: null;
|
|
15
|
+
appMenuInjectionContainer.insertBefore(gmailAppMenuItemView.element, siblingElement);
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
Logger.error(err);
|
|
19
|
+
}
|
|
20
|
+
return gmailAppMenuItemView;
|
|
21
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import once from 'lodash/once';
|
|
2
|
+
import GmailElementGetter from '../gmail-element-getter';
|
|
3
|
+
import waitFor from '../../../lib/wait-for';
|
|
4
|
+
import Logger from '../../../lib/logger';
|
|
5
|
+
import { CollapsiblePanelView } from '../../../views/collapsible-panel-view';
|
|
6
|
+
export async function addCollapsiblePanel(driver, panelDescriptor) {
|
|
7
|
+
const injectionContainer = await waitForAppMenuParentReady();
|
|
8
|
+
const collapsiblePanelView = new CollapsiblePanelView(driver);
|
|
9
|
+
collapsiblePanelView.panelDescriptor = panelDescriptor;
|
|
10
|
+
if (!injectionContainer || !collapsiblePanelView.element)
|
|
11
|
+
return;
|
|
12
|
+
const appMenu = await GmailElementGetter.getAppMenuAsync();
|
|
13
|
+
if (!appMenu)
|
|
14
|
+
return;
|
|
15
|
+
appMenu.insertAdjacentElement('afterend', collapsiblePanelView.element);
|
|
16
|
+
return collapsiblePanelView;
|
|
17
|
+
}
|
|
18
|
+
const waitForAppMenuParentReady = once(async () => {
|
|
19
|
+
if (!GmailElementGetter.isStandalone()) {
|
|
20
|
+
try {
|
|
21
|
+
const menuParentElement = await waitFor(() => GmailElementGetter.getAppMenuContainer());
|
|
22
|
+
return menuParentElement;
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
Logger.error(e);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import GmailElementGetter from '../gmail-element-getter';
|
|
2
|
+
import GmailNavItemView from '../views/gmail-nav-item-view';
|
|
3
|
+
import Logger from '../../../lib/logger';
|
|
4
|
+
import waitFor from '../../../lib/wait-for';
|
|
5
|
+
import insertElementInOrder from '../../../lib/dom/insert-element-in-order';
|
|
6
|
+
import makeMutationObserverStream from '../../../lib/dom/make-mutation-observer-stream';
|
|
7
|
+
import querySelector from '../../../lib/dom/querySelectorOrFail';
|
|
8
|
+
import once from 'lodash/once';
|
|
9
|
+
export default async function addNavItem(driver, orderGroup, navItemDescriptor, navMenuInjectionContainer) {
|
|
10
|
+
await waitForMenuReady();
|
|
11
|
+
const gmailNavItemView = new GmailNavItemView(driver, orderGroup, 1);
|
|
12
|
+
gmailNavItemView.setNavItemDescriptor(navItemDescriptor);
|
|
13
|
+
if (!GmailElementGetter.isStandalone()) {
|
|
14
|
+
try {
|
|
15
|
+
const attacher = _attachNavItemView(gmailNavItemView, navMenuInjectionContainer);
|
|
16
|
+
attacher();
|
|
17
|
+
gmailNavItemView
|
|
18
|
+
.getEventStream()
|
|
19
|
+
.filter((event) => event.eventName === 'orderChanged')
|
|
20
|
+
.onValue(attacher);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
Logger.error(err);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return gmailNavItemView;
|
|
27
|
+
}
|
|
28
|
+
export const waitForMenuReady = once(async () => {
|
|
29
|
+
const appMenu = await GmailElementGetter.getAppMenuAsync();
|
|
30
|
+
if (!appMenu) {
|
|
31
|
+
await waitForNavMenuReady();
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const waitForNavMenuReady = once(async () => {
|
|
35
|
+
if (!GmailElementGetter.isStandalone()) {
|
|
36
|
+
await GmailElementGetter.waitForGmailModeToSettle();
|
|
37
|
+
await waitFor(() => document.querySelector('.aeN[role=navigation], .aeN [role=navigation]'));
|
|
38
|
+
// Wait for contents of navmenu to load (needed to figure out if it's integrated gmail mode)
|
|
39
|
+
await waitFor(() => document.querySelector('.Ls77Lb.aZ6 > .pp'));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
function _attachNavItemView(gmailNavItemView, navMenuInjectionContainer) {
|
|
43
|
+
if (navMenuInjectionContainer) {
|
|
44
|
+
return () => {
|
|
45
|
+
insertElementInOrder(navMenuInjectionContainer, gmailNavItemView.getElement());
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (!GmailElementGetter.shouldAddNavItemsInline()) {
|
|
49
|
+
// If we're in the modern (non-classic-hangouts) leftnav, then put
|
|
50
|
+
// the added nav items in a floating section at the bottom separate
|
|
51
|
+
// from the Mail section.
|
|
52
|
+
return function () {
|
|
53
|
+
const navMenuInjectionContainer = GmailElementGetter.getSeparateSectionNavItemMenuInjectionContainer();
|
|
54
|
+
if (!navMenuInjectionContainer) {
|
|
55
|
+
throw new Error('should not happen');
|
|
56
|
+
}
|
|
57
|
+
const nonMailLeftNavSections = Array.from(document.querySelectorAll('.Xa.wT:not([data-group-order-hint])')).slice(1);
|
|
58
|
+
nonMailLeftNavSections.forEach((div) => {
|
|
59
|
+
div.dataset.groupOrderHint = 'zz_gmail';
|
|
60
|
+
});
|
|
61
|
+
insertElementInOrder(navMenuInjectionContainer, gmailNavItemView.getElement());
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// If we're in the old classic-hangouts-compatible leftnav, then
|
|
66
|
+
// inject our added nav items among Gmail's own nav items.
|
|
67
|
+
return function () {
|
|
68
|
+
insertElementInOrder(_getNavItemsHolder(), gmailNavItemView.getElement());
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function _getNavItemsHolder() {
|
|
73
|
+
const holder = document.querySelector('.inboxsdk__navMenu');
|
|
74
|
+
if (!holder) {
|
|
75
|
+
return _createNavItemsHolder();
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
return querySelector(holder, '.TK');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function _createNavItemsHolder() {
|
|
82
|
+
const holder = document.createElement('div');
|
|
83
|
+
holder.setAttribute('class', 'LrBjie inboxsdk__navMenu');
|
|
84
|
+
holder.innerHTML = '<div class="TK"></div>';
|
|
85
|
+
const navMenuInjectionContainer = GmailElementGetter.getSameSectionNavItemMenuInjectionContainer();
|
|
86
|
+
if (!navMenuInjectionContainer)
|
|
87
|
+
throw new Error('should not happen');
|
|
88
|
+
navMenuInjectionContainer.insertBefore(holder, navMenuInjectionContainer.children[2]);
|
|
89
|
+
makeMutationObserverStream(holder, {
|
|
90
|
+
attributes: true,
|
|
91
|
+
attributeFilter: ['class'],
|
|
92
|
+
}).onValue(function () {
|
|
93
|
+
if (holder.classList.contains('TA')) {
|
|
94
|
+
holder.classList.remove('TA');
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return querySelector(holder, '.TK');
|
|
98
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { convertBase } from '@macil/simple-base-converter';
|
|
2
|
+
const B64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
3
|
+
const GMAIL_ALPHABET = 'BCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz';
|
|
4
|
+
export default function encodeDraftUrlId(syncThreadId, syncMessageId) {
|
|
5
|
+
const raw = `${syncThreadId.replace(/^thread-/, '')}+${syncMessageId}`;
|
|
6
|
+
const b64 = Buffer.from(raw).toString('base64').replace(/=/g, '');
|
|
7
|
+
return convertBase(b64, B64_ALPHABET, GMAIL_ALPHABET);
|
|
8
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import getOriginalMessagePage from '../../../driver-common/getOriginalMessagePage';
|
|
2
|
+
import htmlToText from '../../../../common/html-to-text';
|
|
3
|
+
export default async function getRfcMessageIdForGmailMessageId(driver, gmailMessageId) {
|
|
4
|
+
const text = await getOriginalMessagePage(driver, {
|
|
5
|
+
oldGmailMessageID: gmailMessageId,
|
|
6
|
+
});
|
|
7
|
+
const match = text.match(/^Message-ID:\s+(\S+)\s*$/im);
|
|
8
|
+
if (!match) {
|
|
9
|
+
throw new Error("Failed to find rfc id for gmail thread id. Message may not exist in user's account.");
|
|
10
|
+
}
|
|
11
|
+
return htmlToText(match[1]);
|
|
12
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import gmailAjax from '../../../driver-common/gmailAjax';
|
|
2
|
+
import querystring from 'querystring';
|
|
3
|
+
import * as GmailResponseProcessor from '../gmail-response-processor';
|
|
4
|
+
import getRfcMessageIdForGmailMessageId from './get-rfc-message-id-for-gmail-message-id';
|
|
5
|
+
export default async function getRfcMessageIdForGmailThreadId(driver, gmailThreadId) {
|
|
6
|
+
const messageIdQuery = {
|
|
7
|
+
ui: 2,
|
|
8
|
+
ik: driver.getPageCommunicator().getIkValue(),
|
|
9
|
+
view: 'cv',
|
|
10
|
+
th: gmailThreadId,
|
|
11
|
+
nsc: 1,
|
|
12
|
+
mb: 0,
|
|
13
|
+
rt: 'j',
|
|
14
|
+
search: 'all',
|
|
15
|
+
};
|
|
16
|
+
const gmailMessageIdResponse = await gmailAjax({
|
|
17
|
+
method: 'POST',
|
|
18
|
+
url: document.location.origin +
|
|
19
|
+
document.location.pathname +
|
|
20
|
+
'?' +
|
|
21
|
+
querystring.stringify(messageIdQuery),
|
|
22
|
+
canRetry: true,
|
|
23
|
+
});
|
|
24
|
+
const extractedIds = GmailResponseProcessor.extractMessageIdsFromThreadBatchRequest(gmailMessageIdResponse.text);
|
|
25
|
+
if (Object.keys(extractedIds).length !== 1) {
|
|
26
|
+
throw new Error(`Expected to find 1 thread/message ID pair, but got ${Object.keys(extractedIds).length}`);
|
|
27
|
+
}
|
|
28
|
+
const gmailMessageId = extractedIds[gmailThreadId];
|
|
29
|
+
if (!gmailMessageId) {
|
|
30
|
+
throw new Error('Could not find a message ID for given thread ID');
|
|
31
|
+
}
|
|
32
|
+
return getRfcMessageIdForGmailMessageId(driver, gmailMessageId);
|
|
33
|
+
}
|
package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/open-draft-by-message-id.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import qs from 'querystring';
|
|
2
|
+
import getSyncThreadsForSearch from '../../../driver-common/getSyncThreadsForSearch';
|
|
3
|
+
import encodeDraftUrlId from './encodeDraftUrlId';
|
|
4
|
+
import getRfcMessageIdForGmailMessageId from './get-rfc-message-id-for-gmail-message-id';
|
|
5
|
+
export default async function openDraftByMessageID(driver, messageID) {
|
|
6
|
+
let newHash;
|
|
7
|
+
if (driver.isUsingSyncAPI()) {
|
|
8
|
+
const rfcMessageID = await getRfcMessageIdForGmailMessageId(driver, messageID);
|
|
9
|
+
const { threads } = await getSyncThreadsForSearch(driver, 'rfc822msgid:' + rfcMessageID);
|
|
10
|
+
if (threads.length === 0) {
|
|
11
|
+
throw new Error('Failed to get sync message id');
|
|
12
|
+
}
|
|
13
|
+
const thread = threads[0];
|
|
14
|
+
const syncMessageData = thread.extraMetaData.syncMessageData.find((m) => m.oldMessageID === messageID);
|
|
15
|
+
if (!syncMessageData) {
|
|
16
|
+
throw new Error('Failed to find syncMessageData');
|
|
17
|
+
}
|
|
18
|
+
const { syncMessageID } = syncMessageData;
|
|
19
|
+
newHash = makeNewSyncHash(window.location.hash, thread.syncThreadID, syncMessageID);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
newHash = makeNewHash(window.location.hash, messageID);
|
|
23
|
+
}
|
|
24
|
+
window.location.hash = newHash;
|
|
25
|
+
}
|
|
26
|
+
export function makeNewHash(oldHash, messageID) {
|
|
27
|
+
oldHash = '#' + oldHash.replace(/^#/, '');
|
|
28
|
+
const [pre, query] = oldHash.split('?');
|
|
29
|
+
const params = qs.parse(query);
|
|
30
|
+
if (params.compose) {
|
|
31
|
+
params.compose += ',' + messageID;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
params.compose = messageID;
|
|
35
|
+
}
|
|
36
|
+
return pre + '?' + qs.stringify(params);
|
|
37
|
+
}
|
|
38
|
+
export function makeNewSyncHash(oldHash, syncThreadID, syncMessageID) {
|
|
39
|
+
oldHash = '#' + oldHash.replace(/^#/, '');
|
|
40
|
+
const [pre, query] = oldHash.split('?');
|
|
41
|
+
const params = qs.parse(query);
|
|
42
|
+
params.compose = encodeDraftUrlId(syncThreadID, syncMessageID);
|
|
43
|
+
return pre + '?' + qs.stringify(params);
|
|
44
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import Logger from '../../../lib/logger';
|
|
2
|
+
import waitFor from '../../../lib/wait-for';
|
|
3
|
+
import GmailElementGetter from '../gmail-element-getter';
|
|
4
|
+
const RGB_REGEX = /^rgb\s*\(\s*(\d+),\s*(\d+),\s*(\d+)\s*\)/;
|
|
5
|
+
function getDensity() {
|
|
6
|
+
// get the padding amount on the left-nav-menu entries to find the active
|
|
7
|
+
// density setting value.
|
|
8
|
+
const navItemOuter = document.querySelector('.TO .TN');
|
|
9
|
+
if (!navItemOuter) {
|
|
10
|
+
Logger.error(new Error('Failed to find nav item outer element'));
|
|
11
|
+
return 'compact';
|
|
12
|
+
}
|
|
13
|
+
const padding = parseInt(getComputedStyle(navItemOuter).getPropertyValue('padding-top'), 10);
|
|
14
|
+
if (padding >= 6) {
|
|
15
|
+
return 'comfortable';
|
|
16
|
+
}
|
|
17
|
+
else if (padding >= 3) {
|
|
18
|
+
return 'cozy';
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
return 'compact';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function isDarkTheme() {
|
|
25
|
+
// get the color of the left-nav-menu entries to determine whether Gmail is
|
|
26
|
+
// in dark theme mode.
|
|
27
|
+
const navItem = document.querySelector('.aio');
|
|
28
|
+
if (!navItem) {
|
|
29
|
+
Logger.error(new Error('Failed to find nav item'));
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const colorString = getComputedStyle(navItem).getPropertyValue('color');
|
|
33
|
+
const colorMatch = RGB_REGEX.exec(colorString);
|
|
34
|
+
if (!colorMatch) {
|
|
35
|
+
Logger.error(new Error('Failed to read color string'), { colorString });
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const r = +colorMatch[1], g = +colorMatch[2], b = +colorMatch[3];
|
|
39
|
+
// rgb(32, 33, 36) is the default color of nav items in Material Gmail
|
|
40
|
+
if (r === 32 && g === 33 && b === 36) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (r !== g || r !== b) {
|
|
44
|
+
Logger.error(new Error('Nav item color not grayscale'), { r, g, b });
|
|
45
|
+
}
|
|
46
|
+
return r > 128;
|
|
47
|
+
}
|
|
48
|
+
export default function trackGmailStyles() {
|
|
49
|
+
if (document.head.hasAttribute('data-inboxsdk-gmail-style-tracker') ||
|
|
50
|
+
GmailElementGetter.isStandalone()) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
document.head.setAttribute('data-inboxsdk-gmail-style-tracker', 'true');
|
|
54
|
+
let currentDensity = null;
|
|
55
|
+
let currentDarkTheme = null;
|
|
56
|
+
function checkStyles() {
|
|
57
|
+
const newDensity = getDensity();
|
|
58
|
+
if (currentDensity !== newDensity) {
|
|
59
|
+
if (currentDensity) {
|
|
60
|
+
document.body.classList.remove('inboxsdk__gmail_density_' + currentDensity);
|
|
61
|
+
}
|
|
62
|
+
currentDensity = newDensity;
|
|
63
|
+
document.body.classList.add('inboxsdk__gmail_density_' + currentDensity);
|
|
64
|
+
}
|
|
65
|
+
const newDarkTheme = isDarkTheme();
|
|
66
|
+
if (currentDarkTheme !== newDarkTheme) {
|
|
67
|
+
currentDarkTheme = newDarkTheme;
|
|
68
|
+
if (currentDarkTheme) {
|
|
69
|
+
document.body.classList.add('inboxsdk__gmail_dark_theme');
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
document.body.classList.remove('inboxsdk__gmail_dark_theme');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
waitFor(() => document.querySelector('.TO .TN') && document.querySelector('.aio'))
|
|
77
|
+
.then(() => {
|
|
78
|
+
// Gmail changes an inline <style> sheet when the display density changes.
|
|
79
|
+
// Watch for changes to all <style> elements.
|
|
80
|
+
const observer = new MutationObserver(checkStyles);
|
|
81
|
+
const options = {
|
|
82
|
+
characterData: true,
|
|
83
|
+
childList: true,
|
|
84
|
+
};
|
|
85
|
+
Array.from(document.styleSheets).forEach((sheet) => {
|
|
86
|
+
if (sheet.ownerNode.tagName == 'STYLE') {
|
|
87
|
+
observer.observe(sheet.ownerNode, options);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
checkStyles();
|
|
91
|
+
})
|
|
92
|
+
.catch((err) => {
|
|
93
|
+
Logger.error(err);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import last from 'lodash/last';
|
|
2
|
+
import * as Kefir from 'kefir';
|
|
3
|
+
import streamWaitFor from '../../../lib/stream-wait-for';
|
|
4
|
+
import makeMutationObserverChunkedStream from '../../../lib/dom/make-mutation-observer-chunked-stream';
|
|
5
|
+
import makeElementChildStream from '../../../lib/dom/make-element-child-stream';
|
|
6
|
+
export default function getMainContentElementChangedStream(GmailElementGetter) {
|
|
7
|
+
const s = waitForMainContentContainer(GmailElementGetter)
|
|
8
|
+
.flatMap((mainContentContainer) => makeElementChildStream(mainContentContainer)
|
|
9
|
+
.filter(({ el }) => el.classList.contains('nH'))
|
|
10
|
+
.flatMap(({ el, removalStream }) => makeMutationObserverChunkedStream(el, {
|
|
11
|
+
attributes: true,
|
|
12
|
+
attributeFilter: ['style'],
|
|
13
|
+
})
|
|
14
|
+
.map((x) => last(x))
|
|
15
|
+
.toProperty(() => {
|
|
16
|
+
return { target: el };
|
|
17
|
+
})
|
|
18
|
+
.filter(_isNowVisible)
|
|
19
|
+
.map((e) => e.target)
|
|
20
|
+
.takeUntilBy(removalStream)))
|
|
21
|
+
.toProperty();
|
|
22
|
+
// Make sure we always have a subscriber. The function breaks if it loses all
|
|
23
|
+
// subscribers and then re-gains some.
|
|
24
|
+
s.onValue(() => {
|
|
25
|
+
// ignore, just activate
|
|
26
|
+
});
|
|
27
|
+
return s;
|
|
28
|
+
}
|
|
29
|
+
function waitForMainContentContainer(GmailElementGetter) {
|
|
30
|
+
if (GmailElementGetter.isStandaloneComposeWindow()) {
|
|
31
|
+
return Kefir.never();
|
|
32
|
+
}
|
|
33
|
+
return streamWaitFor(() => GmailElementGetter.getMainContentContainer());
|
|
34
|
+
}
|
|
35
|
+
function _isNowVisible(mutation) {
|
|
36
|
+
const el = mutation.target;
|
|
37
|
+
return el.style.display !== 'none';
|
|
38
|
+
}
|