diff options
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 | ||
15 | afterSign: ./build-helpers/notarize.js | 16 | afterSign: ./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 |
68 | export const colorModalOverlayBackground = color(legacyStyles.darkThemeBlack).alpha(0.8).rgb().string(); | 68 | export const colorModalOverlayBackground = color(legacyStyles.darkThemeBlack).alpha(0.8).rgb().string(); |
69 | export const colorModalBackground = colorContentBackground; | ||
69 | 70 | ||
70 | // Services | 71 | // Services |
71 | export const services = merge({}, defaultStyles.services, { | 72 | export 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 |
147 | export const colorModalOverlayBackground = color('#000').alpha(0.5).rgb().string(); | 147 | export const colorModalOverlayBackground = color('#000').alpha(0.5).rgb().string(); |
148 | export const colorModalBackground = colorContentBackground; | ||
148 | 149 | ||
149 | // Services | 150 | // Services |
150 | export const services = { | 151 | export 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'; | |||
13 | import ServiceWebview from './ServiceWebview'; | 13 | import ServiceWebview from './ServiceWebview'; |
14 | import SettingsStore from '../../../stores/SettingsStore'; | 14 | import SettingsStore from '../../../stores/SettingsStore'; |
15 | import WebControlsScreen from '../../../features/webControls/containers/WebControlsScreen'; | 15 | import WebControlsScreen from '../../../features/webControls/containers/WebControlsScreen'; |
16 | import { CUSTOM_WEBSITE_ID } from '../../../features/webControls/constants'; | ||
16 | 17 | ||
17 | export default @observer @inject('stores') class ServiceView extends Component { | 18 | export 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'; | |||
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import injectSheet from 'react-jss'; | 4 | import injectSheet from 'react-jss'; |
5 | import { Icon } from '@meetfranz/ui'; | 5 | import { Icon } from '@meetfranz/ui'; |
6 | import { defineMessages, intlShape } from 'react-intl'; | ||
6 | 7 | ||
7 | import { | 8 | import { |
8 | mdiReload, mdiArrowRight, mdiArrowLeft, mdiHomeOutline, | 9 | mdiReload, mdiArrowRight, mdiArrowLeft, mdiHomeOutline, mdiEarth, |
9 | } from '@mdi/js'; | 10 | } from '@mdi/js'; |
10 | 11 | ||
12 | const 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 | |||
11 | const styles = theme => ({ | 35 | const 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 | ||
10 | const URL_EVENTS = [ | 10 | const 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'; | |||
5 | import injectSheet from 'react-jss'; | 5 | import injectSheet from 'react-jss'; |
6 | import classnames from 'classnames'; | 6 | import classnames from 'classnames'; |
7 | import { defineMessages, intlShape } from 'react-intl'; | 7 | import { defineMessages, intlShape } from 'react-intl'; |
8 | import { ctrlKey } from '../../../environment'; | ||
8 | 9 | ||
9 | const { Menu } = remote; | 10 | const { 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'; | |||
9 | import { announcementsStore } from '../features/announcements'; | 9 | import { announcementsStore } from '../features/announcements'; |
10 | import { todosStore } from '../features/todos'; | 10 | import { todosStore } from '../features/todos'; |
11 | import { todoActions } from '../features/todos/actions'; | 11 | import { todoActions } from '../features/todos/actions'; |
12 | import { CUSTOM_WEBSITE_ID } from '../features/webControls/constants'; | ||
12 | 13 | ||
13 | const { app, Menu, dialog } = remote; | 14 | const { 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 | ||
286 | function getActiveWebview() { | 291 | function 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'; | |||
19 | import './notifications'; | 19 | import './notifications'; |
20 | 20 | ||
21 | import { DEFAULT_APP_SETTINGS } from '../config'; | 21 | import { DEFAULT_APP_SETTINGS } from '../config'; |
22 | import { isDevMode } from '../environment'; | ||
22 | 23 | ||
23 | const debug = require('debug')('Ferdi:Plugin'); | 24 | const 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 |
196 | const originalWindowOpen = window.open; | 197 | const originalWindowOpen = window.open; |
197 | 198 | ||
199 | |||
198 | window.open = (url, frameName, features) => { | 200 | window.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 | |||
235 | if (isDevMode) { | ||
236 | window.log = console.log; | ||
237 | } | ||