aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--electron-builder.yml1
-rw-r--r--package.json2
-rw-r--r--packages/theme/src/themes/dark/index.ts1
-rw-r--r--packages/theme/src/themes/default/index.ts1
-rw-r--r--src/components/services/content/ServiceView.js16
-rw-r--r--src/components/services/content/ServiceWebview.js14
-rw-r--r--src/components/services/content/Services.js12
-rw-r--r--src/components/ui/Modal/styles.js2
-rw-r--r--src/features/delayApp/index.js4
-rw-r--r--src/features/webControls/components/WebControls.js59
-rw-r--r--src/features/webControls/constants.js1
-rw-r--r--src/features/webControls/containers/WebControlsScreen.js13
-rw-r--r--src/features/workspaces/components/WorkspaceDrawer.js4
-rw-r--r--src/features/workspaces/components/WorkspaceDrawerItem.js4
-rw-r--r--src/i18n/locales/en-US.json6
-rw-r--r--src/i18n/messages/src/features/webControls/components/WebControls.json67
-rw-r--r--src/i18n/messages/src/features/workspaces/components/WorkspaceDrawerItem.json8
-rw-r--r--src/index.js17
-rw-r--r--src/lib/Menu.js23
-rw-r--r--src/models/Service.js41
-rw-r--r--src/stores/ServicesStore.js3
-rw-r--r--src/webview/recipe.js12
22 files changed, 262 insertions, 49 deletions
diff --git a/electron-builder.yml b/electron-builder.yml
index b2c243e49..df7d2223a 100644
--- a/electron-builder.yml
+++ b/electron-builder.yml
@@ -11,6 +11,7 @@ mac:
11 gatekeeperAssess: false 11 gatekeeperAssess: false
12 category: public.app-category.productivity 12 category: public.app-category.productivity
13 icon: ./build-helpers/images/icon.icns 13 icon: ./build-helpers/images/icon.icns
14 darkModeSupport: true
14 15
15afterSign: ./build-helpers/notarize.js 16afterSign: ./build-helpers/notarize.js
16 17
diff --git a/package.json b/package.json
index eb5e0de6c..838be1299 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
2 "name": "ferdi", 2 "name": "ferdi",
3 "productName": "Ferdi", 3 "productName": "Ferdi",
4 "appId": "com.kytwb.ferdi", 4 "appId": "com.kytwb.ferdi",
5 "version": "5.3.4-beta.7", 5 "version": "5.4.0-beta.1",
6 "description": "Messaging app for WhatsApp, Slack, Telegram, HipChat, Hangouts and many many more.", 6 "description": "Messaging app for WhatsApp, Slack, Telegram, HipChat, Hangouts and many many more.",
7 "copyright": "kytwb", 7 "copyright": "kytwb",
8 "main": "index.js", 8 "main": "index.js",
diff --git a/packages/theme/src/themes/dark/index.ts b/packages/theme/src/themes/dark/index.ts
index e94f54c55..67d0cfb71 100644
--- a/packages/theme/src/themes/dark/index.ts
+++ b/packages/theme/src/themes/dark/index.ts
@@ -66,6 +66,7 @@ export const selectSearchColor = inputBackground;
66 66
67// Modal 67// Modal
68export const colorModalOverlayBackground = color(legacyStyles.darkThemeBlack).alpha(0.8).rgb().string(); 68export const colorModalOverlayBackground = color(legacyStyles.darkThemeBlack).alpha(0.8).rgb().string();
69export const colorModalBackground = colorContentBackground;
69 70
70// Services 71// Services
71export const services = merge({}, defaultStyles.services, { 72export const services = merge({}, defaultStyles.services, {
diff --git a/packages/theme/src/themes/default/index.ts b/packages/theme/src/themes/default/index.ts
index 79a6d676f..1862fef6f 100644
--- a/packages/theme/src/themes/default/index.ts
+++ b/packages/theme/src/themes/default/index.ts
@@ -145,6 +145,7 @@ export const badgeBorderRadius = 50;
145 145
146// Modal 146// Modal
147export const colorModalOverlayBackground = color('#000').alpha(0.5).rgb().string(); 147export const colorModalOverlayBackground = color('#000').alpha(0.5).rgb().string();
148export const colorModalBackground = colorContentBackground;
148 149
149// Services 150// Services
150export const services = { 151export const services = {
diff --git a/src/components/services/content/ServiceView.js b/src/components/services/content/ServiceView.js
index f6686548d..273653ea2 100644
--- a/src/components/services/content/ServiceView.js
+++ b/src/components/services/content/ServiceView.js
@@ -13,6 +13,7 @@ import ServiceDisabled from './ServiceDisabled';
13import ServiceWebview from './ServiceWebview'; 13import ServiceWebview from './ServiceWebview';
14import SettingsStore from '../../../stores/SettingsStore'; 14import SettingsStore from '../../../stores/SettingsStore';
15import WebControlsScreen from '../../../features/webControls/containers/WebControlsScreen'; 15import WebControlsScreen from '../../../features/webControls/containers/WebControlsScreen';
16import { CUSTOM_WEBSITE_ID } from '../../../features/webControls/constants';
16 17
17export default @observer @inject('stores') class ServiceView extends Component { 18export default @observer @inject('stores') class ServiceView extends Component {
18 static propTypes = { 19 static propTypes = {
@@ -183,11 +184,16 @@ export default @observer @inject('stores') class ServiceView extends Component {
183 <WebControlsScreen service={service} /> 184 <WebControlsScreen service={service} />
184 )} 185 )}
185 {!this.state.hibernate ? ( 186 {!this.state.hibernate ? (
186 <ServiceWebview 187 <>
187 service={service} 188 {service.recipe.id === CUSTOM_WEBSITE_ID && (
188 setWebviewReference={setWebviewReference} 189 <WebControlsScreen service={service} />
189 detachService={detachService} 190 )}
190 /> 191 <ServiceWebview
192 service={service}
193 setWebviewReference={setWebviewReference}
194 detachService={detachService}
195 />
196 </>
191 ) : ( 197 ) : (
192 <div> 198 <div>
193 <span role="img" aria-label="Sleeping Emoji">😴</span> 199 <span role="img" aria-label="Sleeping Emoji">😴</span>
diff --git a/src/components/services/content/ServiceWebview.js b/src/components/services/content/ServiceWebview.js
index 03d6d5bcc..e6ebb6afb 100644
--- a/src/components/services/content/ServiceWebview.js
+++ b/src/components/services/content/ServiceWebview.js
@@ -38,6 +38,13 @@ class ServiceWebview extends Component {
38 detachService({ service }); 38 detachService({ service });
39 } 39 }
40 40
41 refocusWebview = () => {
42 const { webview } = this;
43 if (!webview) return;
44 webview.view.blur();
45 webview.view.focus();
46 };
47
41 render() { 48 render() {
42 const { 49 const {
43 service, 50 service,
@@ -46,7 +53,12 @@ class ServiceWebview extends Component {
46 53
47 return ( 54 return (
48 <ElectronWebView 55 <ElectronWebView
49 ref={(webview) => { this.webview = webview; }} 56 ref={(webview) => {
57 this.webview = webview;
58 if (webview && webview.view) {
59 webview.view.addEventListener('did-stop-loading', this.refocusWebview);
60 }
61 }}
50 autosize 62 autosize
51 src={service.url} 63 src={service.url}
52 preload="./webview/recipe.js" 64 preload="./webview/recipe.js"
diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js
index 1afbaabc4..edff29ae8 100644
--- a/src/components/services/content/Services.js
+++ b/src/components/services/content/Services.js
@@ -64,16 +64,24 @@ export default @observer @injectSheet(styles) class Services extends Component {
64 64
65 state = { 65 state = {
66 showConfetti: true, 66 showConfetti: true,
67 } 67 };
68
69 _confettiTimeout = null;
68 70
69 componentDidMount() { 71 componentDidMount() {
70 window.setTimeout(() => { 72 this._confettiTimeout = window.setTimeout(() => {
71 this.setState({ 73 this.setState({
72 showConfetti: false, 74 showConfetti: false,
73 }); 75 });
74 }, ms('8s')); 76 }, ms('8s'));
75 } 77 }
76 78
79 componentWillUnmount() {
80 if (this._confettiTimeout) {
81 clearTimeout(this._confettiTimeout);
82 }
83 }
84
77 render() { 85 render() {
78 const { 86 const {
79 services, 87 services,
diff --git a/src/components/ui/Modal/styles.js b/src/components/ui/Modal/styles.js
index 49b970c97..c2bebf9bb 100644
--- a/src/components/ui/Modal/styles.js
+++ b/src/components/ui/Modal/styles.js
@@ -13,7 +13,7 @@ export default theme => ({
13 display: 'flex', 13 display: 'flex',
14 }, 14 },
15 modal: { 15 modal: {
16 background: '#FFF', 16 background: theme.colorModalBackground,
17 maxWidth: '90%', 17 maxWidth: '90%',
18 height: 'auto', 18 height: 'auto',
19 margin: 'auto auto', 19 margin: 'auto auto',
diff --git a/src/features/delayApp/index.js b/src/features/delayApp/index.js
index a80ea4c72..5cc6c9506 100644
--- a/src/features/delayApp/index.js
+++ b/src/features/delayApp/index.js
@@ -57,12 +57,12 @@ export default function init(stores) {
57 57
58 setVisibility(true); 58 setVisibility(true);
59 59
60 timeLastDelay = moment();
61 shownAfterLaunch = true;
62 60
63 setTimeout(() => { 61 setTimeout(() => {
64 debug('Resetting app delay'); 62 debug('Resetting app delay');
65 63
64 shownAfterLaunch = true;
65 timeLastDelay = moment();
66 setVisibility(false); 66 setVisibility(false);
67 }, config.delayDuration + 1000); // timer needs to be able to hit 0 67 }, config.delayDuration + 1000); // timer needs to be able to hit 0
68 } else { 68 } else {
diff --git a/src/features/webControls/components/WebControls.js b/src/features/webControls/components/WebControls.js
index 03f601a17..c6331073b 100644
--- a/src/features/webControls/components/WebControls.js
+++ b/src/features/webControls/components/WebControls.js
@@ -3,11 +3,35 @@ import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import injectSheet from 'react-jss'; 4import injectSheet from 'react-jss';
5import { Icon } from '@meetfranz/ui'; 5import { Icon } from '@meetfranz/ui';
6import { defineMessages, intlShape } from 'react-intl';
6 7
7import { 8import {
8 mdiReload, mdiArrowRight, mdiArrowLeft, mdiHomeOutline, 9 mdiReload, mdiArrowRight, mdiArrowLeft, mdiHomeOutline, mdiEarth,
9} from '@mdi/js'; 10} from '@mdi/js';
10 11
12const messages = defineMessages({
13 goHome: {
14 id: 'webControls.goHome',
15 defaultMessage: '!!!Home',
16 },
17 openInBrowser: {
18 id: 'webControls.openInBrowser',
19 defaultMessage: '!!!Open in Browser',
20 },
21 back: {
22 id: 'webControls.back',
23 defaultMessage: '!!!Back',
24 },
25 forward: {
26 id: 'webControls.forward',
27 defaultMessage: '!!!Forward',
28 },
29 reload: {
30 id: 'webControls.reload',
31 defaultMessage: '!!!Reload',
32 },
33});
34
11const styles = theme => ({ 35const styles = theme => ({
12 root: { 36 root: {
13 background: theme.colorBackground, 37 background: theme.colorBackground,
@@ -18,7 +42,7 @@ const styles = theme => ({
18 display: 'flex', 42 display: 'flex',
19 flexDirection: 'row', 43 flexDirection: 'row',
20 alignItems: 'center', 44 alignItems: 'center',
21 padding: [0, 20], 45 padding: [0, 10],
22 46
23 '& + div': { 47 '& + div': {
24 height: 'calc(100% - 50px)', 48 height: 'calc(100% - 50px)',
@@ -45,7 +69,7 @@ const styles = theme => ({
45 input: { 69 input: {
46 marginBottom: 0, 70 marginBottom: 0,
47 height: 'auto', 71 height: 'auto',
48 marginLeft: 10, 72 margin: [0, 10],
49 flex: 1, 73 flex: 1,
50 border: 0, 74 border: 0,
51 padding: [4, 10], 75 padding: [4, 10],
@@ -68,10 +92,15 @@ class WebControls extends Component {
68 canGoForward: PropTypes.bool.isRequired, 92 canGoForward: PropTypes.bool.isRequired,
69 goForward: PropTypes.func.isRequired, 93 goForward: PropTypes.func.isRequired,
70 reload: PropTypes.func.isRequired, 94 reload: PropTypes.func.isRequired,
95 openInBrowser: PropTypes.func.isRequired,
71 url: PropTypes.string.isRequired, 96 url: PropTypes.string.isRequired,
72 navigate: PropTypes.func.isRequired, 97 navigate: PropTypes.func.isRequired,
73 } 98 }
74 99
100 static contextTypes = {
101 intl: intlShape,
102 };
103
75 static getDerivedStateFromProps(props, state) { 104 static getDerivedStateFromProps(props, state) {
76 const { url } = props; 105 const { url } = props;
77 const { editUrl } = state; 106 const { editUrl } = state;
@@ -100,6 +129,7 @@ class WebControls extends Component {
100 canGoForward, 129 canGoForward,
101 goForward, 130 goForward,
102 reload, 131 reload,
132 openInBrowser,
103 url, 133 url,
104 navigate, 134 navigate,
105 } = this.props; 135 } = this.props;
@@ -109,12 +139,16 @@ class WebControls extends Component {
109 editUrl, 139 editUrl,
110 } = this.state; 140 } = this.state;
111 141
142 const { intl } = this.context;
143
112 return ( 144 return (
113 <div className={classes.root}> 145 <div className={classes.root}>
114 <button 146 <button
115 onClick={goHome} 147 onClick={goHome}
116 type="button" 148 type="button"
117 className={classes.button} 149 className={classes.button}
150 data-tip={intl.formatMessage(messages.goHome)}
151 data-place="bottom"
118 > 152 >
119 <Icon 153 <Icon
120 icon={mdiHomeOutline} 154 icon={mdiHomeOutline}
@@ -126,6 +160,8 @@ class WebControls extends Component {
126 type="button" 160 type="button"
127 className={classes.button} 161 className={classes.button}
128 disabled={!canGoBack} 162 disabled={!canGoBack}
163 data-tip={intl.formatMessage(messages.back)}
164 data-place="bottom"
129 > 165 >
130 <Icon 166 <Icon
131 icon={mdiArrowLeft} 167 icon={mdiArrowLeft}
@@ -137,6 +173,8 @@ class WebControls extends Component {
137 type="button" 173 type="button"
138 className={classes.button} 174 className={classes.button}
139 disabled={!canGoForward} 175 disabled={!canGoForward}
176 data-tip={intl.formatMessage(messages.forward)}
177 data-place="bottom"
140 > 178 >
141 <Icon 179 <Icon
142 icon={mdiArrowRight} 180 icon={mdiArrowRight}
@@ -147,6 +185,8 @@ class WebControls extends Component {
147 onClick={reload} 185 onClick={reload}
148 type="button" 186 type="button"
149 className={classes.button} 187 className={classes.button}
188 data-tip={intl.formatMessage(messages.reload)}
189 data-place="bottom"
150 > 190 >
151 <Icon 191 <Icon
152 icon={mdiReload} 192 icon={mdiReload}
@@ -160,6 +200,7 @@ class WebControls extends Component {
160 inputUrl: event.target.value, 200 inputUrl: event.target.value,
161 })} 201 })}
162 onFocus={(event) => { 202 onFocus={(event) => {
203 console.log('on focus event');
163 event.target.select(); 204 event.target.select();
164 this.setState({ 205 this.setState({
165 editUrl: true, 206 editUrl: true,
@@ -182,6 +223,18 @@ class WebControls extends Component {
182 }} 223 }}
183 ref={this.inputRef} 224 ref={this.inputRef}
184 /> 225 />
226 <button
227 onClick={openInBrowser}
228 type="button"
229 className={classes.button}
230 data-tip={intl.formatMessage(messages.openInBrowser)}
231 data-place="bottom"
232 >
233 <Icon
234 icon={mdiEarth}
235 className={classes.icon}
236 />
237 </button>
185 </div> 238 </div>
186 ); 239 );
187 } 240 }
diff --git a/src/features/webControls/constants.js b/src/features/webControls/constants.js
new file mode 100644
index 000000000..270a3568e
--- /dev/null
+++ b/src/features/webControls/constants.js
@@ -0,0 +1 @@
export const CUSTOM_WEBSITE_ID = 'franz-custom-website';
diff --git a/src/features/webControls/containers/WebControlsScreen.js b/src/features/webControls/containers/WebControlsScreen.js
index 1452d5a3d..cada01a6f 100644
--- a/src/features/webControls/containers/WebControlsScreen.js
+++ b/src/features/webControls/containers/WebControlsScreen.js
@@ -9,7 +9,6 @@ import Service from '../../../models/Service';
9 9
10const URL_EVENTS = [ 10const URL_EVENTS = [
11 'load-commit', 11 'load-commit',
12 // 'dom-ready',
13 'will-navigate', 12 'will-navigate',
14 'did-navigate', 13 'did-navigate',
15 'did-navigate-in-page', 14 'did-navigate-in-page',
@@ -97,11 +96,20 @@ class WebControlsScreen extends Component {
97 this.url = url; 96 this.url = url;
98 } 97 }
99 98
99 openInBrowser() {
100 const { openExternalUrl } = this.props.actions.app;
101
102 if (!this.webview) return;
103
104 openExternalUrl({ url: this.url });
105 }
106
100 render() { 107 render() {
101 return ( 108 return (
102 <WebControls 109 <WebControls
103 goHome={() => this.goHome()} 110 goHome={() => this.goHome()}
104 reload={() => this.reload()} 111 reload={() => this.reload()}
112 openInBrowser={() => this.openInBrowser()}
105 canGoBack={this.canGoBack} 113 canGoBack={this.canGoBack}
106 goBack={() => this.goBack()} 114 goBack={() => this.goBack()}
107 canGoForward={this.canGoForward} 115 canGoForward={this.canGoForward}
@@ -121,6 +129,9 @@ WebControlsScreen.wrappedComponent.propTypes = {
121 services: PropTypes.instanceOf(ServicesStore).isRequired, 129 services: PropTypes.instanceOf(ServicesStore).isRequired,
122 }).isRequired, 130 }).isRequired,
123 actions: PropTypes.shape({ 131 actions: PropTypes.shape({
132 app: PropTypes.shape({
133 openExternalUrl: PropTypes.func.isRequired,
134 }).isRequired,
124 service: PropTypes.shape({ 135 service: PropTypes.shape({
125 reloadActive: PropTypes.func.isRequired, 136 reloadActive: PropTypes.func.isRequired,
126 }).isRequired, 137 }).isRequired,
diff --git a/src/features/workspaces/components/WorkspaceDrawer.js b/src/features/workspaces/components/WorkspaceDrawer.js
index f4ee89a14..e991b9909 100644
--- a/src/features/workspaces/components/WorkspaceDrawer.js
+++ b/src/features/workspaces/components/WorkspaceDrawer.js
@@ -199,8 +199,9 @@ class WorkspaceDrawer extends Component {
199 }} 199 }}
200 services={getServicesForWorkspace(null)} 200 services={getServicesForWorkspace(null)}
201 isActive={actualWorkspace == null} 201 isActive={actualWorkspace == null}
202 shortcutIndex={0}
202 /> 203 />
203 {workspaces.map(workspace => ( 204 {workspaces.map((workspace, index) => (
204 <WorkspaceDrawerItem 205 <WorkspaceDrawerItem
205 key={workspace.id} 206 key={workspace.id}
206 name={workspace.name} 207 name={workspace.name}
@@ -212,6 +213,7 @@ class WorkspaceDrawer extends Component {
212 }} 213 }}
213 onContextMenuEditClick={() => workspaceActions.edit({ workspace })} 214 onContextMenuEditClick={() => workspaceActions.edit({ workspace })}
214 services={getServicesForWorkspace(workspace)} 215 services={getServicesForWorkspace(workspace)}
216 shortcutIndex={index + 1}
215 /> 217 />
216 ))} 218 ))}
217 <div 219 <div
diff --git a/src/features/workspaces/components/WorkspaceDrawerItem.js b/src/features/workspaces/components/WorkspaceDrawerItem.js
index 59a2144d3..18f424d8a 100644
--- a/src/features/workspaces/components/WorkspaceDrawerItem.js
+++ b/src/features/workspaces/components/WorkspaceDrawerItem.js
@@ -5,6 +5,7 @@ import { observer } from 'mobx-react';
5import injectSheet from 'react-jss'; 5import injectSheet from 'react-jss';
6import classnames from 'classnames'; 6import classnames from 'classnames';
7import { defineMessages, intlShape } from 'react-intl'; 7import { defineMessages, intlShape } from 'react-intl';
8import { ctrlKey } from '../../../environment';
8 9
9const { Menu } = remote; 10const { Menu } = remote;
10 11
@@ -69,6 +70,7 @@ class WorkspaceDrawerItem extends Component {
69 onClick: PropTypes.func.isRequired, 70 onClick: PropTypes.func.isRequired,
70 services: PropTypes.arrayOf(PropTypes.string).isRequired, 71 services: PropTypes.arrayOf(PropTypes.string).isRequired,
71 onContextMenuEditClick: PropTypes.func, 72 onContextMenuEditClick: PropTypes.func,
73 shortcutIndex: PropTypes.number.isRequired,
72 }; 74 };
73 75
74 static defaultProps = { 76 static defaultProps = {
@@ -87,6 +89,7 @@ class WorkspaceDrawerItem extends Component {
87 onClick, 89 onClick,
88 onContextMenuEditClick, 90 onContextMenuEditClick,
89 services, 91 services,
92 shortcutIndex,
90 } = this.props; 93 } = this.props;
91 const { intl } = this.context; 94 const { intl } = this.context;
92 95
@@ -112,6 +115,7 @@ class WorkspaceDrawerItem extends Component {
112 onContextMenu={() => ( 115 onContextMenu={() => (
113 onContextMenuEditClick && contextMenu.popup(remote.getCurrentWindow()) 116 onContextMenuEditClick && contextMenu.popup(remote.getCurrentWindow())
114 )} 117 )}
118 data-tip={`${shortcutIndex <= 9 ? `(${ctrlKey}+Alt+${shortcutIndex})` : ''}`}
115 > 119 >
116 <span 120 <span
117 className={classnames([ 121 className={classnames([
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 319a55f28..140ca740b 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -102,6 +102,7 @@
102 "menu.services": "Services", 102 "menu.services": "Services",
103 "menu.services.activatePreviousService": "Activate previous service", 103 "menu.services.activatePreviousService": "Activate previous service",
104 "menu.services.addNewService": "Add New Service...", 104 "menu.services.addNewService": "Add New Service...",
105 "menu.services.goHome": "Home",
105 "menu.services.setNextServiceActive": "Activate next service", 106 "menu.services.setNextServiceActive": "Activate next service",
106 "menu.todos": "Todos", 107 "menu.todos": "Todos",
107 "menu.todos.enableTodos": "Enable Todos", 108 "menu.todos.enableTodos": "Enable Todos",
@@ -415,6 +416,11 @@
415 "validation.oneRequired": "At least one is required", 416 "validation.oneRequired": "At least one is required",
416 "validation.required": "{field} is required", 417 "validation.required": "{field} is required",
417 "validation.url": "{field} is not a valid URL", 418 "validation.url": "{field} is not a valid URL",
419 "webControls.back": "Back",
420 "webControls.forward": "Forward",
421 "webControls.goHome": "Home",
422 "webControls.openInBrowser": "Open in Browser",
423 "webControls.reload": "Reload",
418 "welcome.loginButton": "Login to your account", 424 "welcome.loginButton": "Login to your account",
419 "welcome.signupButton": "Create a free account", 425 "welcome.signupButton": "Create a free account",
420 "workspaceDrawer.addNewWorkspaceLabel": "Add new workspace", 426 "workspaceDrawer.addNewWorkspaceLabel": "Add new workspace",
diff --git a/src/i18n/messages/src/features/webControls/components/WebControls.json b/src/i18n/messages/src/features/webControls/components/WebControls.json
new file mode 100644
index 000000000..969437e98
--- /dev/null
+++ b/src/i18n/messages/src/features/webControls/components/WebControls.json
@@ -0,0 +1,67 @@
1[
2 {
3 "id": "webControls.goHome",
4 "defaultMessage": "!!!Home",
5 "file": "src/features/webControls/components/WebControls.js",
6 "start": {
7 "line": 13,
8 "column": 10
9 },
10 "end": {
11 "line": 16,
12 "column": 3
13 }
14 },
15 {
16 "id": "webControls.openInBrowser",
17 "defaultMessage": "!!!Open in Browser",
18 "file": "src/features/webControls/components/WebControls.js",
19 "start": {
20 "line": 17,
21 "column": 17
22 },
23 "end": {
24 "line": 20,
25 "column": 3
26 }
27 },
28 {
29 "id": "webControls.back",
30 "defaultMessage": "!!!Back",
31 "file": "src/features/webControls/components/WebControls.js",
32 "start": {
33 "line": 21,
34 "column": 8
35 },
36 "end": {
37 "line": 24,
38 "column": 3
39 }
40 },
41 {
42 "id": "webControls.forward",
43 "defaultMessage": "!!!Forward",
44 "file": "src/features/webControls/components/WebControls.js",
45 "start": {
46 "line": 25,
47 "column": 11
48 },
49 "end": {
50 "line": 28,
51 "column": 3
52 }
53 },
54 {
55 "id": "webControls.reload",
56 "defaultMessage": "!!!Reload",
57 "file": "src/features/webControls/components/WebControls.js",
58 "start": {
59 "line": 29,
60 "column": 10
61 },
62 "end": {
63 "line": 32,
64 "column": 3
65 }
66 }
67] \ No newline at end of file
diff --git a/src/i18n/messages/src/features/workspaces/components/WorkspaceDrawerItem.json b/src/i18n/messages/src/features/workspaces/components/WorkspaceDrawerItem.json
index 4ff190606..1b6664787 100644
--- a/src/i18n/messages/src/features/workspaces/components/WorkspaceDrawerItem.json
+++ b/src/i18n/messages/src/features/workspaces/components/WorkspaceDrawerItem.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!No services added yet", 4 "defaultMessage": "!!!No services added yet",
5 "file": "src/features/workspaces/components/WorkspaceDrawerItem.js", 5 "file": "src/features/workspaces/components/WorkspaceDrawerItem.js",
6 "start": { 6 "start": {
7 "line": 12, 7 "line": 13,
8 "column": 22 8 "column": 22
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 15, 11 "line": 16,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!edit", 17 "defaultMessage": "!!!edit",
18 "file": "src/features/workspaces/components/WorkspaceDrawerItem.js", 18 "file": "src/features/workspaces/components/WorkspaceDrawerItem.js",
19 "start": { 19 "start": {
20 "line": 16, 20 "line": 17,
21 "column": 19 21 "column": 19
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 19, 24 "line": 20,
25 "column": 3 25 "column": 3
26 } 26 }
27 } 27 }
diff --git a/src/index.js b/src/index.js
index 449bbad19..2ee404c0b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -336,22 +336,7 @@ app.on('login', (event, webContents, request, authInfo, callback) => {
336 debug('browser login event', authInfo); 336 debug('browser login event', authInfo);
337 event.preventDefault(); 337 event.preventDefault();
338 338
339 if (authInfo.isProxy && authInfo.scheme === 'basic') { 339 if (!authInfo.isProxy && authInfo.scheme === 'basic') {
340 debug('Sending service echo ping');
341 webContents.send('get-service-id');
342
343 ipcMain.once('service-id', (e, id) => {
344 debug('Received service id', id);
345
346 const ps = proxySettings.get(id);
347 if (ps) {
348 debug('Sending proxy auth callback for service', id);
349 callback(ps.user, ps.password);
350 } else {
351 debug('No proxy auth config found for', id);
352 }
353 });
354 } else if (authInfo.scheme === 'basic') {
355 debug('basic auth handler', authInfo); 340 debug('basic auth handler', authInfo);
356 basicAuthHandler(mainWindow, authInfo); 341 basicAuthHandler(mainWindow, authInfo);
357 } 342 }
diff --git a/src/lib/Menu.js b/src/lib/Menu.js
index 01aee6f5c..f223283f9 100644
--- a/src/lib/Menu.js
+++ b/src/lib/Menu.js
@@ -9,6 +9,7 @@ import { announcementActions } from '../features/announcements/actions';
9import { announcementsStore } from '../features/announcements'; 9import { announcementsStore } from '../features/announcements';
10import { todosStore } from '../features/todos'; 10import { todosStore } from '../features/todos';
11import { todoActions } from '../features/todos/actions'; 11import { todoActions } from '../features/todos/actions';
12import { CUSTOM_WEBSITE_ID } from '../features/webControls/constants';
12 13
13const { app, Menu, dialog } = remote; 14const { app, Menu, dialog } = remote;
14 15
@@ -281,6 +282,10 @@ const menuItems = defineMessages({
281 id: 'menu.todos.enableTodos', 282 id: 'menu.todos.enableTodos',
282 defaultMessage: '!!!Enable Todos', 283 defaultMessage: '!!!Enable Todos',
283 }, 284 },
285 serviceGoHome: {
286 id: 'menu.services.goHome',
287 defaultMessage: '!!!Home',
288 },
284}); 289});
285 290
286function getActiveWebview() { 291function getActiveWebview() {
@@ -767,8 +772,12 @@ export default class FranzMenu {
767 accelerator: `${cmdKey}+R`, 772 accelerator: `${cmdKey}+R`,
768 click: () => { 773 click: () => {
769 if (this.stores.user.isLoggedIn 774 if (this.stores.user.isLoggedIn
770 && this.stores.services.enabled.length > 0) { 775 && this.stores.services.enabled.length > 0) {
771 this.actions.service.reloadActive(); 776 if (this.stores.services.active.recipe.id === CUSTOM_WEBSITE_ID) {
777 this.stores.services.active.webview.reload();
778 } else {
779 this.actions.service.reloadActive();
780 }
772 } else { 781 } else {
773 window.location.reload(); 782 window.location.reload();
774 } 783 }
@@ -995,6 +1004,16 @@ export default class FranzMenu {
995 }, 1004 },
996 }))); 1005 })));
997 1006
1007 if (services.active && services.active.recipe.id === CUSTOM_WEBSITE_ID) {
1008 menu.push({
1009 type: 'separator',
1010 }, {
1011 label: intl.formatMessage(menuItems.serviceGoHome),
1012 accelerator: `${cmdKey}+shift+H`,
1013 click: () => this.actions.service.reloadActive(),
1014 });
1015 }
1016
998 return menu; 1017 return menu;
999 } 1018 }
1000 1019
diff --git a/src/models/Service.js b/src/models/Service.js
index 3ab6e2603..ddc169d47 100644
--- a/src/models/Service.js
+++ b/src/models/Service.js
@@ -192,19 +192,24 @@ export default class Service {
192 return userAgent; 192 return userAgent;
193 } 193 }
194 194
195 initializeWebViewEvents({ handleIPCMessage, openWindow }) { 195 initializeWebViewEvents({ handleIPCMessage, openWindow, stores }) {
196 const webContents = this.webview.getWebContents();
197
196 this.webview.addEventListener('ipc-message', e => handleIPCMessage({ 198 this.webview.addEventListener('ipc-message', e => handleIPCMessage({
197 serviceId: this.id, 199 serviceId: this.id,
198 channel: e.channel, 200 channel: e.channel,
199 args: e.args, 201 args: e.args,
200 })); 202 }));
201 203
202 this.webview.addEventListener('new-window', (event, url, frameName, options) => openWindow({ 204 this.webview.addEventListener('new-window', (event, url, frameName, options) => {
203 event, 205 console.log('open window', event, url, frameName, options);
204 url, 206 openWindow({
205 frameName, 207 event,
206 options, 208 url,
207 })); 209 frameName,
210 options,
211 });
212 });
208 213
209 this.webview.addEventListener('did-start-loading', (event) => { 214 this.webview.addEventListener('did-start-loading', (event) => {
210 debug('Did start load', this.name, event); 215 debug('Did start load', this.name, event);
@@ -238,6 +243,28 @@ export default class Service {
238 debug('Service crashed', this.name); 243 debug('Service crashed', this.name);
239 this.hasCrashed = true; 244 this.hasCrashed = true;
240 }); 245 });
246
247 webContents.on('login', (event, request, authInfo, callback) => {
248 // const authCallback = callback;
249 debug('browser login event', authInfo);
250 event.preventDefault();
251
252 if (authInfo.isProxy && authInfo.scheme === 'basic') {
253 debug('Sending service echo ping');
254 webContents.send('get-service-id');
255
256 debug('Received service id', this.id);
257
258 const ps = stores.settings.proxy[this.id];
259
260 if (ps) {
261 debug('Sending proxy auth callback for service', this.id);
262 callback(ps.user, ps.password);
263 } else {
264 debug('No proxy auth config found for', this.id);
265 }
266 }
267 });
241 } 268 }
242 269
243 initializeWebViewListener() { 270 initializeWebViewListener() {
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index 3173004d4..076ecc204 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -370,6 +370,7 @@ export default class ServicesStore extends Store {
370 service.initializeWebViewEvents({ 370 service.initializeWebViewEvents({
371 handleIPCMessage: this.actions.service.handleIPCMessage, 371 handleIPCMessage: this.actions.service.handleIPCMessage,
372 openWindow: this.actions.service.openWindow, 372 openWindow: this.actions.service.openWindow,
373 stores: this.stores,
373 }); 374 });
374 service.initializeWebViewListener(); 375 service.initializeWebViewListener();
375 } 376 }
@@ -727,6 +728,8 @@ export default class ServicesStore extends Store {
727 const serviceData = data; 728 const serviceData = data;
728 const recipe = this.stores.recipes.one(recipeId); 729 const recipe = this.stores.recipes.one(recipeId);
729 730
731 if (!recipe) return;
732
730 if (recipe.hasTeamId && recipe.hasCustomUrl && data.team && data.customUrl) { 733 if (recipe.hasTeamId && recipe.hasCustomUrl && data.team && data.customUrl) {
731 delete serviceData.team; 734 delete serviceData.team;
732 } 735 }
diff --git a/src/webview/recipe.js b/src/webview/recipe.js
index b30199f03..3f2338b68 100644
--- a/src/webview/recipe.js
+++ b/src/webview/recipe.js
@@ -19,6 +19,7 @@ import contextMenu from './contextMenu';
19import './notifications'; 19import './notifications';
20 20
21import { DEFAULT_APP_SETTINGS } from '../config'; 21import { DEFAULT_APP_SETTINGS } from '../config';
22import { isDevMode } from '../environment';
22 23
23const debug = require('debug')('Ferdi:Plugin'); 24const debug = require('debug')('Ferdi:Plugin');
24 25
@@ -39,7 +40,7 @@ class RecipeController {
39 'settings-update': 'updateAppSettings', 40 'settings-update': 'updateAppSettings',
40 'service-settings-update': 'updateServiceSettings', 41 'service-settings-update': 'updateServiceSettings',
41 'get-service-id': 'serviceIdEcho', 42 'get-service-id': 'serviceIdEcho',
42 } 43 };
43 44
44 constructor() { 45 constructor() {
45 this.initialize(); 46 this.initialize();
@@ -195,6 +196,7 @@ new RecipeController();
195// Patching window.open 196// Patching window.open
196const originalWindowOpen = window.open; 197const originalWindowOpen = window.open;
197 198
199
198window.open = (url, frameName, features) => { 200window.open = (url, frameName, features) => {
199 if (!url && !frameName && !features) { 201 if (!url && !frameName && !features) {
200 // The service hasn't yet supplied a URL (as used in Skype). 202 // The service hasn't yet supplied a URL (as used in Skype).
@@ -209,7 +211,7 @@ window.open = (url, frameName, features) => {
209 // Has the service changed the URL yet? 211 // Has the service changed the URL yet?
210 if (newWindow.location.href !== '') { 212 if (newWindow.location.href !== '') {
211 // Open the new URL 213 // Open the new URL
212 window.open(newWindow.location.href); 214 ipcRenderer.sendToHost('new-window', newWindow.location.href);
213 clearInterval(checkInterval); 215 clearInterval(checkInterval);
214 } 216 }
215 }, 0); 217 }, 0);
@@ -223,9 +225,13 @@ window.open = (url, frameName, features) => {
223 } 225 }
224 226
225 // We need to differentiate if the link should be opened in a popup or in the systems default browser 227 // We need to differentiate if the link should be opened in a popup or in the systems default browser
226 if (!frameName && !features) { 228 if (!frameName && !features && typeof features !== 'string') {
227 return ipcRenderer.sendToHost('new-window', url); 229 return ipcRenderer.sendToHost('new-window', url);
228 } 230 }
229 231
230 return originalWindowOpen(url, frameName, features); 232 return originalWindowOpen(url, frameName, features);
231}; 233};
234
235if (isDevMode) {
236 window.log = console.log;
237}