123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- import * as React from 'react';
-
- import util from './utils';
- import Notif from './AlarmNotif';
- import Alarms from './Alarms';
- import ClockDisp from './ClockDisp';
-
- import './Clock.css';
-
- /* A clock UI for a websocket server handling timezones & alarms */
- class Clock extends React.Component {
- constructor(props:{ wsurl:string // Websocket URL
- }) {
- super(props)
-
- // Checking cookies in order to find an existing SESSION
- let decoded = '; '+decodeURIComponent(document.cookie)
- let cookies = decoded.split('; SESSION=')
- let session_id = 'noid'
- if(cookies.length == 2) {
- session_id = cookies[1].split(';')[0]
- }
-
- this.state = {ws: null, // Stores the websocket instance
- time: '', // Stores time received from ws
- changetz: false, // True when change tz form displayed
- tzname: '', // The current tz name
- new_tz: '', // tz form input value
-
- alarms: {}, // alarms received from ws
- err: '', // error zone content
- session_id: session_id, // session id
- }
-
- this.handleSubmitTz = this.handleSubmitTz.bind(this)
- this.handleChangeTz = this.handleChangeTz.bind(this)
- this.alrmUpdate = this.alrmUpdate.bind(this)
- this.wsConnect = this.wsConnect.bind(this)
-
- }
-
- render() {
- let newtz_lbl = 'Change timezone';
- let newtz = <button onClick={this.handleSubmitTz}>{newtz_lbl}</button>;
-
- if(this.state.changetz) {
- newtz = (
- <form onSubmit={this.handleSubmitTz}>
- <input
- type="submit"
- value={newtz_lbl}
- />
- <br/>
- <label
- htmlFor="new-tz"
- >
- <a
- href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List"
- target="_blank"
- >
- Timezone name
- </a>
- </label>
- <input
- id="new-tz"
- onChange={this.handleChangeTz}
- value={this.state.new_tz}
- placeholder="Continent/City"
- />
- </form>
- );
- }
-
- return (<>
- <ClockDisp
- time={this.state.time}
- tzname={this.state.tzname}
- />
- <div className="NewTz">
- {newtz}
- </div>
- <Alarms
- alarms={this.state.alarms}
- ws={this.state.ws}
- alrmUpd={this.alrmUpdate}
- />
- <p>{this.state.err}</p>
- </>
- );
- }
-
- componentDidMount() {
- this.wsConnect() // Connect to websocket after clock mount
- }
-
- /* Tz edit input form submit handler */
- handleSubmitTz(evt) {
- evt.preventDefault();
- if(!this.state.changetz) {
- this.setState({changetz:true})
- return;
- }
- if(this.state.new_tz.length == 0) {
- this.setState({changetz:false})
- return;
- }
- this.state.ws.send('tzset '+this.state.new_tz)
- this.setState({changetz:false, new_tz:''})
- this.alrmUpdate();
- return;
- }
-
- /* Tz edit input change handler */
- handleChangeTz(evt) {
- this.setState({new_tz:evt.target.value})
- }
-
- /* Send an alarm listing request using the websocket
- * Note : actual update will be done onmessage on ws when an
- * OK:<JSON> message will be received
- */
- alrmUpdate() {
- this.state.ws.send('alarm list --all')
- }
-
- /* Connect/reconnect the websocket */
- wsConnect() {
- try {
- var ws = new WebSocket(this.props.wsurl);
- } catch(expt) {
- if(expt !== null) {
- console.log(expt)
- }
- return this.wsConnect()
- }
-
- /* On connection open send the session ID */
- ws.onopen = (evt) => {
- this.state.ws.send(this.state.session_id)
- this.setState({err:''})
- }
-
- /* Incoming messages handler
- * Handles the different type of server messages :
- * - SESSION:<SESSION_ID> : indicating the session ID to send on next connection
- * - OK:[<data>] : on command success with data JSON array on alarm listing
- * - ERR:<REASON> : on command error
- * - TZN:<TZNAME> : indicating the tz name
- * - ALRM:<NAME>: indicating an alarm is ringing
- */
- ws.onmessage = (message_evt) => {
- var msg = message_evt.data
- if(msg.startsWith('SESSION:')) {
- let session_id = msg.substring(8, msg.length)
- let expire = new Date();
-
- expire.setTime(expire.getTime()+(3600*24*1000))
-
- let cookie = 'SESSION='+session_id
- cookie += '; expires='+expire.toUTCString()
- cookie += '; path=/';
- cookie += '; SameSite=strict';
- document.cookie = cookie
-
- if(session_id != this.state.session_id) {
- console.log('New session')
- let tzname = Intl.DateTimeFormat().resolvedOptions().timeZone;
- this.state.ws.send('alarm add alrm1')
- this.state.ws.send('tzset '+util.escape_arg(tzname))
- } else {
- console.log('Restoring session '+session_id)
- }
- this.alrmUpdate()
-
- } else if(msg.startsWith('OK:')) {
- msg = msg.substring(3, msg.length)
- if(msg.length > 0) {
- let alarms = JSON.parse(msg)
- this.setState({alarms:alarms})
- for(let name in alarms) {
- if(alarms[name].ringing) {
- Notif.set(name,
- alarms[name].time)
- } else {
- Notif.unset(name)
- }
- }
- }
- }
- else if(msg.startsWith('ERR:')) {
- this.setState({err:msg})
- } else if (msg.startsWith('TZN:')) {
- msg = msg.substring(4, msg.length)
- this.setState({tzname:msg})
- } else if (msg.startsWith('ALRM:')) {
- msg = msg.substring(5, msg.length)
- this.alrmUpdate()
- } else {
- this.setState({time:msg})
- }
- }
-
- /* Websocket onclose handler : attempt to reconnect after 2s */
- ws.onclose = (evt) => {
- this.setState({ err: 'Connection lost, reconnecting...',
- ws:null})
- setTimeout( () => {
- this.wsConnect()
- }, 2000)
- }
-
- this.setState({ws:ws})
- }
- }
-
- export default Clock;
|