aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Stefan Malzner <stefan@adlk.io>2018-12-21 11:35:00 +0100
committerLibravatar Stefan Malzner <stefan@adlk.io>2018-12-21 11:35:00 +0100
commit2fc67d1c9bc2038179771238a0cb5d58995e05c3 (patch)
tree93aaaee39bf0ace0b53566ca4b5c2189ace2525c /src
parentbump version to b22 (diff)
downloadferdium-app-2fc67d1c9bc2038179771238a0cb5d58995e05c3.tar.gz
ferdium-app-2fc67d1c9bc2038179771238a0cb5d58995e05c3.tar.zst
ferdium-app-2fc67d1c9bc2038179771238a0cb5d58995e05c3.zip
BasicAuth first draft
Diffstat (limited to 'src')
-rw-r--r--src/app.js1
-rw-r--r--src/components/layout/AppLayout.js2
-rw-r--r--src/components/ui/Modal/index.js59
-rw-r--r--src/components/ui/Modal/styles.js32
-rw-r--r--src/electron/ipc-api/settings.js1
-rw-r--r--src/features/basicAuth/Component.js102
-rw-r--r--src/features/basicAuth/Form.js17
-rw-r--r--src/features/basicAuth/index.js68
-rw-r--r--src/features/basicAuth/mainIpcHandler.js9
-rw-r--r--src/features/basicAuth/styles.js12
-rw-r--r--src/index.js39
-rw-r--r--src/stores/FeaturesStore.js2
-rw-r--r--src/theme/dark/index.js3
-rw-r--r--src/theme/default/index.js3
14 files changed, 343 insertions, 7 deletions
diff --git a/src/app.js b/src/app.js
index 831dd93ce..6660feb46 100644
--- a/src/app.js
+++ b/src/app.js
@@ -62,6 +62,7 @@ window.addEventListener('load', () => {
62 menu, 62 menu,
63 touchBar, 63 touchBar,
64 analytics, 64 analytics,
65 features: {},
65 render() { 66 render() {
66 const preparedApp = ( 67 const preparedApp = (
67 <Provider stores={stores} actions={actions}> 68 <Provider stores={stores} actions={actions}>
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js
index dbe0bb4b6..28eaa7fdc 100644
--- a/src/components/layout/AppLayout.js
+++ b/src/components/layout/AppLayout.js
@@ -6,6 +6,7 @@ import { TitleBar } from 'electron-react-titlebar';
6 6
7import InfoBar from '../ui/InfoBar'; 7import InfoBar from '../ui/InfoBar';
8import { Component as DelayApp } from '../../features/delayApp'; 8import { Component as DelayApp } from '../../features/delayApp';
9import { Component as BasicAuth } from '../../features/basicAuth';
9import ErrorBoundary from '../util/ErrorBoundary'; 10import ErrorBoundary from '../util/ErrorBoundary';
10 11
11import globalMessages from '../../i18n/globalMessages'; 12import globalMessages from '../../i18n/globalMessages';
@@ -161,6 +162,7 @@ export default @observer class AppLayout extends Component {
161 </InfoBar> 162 </InfoBar>
162 )} 163 )}
163 {isDelayAppScreenVisible && (<DelayApp />)} 164 {isDelayAppScreenVisible && (<DelayApp />)}
165 <BasicAuth />
164 {services} 166 {services}
165 </div> 167 </div>
166 </div> 168 </div>
diff --git a/src/components/ui/Modal/index.js b/src/components/ui/Modal/index.js
new file mode 100644
index 000000000..d84e4c713
--- /dev/null
+++ b/src/components/ui/Modal/index.js
@@ -0,0 +1,59 @@
1import React, { Component } from 'react';
2import ReactModal from 'react-modal';
3import PropTypes from 'prop-types';
4import classnames from 'classnames';
5import injectCSS from 'react-jss';
6
7import styles from './styles';
8
9export default @injectCSS(styles) class Modal extends Component {
10 static propTypes = {
11 children: PropTypes.node.isRequired,
12 className: PropTypes.string,
13 classes: PropTypes.object.isRequired,
14 isOpen: PropTypes.bool.isRequired,
15 portal: PropTypes.string,
16 close: PropTypes.func.isRequired,
17 }
18
19 static defaultProps = {
20 className: null,
21 portal: 'modal-portal',
22 }
23
24 render() {
25 const {
26 children,
27 className,
28 classes,
29 isOpen,
30 portal,
31 close,
32 } = this.props;
33
34 return (
35 <ReactModal
36 isOpen={isOpen}
37 className={classnames({
38 [`${classes.modal}`]: true,
39 [`${className}`]: className,
40 })}
41 portalClassName={classes.component}
42 overlayClassName={classes.overlay}
43 portal={portal}
44 onRequestClose={close}
45 >
46 {/* <button
47 type="button"
48 className={classnames({
49 [`${classes.close}`]: true,
50 'mdi mdi-close': true,
51 })}
52 /> */}
53 <div className={classes.content}>
54 {children}
55 </div>
56 </ReactModal>
57 );
58 }
59}
diff --git a/src/components/ui/Modal/styles.js b/src/components/ui/Modal/styles.js
new file mode 100644
index 000000000..56fecbf55
--- /dev/null
+++ b/src/components/ui/Modal/styles.js
@@ -0,0 +1,32 @@
1export default theme => ({
2 component: {
3 zIndex: 500,
4 position: 'absolute',
5 },
6 overlay: {
7 background: theme.colorModalOverlayBackground,
8 position: 'fixed',
9 top: 0,
10 left: 0,
11 right: 0,
12 bottom: 0,
13 display: 'flex',
14 },
15 modal: {
16 background: '#FFF',
17 maxWidth: '90%',
18 height: 'auto',
19 margin: 'auto auto',
20 borderRadius: 6,
21 boxShadow: '0px 13px 40px 0px rgba(0,0,0,0.2)',
22 position: 'relative',
23 },
24 content: {
25 padding: 20,
26 },
27 close: {
28 position: 'absolute',
29 top: 0,
30 right: 0,
31 },
32});
diff --git a/src/electron/ipc-api/settings.js b/src/electron/ipc-api/settings.js
index ce006bb92..b651db306 100644
--- a/src/electron/ipc-api/settings.js
+++ b/src/electron/ipc-api/settings.js
@@ -2,7 +2,6 @@ import { ipcMain } from 'electron';
2 2
3export default (params) => { 3export default (params) => {
4 ipcMain.on('getAppSettings', (event, type) => { 4 ipcMain.on('getAppSettings', (event, type) => {
5 console.log('getAppSettings', type, params.settings[type].all);
6 params.mainWindow.webContents.send('appSettings', { 5 params.mainWindow.webContents.send('appSettings', {
7 type, 6 type,
8 data: params.settings[type].all, 7 data: params.settings[type].all,
diff --git a/src/features/basicAuth/Component.js b/src/features/basicAuth/Component.js
new file mode 100644
index 000000000..13395fb40
--- /dev/null
+++ b/src/features/basicAuth/Component.js
@@ -0,0 +1,102 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import injectSheet from 'react-jss';
4import { observer } from 'mobx-react';
5import classnames from 'classnames';
6
7import Modal from '../../components/ui/Modal';
8import Input from '../../components/ui/Input';
9import Button from '../../components/ui/Button';
10
11import {
12 state,
13 resetState,
14 sendCredentials,
15 cancelLogin,
16} from '.';
17import Form from './Form';
18
19import styles from './styles';
20
21export default @injectSheet(styles) @observer class BasicAuthModal extends Component {
22 static propTypes = {
23 classes: PropTypes.object.isRequired,
24 }
25
26 submit(e) {
27 e.preventDefault();
28
29 const values = Form.values();
30 console.log('form submit', values);
31
32 sendCredentials(values.user, values.password);
33 resetState();
34 }
35
36 cancel() {
37 cancelLogin();
38 this.close();
39 }
40
41 close() {
42 resetState();
43 state.isModalVisible = false;
44 }
45
46 render() {
47 const {
48 classes,
49 } = this.props;
50
51 const {
52 isModalVisible,
53 authInfo,
54 } = state;
55
56 if (!authInfo) {
57 return null;
58 }
59
60 return (
61 <Modal
62 isOpen={isModalVisible}
63 className={classes.modal}
64 close={this.cancel.bind(this)}
65 >
66 <h1>Sign in</h1>
67 <p>
68 http
69 {authInfo.port === 443 && 's'}
70 ://
71 {authInfo.host}
72 </p>
73 <form
74 onSubmit={this.submit.bind(this)}
75 className={classnames('franz-form', classes.form)}
76 >
77 <Input
78 field={Form.$('user')}
79 showLabel={false}
80 />
81 <Input
82 field={Form.$('password')}
83 showLabel={false}
84 showPasswordToggle
85 />
86 <div className={classes.buttons}>
87 <Button
88 type="button"
89 label="Cancel"
90 buttonType="secondary"
91 onClick={this.cancel.bind(this)}
92 />
93 <Button
94 type="submit"
95 label="Sign In"
96 />
97 </div>
98 </form>
99 </Modal>
100 );
101 }
102}
diff --git a/src/features/basicAuth/Form.js b/src/features/basicAuth/Form.js
new file mode 100644
index 000000000..95721d0e9
--- /dev/null
+++ b/src/features/basicAuth/Form.js
@@ -0,0 +1,17 @@
1import Form from '../../lib/Form';
2
3export default new Form({
4 fields: {
5 user: {
6 label: 'user',
7 placeholder: 'Username',
8 value: '',
9 },
10 password: {
11 label: 'Password',
12 placeholder: 'Password',
13 value: '',
14 type: 'password',
15 },
16 },
17});
diff --git a/src/features/basicAuth/index.js b/src/features/basicAuth/index.js
new file mode 100644
index 000000000..99abf0d11
--- /dev/null
+++ b/src/features/basicAuth/index.js
@@ -0,0 +1,68 @@
1import { ipcRenderer } from 'electron';
2import { observable } from 'mobx';
3
4import BasicAuthComponent from './Component';
5
6const debug = require('debug')('Franz:feature:basicAuth');
7
8const defaultState = {
9 isModalVisible: false,
10 service: null,
11 authInfo: null,
12};
13
14export const state = observable(defaultState);
15
16export function resetState() {
17 Object.assign(state, defaultState);
18 console.log('reset state', state);
19}
20
21export default function initialize(stores) {
22 debug('Initialize basicAuth feature');
23
24 window.franz.features.basicAuth = {
25 state,
26 };
27
28 ipcRenderer.on('feature:basic-auth-request', (e, data) => {
29 debug(e, data);
30 // state.serviceId = data.serviceId;
31 state.authInfo = data.authInfo;
32 state.isModalVisible = true;
33 });
34
35 // autorun(() => {
36 // // if (state.serviceId) {
37 // // const service = stores.services.one(state.serviceId);
38 // // if (service) {
39 // // state.service = service;
40 // // }
41 // // }
42 // });
43}
44
45export function mainIpcHandler(mainWindow, authInfo) {
46 debug('Sending basic auth call', authInfo);
47
48 mainWindow.webContents.send('feature:basic-auth-request', {
49 authInfo,
50 });
51}
52
53export function sendCredentials(user, password) {
54 debug('Sending credentials to main', user, password);
55
56 ipcRenderer.send('feature-basic-auth-credentials', {
57 user,
58 password,
59 });
60}
61
62export function cancelLogin() {
63 debug('Cancel basic auth event');
64
65 ipcRenderer.send('feature-basic-auth-cancel');
66}
67
68export const Component = BasicAuthComponent;
diff --git a/src/features/basicAuth/mainIpcHandler.js b/src/features/basicAuth/mainIpcHandler.js
new file mode 100644
index 000000000..87ac0b6df
--- /dev/null
+++ b/src/features/basicAuth/mainIpcHandler.js
@@ -0,0 +1,9 @@
1const debug = require('debug')('Franz:feature:basicAuth:main');
2
3export default function mainIpcHandler(mainWindow, authInfo) {
4 debug('Sending basic auth call', authInfo);
5
6 mainWindow.webContents.send('feature:basic-auth', {
7 authInfo,
8 });
9}
diff --git a/src/features/basicAuth/styles.js b/src/features/basicAuth/styles.js
new file mode 100644
index 000000000..6bdaf9a6e
--- /dev/null
+++ b/src/features/basicAuth/styles.js
@@ -0,0 +1,12 @@
1export default {
2 modal: {
3 width: 300,
4 },
5 buttons: {
6 display: 'flex',
7 justifyContent: 'space-between',
8 },
9 form: {
10 marginTop: 15,
11 },
12};
diff --git a/src/index.js b/src/index.js
index 830166dcf..a4bc44a6c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,5 +1,8 @@
1import { 1import {
2 app, BrowserWindow, shell, ipcMain, 2 app,
3 BrowserWindow,
4 shell,
5 ipcMain,
3} from 'electron'; 6} from 'electron';
4 7
5import fs from 'fs-extra'; 8import fs from 'fs-extra';
@@ -7,9 +10,14 @@ import path from 'path';
7import windowStateKeeper from 'electron-window-state'; 10import windowStateKeeper from 'electron-window-state';
8 11
9import { 12import {
10 isDevMode, isMac, isWindows, isLinux, 13 isDevMode,
14 isMac,
15 isWindows,
16 isLinux,
11} from './environment'; 17} from './environment';
12 18
19import { mainIpcHandler as basicAuthHandler } from './features/basicAuth';
20
13// DEV MODE: Save user data into FranzDev 21// DEV MODE: Save user data into FranzDev
14if (isDevMode) { 22if (isDevMode) {
15 app.setPath('userData', path.join(app.getPath('appData'), 'FranzDev')); 23 app.setPath('userData', path.join(app.getPath('appData'), 'FranzDev'));
@@ -229,23 +237,42 @@ app.on('ready', () => {
229}); 237});
230 238
231// This is the worst possible implementation as the webview.webContents based callback doesn't work 🖕 239// This is the worst possible implementation as the webview.webContents based callback doesn't work 🖕
240// TODO: rewrite to handle multiple login calls
241const noop = () => null;
242let authCallback = noop;
232app.on('login', (event, webContents, request, authInfo, callback) => { 243app.on('login', (event, webContents, request, authInfo, callback) => {
233 event.preventDefault(); 244 authCallback = callback;
234 debug('browser login event', authInfo); 245 debug('browser login event', authInfo);
246 event.preventDefault();
235 if (authInfo.isProxy && authInfo.scheme === 'basic') { 247 if (authInfo.isProxy && authInfo.scheme === 'basic') {
236 webContents.send('get-service-id'); 248 webContents.send('get-service-id');
237 249
238 ipcMain.on('service-id', (e, id) => { 250 ipcMain.once('service-id', (e, id) => {
239 debug('Received service id', id); 251 debug('Received service id', id);
240 252
241 const ps = proxySettings.get(id); 253 const ps = proxySettings.get(id);
242 callback(ps.user, ps.password); 254 callback(ps.user, ps.password);
243 }); 255 });
244 } else { 256 } else if (authInfo.scheme === 'basic') {
245 // TODO: implement basic auth 257 console.log('basic auth handler', authInfo);
258 basicAuthHandler(mainWindow, authInfo);
246 } 259 }
247}); 260});
248 261
262ipcMain.on('feature-basic-auth-credentials', (e, { user, password }) => {
263 debug('Received basic auth credentials', user, '********');
264
265 authCallback(user, password);
266 authCallback = noop;
267});
268
269ipcMain.on('feature-basic-auth-cancel', () => {
270 debug('Cancel basic auth');
271
272 authCallback(null);
273 authCallback = noop;
274});
275
249// Quit when all windows are closed. 276// Quit when all windows are closed.
250app.on('window-all-closed', () => { 277app.on('window-all-closed', () => {
251 // On OS X it is common for applications and their menu bar 278 // On OS X it is common for applications and their menu bar
diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js
index 2a0713b6f..2eccf87ee 100644
--- a/src/stores/FeaturesStore.js
+++ b/src/stores/FeaturesStore.js
@@ -6,6 +6,7 @@ import CachedRequest from './lib/CachedRequest';
6import delayApp from '../features/delayApp'; 6import delayApp from '../features/delayApp';
7import spellchecker from '../features/spellchecker'; 7import spellchecker from '../features/spellchecker';
8import serviceProxy from '../features/serviceProxy'; 8import serviceProxy from '../features/serviceProxy';
9import basicAuth from '../features/basicAuth';
9 10
10import { DEFAULT_FEATURES_CONFIG } from '../config'; 11import { DEFAULT_FEATURES_CONFIG } from '../config';
11 12
@@ -47,5 +48,6 @@ export default class FeaturesStore extends Store {
47 delayApp(this.stores, this.actions); 48 delayApp(this.stores, this.actions);
48 spellchecker(this.stores, this.actions); 49 spellchecker(this.stores, this.actions);
49 serviceProxy(this.stores, this.actions); 50 serviceProxy(this.stores, this.actions);
51 basicAuth(this.stores, this.actions);
50 } 52 }
51} 53}
diff --git a/src/theme/dark/index.js b/src/theme/dark/index.js
index fb1713184..429284f9e 100644
--- a/src/theme/dark/index.js
+++ b/src/theme/dark/index.js
@@ -11,3 +11,6 @@ export const colorText = legacyStyles.darkThemeTextColor;
11// Loader 11// Loader
12export const colorFullscreenLoaderSpinner = '#FFF'; 12export const colorFullscreenLoaderSpinner = '#FFF';
13export const colorWebviewLoaderBackground = hexToRgba(legacyStyles.darkThemeGrayDarkest, 0.5); 13export const colorWebviewLoaderBackground = hexToRgba(legacyStyles.darkThemeGrayDarkest, 0.5);
14
15// Modal
16export const colorModalOverlayBackground = hexToRgba(legacyStyles.darkThemeGrayDarkest, 0.8);
diff --git a/src/theme/default/index.js b/src/theme/default/index.js
index 37827621c..3ce8e7f0e 100644
--- a/src/theme/default/index.js
+++ b/src/theme/default/index.js
@@ -27,3 +27,6 @@ export const colorSubscriptionContainerActionButtonColor = '#FFF';
27export const colorAppLoaderSpinner = '#FFF'; 27export const colorAppLoaderSpinner = '#FFF';
28export const colorFullscreenLoaderSpinner = legacyStyles.themeGrayDark; 28export const colorFullscreenLoaderSpinner = legacyStyles.themeGrayDark;
29export const colorWebviewLoaderBackground = hexToRgba(legacyStyles.themeGrayLighter, 0.8); 29export const colorWebviewLoaderBackground = hexToRgba(legacyStyles.themeGrayLighter, 0.8);
30
31// Modal
32export const colorModalOverlayBackground = hexToRgba(legacyStyles.themeGrayLighter, 0.8);