Combine home and notification page and store ID in local storage

This commit is contained in:
Tanner Collin 2017-01-09 14:38:15 -07:00
parent 59ad62e2af
commit 86f23ed1a4
8 changed files with 246 additions and 270 deletions

View File

@ -29,7 +29,7 @@
"moment": "^2.17.1", "moment": "^2.17.1",
"qrcode.react": "^0.6.1", "qrcode.react": "^0.6.1",
"react": "^0.13.0", "react": "^0.13.0",
"react-router": "^0.13.3", "react-router": "^2.0.0",
"shortid": "^2.2.6", "shortid": "^2.2.6",
"socket.io": "^1.7.2", "socket.io": "^1.7.2",
"socket.io-client": "^1.7.2" "socket.io-client": "^1.7.2"

View File

@ -6,7 +6,7 @@ body {
background: #84DCCF; background: #84DCCF;
text-align: center; text-align: center;
box-shadow: 0 0 7px #555; box-shadow: 0 0 7px #555;
margin-bottom: 2.0rem; margin-bottom: 2.5rem;
} }
.title { .title {
@ -40,6 +40,11 @@ body {
color: #cc0000; color: #cc0000;
} }
.storSupport p {
background: rgba(132, 220, 207, 0.2);
padding: 1rem;
}
code { code {
white-space: normal; white-space: normal;
} }

View File

@ -1,33 +0,0 @@
import React from 'react';
import Router, { RouteHandler } from 'react-router';
class App extends React.Component {
constructor( props, context ) {
super();
this.publishRouter( context.router );
}
render() {
let params = this.context.router.getCurrentParams();
return (
<RouteHandler {...params} />
);
}
publishRouter( router ){
var routes = {};
// Use route names as constants
router.routes[0].childRoutes.forEach( function( r ){
routes[ r.name ] = r.path;
});
// Render the router accessible without contexts
Router.currentRouter = router;
Router.routes = routes;
}
}
App.contextTypes = {
router: React.PropTypes.func
};
export default App;

View File

@ -1,10 +1,9 @@
import React from 'react'; import React from 'react'
import Router from 'react-router'; import { Router, Route, browserHistory } from 'react-router'
import routes from './routes'; import Site from './ui/Site';
Router.run( routes, Router.HistoryLocation, function( Handler ){ React.render((
React.render( <Router history={browserHistory}>
React.createElement( Handler ), <Route path="/*" component={Site} />
document.getElementById('root') </Router>
); ), document.getElementById('root'))
});

View File

@ -1,13 +0,0 @@
'use strict';
import React from 'react';
import Router, {Route, NotFoundRoute, DefaultRoute} from 'react-router';
import App from './App';
import Site from './ui/Site';
var routes = (
<Route name="app" path="/*" handler={ App } >
<DefaultRoute name="site" handler={ Site } />
</Route>
);
export default routes;

View File

@ -1,14 +1,130 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import Shortid from 'shortid'; import io from 'socket.io-client';
import { Router, Route, Link } from 'react-router'; import QRCode from 'qrcode.react';
export default class Home extends React.Component { export default class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
supported: false,
registration: null,
haveperm: false,
connected: false,
socket: io.connect()
}
}
componentDidMount() {
this.connect();
this.checksupport();
}
componentWillUnmount() {
this.setState({socket: this.state.socket.removeAllListeners()});
}
connect() {
let socket = this.state.socket;
let room = this.props.id;
socket.on('connect', () => {
socket.emit('room', room);
this.setState({connected: true});
socket.on('disconnect', () => {
this.setState({connected: false});
});
});
socket.on('message', (data) => {
console.log("Notification: " + data);
this.sendNotification(data);
});
}
sendNotification(data) {
let title = data || 'Received a notification!';
let options = {
body: 'Notification from Notica',
icon: 'img/icon.png',
iconUrl: 'img/icon.png',
vibrate: [200, 100, 200]
};
if (this.state.registration) {
console.log(this.state.registration.showNotification(title, options));
} else {
console.log(new Notification(title, options));
}
}
checksupport() {
let supported = ('Notification' in window);
this.setState({supported: supported});
if (supported) {
Notification.requestPermission(permission => {
this.checkperm(permission);
try {
navigator.serviceWorker.register('/js/sw.js').then((reg) => {
this.setState({registration: reg});
});
} catch (e) { // If we are on a browser without serviceWorker
this.setState({registration: false});
}
}.bind(this));
}
}
checkperm(permission) {
if (permission === 'granted') {
this.setState({haveperm: true});
}
else {
this.setState({haveperm: false});
}
}
render(){ render(){
let id = this.props.urlid || Shortid.generate(); let id = this.props.id;
let storSupport = this.props.storSupport;
let supported = this.state.supported;
let haveperm = this.state.haveperm;
let connected = this.state.connected;
return ( return (
<div className="container"> <div className="container">
<div className="row">
<div className="twelve columns">
{ supported || <div className="error"><p>
<i className="fa fa-times" aria-hidden="true"></i> This browser does not support desktop notifications.
</p></div>}
{ !haveperm && supported && <div>
<p>
Please give this site permission to display notifications.
<br />
<a className="button" href="javascript:void(0)" onClick={() => this.checkperm(Notification.permission)}>
Check Again
</a>
</p>
</div>}
{ !connected && supported && <div className="error">
<p>
<i className="fa fa-times" aria-hidden="true"></i> Unable to connect to the Notica server.
<br />
Attempting to reconnect...
</p>
</div>}
{ haveperm && connected && supported && <p>
<i className="fa fa-check" aria-hidden="true"></i> This page is monitoring for notifications.
</p>}
</div>
</div>
<div className="row"> <div className="row">
<div className="twelve columns"> <div className="twelve columns">
<h4>Usage</h4> <h4>Usage</h4>
@ -21,7 +137,7 @@ export default class Home extends React.Component {
<div className="six columns"> <div className="six columns">
<p><code>$ long-running-command; notica Finished!</code></p> <p><code>$ long-running-command; notica Finished!</code></p>
<p> <p>
This will wait until the first command completes before running Notica. That way you can go do other things while your long task runs. Then you will recieve a notification on any devices that have the Notica website open. This will wait until the first command completes before running Notica. That way you can go do other things while your long task runs. Then you will recieve a notification on any devices that have the Notica website open with your unique ID.
</p> </p>
</div> </div>
<div className="six columns"> <div className="six columns">
@ -37,7 +153,7 @@ export default class Home extends React.Component {
$ echo 'notica() &#123; curl --data "d:$*" https://notica.us/{id} ; &#125;' >> ~/.bashrc && source ~/.bashrc $ echo 'notica() &#123; curl --data "d:$*" https://notica.us/{id} ; &#125;' >> ~/.bashrc && source ~/.bashrc
</code> </code>
</p> </p>
<p>Go to this link to receive your notifications (bookmark it since it's yours): <Link to={'/' + id}>https://notica.us/{id}</Link></p> <p>Now open this page on any devices you want to receive the notifications on: <a href={'https://notica.us/' + id}>{'https://notica.us/' + id}</a></p>
<h4>Setup</h4> <h4>Setup</h4>
<p>Curl is required to use Notica.</p> <p>Curl is required to use Notica.</p>
@ -52,10 +168,66 @@ export default class Home extends React.Component {
<code>$ source .bashrc</code> <code>$ source .bashrc</code>
</p> </p>
<p> <p>
All done! Now go to this link (bookmark it since it's yours): <br /> All done! Now open this page on any devices you want to receive the notifications on: <a href={'https://notica.us/' + id}>{'https://notica.us/' + id}</a><br />
<Link to={'/' + id}>https://notica.us/{id}</Link>
</p> </p>
{ storSupport && <div className="storSupport"><p>
<i className="fa fa-info-circle" aria-hidden="true"></i> Notica uses Local Storage to keep track of your unique ID. If you would like to generate a new random ID, <a href="/clear">click here</a>.
</p></div>}
</div>
</div>
<div className="row">
<div className="six columns">
<h4>Examples</h4>
<p>Here are some different ways to use Notica:</p>
<p>
Just run it from your terminal: <br />
<code>
$ notica
</code>
</p>
<p>
Add a custom message: <br />
<code>
$ notica Hello world!
</code>
</p>
<p>
Get an alert when a command finishes: <br />
<code>
$ sudo apt-get update; notica Done!
</code>
</p>
<p>
Get an alert when a command succeeds: <br />
<code>
$ make all && notica Success!
</code>
</p>
<p>
Get an alert when a command fails: <br />
<code>
$ make all || notica Failed!
</code>
</p>
</div>
<div className="six columns">
<h4>Tips</h4>
<p>Bookmark this page! It is unique to the function in your <code className="smallcode">.bashrc</code> file.
Notifications will be sent to all open pages with the same ID code in the URL.</p>
<p>
Use quotes on messages with special characters: <br />
<code>
$ notica "This is awesome :)"
</code>
</p>
<p>
Open this page on your phone:
<center><QRCode value={'https://notica.us/' + id} /></center>
</p>
</div>
</div>
<div className="row">
<div className="twelve columns">
<h4>About</h4> <h4>About</h4>
<p> <p>
Notica was written by <a href="http://tannercollin.com" target="_blank">Tanner Collin</a> after he got tired of checking if his commands were done running. Notica was written by <a href="http://tannercollin.com" target="_blank">Tanner Collin</a> after he got tired of checking if his commands were done running.

View File

@ -1,189 +0,0 @@
'use strict';
import React from 'react';
import io from 'socket.io-client';
import { Router, Route, Link } from 'react-router';
import QRCode from 'qrcode.react';
export default class NotifPage extends React.Component {
constructor(props) {
super(props);
this.state = {
supported: false,
registration: null,
haveperm: false,
connected: false,
socket: io.connect()
}
}
componentDidMount() {
this.connect();
this.checksupport();
}
componentWillUnmount() {
this.setState({socket: this.state.socket.removeAllListeners()});
}
connect() {
let socket = this.state.socket;
let room = this.props.urlid;
socket.on('connect', () => {
socket.emit('room', room);
this.setState({connected: true});
socket.on('disconnect', () => {
this.setState({connected: false});
});
});
socket.on('message', (data) => {
console.log("Notification: " + data);
this.sendNotification(data);
});
}
sendNotification(data) {
let title = data || 'Received a notification!';
let options = {
body: 'Notification from Notica',
icon: 'img/icon.png',
iconUrl: 'img/icon.png',
vibrate: [200, 100, 200]
};
if (this.state.registration) {
console.log(this.state.registration.showNotification(title, options));
} else {
console.log(new Notification(title, options));
}
}
checksupport() {
let supported = ('Notification' in window);
this.setState({supported: supported});
if (supported) {
Notification.requestPermission(permission => {
this.checkperm(permission);
try {
navigator.serviceWorker.register('/js/sw.js').then((reg) => {
this.setState({registration: reg});
});
} catch (e) { // If we are on a browser without serviceWorker
this.setState({registration: false});
}
}.bind(this));
}
}
checkperm(permission) {
if (permission === 'granted') {
this.setState({haveperm: true});
}
else {
this.setState({haveperm: false});
}
}
render() {
let supported = this.state.supported;
let haveperm = this.state.haveperm;
let connected = this.state.connected;
let urlid = this.props.urlid;
return (
<div className="container">
<div className="row">
<div className="twelve columns">
<h4>Notification Page</h4>
{ supported || <div className="error"><p>
<i className="fa fa-times" aria-hidden="true"></i> This browser does not support desktop notifications.
</p></div>}
{ !haveperm && supported && <div>
<p>
Please give this site permission to display notifications.
<br />
<a className="button" href="#" onClick={() => this.checkperm(Notification.permission)}>
Check Again
</a>
</p>
</div>}
{ !connected && supported && <div className="error">
<p>
<i className="fa fa-times" aria-hidden="true"></i> Unable to connect to the Notica server.
<br />
Attempting to reconnect...
</p>
</div>}
{ haveperm && connected && supported && <p>
<i className="fa fa-check" aria-hidden="true"></i> This page is monitoring for notifications.
</p>}
</div>
</div>
<div className="row">
<div className="six columns">
<h4>Examples</h4>
<p>Here are some different ways to use Notica:</p>
<p>
Just run it from your terminal: <br />
<code>
$ notica
</code>
</p>
<p>
Add a custom message: <br />
<code>
$ notica Hello world!
</code>
</p>
<p>
Get an alert when a command finishes: <br />
<code>
$ sudo apt-get update; notica Done!
</code>
</p>
<p>
Get an alert when a command succeeds: <br />
<code>
$ make all && notica Success!
</code>
</p>
<p>
Get an alert when a command fails: <br />
<code>
$ make all || notica Failed!
</code>
</p>
</div>
<div className="six columns">
<h4>Tips</h4>
<p>Bookmark this page! It is unique to the function in your <code className="smallcode">.bashrc</code> file.
Notifications will be sent to all open pages with the same ID code in the URL.</p>
<p>
Use quotes on messages with special characters: <br />
<code>
$ notica "This is awesome :)"
</code>
</p>
<p>
Need to set Notica up again? <br />
<Link to={'/home/' + urlid}>
Click here to go back to the instructions.
</Link>
</p>
<p>
Open this page on your phone:
<center><QRCode value={'https://notica.us/' + urlid} /></center>
</p>
</div>
</div>
</div>
);
}
}

View File

@ -1,37 +1,72 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import Home from './Home'; import Home from './Home';
import NotifPage from './NotifPage';
import Error from './Error'; import Error from './Error';
import Shortid from 'shortid'; import Shortid from 'shortid';
import { Router, Route, Link } from 'react-router'; import { Link, browserHistory } from 'react-router';
export default class Site extends React.Component { export default class Site extends React.Component {
render(){ constructor(props) {
let url = this.props.splat; super(props);
let page = null;
let id = ''; this.state = {
page: null,
id: '',
storSupport: (typeof localStorage !== 'undefined')
}
}
componentWillMount() {
this.setPage();
}
componentDidUpdate(prevProps) {
let oldUrl = prevProps.params.splat;
let newUrl = this.props.params.splat;
if (newUrl !== oldUrl) this.setPage();
}
setId(url) {
if (this.state.storSupport) {
if (localStorage.getItem('id')) {
this.state.id = url || localStorage.getItem('id');
} else {
this.state.id = url || Shortid.generate();
}
localStorage.setItem('id', this.state.id);
} else {
this.state.id = url || Shortid.generate();
}
}
setPage() {
let url = this.props.params.splat;
if (url == 'clear') {
localStorage.clear();
url = '';
}
if (url == '') { if (url == '') {
page = <Home />; this.setId();
} browserHistory.push('/' + this.state.id);
else if (url.substring(0, 4) == 'home') { this.state.page = <Home id={this.state.id} storSupport={this.state.storSupport} />;
id = url.substring(5);
page = <Home urlid={id} />;
} }
else if (Shortid.isValid(url)) { else if (Shortid.isValid(url)) {
id = url; this.setId(url);
page = <NotifPage urlid={url} />; this.state.page = <Home id={this.state.id} storSupport={this.state.storSupport} />;
} }
else { else {
page = <Error />; this.state.page = <Error />;
}
} }
render(){
return ( return (
<div> <div>
<div className="hero"> <div className="hero">
<div className="title"> <div className="title">
<Link to={'/home/' + id}> <Link to={'/' + this.state.id}>
<img src="/img/logo.svg" /> <img src="/img/logo.svg" />
<span className="name">Notica</span> <span className="name">Notica</span>
</Link> </Link>
@ -40,7 +75,7 @@ export default class Site extends React.Component {
Send browser notifications from your terminal. No installation. No registration. Send browser notifications from your terminal. No installation. No registration.
</div> </div>
</div> </div>
{page} {this.state.page}
</div> </div>
); );
} }