better error handling and reporting

This commit is contained in:
or-else
2017-09-17 18:36:01 -07:00
parent d2a226852b
commit 8f13d2c856
3 changed files with 201 additions and 102 deletions
+42 -8
View File
@@ -505,17 +505,52 @@ table.permission-editor td.checkbox {
/* Area which displays an error message */ /* Area which displays an error message */
#alert-box { .alert-box {
background-color: transparent; position: relative;
height: 0; display: none;
overflow: hidden; overflow: hidden;
padding: 1rem 1.5rem 1rem 0.5rem;
} }
#alert-box.error-alert { .alert-box .cancel {
padding: 0.5rem; position: absolute;
right: 0.25rem;
top: 0.5rem;
}
.alert-box.error {
display: block;
background-color: #ffcdd2; background-color: #ffcdd2;
border-color: #d32f2f; border-bottom: 1px solid #ffbfc8;
color: #b71c1c; color: #b71c1c;
height: auto; }
.alert-box.warning {
display: block;
background-color: #fff9c4;
border-bottom: 1px solid #fff59d;
color: #607d8b;
}
.alert-box .cancel .material-icons {
font-size: 1rem;
}
.alert-box.error .cancel .material-icons {
color: #b71c1c;
}
.alert-box.warning .cancel .material-icons {
color: #757575;
}
.alert-box .icon {
display: inline-block;
vertical-align: middle;
margin-right: 0.5rem;
}
.alert-box .icon .material-icons {
font-size: 1.5rem;
}
.alert-box.warning .icon .material-icons {
color: #fbc02d;
}
.alert-box.error .icon .material-icons {
color: #ef5350;
} }
/* List of contacts */ /* List of contacts */
@@ -1488,7 +1523,6 @@ ul.tabbar li.active > a:after {
border-top: 0.35rem solid #1e88e5; border-top: 0.35rem solid #1e88e5;
width: 2rem; width: 2rem;
height: 2rem; height: 2rem;
-webkit-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite; animation: spin 1.5s linear infinite;
} }
-4
View File
@@ -521,8 +521,6 @@ Sample JSON representation of the text above:
return a.at - b.at; return a.at - b.at;
}); });
console.log(spans);
// Denormalize entities into spans. Create a copy of the objects to leave // Denormalize entities into spans. Create a copy of the objects to leave
// original Drafty object unchanged. // original Drafty object unchanged.
spans = spans.map(function(s) { spans = spans.map(function(s) {
@@ -536,8 +534,6 @@ Sample JSON representation of the text above:
return {tp: tp, data: data, at: s.at, len: s.len}; return {tp: tp, data: data, at: s.at, len: s.len};
}); });
console.log(spans);
return forEach(txt, 0, txt.length, spans, formatter, context); return forEach(txt, 0, txt.length, spans, formatter, context);
}, },
+159 -90
View File
@@ -15,9 +15,9 @@
<!-- Styles for local elements --> <!-- Styles for local elements -->
<link rel="stylesheet" href="css/base.css" /> <link rel="stylesheet" href="css/base.css" />
<!-- ReactJs scripts --> <!-- ReactJs scripts -->
<script src="https://unpkg.com/react@15.4/dist/react.js"></script> <script src="https://unpkg.com/react@15/dist/react.min.js"></script>
<script src="https://unpkg.com/react-dom@15.4/dist/react-dom.js"></script> <script src="https://unpkg.com/react-dom@15/dist/react-dom.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6.24.0/babel.min.js"></script> <script src="https://unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
<script src="tinode.js"></script> <script src="tinode.js"></script>
<script src="drafty.js"></script> <script src="drafty.js"></script>
</head> </head>
@@ -133,7 +133,7 @@
/* BEGIN Context Menu: popup/dropdown menu */ /* BEGIN Context Menu: popup/dropdown menu */
// Function is used by context menu to set permissions. // Function is used by context menu to set permissions.
function topicPermissionSetter(mode, topicName, uid) { function topicPermissionSetter(mode, topicName, uid, errorHandler) {
var topic = Tinode.getTopic(topicName); var topic = Tinode.getTopic(topicName);
if (!topic) { if (!topic) {
console.log("Topic not found: " + topicName); console.log("Topic not found: " + topicName);
@@ -151,7 +151,14 @@
} else { } else {
am = topic.getAccessMode().updateWant(mode).getWant(); am = topic.getAccessMode().updateWant(mode).getWant();
} }
topic.setMeta({sub: {user: uid, mode: am}}); var instance = this;
topic.setMeta({sub: {user: uid, mode: am}}).catch(function(err) {
if (errorHandler) {
errorHandler(err.message, "err");
} else {
console.log(err);
}
});
} }
// Context menu items. // Context menu items.
@@ -164,6 +171,7 @@
console.log("Topic not found: " + topicName); console.log("Topic not found: " + topicName);
return; return;
} }
topic.delMessagesAll(false).then(function(){ topic.delMessagesAll(false).then(function(){
console.log("refresh message list"); console.log("refresh message list");
}); });
@@ -232,7 +240,7 @@
e.stopPropagation(); e.stopPropagation();
this.props.hide(); this.props.hide();
var item = this.props.items[e.currentTarget.dataset.id]; var item = this.props.items[e.currentTarget.dataset.id];
item.handler(this.props.topic, this.props.user); item.handler(this.props.topic, this.props.user, this.props.onError);
}, },
render: function() { render: function() {
@@ -280,13 +288,17 @@
/* END Popup/dropdown menu */ /* END Popup/dropdown menu */
/* The X menu to be displayed in title bars */ /* The X menu to be displayed in title bars */
var MenuCancel = React.createClass({ class MenuCancel extends React.PureComponent {
render: function() { constructor(props) {
super(props);
}
render() {
return ( return (
<a href="#" onClick={this.props.onCancel}><i className="material-icons">close</i></a> <a href="#" onClick={this.props.onCancel}><i className="material-icons">close</i></a>
); );
} }
}); }
class LoadSpinner extends React.PureComponent { class LoadSpinner extends React.PureComponent {
render() { render() {
@@ -714,31 +726,20 @@
login: this.props.login, login: this.props.login,
password: '', password: '',
hostName: this.props.serverAddress, hostName: this.props.serverAddress,
errorCleared: false
}; };
}, },
clearError: function() {
if (!this.state.errorCleared) {
this.props.onClearError();
this.setState({errorCleared: true});
}
},
handleLoginChange: function(e) { handleLoginChange: function(e) {
this.clearError();
this.setState({login: e.target.value}); this.setState({login: e.target.value});
}, },
handlePasswordChange: function(e) { handlePasswordChange: function(e) {
this.clearError();
this.setState({password: e.target.value}); this.setState({password: e.target.value});
}, },
handleServerAddressChange: function(name) { handleServerAddressChange: function(name) {
this.clearError();
this.setState({hostName: name}); this.setState({hostName: name});
this.props.onServerAddressChange(name); this.props.onServerAddressChange(name);
}, },
handleSubmit: function(e) { handleSubmit: function(e) {
e.preventDefault(); e.preventDefault();
this.setState({errorCleared: false});
this.props.onLogin(this.state.login.trim(), this.state.password.trim()); this.props.onLogin(this.state.login.trim(), this.state.password.trim());
}, },
@@ -1000,20 +1001,11 @@
</div> </div>
); );
} }
}) });
var SidepanelView = React.createClass({ var SidepanelView = React.createClass({
getInitialState: function() {
return {
showErrorText: true
};
},
handleClearErrorText: function() {
this.setState({showErrorText: false});
},
handleLoginRequested: function(login, password) { handleLoginRequested: function(login, password) {
this.props.onLoginRequest(login, password); this.props.onLoginRequest(login, password);
this.setState({showErrorText: true});
}, },
render: function() { render: function() {
var title = null; var title = null;
@@ -1046,17 +1038,16 @@
onNewTopic={this.props.onNewTopic} onNewTopic={this.props.onNewTopic}
onCancel={this.props.onCancel} /> onCancel={this.props.onCancel} />
<SidepanelView.ErrorPanel <ErrorPanel
showError={this.state.showErrorText} level={this.props.errorLevel}
errorText={this.props.errorText} /> text={this.props.errorText} />
{this.props.state === 'login' ? {this.props.state === 'login' ?
<LoginView login={this.props.login} <LoginView login={this.props.login}
disabled={this.props.loginDisabled} disabled={this.props.loginDisabled}
serverAddress={this.props.serverAddress} serverAddress={this.props.serverAddress}
onLogin={this.handleLoginRequested} onLogin={this.handleLoginRequested}
onServerAddressChange={this.props.onConnectionSettings} onServerAddressChange={this.props.onConnectionSettings} /> :
onClearError={this.handleClearErrorText} /> :
this.props.state === 'register' ? this.props.state === 'register' ?
<CreateAccountView <CreateAccountView
@@ -1095,16 +1086,31 @@
} }
}); });
SidepanelView.ErrorPanel = React.createClass({ var ErrorPanel = React.createClass({
getInitialState: function() {
return {
show: false,
};
},
componentWillReceiveProps: function(newProps) {
this.setState({show: !(!newProps.level)});
},
hide: function() {
this.setState({show: false});
},
render: function() { render: function() {
var errorClass = null; var icon = this.props.level == "err" ? "error": "warning";
var errorText = ' ';
if (this.props.errorText && this.props.showError) {
errorClass = "error-alert";
errorText = this.props.errorText;
}
return ( return (
<div id="alert-box" className={errorClass}>{errorText}</div> <div className={this.state.show ?
(this.props.level == "err" ? "alert-box error" : "alert-box warning") :
"alert-box"}>
<div className="icon"><i className="material-icons">{icon}</i></div>
{this.props.text}
<div className="cancel"><MenuCancel onCancel={this.hide} /></div>
</div>
); );
} }
}); });
@@ -1891,8 +1897,6 @@
resetDesc: function(topic) { resetDesc: function(topic) {
var defacs = topic.getDefaultAccess() || {}; var defacs = topic.getDefaultAccess() || {};
var acs = topic.getAccessMode(); var acs = topic.getAccessMode();
console.log("InfoView: reset desc");
console.log(topic.public);
this.setState({ this.setState({
owner: acs && acs.isOwner(), owner: acs && acs.isOwner(),
admin: acs && acs.isAdmin(), admin: acs && acs.isAdmin(),
@@ -2104,6 +2108,10 @@
<MenuCancel onCancel={this.props.onCancel} /> <MenuCancel onCancel={this.props.onCancel} />
</div> </div>
</div> </div>
{this.props.displayMobile ?
<ErrorPanel
level={this.props.errorLevel}
text={this.props.errorText} /> : null}
{this.state.showMemberPanel ? {this.state.showMemberPanel ?
<GroupManager <GroupManager
members={this.state.contactList} members={this.state.contactList}
@@ -2381,6 +2389,7 @@
this.propsChange(nextProps); this.propsChange(nextProps);
}, },
propsChange: function(props) { propsChange: function(props) {
var instance = this;
if (!props || !props.topic) { if (!props || !props.topic) {
this.setState({messages: [], topic: null}); this.setState({messages: [], topic: null});
this.leave(); this.leave();
@@ -2405,7 +2414,9 @@
.withLaterDesc() .withLaterDesc()
.withLaterSub() .withLaterSub()
.withLaterData(MESSAGES_PAGE) .withLaterData(MESSAGES_PAGE)
.build()); .build()).catch(function(err){
instance.props.onError(err.message, "err");
});
} }
// The user switched to the new topic before the timer for // The user switched to the new topic before the timer for
@@ -2425,7 +2436,10 @@
var oldTopic = Tinode.getTopic(this.state.topic); var oldTopic = Tinode.getTopic(this.state.topic);
if (oldTopic) { if (oldTopic) {
if (oldTopic.isSubscribed()) { if (oldTopic.isSubscribed()) {
oldTopic.leave(false); oldTopic.leave(false).catch(function(err) {
// do nothing
console.log(err);
});
} }
oldTopic.onData = undefined; oldTopic.onData = undefined;
oldTopic.onInfo = undefined; oldTopic.onInfo = undefined;
@@ -2454,6 +2468,9 @@
newState.fetchingMessages = true; newState.fetchingMessages = true;
topic.getMessagesPage(MESSAGES_PAGE).then(function() { topic.getMessagesPage(MESSAGES_PAGE).then(function() {
instance.setState({fetchingMessages: false}); instance.setState({fetchingMessages: false});
}).catch(function(err) {
instance.setState({fetchingMessages: false});
instance.props.onError(err.message, "err");
}); });
} }
} }
@@ -2623,6 +2640,10 @@
</a> </a>
</div> </div>
</div> </div>
{this.props.displayMobile ?
<ErrorPanel
level={this.props.errorLevel}
text={this.props.errorText} /> : null}
<LoadSpinner show={this.state.fetchingMessages} /> <LoadSpinner show={this.state.fetchingMessages} />
<div id="messages-panel" ref={this.handleScrollReference}> <div id="messages-panel" ref={this.handleScrollReference}>
<ul id="scroller" className={chatBoxClass}> <ul id="scroller" className={chatBoxClass}>
@@ -2770,6 +2791,8 @@
password: '', password: '',
myUserId: null, myUserId: null,
errorText: '', errorText: '',
errorLevel: null,
liveConnection: navigator.onLine,
topicSelected: '', topicSelected: '',
loginDisabled: false, loginDisabled: false,
subscriptions: {}, subscriptions: {},
@@ -2788,7 +2811,11 @@
}; };
}, },
componentDidMount: function() { componentDidMount: function() {
var instance = this;
window.addEventListener('resize', this.handleResize); window.addEventListener('resize', this.handleResize);
window.addEventListener('online', function(e) { instance.handleOnline(true); });
window.addEventListener('offline', function(e) { instance.handleOnline(false); });
Tinode.enableLogging(true, true); Tinode.enableLogging(true, true);
Tinode.onConnect = this.handleConnected; Tinode.onConnect = this.handleConnected;
Tinode.onDisconnect = this.handleDisconnect; Tinode.onDisconnect = this.handleDisconnect;
@@ -2798,25 +2825,39 @@
tnSetup: function(serverAddress, transport) { tnSetup: function(serverAddress, transport) {
Tinode.setup(APP_NAME, serverAddress, API_KEY, transport); Tinode.setup(APP_NAME, serverAddress, API_KEY, transport);
}, },
componentWillUnmount: function(){ handleResize: function() {
window.removeEventListener('resize', this.handleResize);
},
handleResize: function(){
var mobile = document.documentElement.clientWidth <= MEDIA_BREAKPOINT; var mobile = document.documentElement.clientWidth <= MEDIA_BREAKPOINT;
if (this.state.displayMobile != mobile) { if (this.state.displayMobile != mobile) {
this.setState({displayMobile: mobile}); this.setState({displayMobile: mobile});
} }
}, },
handleOnline: function(online) {
var newState = {liveConnection: online};
if (online) {
this.handleError("", null);
} else {
this.handleError("No connection", "warn");
}
this.setState({liveConnection: online});
},
handleError: function(err, level) {
this.setState({errorText: err, errorLevel: level});
},
// User clicked Login button in the side panel. // User clicked Login button in the side panel.
handleLoginRequest: function(login, password) { handleLoginRequest: function(login, password) {
var instance = this; var instance = this;
this.setState({loginDisabled: true, errorText: '', login: login, password: password}); this.setState({loginDisabled: true, login: login, password: password});
this.handleError("", null);
if (Tinode.isConnected()) { if (Tinode.isConnected()) {
this.doLogin(login, password); this.doLogin(login, password);
} else { } else {
Tinode.connect().catch(function(err) { Tinode.connect().catch(function(err) {
// Socket error // Socket error
instance.setState({loginDisabled: false, errorText: err.message}); instance.setState({loginDisabled: false});
instance.handleError(err.message, "err");
}); });
} }
}, },
@@ -2844,39 +2885,33 @@
if (promise) { if (promise) {
promise.then(function() { promise.then(function() {
instance.handleLoginSuccessful(instance); instance.handleLoginSuccessful(instance);
}) }).catch(function(err) {
.catch(function(err) {
// Login failed, report error // Login failed, report error
instance.setState({loginDisabled: false, errorText: err.message}); instance.setState({loginDisabled: false});
instance.handleError(err.message, "err");
}); });
} }
}, },
handleLoginSuccessful: function(instance) { handleLoginSuccessful: function(instance) {
instance.setState({errorText: ''}); instance.handleError("", null);
// Logged in fine, subscribe to 'me' attaching callbacks from the contacts view. // Logged in fine, subscribe to 'me' attaching callbacks from the contacts view.
var me = Tinode.getMeTopic(); var me = Tinode.getMeTopic();
me.onMetaDesc = instance.tnMeMetaDesc; me.onMetaDesc = instance.tnMeMetaDesc;
instance.setState({sidePanelSelected: "contacts", myUserId: Tinode.getCurrentUserID()}); instance.setState({sidePanelSelected: "contacts", myUserId: Tinode.getCurrentUserID()});
// Subscribe, fetch topic desc, the list of subscriptions. Messages are not fetched. // Subscribe, fetch topic desc, the list of subscriptions. Messages are not fetched.
me.subscribe(me.startMetaQuery().withLaterSub().withDesc().build()); me.subscribe(me.startMetaQuery().withLaterSub().withDesc().build()).catch(function(err){
instance.handleError(err.message, "err");
});
}, },
handleDisconnect: function(err) { handleDisconnect: function(err) {
this.setState({ this.setState({
sidePanelSelected: 'login',
sidePanelTitle: null,
sidePanelAvatar: null,
dialogSelected: null, dialogSelected: null,
myUserId: null, errorText: err && err.message ? err.message : 'Disconnected',
errorText: err && err.message ? err.message : 'disconnected', errorLevel: err && err.message ? "err" : "warn",
topicSelected: '',
loginDisabled: false, loginDisabled: false,
subscriptions: {},
showInfoPanel: false,
infoPanelShowMembers: false,
mobilePanel: 'sidepanel',
contextMenuVisible: false, contextMenuVisible: false,
contextMenuBounds: null, contextMenuBounds: null,
contextMenuClickAt: null, contextMenuClickAt: null,
@@ -2907,11 +2942,14 @@
var fnd = Tinode.getFndTopic(); var fnd = Tinode.getFndTopic();
fnd.onSubsUpdated = this.tnFndSubsUpdated; fnd.onSubsUpdated = this.tnFndSubsUpdated;
fnd.onMetaDesc = this.tnFndMetaDesc; fnd.onMetaDesc = this.tnFndMetaDesc;
var instance = this;
if (fnd.isSubscribed()) { if (fnd.isSubscribed()) {
this.setState({contactsSearchQuery: fnd.private ? fnd.private.toString() : ''}); this.setState({contactsSearchQuery: fnd.private ? fnd.private.toString() : ''});
this.tnFndSubsUpdated(); this.tnFndSubsUpdated();
} else { } else {
fnd.subscribe(fnd.startMetaQuery().withSub().withLaterDesc().build()); fnd.subscribe(fnd.startMetaQuery().withSub().withLaterDesc().build()).catch(function(err){
instance.handleError(err.message, "err");
});
} }
}, },
@@ -2933,10 +2971,13 @@
@param query {Array} is an array of contacts to search for @param query {Array} is an array of contacts to search for
*/ */
handleSearchContacts: function(query) { handleSearchContacts: function(query) {
var fnd = Tinode.getFndTopic(); var fnd = Tinode.getFndTopic();
fnd.setMeta({desc: {private: query}}).then(function(ctrl) { var instance = this;
fnd.getMeta(fnd.startMetaQuery().withSub().withLaterDesc().build()); fnd.setMeta({desc: {private: query}}).then(function(ctrl) {
}); fnd.getMeta(fnd.startMetaQuery().withSub().withLaterDesc().build());
}).catch(function(err){
instance.handleError(err.message, "err");
});
}, },
// User clicked on a contact in the side panel or deleted a contact. // User clicked on a contact in the side panel or deleted a contact.
@@ -2968,14 +3009,19 @@
// User clicked on a message send button. // User clicked on a message send button.
handleSendMessage: function(msg) { handleSendMessage: function(msg) {
var topic = Tinode.getTopic(this.state.topicSelected); var topic = Tinode.getTopic(this.state.topicSelected);
var instance = this;
// Check if the message is formatted. // Check if the message is formatted.
var dft = Drafty.parse(msg); var dft = Drafty.parse(msg);
if (Drafty.isPlainText(dft)) { if (Drafty.isPlainText(dft)) {
// The message is simple text. Sned if as text/plain. // The message is simple text. Sned if as text/plain.
topic.publish(msg); topic.publish(msg).catch(function(err) {
instance.handleError(err.message, "err");
});;
} else { } else {
// Formatted message. Send it as a Drafty object. // Formatted message. Send it as a Drafty object.
topic.publish(dft, undefined, Drafty.getContentType()); topic.publish(dft, undefined, Drafty.getContentType()).catch(function(err) {
instance.handleError(err.message, "err");
});
} }
}, },
// User chose a Sign Up menu item. // User chose a Sign Up menu item.
@@ -2990,21 +3036,24 @@
.then(function() { .then(function() {
return Tinode.createAccountBasic(login_, password_, return Tinode.createAccountBasic(login_, password_,
{public: public_, private: private_, tags: tags_}); {public: public_, private: private_, tags: tags_});
}) }).then(function() {
.then(function() {
instance.handleLoginSuccessful(instance); instance.handleLoginSuccessful(instance);
}) }).catch(function(err) {
.catch(function(err) { instance.handleError(err.message, "err");
instance.setState({errorText: err.message});
}); });
}, },
handleUpdateAccountRequest: function(password, pub, priv) { handleUpdateAccountRequest: function(password, pub, priv) {
var instance = this;
if (pub || priv) { if (pub || priv) {
Tinode.getMeTopic().setMeta({desc: {public: pub, private: priv}}); Tinode.getMeTopic().setMeta({desc: {public: pub, private: priv}}).catch(function(err) {
instance.handleError(err.message, "err");
});
} }
if (password) { if (password) {
Tinode.updateAccountBasic(null, Tinode.getCurrentLogin(), password); Tinode.updateAccountBasic(null, Tinode.getCurrentLogin(), password).catch(function(err) {
instance.handleError(err.message, "err");
});
} }
}, },
// User chose Settings menu item. // User chose Settings menu item.
@@ -3043,7 +3092,6 @@
topic.subscribe(query.build(), setParams).then(function() { topic.subscribe(query.build(), setParams).then(function() {
instance.setState({ instance.setState({
errorText: '',
topicSelected: topic.name, topicSelected: topic.name,
sidePanelSelected: 'contacts', sidePanelSelected: 'contacts',
mobilePanel: 'topic-view', mobilePanel: 'topic-view',
@@ -3051,11 +3099,12 @@
infoPanelShowMembers: peerName ? false : true infoPanelShowMembers: peerName ? false : true
}); });
}).catch(function(err) { }).catch(function(err) {
instance.setState({errorText: err.message}); instance.handleError(err.message, "err");
}); });
}, },
handleTopicUpdateRequest: function(topicName, pub, priv, permissions) { handleTopicUpdateRequest: function(topicName, pub, priv, permissions) {
var instance = this;
var topic = Tinode.getTopic(topicName); var topic = Tinode.getTopic(topicName);
if (topic) { if (topic) {
var params = {}; var params = {};
@@ -3068,11 +3117,14 @@
if (permissions) { if (permissions) {
params.defacs = permissions; params.defacs = permissions;
} }
topic.setMeta({desc: params}); topic.setMeta({desc: params}).catch(function(err) {
instance.handleError(err.message, "err");
});
} }
}, },
handleChangePermissions: function(topicName, mode, uid) { handleChangePermissions: function(topicName, mode, uid) {
var instance = this;
var topic = Tinode.getTopic(topicName); var topic = Tinode.getTopic(topicName);
if (topic) { if (topic) {
var am = topic.getAccessMode(); var am = topic.getAccessMode();
@@ -3083,7 +3135,9 @@
am.updateWant(mode); am.updateWant(mode);
mode = am.getWant(); mode = am.getWant();
} }
topic.setMeta({sub: {user: uid, mode: mode}}); topic.setMeta({sub: {user: uid, mode: mode}}).catch(function(err) {
instance.handleError(err.message, "err");
});
} }
}, },
@@ -3100,6 +3154,8 @@
showInfoPanel: false, showInfoPanel: false,
infoPanelShowMembers: false infoPanelShowMembers: false
}); });
}).catch(function(err) {
instance.handleError(err.message, "err");
}); });
}, },
@@ -3171,9 +3227,12 @@
}); });
} }
var instance = this;
if (removed && removed.length > 0) { if (removed && removed.length > 0) {
removed.map(function(uid) { removed.map(function(uid) {
topic.delSubscription(uid); topic.delSubscription(uid).catch(function(err) {
instance.handleError(err.message, "err");
});;
}); });
} }
}, },
@@ -3194,11 +3253,12 @@
user={this.state.contextMenuUser} user={this.state.contextMenuUser}
items={this.state.contextMenuItems} items={this.state.contextMenuItems}
hide={this.handleHideContextMenu} hide={this.handleHideContextMenu}
onAction={this.handleContextMenuAction} /> onAction={this.handleContextMenuAction}
onError={this.handleError} />
<SidepanelView <SidepanelView
displayMobile={this.state.displayMobile} displayMobile={this.state.displayMobile}
hideSelf={(this.state.displayMobile && this.state.mobilePanel !== 'sidepanel')} hideSelf={this.state.displayMobile && this.state.mobilePanel !== 'sidepanel'}
state={this.state.sidePanelSelected} state={this.state.sidePanelSelected}
title={this.state.sidePanelTitle} title={this.state.sidePanelTitle}
avatar={this.state.sidePanelAvatar} avatar={this.state.sidePanelAvatar}
@@ -3207,6 +3267,7 @@
myUserId={this.state.myUserId} myUserId={this.state.myUserId}
loginDisabled={this.state.loginDisabled} loginDisabled={this.state.loginDisabled}
errorText={this.state.errorText} errorText={this.state.errorText}
errorLevel={this.state.errorLevel}
topicSelected={this.state.topicSelected} topicSelected={this.state.topicSelected}
serverAddress={this.state.serverAddress} serverAddress={this.state.serverAddress}
@@ -3230,7 +3291,6 @@
showContextMenu={this.handleShowContextMenu} /> showContextMenu={this.handleShowContextMenu} />
<MessagesView <MessagesView
onHideMessagesView={this.handleHideMessagesView}
displayMobile={this.state.displayMobile} displayMobile={this.state.displayMobile}
hideSelf={(this.state.displayMobile && hideSelf={(this.state.displayMobile &&
(this.state.mobilePanel !== 'topic-view' || this.state.showInfoPanel))} (this.state.mobilePanel !== 'topic-view' || this.state.showInfoPanel))}
@@ -3238,15 +3298,24 @@
myUserId={this.state.myUserId} myUserId={this.state.myUserId}
serverVersion={this.state.serverVersion} serverVersion={this.state.serverVersion}
serverAddress={this.state.serverAddress} serverAddress={this.state.serverAddress}
errorText={this.state.errorText}
errorLevel={this.state.errorLevel}
onHideMessagesView={this.handleHideMessagesView}
onData={this.tnData} onData={this.tnData}
onError={this.handleError}
showContextMenu={this.handleShowContextMenu} showContextMenu={this.handleShowContextMenu}
sendMessage={this.handleSendMessage} /> sendMessage={this.handleSendMessage} />
{this.state.showInfoPanel ? {this.state.showInfoPanel ?
<InfoView <InfoView
displayMobile={this.state.displayMobile}
topic={this.state.topicSelected} topic={this.state.topicSelected}
foundContacts={this.state.foundContacts} foundContacts={this.state.foundContacts}
myUserId={this.state.myUserId} myUserId={this.state.myUserId}
errorText={this.state.errorText}
errorLevel={this.state.errorLevel}
showMemberPanel={this.state.infoPanelShowMembers} showMemberPanel={this.state.infoPanelShowMembers}
onTopicDescUpdate={this.handleTopicUpdateRequest} onTopicDescUpdate={this.handleTopicUpdateRequest}
onCancel={this.handleHideInfoView} onCancel={this.handleHideInfoView}