From 3e089c182bc859bf3e1ac498b2ef96f993fb2180 Mon Sep 17 00:00:00 2001 From: Ioan Moldovan Date: Mon, 24 Nov 2025 19:45:40 -0500 Subject: [PATCH 1/5] feat: improve my keys screen --- extension/chrome/settings/index.ts | 55 ++++++++++++++++++++++++++---- extension/css/settings.css | 4 +++ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/extension/chrome/settings/index.ts b/extension/chrome/settings/index.ts index 40128469777..6d4892b18bd 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, // keep track for actions that might rely on index? actually actions seem to use data attributes mostly, except maybe tests relying on order + }); + } + + // 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..9e16d0a70e6 100644 --- a/extension/css/settings.css +++ b/extension/css/settings.css @@ -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; } From c19485b18c4465b700d8cb00f0494f2e8100c940 Mon Sep 17 00:00:00 2001 From: Ioan Moldovan Date: Mon, 24 Nov 2025 21:11:49 -0500 Subject: [PATCH 2/5] fix: flaky test --- test/source/tests/setup.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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'); }) From 4aea663af881da9d43e109218392f811222a0c0b Mon Sep 17 00:00:00 2001 From: Ioan Moldovan Date: Tue, 25 Nov 2025 18:55:50 -0500 Subject: [PATCH 3/5] fix: compact my keys section --- extension/chrome/settings/index.ts | 20 ++++++----- extension/css/settings.css | 56 +++++++++++++++--------------- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/extension/chrome/settings/index.ts b/extension/chrome/settings/index.ts index 6d4892b18bd..608573be0a0 100644 --- a/extension/chrome/settings/index.ts +++ b/extension/chrome/settings/index.ts @@ -511,7 +511,7 @@ View.run( createdTime: prv.created, dateStr: date, isExpired, - originalIndex: i, // keep track for actions that might rely on index? actually actions seem to use data attributes mostly, except maybe tests relying on order + originalIndex: i, }); } @@ -521,10 +521,10 @@ View.run( // 3. Build HTML // Header row html += ` -
-
Email
+
+
Email
Created
-
Fingerprint
+
Fingerprint
Status
`; @@ -544,11 +544,13 @@ View.run( 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 += `
+ // Compact layout: reduced padding via CSS (4px), no inline padding here. + // Added white-space: nowrap and overflow handling to keep single line. + html += `
`; + html += `
${escapedLink}
`; + html += `
${Xss.escape(dateStr)}
`; + html += `
${fpHtml}
`; + html += `
${KeyUtil.statusHtml(ki.id, prv)} ${removeKeyBtn}
`; diff --git a/extension/css/settings.css b/extension/css/settings.css index 9e16d0a70e6..857f69ed14a 100644 --- a/extension/css/settings.css +++ b/extension/css/settings.css @@ -28,8 +28,8 @@ html { font-size: 14px; } -#header-row .logo-row .subscription > span, -#header-row .logo-row .subscription > button { +#header-row .logo-row .subscription>span, +#header-row .logo-row .subscription>button { display: inline-block; margin-left: 4px; border: 1px solid #31a217; @@ -37,7 +37,7 @@ html { padding: 0 5px; } -#header-row .logo-row .subscription > button:focus { +#header-row .logo-row .subscription>button:focus { outline: none; box-shadow: inset 0 0 0 1px #fff; } @@ -201,7 +201,7 @@ a.gray_link { margin: 0 auto !important; } -.settings-icons-rows > div { +.settings-icons-rows>div { min-width: 145px; } @@ -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; } @@ -587,21 +587,21 @@ body#settings .settings-border .key_list { top: 28px; } -@media (width <= 991px) { +@media (width <=991px) { .my-keys-row .settings-border { margin: 35px auto !important; width: 100% !important; } } -@media (width <= 776px) { +@media (width <=776px) { #public-profile-indicator { left: 0; right: 0; } } -@media (width <= 448px) { +@media (width <=448px) { #footer-row { position: relative; } @@ -612,7 +612,7 @@ body#settings .settings-border .key_list { font-size: 14px; } -#settings.client > #banner { +#settings.client>#banner { background: white; margin-left: 0; margin-right: 0; @@ -648,12 +648,12 @@ body#settings .settings-border .key_list { padding-bottom: 200px; } -#settings.client .threads > p { +#settings.client .threads>p { padding-left: 20px; opacity: 0.7; } -#settings.client .threads > .line { +#settings.client .threads>.line { padding-left: 20px; text-align: left; border-top: 1px solid #31a217; @@ -661,25 +661,25 @@ body#settings .settings-border .key_list { width: auto; } -#settings.client .threads > .line:last-child { +#settings.client .threads>.line:last-child { border-bottom: 1px solid #31a217; } -#settings.client .threads > .line.loaded:hover { +#settings.client .threads>.line.loaded:hover { background-color: #fff; cursor: pointer; } -#settings.client .threads > .line .loading { +#settings.client .threads>.line .loading { display: inline-block; } -#settings.client .threads > .line .from_container { +#settings.client .threads>.line .from_container { display: inline-block; width: 300px; } -#settings.client .threads > .line .from_container .msg_count { +#settings.client .threads>.line .from_container .msg_count { font-size: 90%; opacity: 0.5; padding-left: 8px; @@ -688,7 +688,7 @@ body#settings .settings-border .key_list { } /* #settings.client .threads .line .subject { } */ -#settings.client .threads > .line .date { +#settings.client .threads>.line .date { position: absolute; right: 20px; } @@ -701,8 +701,8 @@ body#settings .settings-border .key_list { border-top: 1px dotted #c0c0c0; } -#settings.client .thread .message > iframe, -#settings.client .thread .reply > iframe { +#settings.client .thread .message>iframe, +#settings.client .thread .reply>iframe { width: 100%; border: none; } @@ -726,12 +726,12 @@ body#settings .settings-border .key_list { cursor: pointer; } -#settings.client > table { +#settings.client>table { width: 100%; min-height: 90%; } -#settings.client > table td.menu { +#settings.client>table td.menu { width: 200px; min-width: 200px; max-width: 200px; @@ -740,14 +740,14 @@ body#settings .settings-border .key_list { background: white; } -#settings.client > table td.menu .button { +#settings.client>table td.menu .button { min-width: 160px !important; margin-bottom: 8px !important; text-align: left !important; white-space: nowrap; } -#settings.client > table td.menu span.label { +#settings.client>table td.menu span.label { margin-left: 0; margin-bottom: 2px; font-size: 12px; @@ -758,7 +758,7 @@ body#settings .settings-border .key_list { opacity: 0.7; } -#settings.client > #banner > img { +#settings.client>#banner>img { height: 32px; width: 32px; float: right; @@ -767,11 +767,11 @@ body#settings .settings-border .key_list { margin-left: 20px; } -#settings.client > #banner > img.action_open_settings { +#settings.client>#banner>img.action_open_settings { cursor: pointer; } -#settings.client > #banner > .action_finish_session { +#settings.client>#banner>.action_finish_session { height: 32px; margin-top: -6px; float: right; @@ -779,7 +779,7 @@ body#settings .settings-border .key_list { cursor: pointer; } -#settings.client > #banner > .action_finish_session img { +#settings.client>#banner>.action_finish_session img { height: 32px; } @@ -851,4 +851,4 @@ span.fc-badge { .fc-badge-light-gray { background-color: #bcbcbc; color: #444 !important; -} +} \ No newline at end of file From c9046a2cd1288b086380987794d0019dd9f34065 Mon Sep 17 00:00:00 2001 From: Ioan Moldovan Date: Wed, 26 Nov 2025 19:14:32 -0500 Subject: [PATCH 4/5] fix: pr review --- extension/chrome/settings/index.ts | 20 ++++++------ extension/css/settings.css | 52 +++++++++++++++--------------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/extension/chrome/settings/index.ts b/extension/chrome/settings/index.ts index 608573be0a0..6d4892b18bd 100644 --- a/extension/chrome/settings/index.ts +++ b/extension/chrome/settings/index.ts @@ -511,7 +511,7 @@ View.run( createdTime: prv.created, dateStr: date, isExpired, - originalIndex: i, + originalIndex: i, // keep track for actions that might rely on index? actually actions seem to use data attributes mostly, except maybe tests relying on order }); } @@ -521,10 +521,10 @@ View.run( // 3. Build HTML // Header row html += ` -
-
Email
+
+
Email
Created
-
Fingerprint
+
Fingerprint
Status
`; @@ -544,13 +544,11 @@ View.run( const rowClass = isExpired ? 'key-content-row expired' : 'key-content-row'; const opacityStyle = isExpired ? 'opacity: 0.7;' : ''; - // Compact layout: reduced padding via CSS (4px), no inline padding here. - // Added white-space: nowrap and overflow handling to keep single line. - html += `
`; - html += `
${escapedLink}
`; - html += `
${Xss.escape(dateStr)}
`; - html += `
${fpHtml}
`; - html += `
+ html += `
`; + html += `
${escapedLink}
`; + html += `
${Xss.escape(dateStr)}
`; + html += `
${fpHtml}
`; + html += `
${KeyUtil.statusHtml(ki.id, prv)} ${removeKeyBtn}
`; diff --git a/extension/css/settings.css b/extension/css/settings.css index 857f69ed14a..47ec9699932 100644 --- a/extension/css/settings.css +++ b/extension/css/settings.css @@ -28,8 +28,8 @@ html { font-size: 14px; } -#header-row .logo-row .subscription>span, -#header-row .logo-row .subscription>button { +#header-row .logo-row .subscription > span, +#header-row .logo-row .subscription > button { display: inline-block; margin-left: 4px; border: 1px solid #31a217; @@ -37,7 +37,7 @@ html { padding: 0 5px; } -#header-row .logo-row .subscription>button:focus { +#header-row .logo-row .subscription > button:focus { outline: none; box-shadow: inset 0 0 0 1px #fff; } @@ -201,7 +201,7 @@ a.gray_link { margin: 0 auto !important; } -.settings-icons-rows>div { +.settings-icons-rows > div { min-width: 145px; } @@ -587,21 +587,21 @@ body#settings .settings-border .key_list { top: 28px; } -@media (width <=991px) { +@media (width <= 991px) { .my-keys-row .settings-border { margin: 35px auto !important; width: 100% !important; } } -@media (width <=776px) { +@media (width <= 776px) { #public-profile-indicator { left: 0; right: 0; } } -@media (width <=448px) { +@media (width <= 448px) { #footer-row { position: relative; } @@ -612,7 +612,7 @@ body#settings .settings-border .key_list { font-size: 14px; } -#settings.client>#banner { +#settings.client > #banner { background: white; margin-left: 0; margin-right: 0; @@ -648,12 +648,12 @@ body#settings .settings-border .key_list { padding-bottom: 200px; } -#settings.client .threads>p { +#settings.client .threads > p { padding-left: 20px; opacity: 0.7; } -#settings.client .threads>.line { +#settings.client .threads > .line { padding-left: 20px; text-align: left; border-top: 1px solid #31a217; @@ -661,25 +661,25 @@ body#settings .settings-border .key_list { width: auto; } -#settings.client .threads>.line:last-child { +#settings.client .threads > .line:last-child { border-bottom: 1px solid #31a217; } -#settings.client .threads>.line.loaded:hover { +#settings.client .threads > .line.loaded:hover { background-color: #fff; cursor: pointer; } -#settings.client .threads>.line .loading { +#settings.client .threads > .line .loading { display: inline-block; } -#settings.client .threads>.line .from_container { +#settings.client .threads > .line .from_container { display: inline-block; width: 300px; } -#settings.client .threads>.line .from_container .msg_count { +#settings.client .threads > .line .from_container .msg_count { font-size: 90%; opacity: 0.5; padding-left: 8px; @@ -688,7 +688,7 @@ body#settings .settings-border .key_list { } /* #settings.client .threads .line .subject { } */ -#settings.client .threads>.line .date { +#settings.client .threads > .line .date { position: absolute; right: 20px; } @@ -701,8 +701,8 @@ body#settings .settings-border .key_list { border-top: 1px dotted #c0c0c0; } -#settings.client .thread .message>iframe, -#settings.client .thread .reply>iframe { +#settings.client .thread .message > iframe, +#settings.client .thread .reply > iframe { width: 100%; border: none; } @@ -726,12 +726,12 @@ body#settings .settings-border .key_list { cursor: pointer; } -#settings.client>table { +#settings.client > table { width: 100%; min-height: 90%; } -#settings.client>table td.menu { +#settings.client > table td.menu { width: 200px; min-width: 200px; max-width: 200px; @@ -740,14 +740,14 @@ body#settings .settings-border .key_list { background: white; } -#settings.client>table td.menu .button { +#settings.client > table td.menu .button { min-width: 160px !important; margin-bottom: 8px !important; text-align: left !important; white-space: nowrap; } -#settings.client>table td.menu span.label { +#settings.client > table td.menu span.label { margin-left: 0; margin-bottom: 2px; font-size: 12px; @@ -758,7 +758,7 @@ body#settings .settings-border .key_list { opacity: 0.7; } -#settings.client>#banner>img { +#settings.client > #banner > img { height: 32px; width: 32px; float: right; @@ -767,11 +767,11 @@ body#settings .settings-border .key_list { margin-left: 20px; } -#settings.client>#banner>img.action_open_settings { +#settings.client > #banner > img.action_open_settings { cursor: pointer; } -#settings.client>#banner>.action_finish_session { +#settings.client > #banner > .action_finish_session { height: 32px; margin-top: -6px; float: right; @@ -779,7 +779,7 @@ body#settings .settings-border .key_list { cursor: pointer; } -#settings.client>#banner>.action_finish_session img { +#settings.client > #banner > .action_finish_session img { height: 32px; } From 86222574cfe486dc6e044e8b6e411c2a5973756a Mon Sep 17 00:00:00 2001 From: Ioan Moldovan Date: Wed, 26 Nov 2025 19:30:28 -0500 Subject: [PATCH 5/5] fix: pr reviews --- extension/chrome/settings/index.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/extension/chrome/settings/index.ts b/extension/chrome/settings/index.ts index 6d4892b18bd..c2342edd23a 100644 --- a/extension/chrome/settings/index.ts +++ b/extension/chrome/settings/index.ts @@ -511,7 +511,7 @@ View.run( createdTime: prv.created, dateStr: date, isExpired, - originalIndex: i, // keep track for actions that might rely on index? actually actions seem to use data attributes mostly, except maybe tests relying on order + originalIndex: i, }); } @@ -522,10 +522,10 @@ View.run( // Header row html += `
-
Email
+
Email
Created
-
Fingerprint
-
Status
+
Fingerprint
+
Status
`; @@ -545,10 +545,10 @@ View.run( const opacityStyle = isExpired ? 'opacity: 0.7;' : ''; html += `
`; - html += `
${escapedLink}
`; + html += `
${escapedLink}
`; html += `
${Xss.escape(dateStr)}
`; - html += `
${fpHtml}
`; - html += `
+ html += `
${fpHtml}
`; + html += `
${KeyUtil.statusHtml(ki.id, prv)} ${removeKeyBtn}
`;