luminati-proxy-stripped
Version:
A configurable local proxy for luminati.io
148 lines (139 loc) • 4.92 kB
JavaScript
// LICENSE_CODE ZON ISC
; /*jslint react:true, es6:true*/
import React from 'react';
import ReactDOM from 'react-dom';
import {Modal, Tooltip} from './common.js';
import $ from 'jquery';
import {ga_event} from './util.js';
import Pure_component from '../../www/util/pub/pure_component.js';
import classnames from 'classnames';
class Notif_center extends Pure_component {
constructor(props){
super(props);
this.state = {loaded: false, notifs: []};
}
componentWillMount(){
this.setdb_on('head.version', ver=>this.setState({ver}));
this.setdb_on('head.consts', consts=>{
if (!consts||!consts.notifs)
return;
const notifs = consts.notifs.filter(
n=>!n.version||this.state.ver>=n.version);
this.setState({notifs, loaded: true});
});
}
componentDidMount(){
const _this = this;
$('#notif_modal').on('hidden.bs.modal', ()=>{
_this.mark_read_local(); });
}
mark_read_local(){
const all_read = this.state.notifs.map(n=>{
if (n.status=='new')
n.status = 'read';
return n;
});
this.setState({notifs: all_read});
}
mark_read(){
const _this = this;
this.etask(function*(){
const updated = _this.state.notifs.filter(n=>n.status=='new')
.map(u=>({id: u._id, status: 'read'}));
// XXX krzysztof: switch fetch->ajax
yield window.fetch('/api/update_notifs', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({notifs: updated}),
});
});
}
open(){
if (this.state.loaded)
{
ga_event('notif-center', 'open');
this.mark_read();
$('#notif_modal').modal();
}
}
message_clicked(message){
ga_event('notif-center', 'notif-clicked', message.title);
this.etask(function*(){
// XXX krzysztof: switch fetch->ajax
yield window.fetch('/api/update_notifs', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(
{notifs: [{id: message._id, status: 'clicked'}]}),
});
});
if (message.code)
{
$('#notif_modal').modal('hide');
setTimeout(()=>eval(message.code), 500);
}
}
render(){
const number = this.state.notifs.filter(n=>n.status=='new').length;
const tip = 'Notification center: you will receive updates on new'
+' features in LPM here';
return <div className="notif">
<Modal_portal>
<div className="notif_modal">
<Modal id="notif_modal" title="Messages:"
no_cancel_btn>
<div className="notifs">
{!this.state.notifs.length && <No_messages/>}
{this.state.notifs.length &&
<Messages notifs={this.state.notifs}
on_click={this.message_clicked.bind(this)}/>
}
</div>
</Modal>
</div>
</Modal_portal>
<Tooltip title={tip} placement="bottom">
<div onClick={this.open.bind(this)} className="icon">
<Circle_icon number={number}/>
</div>
</Tooltip>
</div>;
}
}
const Circle_icon = ({number})=>{
if (!number)
return null;
return <div className="circle_wrapper">
<div className="circle">{number}</div>
</div>;
};
const No_new_messages = ()=>
<h4 className="no_messages">You have no new messages.</h4>;
const No_messages = ()=>
<h4 className="no_messages">You don't have any messages yet.</h4>;
const Messages = ({notifs, on_click})=>{
const new_messages = notifs.filter(n=>n.status=='new').length;
return <div>
{!new_messages && <No_new_messages/>}
{notifs.map(m=>
<Message on_click={()=>on_click(m)} clickable={!!m.code}
key={m.msg_id} {...m}/>
)}
</div>;
};
const Message = ({title, message, status, clickable, on_click})=>{
const classes = classnames('message', {unread: status=='new', clickable});
if (status=='new')
title = 'NEW! '+title;
return <div onClick={on_click} className={classes}>
<div className="subject">{title}</div>
<div className="text">{message}</div>
</div>;
};
class Modal_portal extends Pure_component {
render(){
return ReactDOM.createPortal(this.props.children,
document.getElementById('notif_react_modal'));
}
}
export default Notif_center;