selecting other fields for browser extensions

This commit is contained in:
antelle 2021-04-29 12:55:00 +02:00
parent b7e53185b0
commit 0be60475cf
No known key found for this signature in database
GPG Key ID: 63C9777AAB7C563C
5 changed files with 120 additions and 19 deletions

View File

@ -290,7 +290,7 @@ function focusKeeWeb() {
} }
} }
async function findEntry(request, filterOptions) { async function findEntry(request, returnIfOneMatch, filterOptions) {
const payload = decryptRequest(request); const payload = decryptRequest(request);
await checkContentRequestPermissions(request); await checkContentRequestPermissions(request);
@ -316,7 +316,7 @@ async function findEntry(request, filterOptions) {
let entry; let entry;
if (entries.length) { if (entries.length) {
if (entries.length === 1 && client.permissions.askGet === 'multiple') { if (entries.length === 1 && returnIfOneMatch && client.permissions.askGet === 'multiple') {
entry = entries[0]; entry = entries[0];
} }
} else { } else {
@ -481,7 +481,7 @@ const ProtocolHandlers = {
}, },
async 'get-logins'(request) { async 'get-logins'(request) {
const entry = await findEntry(request); const entry = await findEntry(request, true);
return encryptResponse(request, { return encryptResponse(request, {
success: 'true', success: 'true',
@ -504,7 +504,7 @@ const ProtocolHandlers = {
}, },
async 'get-totp-by-url'(request) { async 'get-totp-by-url'(request) {
const entry = await findEntry(request, { otp: true }); const entry = await findEntry(request, true, { otp: true });
entry.initOtpGenerator(); entry.initOtpGenerator();
@ -548,6 +548,37 @@ const ProtocolHandlers = {
}); });
}, },
async 'get-any-field'(request) {
const entry = await findEntry(request, false);
const selectEntryFieldView = new SelectEntryFieldView({
entry
});
const inactivityTimer = setTimeout(() => {
selectEntryFieldView.emit('result', undefined);
}, Timeouts.KeeWebConnectRequest);
const field = await selectEntryFieldView.showAndGetResult();
clearTimeout(inactivityTimer);
if (!field) {
throw makeError(Errors.userRejected);
}
let value = entry.fields[field];
if (value.isProtected) {
value = value.getText();
}
return encryptResponse(request, {
success: 'true',
version: getVersion(request),
field,
value
});
},
async 'get-totp'(request) { async 'get-totp'(request) {
decryptRequest(request); decryptRequest(request);
await checkContentRequestPermissions(request); await checkContentRequestPermissions(request);

View File

@ -3,6 +3,7 @@ import { Events } from 'framework/events';
import { Keys } from 'const/keys'; import { Keys } from 'const/keys';
import { Scrollable } from 'framework/views/scrollable'; import { Scrollable } from 'framework/views/scrollable';
import template from 'templates/select/select-entry-field.hbs'; import template from 'templates/select/select-entry-field.hbs';
import { PasswordPresenter } from 'util/formatting/password-presenter';
class SelectEntryFieldView extends View { class SelectEntryFieldView extends View {
parent = 'body'; parent = 'body';
@ -19,18 +20,29 @@ class SelectEntryFieldView extends View {
constructor(model) { constructor(model) {
super(model); super(model);
this.fields = this.model.entry ? this.getFields(this.model.entry) : [];
this.activeField = this.fields[0]?.field;
this.initScroll(); this.initScroll();
this.listenTo(Events, 'main-window-blur', this.mainWindowBlur); this.listenTo(Events, 'main-window-blur', this.mainWindowBlur);
this.setupKeys(); this.setupKeys();
} }
setupKeys() { setupKeys() {
this.onKey(Keys.DOM_VK_UP, this.upPressed, false, 'select-entry-field');
this.onKey(Keys.DOM_VK_DOWN, this.downPressed, false, 'select-entry-field');
this.onKey(Keys.DOM_VK_ESCAPE, this.escPressed, false, 'select-entry-field'); this.onKey(Keys.DOM_VK_ESCAPE, this.escPressed, false, 'select-entry-field');
this.onKey(Keys.DOM_VK_RETURN, this.enterPressed, false, 'select-entry-field'); this.onKey(Keys.DOM_VK_RETURN, this.enterPressed, false, 'select-entry-field');
} }
render() { render() {
super.render(this.model); super.render({
needsTouch: this.model.needsTouch,
deviceShortName: this.model.deviceShortName,
fields: this.fields,
activeField: this.activeField
});
document.activeElement.blur(); document.activeElement.blur();
@ -44,17 +56,62 @@ class SelectEntryFieldView extends View {
} }
} }
cancelAndClose() { getFields(entry) {
this.result = null; return Object.entries(entry.getAllFields())
this.emit('result', this.result); .map(([field, value]) => ({
field,
value
}))
.filter(({ value }) => value)
.map(({ field, value }) => ({
field,
value: value.isProtected ? PasswordPresenter.present(value.length) : value
}));
}
upPressed(e) {
e.preventDefault();
if (!this.activeField) {
return;
}
const activeIndex = this.fields.findIndex((f) => f.field === this.activeField) - 1;
if (activeIndex >= 0) {
this.activeField = this.fields[activeIndex].field;
this.render();
}
}
downPressed(e) {
e.preventDefault();
if (!this.activeField) {
return;
}
const activeIndex = this.fields.findIndex((f) => f.field === this.activeField) + 1;
if (activeIndex < this.fields.length) {
this.activeField = this.fields[activeIndex].field;
this.render();
}
} }
escPressed() { escPressed() {
this.cancelAndClose(); this.emit('result', undefined);
} }
enterPressed() { enterPressed() {
this.closeWithResult(); if (!this.activeField) {
return;
}
this.emit('result', this.activeField);
}
itemClicked(e) {
const item = e.target.closest('.select-entry-field__item');
this.activeField = item.dataset.field;
this.emit('result', this.activeField);
} }
mainWindowBlur() { mainWindowBlur() {
@ -72,7 +129,7 @@ class SelectEntryFieldView extends View {
} }
cancelClicked() { cancelClicked() {
this.cancelAndClose(); this.emit('result', undefined);
} }
} }

View File

@ -90,17 +90,17 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
word-wrap: break-word; word-wrap: break-word;
&:first-of-type { &.select-entry__item-icon-cell {
width: 2em; width: 2em;
text-align: center; text-align: center;
} }
&:nth-of-type(3) { &.select-entry__item-user-cell {
width: 25%; width: 25%;
} }
&:nth-of-type(4) { &.select-entry__item-url-cell {
width: 25%; width: 25%;
} }
&:nth-of-type(5) { &.select-entry__item-options {
width: 2em; width: 2em;
} }
} }

View File

@ -13,6 +13,19 @@
</div> </div>
<div class="select-entry-field__items"> <div class="select-entry-field__items">
<div class="scroller"> <div class="scroller">
{{#if fields.length}}
<table class="select-entry-field__table">
{{#each fields as |f|}}
<tr class="select-entry-field__item {{#ifeq f.field ../activeField}}select-entry-field__item--active{{/ifeq}}"
data-field="{{f.field}}">
<td>{{f.field}}</td>
<td>{{f.value}}</td>
</tr>
{{/each}}
</table>
{{else}}
<h1 class="select-entry__empty-title muted-color">{{res 'autoTypeNoMatches'}}</h1>
{{/if}}
</div> </div>
<div class="scroller__bar-wrapper"><div class="scroller__bar"></div></div> <div class="scroller__bar-wrapper"><div class="scroller__bar"></div></div>
</div> </div>

View File

@ -1,15 +1,15 @@
<tr class="select-entry__item {{#if active}}select-entry__item--active{{/if}}" data-id="{{id}}" <tr class="select-entry__item {{#if active}}select-entry__item--active{{/if}}" data-id="{{id}}"
id="select-entry__item--{{id}}"> id="select-entry__item--{{id}}">
<td> <td class="select-entry__item-icon-cell">
{{~#if customIcon~}} {{~#if customIcon~}}
<img src="{{customIcon}}" class="select-entry__item-icon select-entry__item-icon--custom {{#if color}}{{color}}{{/if}}" /> <img src="{{customIcon}}" class="select-entry__item-icon select-entry__item-icon--custom {{#if color}}{{color}}{{/if}}" />
{{~else~}} {{~else~}}
<i class="fa fa-{{icon}} {{#if color}}{{color}}-color{{/if}} select-entry__item-icon"></i> <i class="fa fa-{{icon}} {{#if color}}{{color}}-color{{/if}} select-entry__item-icon"></i>
{{~/if~}} {{~/if~}}
</td> </td>
<td>{{#if title}}{{title}}{{else}}({{res 'noTitle'}}){{/if}}</td> <td class="select-entry__item-title-cell">{{#if title}}{{title}}{{else}}({{res 'noTitle'}}){{/if}}</td>
<td>{{user}}</td> <td class="select-entry__item-user-cell">{{user}}</td>
<td>{{url}}</td> <td class="select-entry__item-url-cell">{{url}}</td>
{{#if itemOptions}} {{#if itemOptions}}
<td class="select-entry__item-options"> <td class="select-entry__item-options">
<i class="fa fa-ellipsis-h"></i> <i class="fa fa-ellipsis-h"></i>