diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml
index 18a30f8229a..ea6e7f35c76 100644
--- a/.semaphore/semaphore.yml
+++ b/.semaphore/semaphore.yml
@@ -4,13 +4,10 @@ agent:
machine:
type: e1-standard-8
os_image: ubuntu1804
-
-auto_cancel: # cancel running CI for older commits on same branch if new commits are added
+auto_cancel:
running:
- when: "branch != 'master'"
-
+ when: branch != 'master'
blocks:
-
- name: Tests
dependencies: []
execution_time_limit:
@@ -28,53 +25,37 @@ blocks:
- mkdir ~/git && checkout && mv ~/test-secrets.json ~/git/flowcrypt-browser/test/test-secrets.json
- cd ~/git/flowcrypt-browser
- npm install
- - echo "NODE=$(node --version), NPM=$(npm --version), TSC=$( ./node_modules/typescript/bin/tsc --version)"
+ - 'echo "NODE=$(node --version), NPM=$(npm --version), TSC=$( ./node_modules/typescript/bin/tsc --version)"'
- npm run-script pretest
- - sudo sh -c "echo '209.250.232.81 cron.flowcrypt.com' >> /etc/hosts"
- - sudo sh -c "echo '127.0.0.1 fes.google.mock.flowcryptlocal.test' >> /etc/hosts"
- - sudo sh -c "echo '127.0.0.1 google.mock.flowcryptlocal.test' >> /etc/hosts"
- - sudo sh -c "echo '127.0.0.1 fes.standardsubdomainfes.test' >> /etc/hosts"
- - sudo sh -c "echo '127.0.0.1 standardsubdomainfes.test' >> /etc/hosts"
- - sudo sh -c "echo '127.0.0.1 fes.disablefesaccesstoken.test' >> /etc/hosts"
-
jobs:
-
- name: code quality
commands:
- npm run-script test_tslint
- npm run-script test_eslint
- npm run-script test_stylelint
- npm run-script test_patterns
-
- name: internals
commands:
- npm run-script test_async_stack
- npm run-script test_buf
-
- name: consumer mock - unit tests
commands:
- npm run-script test_local_unit_consumer
-
- name: consumer mock - standard test group
commands:
- npm -v
- node -v
- npm run-script test_ci_chrome_consumer
-
- name: consumer mock - flaky test group
commands:
- npm run-script test_ci_chrome_consumer_flaky
-
- name: enterprise mock - unit tests
commands:
- npm run-script test_local_unit_enterprise
-
- name: enterprise mock - standard test group
commands:
- npm run-script test_ci_chrome_enterprise
- cd ./build && zip -r ~/chrome-enterprise.zip ./chrome-enterprise/* && cd ~
-# - if [ "$SEMAPHORE_GIT_BRANCH" = master ]; artifact push project chrome-enterprise.zip --force; fi
-
epilogue:
on_fail:
commands:
@@ -86,7 +67,7 @@ blocks:
- name: Live Gmail tests
dependencies: []
run:
- when: "branch = 'master' OR branch =~ 'live-test' OR branch =~ 'gmail-test'"
+ when: branch = 'master' OR branch =~ 'live-test' OR branch =~ 'gmail-test'
execution_time_limit:
minutes: 20
task:
@@ -102,15 +83,17 @@ blocks:
- npm install -g npm@8.11.0 && mkdir ~/git && checkout && mv ~/test-secrets.json ~/git/flowcrypt-browser/test/test-secrets.json
- cd ~/git/flowcrypt-browser
- npm install
- - echo "NODE=$(node --version), NPM=$(npm --version), TSC=$( ./node_modules/typescript/bin/tsc --version)"
+ - 'echo "NODE=$(node --version), NPM=$(npm --version), TSC=$( ./node_modules/typescript/bin/tsc --version)"'
- npm run-script pretest
- - sudo sh -c "echo '209.250.232.81 cron.flowcrypt.com' >> /etc/hosts"
- - sudo sh -c "echo '127.0.0.1 fes.google.mock.flowcryptlocal.test' >> /etc/hosts"
- - sudo sh -c "echo '127.0.0.1 google.mock.flowcryptlocal.test' >> /etc/hosts"
- - sudo sh -c "echo '127.0.0.1 fes.standardsubdomainfes.test' >> /etc/hosts"
- - sudo sh -c "echo '127.0.0.1 standardsubdomainfes.test' >> /etc/hosts"
- - sudo sh -c "echo '127.0.0.1 fes.disablefesaccesstoken.test' >> /etc/hosts"
jobs:
- name: Live Gmail tests
commands:
- npm run-script test_ci_chrome_consumer_live_gmail
+ epilogue:
+ on_fail:
+ commands:
+ - |
+ if [ -f build/test/test/debugArtifacts/debugHtmlAttachment-0.html ]; then
+ echo "Uploading debug files as job artifacts..."
+ artifact push job build/test/test/debugArtifacts/debugHtmlAttachment-0.html
+ fi
\ No newline at end of file
diff --git a/extension/changelog.txt b/extension/changelog.txt
index 303dccd0d9b..f054baea6f0 100644
--- a/extension/changelog.txt
+++ b/extension/changelog.txt
@@ -1,5 +1,9 @@
+version 8.4.0 on November 18, 2022: :
Pwd msg prompt fix, add self-sig key
+
+version 8.3.9 on November 8, 2022: :
Reply improvements
+
version 8.3.8 on September 22, 2022:
Stop using legacy pub submit
version 8.3.7 on September 20, 2022:
Reauth fix, EKM removals, configurable pp session
diff --git a/extension/chrome/elements/add_pubkey.ts b/extension/chrome/elements/add_pubkey.ts
index 80d7b75f705..ab250daf39b 100644
--- a/extension/chrome/elements/add_pubkey.ts
+++ b/extension/chrome/elements/add_pubkey.ts
@@ -47,7 +47,7 @@ View.run(class AddPubkeyView extends View {
Xss.sanitizeAppend('select.copy_from_email', `
${escapedEmail} `);
}
this.fetchKeyUi.handleOnPaste($('.pubkey'));
- $('.action_settings').click(this.setHandler(async () => await Browser.openSettingsPage('index.htm', this.acctEmail, '/chrome/settings/modules/contacts.htm')));
+ $('.action_settings').on('click', this.setHandler(async () => await Browser.openSettingsPage('index.htm', this.acctEmail, '/chrome/settings/modules/contacts.htm')));
};
public setHandlers = () => {
@@ -68,8 +68,8 @@ View.run(class AddPubkeyView extends View {
}
});
$('select.copy_from_email').change(this.setHandler((el) => this.copyFromEmailHandler(el)));
- $('.action_ok').click(this.setHandler(() => this.submitHandler()));
- $('.action_close').click(this.setHandler(() => this.closeDialog()));
+ $('.action_ok').on('click', this.setHandler(() => this.submitHandler()));
+ $('.action_close').on('click', this.setHandler(() => this.closeDialog()));
};
private closeDialog = () => {
diff --git a/extension/chrome/elements/attachment.ts b/extension/chrome/elements/attachment.ts
index a032b321d85..80a7be65ccd 100644
--- a/extension/chrome/elements/attachment.ts
+++ b/extension/chrome/elements/attachment.ts
@@ -101,9 +101,9 @@ export class AttachmentDownloadView extends View {
public setHandlers = () => {
Ui.event.protect();
if (this.canClickOnAttachment) {
- this.downloadButton.click(this.setHandlerPrevent('double', () => this.downloadButtonClickedHandler()));
- this.downloadButton.click((e) => e.stopPropagation());
- $('body').click(async () => {
+ this.downloadButton.on('click', this.setHandlerPrevent('double', () => this.downloadButtonClickedHandler()));
+ this.downloadButton.on('click', (e) => e.stopPropagation());
+ $('body').on('click', async () => {
if ($('body').attr('id') !== 'attachment-preview' && !$('body').hasClass('error-occured')) {
await this.previewAttachmentClickedHandler();
}
@@ -285,7 +285,7 @@ export class AttachmentDownloadView extends View {
.html(`
Failed to decrypt:
see error details Downloading original…`) // xss-escaped
.addClass('error-occured')
.attr('title', '');
- $('.see-error-details').click(async () => {
+ $('.see-error-details').on('click', async () => {
await this.previewAttachmentClickedHandler(true);
});
const name = this.attachment.name;
diff --git a/extension/chrome/elements/attachment_preview.ts b/extension/chrome/elements/attachment_preview.ts
index 86f54662996..d46f02ebad5 100644
--- a/extension/chrome/elements/attachment_preview.ts
+++ b/extension/chrome/elements/attachment_preview.ts
@@ -57,12 +57,12 @@ View.run(class AttachmentPreviewView extends AttachmentDownloadView {
$('.attachment-preview-unavailable').prepend('No preview available'); // xss-escaped
$('#attachment-preview-download').appendTo('.attachment-preview-unavailable');
}
- $('body').click((e) => {
+ $('body').on('click', (e) => {
if (e.target === document.body || $('body').children().toArray().indexOf(e.target) !== -1) {
BrowserMsg.send.closeDialog(this.parentTabId);
}
});
- $('#attachment-preview-download').css('display', 'flex').click((e) => {
+ $('#attachment-preview-download').css('display', 'flex').on('click', (e) => {
e.stopPropagation();
Browser.saveToDownloads(attachmentForSave);
});
diff --git a/extension/chrome/elements/backup.htm b/extension/chrome/elements/backup.htm
index 259dd6c6d56..e08d26f1b86 100644
--- a/extension/chrome/elements/backup.htm
+++ b/extension/chrome/elements/backup.htm
@@ -16,7 +16,7 @@
This backup is protected by your pass phrase. Please make sure
to note your pass phrase down or you may lose access to your encrypted emails!
-
+
Test your Passphrase
Key Fingerprint:
diff --git a/extension/chrome/elements/backup.ts b/extension/chrome/elements/backup.ts
index 4c68d4e4027..c1470cd694e 100644
--- a/extension/chrome/elements/backup.ts
+++ b/extension/chrome/elements/backup.ts
@@ -59,9 +59,9 @@ View.run(class BackupView extends View {
public setHandlers = () => {
if (!this.storedPrvWithMatchingLongid) {
- $("#action_import_key").click(this.setHandler(async () => await Browser.openSettingsPage('index.htm', this.acctEmail, '/chrome/settings/modules/add_key.htm')));
+ $("#action_import_key").on('click', this.setHandler(async () => await Browser.openSettingsPage('index.htm', this.acctEmail, '/chrome/settings/modules/add_key.htm')));
}
- $('.action_test_pass').click(this.setHandler(async () => this.testPassphraseHandler()));
+ $('.action_test_pass').on('click', this.setHandler(async () => this.testPassphraseHandler()));
$('#pass_phrase').keydown(this.setEnterHandlerThatClicks('.action_test_pass'));
};
diff --git a/extension/chrome/elements/compose-modules/compose-draft-module.ts b/extension/chrome/elements/compose-modules/compose-draft-module.ts
index 91536904479..4ee78e4c3fc 100644
--- a/extension/chrome/elements/compose-modules/compose-draft-module.ts
+++ b/extension/chrome/elements/compose-modules/compose-draft-module.ts
@@ -45,7 +45,7 @@ export class ComposeDraftModule extends ViewModule
{
}
public setHandlers = () => {
- $('.delete_draft').click(this.view.setHandler(() => this.deleteDraftClickHandler(), this.view.errModule.handle('delete draft')));
+ $('.delete_draft').on('click', this.view.setHandler(() => this.deleteDraftClickHandler(), this.view.errModule.handle('delete draft')));
this.view.recipientsModule.onRecipientAdded(async () => await this.draftSave(true));
};
@@ -363,10 +363,10 @@ export class ComposeDraftModule extends ViewModule {
`).css({ display: 'flex', height: '100%' });
BrowserMsg.send.setActiveWindow(this.view.parentTabId, { frameId: this.view.frameId });
}
- this.view.S.cached('prompt').find('.action_open_passphrase_dialog').click(this.view.setHandler(async () => {
+ this.view.S.cached('prompt').find('.action_open_passphrase_dialog').on('click', this.view.setHandler(async () => {
BrowserMsg.send.passphraseDialog(this.view.parentTabId, { type: 'draft', longids });
})).focus();
- this.view.S.cached('prompt').find('.action_close').click(this.view.setHandler(() => this.view.renderModule.closeMsg()));
+ this.view.S.cached('prompt').find('.action_close').on('click', this.view.setHandler(() => this.view.renderModule.closeMsg()));
const setActiveWindow = this.view.setHandler(async () => { BrowserMsg.send.setActiveWindow(this.view.parentTabId, { frameId: this.view.frameId }); });
this.view.S.cached('prompt').on('click', setActiveWindow).trigger('click');
await PassphraseStore.waitUntilPassphraseChanged(this.view.acctEmail, longids, 1000, this.view.ppChangedPromiseCancellation);
diff --git a/extension/chrome/elements/compose-modules/compose-err-module.ts b/extension/chrome/elements/compose-modules/compose-err-module.ts
index 6731b3ac864..4d51f524732 100644
--- a/extension/chrome/elements/compose-modules/compose-err-module.ts
+++ b/extension/chrome/elements/compose-modules/compose-err-module.ts
@@ -81,7 +81,7 @@ export class ComposeErrModule extends ViewModule {
await Ui.modal.error(netErrMsg, true);
} else if (ApiErr.isAuthErr(e)) {
BrowserMsg.send.notificationShowAuthPopupNeeded(this.view.parentTabId, { acctEmail: this.view.acctEmail });
- Settings.offerToLoginWithPopupShowModalOnErr(this.view.acctEmail);
+ Settings.offerToLoginWithPopupShowModalOnErr(this.view.acctEmail, () => this.view.sendBtnModule.extractProcessSendMsg());
} else if (ApiErr.isReqTooLarge(e)) {
await Ui.modal.error(`Could not send: message or attachments too large.`);
} else if (ApiErr.isBadReq(e)) {
@@ -151,7 +151,11 @@ export class ComposeErrModule extends ViewModule {
}
};
- public throwIfEncryptionPasswordInvalid = async ({ subject, pwd }: { subject: string, pwd?: string }) => {
+ public throwIfEncryptionPasswordInvalidOrDisabled = async ({ subject, pwd }: { subject: string, pwd?: string }) => {
+ // When DISABLE_FLOWCRYPT_HOSTED_PASSWORD_MESSAGES present, and recipients are missing a public key, and the user is using flowcrypt.com/api (not FES)
+ if (this.view.clientConfiguration.shouldDisablePasswordMessages() && !this.view.isFesUsed()) {
+ throw new ComposerUserError(Lang.compose.addMissingRecipientPubkeys);
+ }
if (pwd) {
if (await this.view.storageModule.isPwdMatchingPassphrase(pwd)) {
throw new ComposerUserError('Please do not use your private key pass phrase as a password for this message.\n\n' +
diff --git a/extension/chrome/elements/compose-modules/compose-input-module.ts b/extension/chrome/elements/compose-modules/compose-input-module.ts
index 1b7fd55fb8b..3a409752845 100644
--- a/extension/chrome/elements/compose-modules/compose-input-module.ts
+++ b/extension/chrome/elements/compose-modules/compose-input-module.ts
@@ -17,7 +17,7 @@ export class ComposeInputModule extends ViewModule {
public squire = new window.Squire(this.view.S.cached('input_text').get(0));
public setHandlers = () => {
- this.view.S.cached('add_intro').click(this.view.setHandler(el => this.actionAddIntroHandler(el), this.view.errModule.handle(`add intro`)));
+ this.view.S.cached('add_intro').on('click', this.view.setHandler(el => this.actionAddIntroHandler(el), this.view.errModule.handle(`add intro`)));
this.handlePaste();
this.handlePasteImages();
this.initShortcuts();
diff --git a/extension/chrome/elements/compose-modules/compose-my-pubkey-module.ts b/extension/chrome/elements/compose-modules/compose-my-pubkey-module.ts
index d5c426f4c16..4ec67842596 100644
--- a/extension/chrome/elements/compose-modules/compose-my-pubkey-module.ts
+++ b/extension/chrome/elements/compose-modules/compose-my-pubkey-module.ts
@@ -18,7 +18,7 @@ export class ComposeMyPubkeyModule extends ViewModule {
public setHandlers = () => {
this.view.S.cached('icon_pubkey').attr('title', Lang.compose.includePubkeyIconTitle);
- this.view.S.cached('icon_pubkey').click(this.view.setHandler((el) => this.iconPubkeyClickHandler(el), this.view.errModule.handle(`set/unset pub attachment`)));
+ this.view.S.cached('icon_pubkey').on('click', this.view.setHandler((el) => this.iconPubkeyClickHandler(el), this.view.errModule.handle(`set/unset pub attachment`)));
};
public iconPubkeyClickHandler = (target: HTMLElement) => {
diff --git a/extension/chrome/elements/compose-modules/compose-pwd-or-pubkey-container-module.ts b/extension/chrome/elements/compose-modules/compose-pwd-or-pubkey-container-module.ts
index a26774e73ac..fbdded24ac8 100644
--- a/extension/chrome/elements/compose-modules/compose-pwd-or-pubkey-container-module.ts
+++ b/extension/chrome/elements/compose-modules/compose-pwd-or-pubkey-container-module.ts
@@ -24,27 +24,27 @@ export class ComposePwdOrPubkeyContainerModule extends ViewModule {
}
public setHandlers = () => {
- this.view.S.cached('input_password').keyup(this.view.setHandlerPrevent('spree', () => this.showHideContainerAndColorSendBtn()));
- this.view.S.cached('input_password').focus(this.view.setHandlerPrevent('spree', () => this.inputPwdFocusHandler()));
- this.view.S.cached('input_password').blur(this.view.setHandler(() => this.inputPwdBlurHandler()));
- this.view.S.cached('expiration_note').find('#expiration_note_settings_link').click(this.view.setHandler(async (el, e) => {
- e.preventDefault();
- await this.view.renderModule.openSettingsWithDialog('security');
- }, this.view.errModule.handle(`render settings dialog`)));
+ this.view.S.cached('input_password').on('focus', this.view.setHandlerPrevent('spree', () => this.inputPwdFocusHandler()));
+ this.view.S.cached('input_password').on('blur', this.view.setHandler(() => this.inputPwdBlurHandler()));
+ this.view.S.cached('expiration_note').find('#expiration_note_settings_link').on(
+ 'click',
+ this.view.setHandler(async (el, e) => {
+ e.preventDefault();
+ await this.view.renderModule.openSettingsWithDialog('security');
+ }, this.view.errModule.handle(`render settings dialog`))
+ );
};
public inputPwdFocusHandler = () => {
const passwordContainerHeight = this.view.S.cached('password_or_pubkey').outerHeight() || 0;
this.view.S.cached('expiration_note').css({ bottom: passwordContainerHeight });
this.view.S.cached('expiration_note').fadeIn();
- this.showHideContainerAndColorSendBtn(); // tslint:disable-line:no-floating-promises
};
public inputPwdBlurHandler = () => {
Catch.setHandledTimeout(() => { // timeout here is needed so will be visible once clicked
this.view.S.cached('expiration_note').fadeOut();
}, 100);
- this.showHideContainerAndColorSendBtn(); // tslint:disable-line:no-floating-promises
};
public showHideContainerAndColorSendBtn = async () => {
@@ -99,35 +99,34 @@ export class ComposePwdOrPubkeyContainerModule extends ViewModule {
return true;
};
+ private initExpirationText = async () => {
+ // Init expiration text element
+ const expirationTextEl = this.view.S.cached('expiration_note').find('#expiration_note_message_expire');
+ const pwdPolicy = this.view.fesUrl ? Lang.compose.enterprisePasswordPolicy : Lang.compose.consumerPasswordPolicy;
+ $('#password-policy-container').html(Xss.htmlSanitize(pwdPolicy.split('\n').join(' '))); // xss-sanitized
+ if (!this.view.acctEmail) {
+ expirationTextEl.text(Str.pluralize(this.MSG_EXPIRE_DAYS_DEFAULT, 'day'));
+ } else {
+ try {
+ const response = await this.view.acctServer.accountGetAndUpdateLocalStore();
+ expirationTextEl.text(Str.pluralize(response.account.default_message_expire, 'day'));
+ } catch (e) {
+ ApiErr.reportIfSignificant(e);
+ expirationTextEl.text(`(unknown days: ${ApiErr.eli5(e)})`);
+ }
+ }
+ };
+
private showMsgPwdUiAndColorBtn = async (anyNopgp: boolean, anyRevoked: boolean) => {
+ const isPasswordMessageDisabled = this.view.clientConfiguration.shouldDisablePasswordMessages() && !this.view.isFesUsed();
if (!this.isVisible()) {
- const expirationTextEl = this.view.S.cached('expiration_note').find('#expiration_note_message_expire');
- const pwdPolicy = this.view.fesUrl ? Lang.compose.enterprisePasswordPolicy : Lang.compose.consumerPasswordPolicy;
- $('#password-policy-container').html(Xss.htmlSanitize(pwdPolicy.split('\n').join(' '))); // xss-sanitized
- if (!this.view.acctEmail) {
- expirationTextEl.text(Str.pluralize(this.MSG_EXPIRE_DAYS_DEFAULT, 'day'));
- } else {
- try {
- const response = await this.view.acctServer.accountGetAndUpdateLocalStore();
- expirationTextEl.text(Str.pluralize(response.account.default_message_expire, 'day'));
- } catch (e) {
- ApiErr.reportIfSignificant(e);
- expirationTextEl.text(`(unknown days: ${ApiErr.eli5(e)})`);
- }
- }
+ await this.initExpirationText();
this.view.S.cached('password_or_pubkey').css('display', 'table-row');
}
- if (this.view.S.cached('input_password').val() || this.view.S.cached('input_password').is(':focus')) {
- this.view.S.cached('password_label').css('display', 'inline-block');
- this.view.S.cached('input_password').attr('placeholder', '');
- } else {
- this.view.S.cached('password_label').css('display', 'none');
- this.view.S.cached('input_password').attr('placeholder', 'message password');
- }
- if (this.view.S.cached('input_intro').is(':visible')) {
- this.view.S.cached('add_intro').css('display', 'none');
+ if (isPasswordMessageDisabled) {
+ this.view.S.cached('password_input_container').hide();
} else {
- this.view.S.cached('add_intro').css('display', 'block');
+ this.view.S.cached('add_intro').show();
}
this.view.S.cached('warning_nopgp').css('display', anyNopgp ? 'inline-block' : 'none');
this.view.S.cached('warning_revoked').css('display', anyRevoked ? 'inline-block' : 'none');
@@ -135,11 +134,11 @@ export class ComposePwdOrPubkeyContainerModule extends ViewModule {
};
private hideMsgPwdUi = () => {
- this.view.S.cached('password_or_pubkey').css('display', 'none');
+ this.view.S.cached('password_or_pubkey').hide();
this.view.S.cached('input_password').val('');
- this.view.S.cached('add_intro').css('display', 'none');
+ this.view.S.cached('add_intro').hide();
this.view.S.cached('input_intro').text('');
- this.view.S.cached('intro_container').css('display', 'none');
+ this.view.S.cached('intro_container').hide();
this.view.sizeModule.setInputTextHeightManuallyIfNeeded();
};
diff --git a/extension/chrome/elements/compose-modules/compose-quote-module.ts b/extension/chrome/elements/compose-modules/compose-quote-module.ts
index 3caf8f33d6e..5347769946a 100644
--- a/extension/chrome/elements/compose-modules/compose-quote-module.ts
+++ b/extension/chrome/elements/compose-modules/compose-quote-module.ts
@@ -38,7 +38,7 @@ export class ComposeQuoteModule extends ViewModule {
}
const sanitizedFooter = textFooter && !this.view.draftModule.wasMsgLoadedFromDraft ? this.view.footerModule.createFooterHtml(textFooter) : undefined;
this.tripleDotSanitizedHtmlContent = { footer: sanitizedFooter, quote: undefined };
- this.view.S.cached('triple_dot').click(this.view.setHandler(el => this.actionRenderTripleDotContentHandle(el)));
+ this.view.S.cached('triple_dot').on('click', this.view.setHandler(el => this.actionRenderTripleDotContentHandle(el)));
};
public addTripleDotQuoteExpandFooterAndQuoteBtn = async (msgId: string, method: 'reply' | 'forward') => {
@@ -77,7 +77,7 @@ export class ComposeQuoteModule extends ViewModule {
if (method === 'forward') {
this.actionRenderTripleDotContentHandle(this.view.S.cached('triple_dot')[0]);
} else {
- this.view.S.cached('triple_dot').click(this.view.setHandler(el => this.actionRenderTripleDotContentHandle(el)));
+ this.view.S.cached('triple_dot').on('click', this.view.setHandler(el => this.actionRenderTripleDotContentHandle(el)));
}
};
diff --git a/extension/chrome/elements/compose-modules/compose-recipients-module.ts b/extension/chrome/elements/compose-modules/compose-recipients-module.ts
index e00360aad57..d16f14fee73 100644
--- a/extension/chrome/elements/compose-modules/compose-recipients-module.ts
+++ b/extension/chrome/elements/compose-modules/compose-recipients-module.ts
@@ -42,11 +42,11 @@ export class ComposeRecipientsModule extends ViewModule {
private dragged: Element | undefined = undefined;
- private googleContactsSearchEnabled: boolean;
+ private googleContactsSearchEnabled: boolean | Promise;
constructor(view: ComposeView) {
super(view);
- this.googleContactsSearchEnabled = this.view.scopes.readContacts && this.view.scopes.readOtherContacts;
+ this.googleContactsSearchEnabled = this.queryIfGoogleSearchEnabled();
}
public setHandlers = (): void => {
@@ -66,22 +66,22 @@ export class ComposeRecipientsModule extends ViewModule {
inputs.on('dragover', (e) => e.preventDefault());
inputs.on('drop', this.view.setHandler((target) => this.inputsDropHandler(target)));
this.view.S.cached('recipients_toggle_elements').on('focus', this.view.setHandler(() => this.collapseInputsIfNeeded()));
- this.view.S.now('cc').click(this.view.setHandler((target) => {
+ this.view.S.now('cc').on('click', this.view.setHandler((target) => {
const newContainer = this.view.S.cached('input_addresses_container_outer').find(`#input-container-cc`);
this.copyCcBccActionsClickHandler(target, newContainer);
}));
- this.view.S.now('bcc').click(this.view.setHandler((target) => {
+ this.view.S.now('bcc').on('click', this.view.setHandler((target) => {
const newContainer = this.view.S.cached('input_addresses_container_outer').find(`#input-container-bcc`);
this.copyCcBccActionsClickHandler(target, newContainer);
}));
- this.view.S.cached('recipients_placeholder').click(this.view.setHandler(() => {
+ this.view.S.cached('recipients_placeholder').on('click', this.view.setHandler(() => {
this.view.S.cached('input_to').focus();
}));
this.view.S.cached('input_to').focus(this.view.setHandler(() => this.focusRecipients()));
this.view.S.cached('cc').focus(this.view.setHandler(() => this.focusRecipients()));
this.view.S.cached('bcc').focus(this.view.setHandler(() => this.focusRecipients()));
- this.view.S.cached('compose_table').click(this.view.setHandler(() => this.hideContacts(), this.view.errModule.handle(`hide contact box`)));
- this.view.S.cached('add_their_pubkey').click(this.view.setHandler(() => this.addTheirPubkeyClickHandler(), this.view.errModule.handle('add pubkey')));
+ this.view.S.cached('compose_table').on('click', this.view.setHandler(() => this.hideContacts(), this.view.errModule.handle(`hide contact box`)));
+ this.view.S.cached('add_their_pubkey').on('click', this.view.setHandler(() => this.addTheirPubkeyClickHandler(), this.view.errModule.handle('add pubkey')));
BrowserMsg.addListener('addToContacts', this.checkReciepientsKeys);
BrowserMsg.addListener('reRenderRecipient', async ({ email }: Bm.ReRenderRecipient) => {
await this.reRenderRecipientFor(email);
@@ -383,6 +383,16 @@ export class ComposeRecipientsModule extends ViewModule {
this.setEmailsPreview();
};
+ private queryIfGoogleSearchEnabled = async () => {
+ try {
+ const scopes = await AcctStore.getScopes(this.view.acctEmail);
+ return scopes.readContacts && scopes.readOtherContacts;
+ } catch (e) {
+ this.view.errModule.debug(`googleContactsSearchEnabled: Error occurred while fetching result: ${e}`);
+ return undefined;
+ }
+ };
+
private inputsBlurHandler = async (target: HTMLElement, e: JQuery.Event) => {
if (this.dragged) { // blur while drag&drop
return;
@@ -493,7 +503,7 @@ export class ComposeRecipientsModule extends ViewModule {
}
} else if (e.key === 'Enter') {
if (currentActive.length) { // If he pressed enter when contacts popover is shown
- currentActive.click(); // select contact
+ currentActive.trigger('click'); // select contact
currentActive.removeClass('active');
} else { // We need to force add recipient even it's invalid
this.parseRenderRecipients($(e.target), true).catch(Catch.reportErr);
@@ -513,7 +523,7 @@ export class ComposeRecipientsModule extends ViewModule {
} else if (e.key === 'Tab') {
e.preventDefault(); // don't switch inputs
e.stopPropagation(); // don't switch inputs
- currentActive.click(); // select contact
+ currentActive.trigger('click'); // select contact
currentActive.removeClass('active');
return true;
} else if (e.key === 'ArrowUp') {
@@ -555,19 +565,19 @@ export class ComposeRecipientsModule extends ViewModule {
const contacts: ContactPreview[] = await ContactStore.search(undefined, { substring });
this.view.errModule.debug(`searchContacts substring: ${substring}`);
this.view.errModule.debug(`searchContacts db count: ${contacts.length}`);
- this.renderSearchRes(input, contacts, { substring });
+ await this.renderSearchRes(input, contacts, { substring });
if (contacts.length >= this.MAX_CONTACTS_LENGTH) {
this.view.errModule.debug(`searchContacts 2, count: ${contacts.length}`);
return;
}
let foundOnGoogle: EmailProviderContact[] = [];
- if (this.googleContactsSearchEnabled) {
+ if ((await this.googleContactsSearchEnabled) !== false) {
this.view.errModule.debug(`searchContacts 3`);
foundOnGoogle = await this.searchContactsOnGoogle(substring, contacts);
await this.addApiLoadedContactsToDb(foundOnGoogle);
this.view.errModule.debug(`searchContacts foundOnGoogle, count: ${foundOnGoogle.length}`);
contacts.push(...foundOnGoogle.map(c => ContactStore.previewObj({ email: c.email, name: c.name })));
- this.renderSearchRes(input, contacts, { substring });
+ await this.renderSearchRes(input, contacts, { substring });
if (contacts.length >= this.MAX_CONTACTS_LENGTH) {
this.view.errModule.debug(`searchContacts 3.b, count: ${contacts.length}`);
return;
@@ -580,7 +590,7 @@ export class ComposeRecipientsModule extends ViewModule {
await this.addApiLoadedContactsToDb(guessed.new);
this.view.errModule.debug(`searchContacts (Gmail Sent Messages), count: ${guessed.new.length}`);
contacts.push(...guessed.new.map(c => ContactStore.previewObj({ email: c.email, name: c.name })));
- this.renderSearchRes(input, contacts, { substring });
+ await this.renderSearchRes(input, contacts, { substring });
});
}
} catch (e) {
@@ -610,12 +620,10 @@ export class ComposeRecipientsModule extends ViewModule {
};
private searchContactsOnGoogle = async (query: string, knownContacts: ContactPreview[]): Promise => {
- if (this.googleContactsSearchEnabled) {
- this.view.errModule.debug(`searchContacts (Google API) 5`);
- const contactsGoogle = await Google.contactsGet(this.view.acctEmail, query, undefined, this.MAX_CONTACTS_LENGTH);
- if (contactsGoogle && contactsGoogle.length) {
- return contactsGoogle.filter(cGmail => !knownContacts.find(c => c.email === cGmail.email));
- }
+ this.view.errModule.debug(`searchContacts (Google API) 5`);
+ const contactsGoogle = await Google.contactsGet(this.view.acctEmail, query, undefined, this.MAX_CONTACTS_LENGTH);
+ if (contactsGoogle && contactsGoogle.length) {
+ return contactsGoogle.filter(cGmail => !knownContacts.find(c => c.email === cGmail.email));
}
return [];
};
@@ -641,7 +649,7 @@ export class ComposeRecipientsModule extends ViewModule {
});
};
- private renderSearchRes = (input: JQuery, contacts: ContactPreview[], query: ProviderContactsQuery) => {
+ private renderSearchRes = async (input: JQuery, contacts: ContactPreview[], query: ProviderContactsQuery) => {
if (!input.is(':focus')) { // focus was moved away from input
return;
}
@@ -695,7 +703,7 @@ export class ComposeRecipientsModule extends ViewModule {
Xss.sanitizeRender(contactEl.find('ul'), ulHtml);
const contactItems = contactEl.find('ul li.select_contact');
contactItems.first().addClass('active');
- contactItems.click(this.view.setHandlerPrevent('double', async (target: HTMLElement) => {
+ contactItems.on('click', this.view.setHandlerPrevent('double', async (target: HTMLElement) => {
const email = Str.parseEmail($(target).attr('email') || '').email;
if (email) {
await this.selectContact(input, email, query);
@@ -709,7 +717,7 @@ export class ComposeRecipientsModule extends ViewModule {
} else {
this.setContactPopupStyle(input);
contactEl.find('ul').html('No Contacts Found '); // xss-direct
- if (!this.googleContactsSearchEnabled) {
+ if ((await this.googleContactsSearchEnabled) === false) {
this.addBtnToAllowSearchContactsFromGoogle(input);
}
}
@@ -722,7 +730,7 @@ export class ComposeRecipientsModule extends ViewModule {
this.view.S.cached('contacts')
.append('') // xss-direct
.find('.allow-google-contact-search')
- .click(this.view.setHandler(async () => {
+ .on('click', this.view.setHandler(async () => {
// Need to use BrowserMsg.send.bg because chrome.windows is undefined in gmail page
const authResult = await BrowserMsg.send.bg.await.reconnectAcctAuthPopup({ acctEmail: this.view.acctEmail, scopes: GoogleAuth.defaultScopes('contacts') });
if (authResult.result === 'Success') {
@@ -852,7 +860,7 @@ export class ComposeRecipientsModule extends ViewModule {
` `;
Xss.sanitizeAppend(el, contentHtml)
.find('img.close-icon')
- .click(this.view.setHandler(target => this.removeRecipient(target.parentElement!), this.view.errModule.handle('remove recipient')));
+ .on('click', this.view.setHandler(target => this.removeRecipient(target.parentElement!), this.view.errModule.handle('remove recipient')));
$(el).removeClass(['failed', 'wrong', 'has_pgp', 'no_pgp', 'expired']);
if (recipient.status === RecipientStatus.WRONG) {
this.view.errModule.debug(`renderPubkeyResult: Setting email to wrong / misspelled in harsh mode: ${recipient.invalid}`);
@@ -873,8 +881,8 @@ export class ComposeRecipientsModule extends ViewModule {
`
);
- $(el).find('.action_retry_pubkey_fetch').click(this.view.setHandler(async () => await this.refreshRecipients(), this.view.errModule.handle('refresh recipient')));
- $(el).find('.remove-reciepient').click(this.view.setHandler(element => this.removeRecipient(element.parentElement!), this.view.errModule.handle('remove recipient')));
+ $(el).find('.action_retry_pubkey_fetch').on('click', this.view.setHandler(async () => await this.refreshRecipients(), this.view.errModule.handle('refresh recipient')));
+ $(el).find('.remove-reciepient').on('click', this.view.setHandler(element => this.removeRecipient(element.parentElement!), this.view.errModule.handle('remove recipient')));
} else if (info && info.sortedPubkeys.length) {
if (info.info.name) {
recipient.name = info.info.name;
diff --git a/extension/chrome/elements/compose-modules/compose-render-module.ts b/extension/chrome/elements/compose-modules/compose-render-module.ts
index 1e0a989228b..6d13321f1df 100644
--- a/extension/chrome/elements/compose-modules/compose-render-module.ts
+++ b/extension/chrome/elements/compose-modules/compose-render-module.ts
@@ -53,7 +53,7 @@ export class ComposeRenderModule extends ViewModule {
await this.renderReplyMsgComposeTable();
} else {
$('#a_reply,#a_reply_all,#a_forward')
- .click(this.view.setHandler((el) => this.actionActivateReplyBoxHandler(el), this.view.errModule.handle(`activate reply box`)));
+ .on('click', this.view.setHandler((el) => this.actionActivateReplyBoxHandler(el), this.view.errModule.handle(`activate reply box`)));
}
}
}
@@ -102,7 +102,7 @@ export class ComposeRenderModule extends ViewModule {
}
this.view.sizeModule.resizeComposeBox();
if (this.responseMethod === 'forward') {
- this.view.S.cached('recipients_placeholder').click();
+ this.view.S.cached('recipients_placeholder').trigger('click');
}
BrowserMsg.send.scrollToReplyBox(this.view.parentTabId, { replyMsgId: `#${this.view.frameId}` });
};
@@ -285,10 +285,10 @@ export class ComposeRenderModule extends ViewModule {
await this.view.recipientsModule.parseRenderRecipients(this.view.S.cached('input_to')); // this will force firefox to render them on load
}
} else {
- $('.close_compose_window').click(this.view.setHandler(() => this.actionCloseHandler(), this.view.errModule.handle(`close compose window`)));
- this.view.S.cached('title').click(() => {
+ $('.close_compose_window').on('click', this.view.setHandler(() => this.actionCloseHandler(), this.view.errModule.handle(`close compose window`)));
+ this.view.S.cached('title').on('click', () => {
if (this.view.sizeModule.composeWindowIsMinimized) {
- $('.minimize_compose_window').click();
+ $('.minimize_compose_window').trigger('click');
}
});
await this.view.quoteModule.addTripleDotQuoteExpandFooterOnlyBtn();
@@ -310,8 +310,8 @@ export class ComposeRenderModule extends ViewModule {
this.view.S.cached('body').keydown(this.view.setHandler((el, ev) => this.onBodyKeydownHandler(el, ev)));
this.view.S.cached('input_to').bind('paste', this.view.setHandler((el, ev) => this.onRecipientPasteHandler(el, ev)));
this.view.inputModule.squire.addEventListener('input', () => this.view.S.cached('send_btn_note').text(''));
- this.view.S.cached('input_addresses_container_inner').click(this.view.setHandler(() => this.onRecipientsClickHandler(), this.view.errModule.handle(`focus recipients`)));
- this.view.S.cached('input_addresses_container_inner').children().click(() => false);
+ this.view.S.cached('input_addresses_container_inner').on('click', this.view.setHandler(() => this.onRecipientsClickHandler(), this.view.errModule.handle(`focus recipients`)));
+ this.view.S.cached('input_addresses_container_inner').children().on('click', () => false);
this.view.S.cached('input_subject').bind('input', this.view.setHandler((el: HTMLInputElement) => this.subjectRTLHandler(el))).trigger('input');
};
@@ -366,7 +366,7 @@ export class ComposeRenderModule extends ViewModule {
if (this.view.sizeModule.composeWindowIsMinimized) {
return e.preventDefault();
}
- Ui.escape(() => !this.view.isReplyBox && $('.close_compose_window').click())(e);
+ Ui.escape(() => !this.view.isReplyBox && $('.close_compose_window').trigger('click'))(e);
const focusableEls = this.getFocusableEls();
const focusIndex = focusableEls.indexOf(e.target);
if (focusIndex !== -1) { // Focus trap (Tab, Shift+Tab)
diff --git a/extension/chrome/elements/compose-modules/compose-reply-btn-popover-module.ts b/extension/chrome/elements/compose-modules/compose-reply-btn-popover-module.ts
index ab7eee4b7f2..4443517dd81 100644
--- a/extension/chrome/elements/compose-modules/compose-reply-btn-popover-module.ts
+++ b/extension/chrome/elements/compose-modules/compose-reply-btn-popover-module.ts
@@ -18,7 +18,7 @@ export class ComposeReplyBtnPopoverModule extends ViewModule {
};
public setHandlers = (): void => {
- this.view.S.cached('toggle_reply_options').click(this.view.setHandler((el, ev) => this.toggleVisible(ev)));
+ this.view.S.cached('toggle_reply_options').on('click', this.view.setHandler((el, ev) => this.toggleVisible(ev)));
};
public render = async (isReply = true) => {
@@ -32,7 +32,7 @@ export class ComposeReplyBtnPopoverModule extends ViewModule {
${Xss.escape(item.text)}
`);
- elem.click(this.view.setHandler(() => this.didOptionClick(option)));
+ elem.on('click', this.view.setHandler(() => this.didOptionClick(option)));
elem.find('.option-name').prepend(` `); // xss-direct
this.view.S.cached('reply_options_container').append(elem); // xss-safe-factory
}
diff --git a/extension/chrome/elements/compose-modules/compose-send-btn-module.ts b/extension/chrome/elements/compose-modules/compose-send-btn-module.ts
index dd512308e37..3ccef566420 100644
--- a/extension/chrome/elements/compose-modules/compose-send-btn-module.ts
+++ b/extension/chrome/elements/compose-modules/compose-send-btn-module.ts
@@ -39,7 +39,7 @@ export class ComposeSendBtnModule extends ViewModule {
public setHandlers = (): void => {
const ctrlEnterHandler = Ui.ctrlEnter(() => !this.view.sizeModule.composeWindowIsMinimized && this.extractProcessSendMsg());
this.view.S.cached('subject').add(this.view.S.cached('compose')).keydown(ctrlEnterHandler);
- this.view.S.cached('send_btn').click(this.view.setHandlerPrevent('double', () => this.extractProcessSendMsg()));
+ this.view.S.cached('send_btn').on('click', this.view.setHandlerPrevent('double', () => this.extractProcessSendMsg()));
this.popover.setHandlers();
};
@@ -86,17 +86,7 @@ export class ComposeSendBtnModule extends ViewModule {
}
};
- private btnText = (): string => {
- if (this.popover.choices.encrypt && this.popover.choices.sign) {
- return SendBtnTexts.BTN_ENCRYPT_SIGN_AND_SEND;
- } else if (this.popover.choices.sign) {
- return SendBtnTexts.BTN_SIGN_AND_SEND;
- } else {
- return SendBtnTexts.BTN_PLAIN_SEND;
- }
- };
-
- private extractProcessSendMsg = async () => {
+ public extractProcessSendMsg = async () => {
if (this.view.S.cached('reply_msg_successful').is(':visible')) {
return;
}
@@ -125,7 +115,10 @@ export class ComposeSendBtnModule extends ViewModule {
Ui.toast(result.supplementaryOperationsErrors[0] as string);
}, 0);
}
- BrowserMsg.send.notificationShow(this.view.parentTabId, { notification: `Your ${this.view.isReplyBox ? 'reply' : 'message'} has been sent.` });
+ BrowserMsg.send.notificationShow(this.view.parentTabId, {
+ notification: `Your ${this.view.isReplyBox ? 'reply' : 'message'} has been sent.`,
+ group: 'compose'
+ });
BrowserMsg.send.focusBody(this.view.parentTabId); // Bring focus back to body so Gmails shortcuts will work
if (this.view.isReplyBox) {
this.view.renderModule.renderReplySuccess(msgObj.renderSentMessage.attachments, msgObj.renderSentMessage.recipients, result.sentIds[0]);
@@ -144,6 +137,16 @@ export class ComposeSendBtnModule extends ViewModule {
}
};
+ private btnText = (): string => {
+ if (this.popover.choices.encrypt && this.popover.choices.sign) {
+ return SendBtnTexts.BTN_ENCRYPT_SIGN_AND_SEND;
+ } else if (this.popover.choices.sign) {
+ return SendBtnTexts.BTN_SIGN_AND_SEND;
+ } else {
+ return SendBtnTexts.BTN_PLAIN_SEND;
+ }
+ };
+
private finalizeSendableMsg = async ({ msg, senderKi }: { msg: SendableMsg, senderKi: KeyInfoWithIdentity | undefined }) => {
const choices = this.view.sendBtnModule.popover.choices;
for (const k of Object.keys(this.additionalMsgHeaders)) {
diff --git a/extension/chrome/elements/compose-modules/compose-send-btn-popover-module.ts b/extension/chrome/elements/compose-modules/compose-send-btn-popover-module.ts
index 14c58bc773f..2d7a5d06724 100644
--- a/extension/chrome/elements/compose-modules/compose-send-btn-popover-module.ts
+++ b/extension/chrome/elements/compose-modules/compose-send-btn-popover-module.ts
@@ -16,7 +16,7 @@ export class ComposeSendBtnPopoverModule extends ViewModule {
public choices: PopoverChoices = { encrypt: true, sign: true, richtext: false }; // defaults, may be changed by user using the popover
public setHandlers = (): void => {
- this.view.S.cached('toggle_send_options').click(this.view.setHandler((el, ev) => this.toggleVisible(ev)));
+ this.view.S.cached('toggle_send_options').on('click', this.view.setHandler((el, ev) => this.toggleVisible(ev)));
};
public render = async () => {
@@ -37,7 +37,7 @@ export class ComposeSendBtnPopoverModule extends ViewModule {
${Xss.escape(item.text)}
`);
this.renderCrossOrTick(elem, popoverOpt, this.choices[popoverOpt]);
- elem.click(this.view.setHandler(() => this.toggleItemTick(elem, popoverOpt)));
+ elem.on('click', this.view.setHandler(() => this.toggleItemTick(elem, popoverOpt)));
if (item.iconPath) {
elem.find('.option-name').prepend(`
`); // xss-direct
}
@@ -138,7 +138,7 @@ export class ComposeSendBtnPopoverModule extends ViewModule
{
} else if (e.key === 'Enter') {
e.stopPropagation();
e.preventDefault();
- currentActive.click();
+ currentActive.trigger('click');
}
};
diff --git a/extension/chrome/elements/compose-modules/compose-size-module.ts b/extension/chrome/elements/compose-modules/compose-size-module.ts
index aeee7c2c8df..262d82f05ec 100644
--- a/extension/chrome/elements/compose-modules/compose-size-module.ts
+++ b/extension/chrome/elements/compose-modules/compose-size-module.ts
@@ -19,14 +19,14 @@ export class ComposeSizeModule extends ViewModule {
private currentWindowSelector = `.secure_compose_window[data-frame-id="${this.view.frameId}"]`;
public setHandlers = () => {
- $('body').click(event => {
+ $('body').on('click', event => {
if (this.composeWindowIsMaximized && $(event.target).is($('body'))) {
this.minimizeComposerWindow();
}
});
if (!this.view.isReplyBox) {
- $('.minimize_compose_window').click(this.view.setHandler(() => this.minimizeComposerWindow()));
- $('.popout').click(this.view.setHandler(() => this.popoutClickHandler()));
+ $('.minimize_compose_window').on('click', this.view.setHandler(() => this.minimizeComposerWindow()));
+ $('.popout').on('click', this.view.setHandler(() => this.popoutClickHandler()));
}
};
diff --git a/extension/chrome/elements/compose-modules/formatters/encrypted-mail-msg-formatter.ts b/extension/chrome/elements/compose-modules/formatters/encrypted-mail-msg-formatter.ts
index 1bb623b7f0b..7f8631e873b 100644
--- a/extension/chrome/elements/compose-modules/formatters/encrypted-mail-msg-formatter.ts
+++ b/extension/chrome/elements/compose-modules/formatters/encrypted-mail-msg-formatter.ts
@@ -243,7 +243,7 @@ export class EncryptedMsgMailFormatter extends BaseMailFormatter {
};
} catch (msgTokenErr) {
if (ApiErr.isAuthErr(msgTokenErr)) {
- Settings.offerToLoginWithPopupShowModalOnErr(this.acctEmail);
+ Settings.offerToLoginWithPopupShowModalOnErr(this.acctEmail, () => this.view.sendBtnModule.extractProcessSendMsg());
throw new ComposerResetBtnTrigger();
} else if (ApiErr.isNetErr(msgTokenErr)) {
throw msgTokenErr;
diff --git a/extension/chrome/elements/compose-modules/formatters/general-mail-formatter.ts b/extension/chrome/elements/compose-modules/formatters/general-mail-formatter.ts
index e4709850e44..5d9602c4a0e 100644
--- a/extension/chrome/elements/compose-modules/formatters/general-mail-formatter.ts
+++ b/extension/chrome/elements/compose-modules/formatters/general-mail-formatter.ts
@@ -44,7 +44,7 @@ export class GeneralMailFormatter {
// encrypt (optionally sign)
const singleFamilyKeys = await view.storageModule.collectSingleFamilyKeys(recipientsEmails, newMsgData.from.email, choices.sign);
if (singleFamilyKeys.emailsWithoutPubkeys.length) {
- await view.errModule.throwIfEncryptionPasswordInvalid(newMsgData);
+ await view.errModule.throwIfEncryptionPasswordInvalidOrDisabled(newMsgData);
}
let signingKey: ParsedKeyInfo | undefined;
if (choices.sign) {
diff --git a/extension/chrome/elements/compose.htm b/extension/chrome/elements/compose.htm
index 5c98392d84d..c73edb95170 100644
--- a/extension/chrome/elements/compose.htm
+++ b/extension/chrome/elements/compose.htm
@@ -60,13 +60,13 @@
-
-
-
-
-
-
+
+
+
+
+
+
New Secure Message
-
+
@@ -146,18 +149,33 @@
- Recipient will have access to this message
for (loading) . You are responsible for sharing this password with them (use other medium to share the password - not email).
-
+ Recipient will have access to this message
+
+ for
+ (loading)
+
+ . You are responsible for sharing this password with them (use other medium to share the password - not
+ email).
+
+
+
(Messages sent to FlowCrypt users don't expire this way and don't need password exchange)
-
emails in grey don't use encryption
-
emails in orange have expired or revoked key
-
Add their Public Key
+
+ emails in grey don't use encryption
+
+
+ emails in orange have expired or revoked key
+
+
+
Add their Public Key
or ask them to get FlowCrypt.
-
Or send email password protected:
-
+
+ Or send email password protected:
+
+
@@ -177,10 +195,11 @@
-
-
+
+
+
+
@@ -229,4 +248,4 @@