diff --git a/extension/chrome/settings/index.ts b/extension/chrome/settings/index.ts index 40128469777..c2342edd23a 100644 --- a/extension/chrome/settings/index.ts +++ b/extension/chrome/settings/index.ts @@ -496,24 +496,67 @@ View.run( private addKeyRowsHtml = async (privateKeys: KeyInfoWithIdentity[]) => { let html = ''; const canRemoveKey = !this.clientConfiguration?.usesKeyManager(); + + // 1. Parse and prepare data + const rows = []; for (let i = 0; i < privateKeys.length; i++) { const ki = privateKeys[i]; const prv = await KeyUtil.parse(ki.private); const created = new Date(prv.created); const date = Str.monthName(created.getMonth()) + ' ' + created.getDate() + ', ' + created.getFullYear(); + const isExpired = KeyUtil.expired(prv); + rows.push({ + ki, + prv, + createdTime: prv.created, + dateStr: date, + isExpired, + originalIndex: i, + }); + } + + // 2. Sort: newest to oldest + rows.sort((a, b) => b.createdTime - a.createdTime); + + // 3. Build HTML + // Header row + html += ` +
+
Email
+
Created
+
Fingerprint
+
Status
+
+ `; + + for (const row of rows) { + const { ki, prv, dateStr, isExpired, originalIndex } = row; + let removeKeyBtn = ''; if (canRemoveKey && privateKeys.length > 1) { - removeKeyBtn = `(remove)`; + removeKeyBtn = `remove`; } + const escapedEmail = Xss.escape(prv.emails[0] || ''); - const escapedLink = `${escapedEmail}`; - const fpHtml = `fingerprint: ${Str.spaced(Xss.escape(ki.fingerprints[0]))}`; - const space = `        `; - html += `
`; - html += `
${escapedLink} from ${Xss.escape(date)}${space}${fpHtml}${space}${KeyUtil.statusHtml(ki.id, prv)}${space}${removeKeyBtn}
`; + const escapedLink = `${escapedEmail}`; + const fpHtml = `${Str.spaced(Xss.escape(ki.fingerprints[0]))}`; + + const rowClass = isExpired ? 'key-content-row expired' : 'key-content-row'; + const opacityStyle = isExpired ? 'opacity: 0.7;' : ''; + + html += `
`; + html += `
${escapedLink}
`; + html += `
${Xss.escape(dateStr)}
`; + html += `
${fpHtml}
`; + html += `
+ ${KeyUtil.statusHtml(ki.id, prv)} + ${removeKeyBtn} +
`; html += `
`; } + Xss.sanitizeAppend('.key_list', html); + $('.action_show_key').on( 'click', this.setHandler(async target => { diff --git a/extension/css/settings.css b/extension/css/settings.css index 1afaa0c5849..47ec9699932 100644 --- a/extension/css/settings.css +++ b/extension/css/settings.css @@ -533,7 +533,7 @@ body#settings .settings-border .row { } body#settings .settings-border .key-content-row { - padding: 7px 0; + padding: 4px 0; border-bottom: 1px solid #e6e6e6; } @@ -544,6 +544,10 @@ body#settings .settings-border .key_list { height: 219px; } +.key-content-row.expired { + opacity: 0.7; +} + #settings-row .box img.security-icon { width: 70px; } @@ -847,4 +851,4 @@ span.fc-badge { .fc-badge-light-gray { background-color: #bcbcbc; color: #444 !important; -} +} \ No newline at end of file diff --git a/test/source/tests/setup.ts b/test/source/tests/setup.ts index ddea1b21bbf..720618cefa0 100644 --- a/test/source/tests/setup.ts +++ b/test/source/tests/setup.ts @@ -851,7 +851,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== await addKeyPopup.waitAndType('@input-armored-key', updatedKey.privateKey); await addKeyPopup.waitAndType('#input_passphrase', passphrase); await addKeyPopup.waitAndClick('.action_add_private_key', { delay: 1 }); - await Util.sleep(1); + await settingsPage.waitAll('@action-show-key-1'); await gmailPage.page.reload(); await Util.sleep(3); await gmailPage.waitTillGone('@webmail-notification-notify_expiring_keys'); @@ -2250,7 +2250,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== const title = await settingsPage.read('@container-overlay-prompt-text'); expect(title).to.contain( 'Failed to store newly generated key on FlowCrypt Email Key Manager, ' + - 'No key has been generated for reject.client.keypair@key-manager-autogen.flowcrypt.test yet. Please ask your administrator.' + 'No key has been generated for reject.client.keypair@key-manager-autogen.flowcrypt.test yet. Please ask your administrator.' ); await settingsPage.click('@action-show-overlay-details'); await settingsPage.waitAll('@container-overlay-details'); @@ -2258,7 +2258,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== const details = await settingsPage.read('@container-overlay-details'); expect(details).to.contain( `405 when PUT-ing https://localhost:${t.context.urls?.port}/flowcrypt-email-key-manager/v1/keys/private string: ` + - 'privateKey -> No key has been generated for reject.client.keypair@key-manager-autogen.flowcrypt.test yet' + 'privateKey -> No key has been generated for reject.client.keypair@key-manager-autogen.flowcrypt.test yet' ); expect(details).to.not.contain('PRIVATE KEY'); })