Skip to content

Commit ef07704

Browse files
committed
Add customized electron prompt dialog
1 parent eff02bb commit ef07704

File tree

10 files changed

+452
-30
lines changed

10 files changed

+452
-30
lines changed

electron/app/js/project.js

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
55
*/
66
const {app, dialog} = require('electron');
7-
const prompt = require('electron-prompt');
7+
const {getCredentialPassphrase} = require('./promptUtils');
88
const {copyFile, mkdir, readFile, writeFile} = require('fs/promises');
99
const path = require('path');
1010
const uuid = require('uuid');
@@ -692,21 +692,17 @@ async function _createCredentialManager(targetWindow, projectFileJsonContent) {
692692
let credentialStorePolicy = _getProjectCredentialStorePolicy(projectFileJsonContent);
693693
return new Promise((resolve, reject) => {
694694
if (credentialStorePolicy === 'passphrase') {
695-
prompt({
696-
title: i18n.t('dialog-passphrase-prompt-title'),
697-
label: i18n.t('dialog-passphrase-prompt-label'),
698-
inputAttrs: { type: 'password', required: true },
699-
resizable: true,
700-
alwaysOnTop: true
701-
}, targetWindow).then(passphrase => {
702-
if (passphrase) {
703-
const credentialManager = new EncryptedCredentialManager(passphrase);
704-
_setCredentialManager(targetWindow, credentialManager);
705-
resolve(credentialManager);
706-
} else {
707-
reject(new Error('Passphrase is required but the user did not provide one.'));
708-
}
709-
}).catch(err => reject(new Error(`Failed to create passphrase credential manager: ${err}`)));
695+
getCredentialPassphrase(targetWindow)
696+
.then(passphrase => {
697+
if (passphrase) {
698+
const credentialManager = new EncryptedCredentialManager(passphrase);
699+
_setCredentialManager(targetWindow, credentialManager);
700+
resolve(credentialManager);
701+
} else {
702+
reject(new Error('Passphrase is required but the user did not provide one.'));
703+
}
704+
})
705+
.catch(err => reject(new Error(`Failed to create passphrase credential manager: ${err}`)));
710706
} else {
711707
let credentialManager;
712708
if (credentialStorePolicy === 'native') {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!DOCTYPE html>
2+
<!--
3+
Copyright (c) 2021, Oracle and/or its affiliates.
4+
Licensed under The Universal Permissive License (UPL), Version 1.0 as shown at https://oss.oracle.com/licenses/upl/
5+
-->
6+
<html>
7+
<head>
8+
<link href="prompt.css" rel="stylesheet" />
9+
</head>
10+
<body>
11+
<div id="container">
12+
<form id="form">
13+
<div id="label">...</div>
14+
<div id="data-container"></div>
15+
<div id="buttons">
16+
<button id="cancel">Cancel</button>
17+
<button type="submit" id="ok">OK</button>
18+
</div>
19+
</form>
20+
</div>
21+
<script src="credential-passphrase.js"></script>
22+
</body>
23+
</html>
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2021, Oracle and/or its affiliates.
4+
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
5+
*/
6+
let promptId = null;
7+
let promptOptions = null;
8+
9+
function promptError(error) {
10+
if (error instanceof Error) {
11+
error = error.message;
12+
}
13+
14+
window.api.ipc.sendSync('prompt-error:' + promptId, error);
15+
}
16+
17+
function promptCancel() {
18+
window.api.ipc.sendSync('prompt-post-data:' + promptId, null);
19+
}
20+
21+
function promptSubmit() {
22+
let data = null;
23+
24+
if(promptOptions.fields) {
25+
data = {};
26+
promptOptions.fields.forEach(field => {
27+
const inputElement = document.getElementById(field.id);
28+
data[field.id] = inputElement.value;
29+
});
30+
31+
} else {
32+
const dataElement = document.querySelector('#data');
33+
34+
if (promptOptions.type === 'input') {
35+
data = dataElement.value;
36+
} else if (promptOptions.type === 'select') {
37+
if (promptOptions.selectMultiple) {
38+
data = dataElement.querySelectorAll('option[selected]').map(o => o.getAttribute('value'));
39+
} else {
40+
data = dataElement.value;
41+
}
42+
}
43+
}
44+
45+
window.api.ipc.sendSync('prompt-post-data:' + promptId, data);
46+
}
47+
48+
function promptCreateInput(promptOptions) {
49+
const dataElement = document.createElement('input');
50+
dataElement.setAttribute('type', 'text');
51+
52+
if (promptOptions.value) {
53+
dataElement.value = promptOptions.value;
54+
} else {
55+
dataElement.value = '';
56+
}
57+
58+
if (promptOptions.inputAttrs && typeof (promptOptions.inputAttrs) === 'object') {
59+
for (const k in promptOptions.inputAttrs) {
60+
if (!Object.prototype.hasOwnProperty.call(promptOptions.inputAttrs, k)) {
61+
continue;
62+
}
63+
64+
dataElement.setAttribute(k, promptOptions.inputAttrs[k]);
65+
}
66+
}
67+
68+
dataElement.addEventListener('keyup', event => {
69+
if (event.key === 'Escape') {
70+
promptCancel();
71+
}
72+
});
73+
74+
dataElement.addEventListener('keypress', event => {
75+
if (event.key === 'Enter') {
76+
event.preventDefault();
77+
document.querySelector('#ok').click();
78+
}
79+
});
80+
81+
return dataElement;
82+
}
83+
84+
function promptCreateSelect() {
85+
const dataElement = document.createElement('select');
86+
let optionElement;
87+
88+
for (const k in promptOptions.selectOptions) {
89+
if (!Object.prototype.hasOwnProperty.call(promptOptions.selectOptions, k)) {
90+
continue;
91+
}
92+
93+
optionElement = document.createElement('option');
94+
optionElement.setAttribute('value', k);
95+
optionElement.textContent = promptOptions.selectOptions[k];
96+
if (k === promptOptions.value) {
97+
optionElement.setAttribute('selected', 'selected');
98+
}
99+
100+
dataElement.append(optionElement);
101+
}
102+
103+
return dataElement;
104+
}
105+
106+
function promptRegister() {
107+
promptId = document.location.hash.replace('#', '');
108+
109+
try {
110+
promptOptions = JSON.parse(window.api.ipc.sendSync('prompt-get-options:' + promptId));
111+
} catch (error) {
112+
return promptError(error);
113+
}
114+
115+
const labelContainer = document.querySelector('#label');
116+
if (labelContainer) {
117+
if (promptOptions.useHtmlLabel) {
118+
labelContainer.innerHTML = promptOptions.label;
119+
} else {
120+
labelContainer.textContent = promptOptions.label;
121+
}
122+
}
123+
124+
if (promptOptions.buttonLabels && promptOptions.buttonLabels.ok) {
125+
document.querySelector('#ok').textContent = promptOptions.buttonLabels.ok;
126+
}
127+
128+
if (promptOptions.buttonLabels && promptOptions.buttonLabels.cancel) {
129+
document.querySelector('#cancel').textContent = promptOptions.buttonLabels.cancel;
130+
}
131+
132+
document.querySelector('#form').addEventListener('submit', promptSubmit);
133+
document.querySelector('#cancel').addEventListener('click', promptCancel);
134+
135+
if (promptOptions.fields) {
136+
const formElement = document.querySelector('#form');
137+
const buttonContainer = document.querySelector('#buttons');
138+
139+
promptOptions.fields.forEach(field => {
140+
if(field.label) {
141+
const labelContainer = document.createElement('div');
142+
labelContainer.setAttribute('class', 'label');
143+
labelContainer.innerHTML = field.label;
144+
formElement.insertBefore(labelContainer, buttonContainer);
145+
}
146+
147+
const dataElement = createDataElement(field);
148+
if(dataElement) {
149+
dataElement.setAttribute('id', field.id);
150+
dataElement.setAttribute('class', 'data');
151+
const dataContainer = document.createElement('div');
152+
dataContainer.setAttribute('class', 'data-container');
153+
dataContainer.append(dataElement);
154+
formElement.insertBefore(dataContainer, buttonContainer);
155+
}
156+
});
157+
158+
} else {
159+
const dataContainerElement = document.querySelector('#data-container');
160+
161+
const dataElement = createDataElement(promptOptions);
162+
if(!dataElement) {
163+
return promptError(`Unhandled input type '${promptOptions.type}'`);
164+
}
165+
166+
dataContainerElement.append(dataElement);
167+
dataElement.setAttribute('id', 'data');
168+
169+
dataElement.focus();
170+
if (promptOptions.type === 'input') {
171+
dataElement.select();
172+
}
173+
}
174+
175+
const height = document.querySelector('body').offsetHeight;
176+
window.api.ipc.sendSync('prompt-size:' + promptId, height);
177+
}
178+
179+
function createDataElement(promptOptions) {
180+
let dataElement;
181+
if (promptOptions.type === 'input') {
182+
dataElement = promptCreateInput(promptOptions);
183+
} else if (promptOptions.type === 'select') {
184+
dataElement = promptCreateSelect();
185+
} else {
186+
dataElement = null;
187+
}
188+
return dataElement;
189+
}
190+
191+
window.addEventListener('error', error => {
192+
if (promptId) {
193+
promptError('An error has occurred on the prompt window: \n' + error);
194+
}
195+
});
196+
197+
document.addEventListener('DOMContentLoaded', promptRegister);

electron/app/js/prompt/preload.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2021, Oracle and/or its affiliates.
4+
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
5+
*/
6+
const { contextBridge, ipcRenderer } = require('electron');
7+
8+
contextBridge.exposeInMainWorld(
9+
'api',
10+
{
11+
ipc: {
12+
sendSync: (channel, ...args) => {
13+
return ipcRenderer.sendSync(channel, ...args);
14+
}
15+
}
16+
}
17+
);

electron/app/js/prompt/prompt.css

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates.
3+
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
4+
*/
5+
body {
6+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
7+
line-height: 1.5em;
8+
color: #333;
9+
background-color: #fff;
10+
padding: 0;
11+
margin: 0;
12+
}
13+
14+
#container {
15+
align-items: center;
16+
justify-content: center;
17+
display: flex;
18+
height: 100%;
19+
overflow: auto;
20+
padding: 1em;
21+
}
22+
23+
#form {
24+
width: 100%;
25+
}
26+
27+
#label, .label {
28+
max-width: 100%;
29+
max-height: 100%;
30+
margin-bottom: .8em;
31+
white-space: nowrap;
32+
overflow: hidden;
33+
text-overflow: ellipsis;
34+
}
35+
36+
#data, .data {
37+
box-sizing: border-box;
38+
border-radius: 2px;
39+
background: #fff;
40+
width: 100%;
41+
padding: .4em .5em;
42+
border: 1px solid black;
43+
min-height: 2.5em;
44+
margin: 0 0 1.2em;
45+
}
46+
47+
select#data {
48+
height: 2em;
49+
}
50+
51+
#data-container, .data-container {
52+
text-align: center;
53+
}
54+
55+
#buttons {
56+
text-align: right;
57+
}
58+
59+
#buttons > button,
60+
#buttons > input[type=submit] {
61+
border-radius: 2px;
62+
border: 0;
63+
margin: 0 0 0 .5em;
64+
font-size: .8em;
65+
line-height: 1em;
66+
padding: .6em 1em
67+
}
68+
69+
#ok {
70+
background-color: #3879D9;
71+
color: white;
72+
}
73+
74+
#cancel {
75+
background-color: #DDD;
76+
color: black;
77+
}

0 commit comments

Comments
 (0)