Skip to content

Commit

Permalink
feat(Custom Websites): Add simple browser controls
Browse files Browse the repository at this point in the history
  • Loading branch information
adlk committed Oct 1, 2019
1 parent 9f4f3e7 commit f8fbaad
Show file tree
Hide file tree
Showing 3 changed files with 329 additions and 5 deletions.
16 changes: 11 additions & 5 deletions src/components/services/content/ServiceView.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import WebviewErrorHandler from './ErrorHandlers/WebviewErrorHandler';
import ServiceDisabled from './ServiceDisabled';
import ServiceRestricted from './ServiceRestricted';
import ServiceWebview from './ServiceWebview';
import WebControlsScreen from '../../../features/webControls/containers/WebControlsScreen';

export default @observer class ServiceView extends Component {
static propTypes = {
Expand Down Expand Up @@ -137,11 +138,16 @@ export default @observer class ServiceView extends Component {
type={service.restrictionType}
/>
) : (
<ServiceWebview
service={service}
setWebviewReference={setWebviewReference}
detachService={detachService}
/>
<>
{service.recipe.id === 'franz-custom-website' && (
<WebControlsScreen service={service} />
)}
<ServiceWebview
service={service}
setWebviewReference={setWebviewReference}
detachService={detachService}
/>
</>
)}
</>
)}
Expand Down
190 changes: 190 additions & 0 deletions src/features/webControls/components/WebControls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { observer } from 'mobx-react';
import injectSheet from 'react-jss';
import { Icon } from '@meetfranz/ui';

import {
mdiReload, mdiArrowRight, mdiArrowLeft, mdiHomeOutline,
} from '@mdi/js';

const styles = theme => ({
root: {
background: theme.colorBackground,
position: 'relative',
borderLeft: [1, 'solid', theme.todos.todosLayer.borderLeftColor],
zIndex: 300,
height: 50,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
padding: [0, 20],

'& + div': {
height: 'calc(100% - 50px)',
},
},
button: {
width: 30,
height: 50,
transition: 'opacity 0.25s',

'&:hover': {
opacity: 0.8,
},

'&:disabled': {
opacity: 0.5,
},
},
icon: {
width: '20px !important',
height: 20,
marginTop: 5,
},
input: {
marginBottom: 0,
height: 'auto',
marginLeft: 10,
flex: 1,
border: 0,
padding: [4, 10],
borderRadius: theme.borderRadius,
background: theme.inputBackground,
color: theme.inputColor,
},
inputButton: {
color: theme.colorText,
},
});

@injectSheet(styles) @observer
class WebControls extends Component {
static propTypes = {
classes: PropTypes.object.isRequired,
goHome: PropTypes.func.isRequired,
canGoBack: PropTypes.bool.isRequired,
goBack: PropTypes.func.isRequired,
canGoForward: PropTypes.bool.isRequired,
goForward: PropTypes.func.isRequired,
reload: PropTypes.func.isRequired,
url: PropTypes.string.isRequired,
navigate: PropTypes.func.isRequired,
}

static getDerivedStateFromProps(props, state) {
const { url } = props;
const { editUrl } = state;

if (!editUrl) {
return {
inputUrl: url,
editUrl: state.editUrl,
};
}
}

inputRef = React.createRef();

state = {
inputUrl: '',
editUrl: false,
}

render() {
const {
classes,
goHome,
canGoBack,
goBack,
canGoForward,
goForward,
reload,
url,
navigate,
} = this.props;

const {
inputUrl,
editUrl,
} = this.state;

return (
<div className={classes.root}>
<button
onClick={goHome}
type="button"
className={classes.button}
>
<Icon
icon={mdiHomeOutline}
className={classes.icon}
/>
</button>
<button
onClick={goBack}
type="button"
className={classes.button}
disabled={!canGoBack}
>
<Icon
icon={mdiArrowLeft}
className={classes.icon}
/>
</button>
<button
onClick={goForward}
type="button"
className={classes.button}
disabled={!canGoForward}
>
<Icon
icon={mdiArrowRight}
className={classes.icon}
/>
</button>
<button
onClick={reload}
type="button"
className={classes.button}
>
<Icon
icon={mdiReload}
className={classes.icon}
/>
</button>
<input
value={editUrl ? inputUrl : url}
className={classes.input}
onChange={event => this.setState({
inputUrl: event.target.value,
})}
onFocus={(event) => {
event.target.select();
this.setState({
editUrl: true,
});
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
this.setState({
editUrl: false,
});
navigate(inputUrl);
this.inputRef.current.blur();
} else if (event.key === 'Escape') {
this.setState({
editUrl: false,
inputUrl: url,
});
event.target.blur();
}
}}
ref={this.inputRef}
/>
</div>
);
}
}

export default WebControls;
128 changes: 128 additions & 0 deletions src/features/webControls/containers/WebControlsScreen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React, { Component } from 'react';
import { observer, inject } from 'mobx-react';
import PropTypes from 'prop-types';

import { autorun, observable } from 'mobx';
import WebControls from '../components/WebControls';
import ServicesStore from '../../../stores/ServicesStore';
import Service from '../../../models/Service';

const URL_EVENTS = [
'load-commit',
// 'dom-ready',
'will-navigate',
'did-navigate',
'did-navigate-in-page',
];

@inject('stores', 'actions') @observer
class WebControlsScreen extends Component {
@observable url = '';

@observable canGoBack = false;

@observable canGoForward = false;

webview = null;

autorunDisposer = null;

componentDidMount() {
const { service } = this.props;

this.autorunDisposer = autorun(() => {
if (service.isAttached) {
this.webview = service.webview;

URL_EVENTS.forEach((event) => {
this.webview.addEventListener(event, (e) => {
if (!e.isMainFrame) return;

this.url = e.url;
this.canGoBack = this.webview.canGoBack();
this.canGoForward = this.webview.canGoForward();
});
});
}
});
}

componentWillUnmount() {
this.autorunDisposer();
}

goHome() {
const { reloadActive } = this.props.actions.service;

if (!this.webview) return;

reloadActive();
}

reload() {
if (!this.webview) return;

this.webview.reload();
}

goBack() {
if (!this.webview) return;

this.webview.goBack();
}

goForward() {
if (!this.webview) return;

this.webview.goForward();
}

navigate(newUrl) {
if (!this.webview) return;

let url = newUrl;

try {
url = new URL(url).toString();
} catch (err) {
// eslint-disable-next-line no-useless-escape
if (url.match(/^((?!-))(xn--)?[a-z0-9][a-z0-9-_]{0,61}[a-z0-9]{0,1}\.(xn--)?([a-z0-9\-]{1,61}|[a-z0-9-]{1,30}\.[a-z]{2,})$/)) {
url = `http://${url}`;
} else {
url = `https://www.google.com/search?query=${url}`;
}
}

this.webview.loadURL(url);
this.url = url;
}

render() {
return (
<WebControls
goHome={() => this.goHome()}
reload={() => this.reload()}
canGoBack={this.canGoBack}
goBack={() => this.goBack()}
canGoForward={this.canGoForward}
goForward={() => this.goForward()}
navigate={url => this.navigate(url)}
url={this.url}
/>
);
}
}

export default WebControlsScreen;

WebControlsScreen.wrappedComponent.propTypes = {
service: PropTypes.instanceOf(Service).isRequired,
stores: PropTypes.shape({
services: PropTypes.instanceOf(ServicesStore).isRequired,
}).isRequired,
actions: PropTypes.shape({
service: PropTypes.shape({
reloadActive: PropTypes.func.isRequired,
}).isRequired,
}).isRequired,
};

1 comment on commit f8fbaad

@leandrogehlen
Copy link

@leandrogehlen leandrogehlen commented on f8fbaad Oct 24, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is necessary to other services like github and gitlab.
Please adde option in service configuration to use this feature.
something like:

  "id": "github",
  "name": "Github",
  "version": "1.0.1",
  "description": "Github",
  "main": "index.js",
  "config": {
    "serviceURL": "https://github.com",
    "hasNotificationSound": false,
    "webview": "webcontrols"
  }

Please sign in to comment.