hash reflects the app state now

This commit is contained in:
or-else
2018-04-22 16:21:17 -07:00
parent e5fc770369
commit a364aa7dea
+59 -43
View File
@@ -261,16 +261,22 @@
// Parse hash as in http://www.example.com/path#hash as if it were // Parse hash as in http://www.example.com/path#hash as if it were
// path and arguments. // path and arguments.
function parseUrlHash(hash) { function parseUrlHash(hash) {
var parser = document.createElement('a'); // Split path from args, path -> parts[0], args->path[1]
parser.href = '/' + hash.substr(1); var parts = hash.split('?', 2);
var params = {}; var params = {};
parser.search.substr(1).split("&").forEach(function(part) { var path = [];
var item = part.split("="); if (parts[0]) {
if (item[0]) { path = parts[0].substr(1).split("/");
params[decodeURIComponent(item[0])] = decodeURIComponent(item[1]); }
} if (parts[1]) {
}); parts[1].split("&").forEach(function(part) {
return {path: parser.pathname.substr(1).split("/"), params: params}; var item = part.split("=");
if (item[0]) {
params[decodeURIComponent(item[0])] = decodeURIComponent(item[1]);
}
});
}
return {path: path, params: params};
} }
function composeUrlHash(path, params) { function composeUrlHash(path, params) {
@@ -1356,7 +1362,8 @@
var title = null; var title = null;
var avatar = false; var avatar = false;
var onCancel = undefined; var onCancel = undefined;
switch (this.props.state) { var view = this.props.state || (this.props.myUserId ? 'contacts' : 'login');
switch (view) {
case 'login': case 'login':
title = "Sign In"; title = "Sign In";
break; break;
@@ -1368,7 +1375,7 @@
title = "Settings"; title = "Settings";
onCancel = this.props.onCancel; onCancel = this.props.onCancel;
break; break;
case 'edit-account': case 'edit':
title = "Edit Account"; title = "Edit Account";
onCancel = this.props.onCancel; onCancel = this.props.onCancel;
break; break;
@@ -1376,11 +1383,11 @@
title = this.props.title; title = this.props.title;
avatar = this.props.avatar ? this.props.avatar : true; avatar = this.props.avatar ? this.props.avatar : true;
break; break;
case 'new-topic': case 'newtpk':
title = "Start New Chat"; title = "Start New Chat";
onCancel = this.props.onCancel; onCancel = this.props.onCancel;
break; break;
case 'validation': case 'cred':
title = "Confirm Credentials"; title = "Confirm Credentials";
onCancel = this.props.onCancel; onCancel = this.props.onCancel;
break; break;
@@ -1388,7 +1395,7 @@
}; };
return ( return (
<div id="sidepanel" className={this.props.hideSelf ? 'nodisplay' : null}> <div id="sidepanel" className={this.props.hideSelf ? 'nodisplay' : null}>
<SideNavbar state={this.props.state} <SideNavbar state={view}
title={title} avatar={avatar} title={title} avatar={avatar}
myUserId={this.props.myUserId} myUserId={this.props.myUserId}
onSignUp={this.props.onSignUp} onSignUp={this.props.onSignUp}
@@ -1401,25 +1408,25 @@
text={this.props.errorText} text={this.props.errorText}
onClearError={this.props.onError} /> onClearError={this.props.onError} />
{this.props.state === 'login' ? {view === '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} /> :
this.props.state === 'register' ? view === 'register' ?
<CreateAccountView <CreateAccountView
onCreateAccount={this.props.onCreateAccount} onCreateAccount={this.props.onCreateAccount}
onCancel={this.props.onCancel} onCancel={this.props.onCancel}
onError={this.props.onError} /> : onError={this.props.onError} /> :
this.props.state === 'settings' ? view === 'settings' ?
<SettingsView onCancel={this.props.onCancel} <SettingsView onCancel={this.props.onCancel}
onUpdate={this.props.onConnectionSettings} onUpdate={this.props.onConnectionSettings}
/> : /> :
this.props.state === 'edit-account' ? view === 'edit' ?
<EditAccountView <EditAccountView
login={this.props.login} login={this.props.login}
onSubmit={this.props.onUpdateAccount} onSubmit={this.props.onUpdateAccount}
@@ -1428,7 +1435,7 @@
onCancel={this.props.onCancel} onCancel={this.props.onCancel}
onError={this.props.onError} /> : onError={this.props.onError} /> :
this.props.state === 'contacts' ? view === 'contacts' ?
<ContactsView <ContactsView
connected={this.props.connected} connected={this.props.connected}
topicSelected={this.props.topicSelected} topicSelected={this.props.topicSelected}
@@ -1437,7 +1444,7 @@
onAcsChange={this.props.onAcsChange} onAcsChange={this.props.onAcsChange}
onOnlineChange={this.props.onOnlineChange} /> : onOnlineChange={this.props.onOnlineChange} /> :
this.props.state === 'new-topic' ? view === 'newtpk' ?
<NewTopicView <NewTopicView
contactsSearchQuery={this.props.contactsSearchQuery} contactsSearchQuery={this.props.contactsSearchQuery}
foundContacts={this.props.foundContacts} foundContacts={this.props.foundContacts}
@@ -1446,7 +1453,7 @@
onCreateTopic={this.props.onCreateTopic} onCreateTopic={this.props.onCreateTopic}
onError={this.props.onError} /> : onError={this.props.onError} /> :
this.props.state === 'validation' ? view === 'cred' ?
<ValidationView <ValidationView
onSubmit={this.props.onValidateCredentials} onSubmit={this.props.onValidateCredentials}
validation={this.props.validation} validation={this.props.validation}
@@ -2194,17 +2201,21 @@
}, },
handleTabClick: function(e) { handleTabClick: function(e) {
e.preventDefault(); e.preventDefault();
window.location.hash = addParamToHash(window.location.hash, 'tab', e.currentTarget.dataset.id);
this.setState({tabSelected: e.currentTarget.dataset.id, contactSelected: null}); this.setState({tabSelected: e.currentTarget.dataset.id, contactSelected: null});
}, },
handleContactSelected: function(sel) { handleContactSelected: function(sel) {
if (this.state.tabSelected === "p2p") { if (this.state.tabSelected === "p2p") {
window.location.hash = removeParamFromHash(window.location.hash, 'tab');
this.props.onCreateTopic(sel, undefined); this.props.onCreateTopic(sel, undefined);
} }
}, },
handleNewGroupSubmit: function(name, dataUrl, priv, tags) { handleNewGroupSubmit: function(name, dataUrl, priv, tags) {
window.location.hash = removeParamFromHash(window.location.hash, 'tab');
this.props.onCreateTopic(undefined, vcard(name, dataUrl), priv, tags); this.props.onCreateTopic(undefined, vcard(name, dataUrl), priv, tags);
}, },
handleGroupByID: function(topicName) { handleGroupByID: function(topicName) {
window.location.hash = removeParamFromHash(window.location.hash, 'tab');
this.props.onCreateTopic(topicName); this.props.onCreateTopic(topicName);
}, },
render: function() { render: function() {
@@ -2217,13 +2228,13 @@
<li className={this.state.tabSelected === "grp" ? "active" : null}> <li className={this.state.tabSelected === "grp" ? "active" : null}>
<a href="javascript:;" data-id="grp" onClick={this.handleTabClick}>group</a> <a href="javascript:;" data-id="grp" onClick={this.handleTabClick}>group</a>
</li> </li>
<li className={this.state.tabSelected === "byId" ? "active" : null}> <li className={this.state.tabSelected === "byid" ? "active" : null}>
<a href="javascript:;" data-id="byId" onClick={this.handleTabClick}>by id</a> <a href="javascript:;" data-id="byId" onClick={this.handleTabClick}>by id</a>
</li> </li>
</ul> </ul>
{this.state.tabSelected === "grp" ? {this.state.tabSelected === "grp" ?
<NewTopicView.Group onSubmit={this.handleNewGroupSubmit} /> : <NewTopicView.Group onSubmit={this.handleNewGroupSubmit} /> :
this.state.tabSelected === "byId" ? this.state.tabSelected === "byid" ?
<NewTopicView.ById <NewTopicView.ById
onSubmit={this.handleGroupByID} onSubmit={this.handleGroupByID}
onError={this.props.onError} /> : onError={this.props.onError} /> :
@@ -3771,7 +3782,6 @@
// When reading from storage, date is returned as string. // When reading from storage, date is returned as string.
token.expires = new Date(token.expires); token.expires = new Date(token.expires);
Tinode.setAuthToken(token); Tinode.setAuthToken(token);
console.log("read token", token);
Tinode.connect().catch(function(err) { Tinode.connect().catch(function(err) {
// Socket error // Socket error
instance.handleError(err.message, "err"); instance.handleError(err.message, "err");
@@ -3864,9 +3874,9 @@
}, },
handleCredentialsRequest: function(params) { handleCredentialsRequest: function(params) {
window.location.hash = "validation/" + this.state.topicSelected + "/" + params.cred; window.location.hash = composeUrlHash(["cred", this.state.topicSelected], {cred: params.cred});
this.setState({ this.setState({
sidePanelSelected: 'validation', sidePanelSelected: 'cred',
validation: params.cred validation: params.cred
}); });
}, },
@@ -3883,7 +3893,7 @@
window.location.hash = ""; window.location.hash = "";
instance.setState({ instance.setState({
connected: true, connected: true,
sidePanelSelected: "contacts", sidePanelSelected: '',
myUserId: Tinode.getCurrentUserID() 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.
@@ -3975,7 +3985,9 @@
if (topicName) { if (topicName) {
// Contact selected // Contact selected
if (this.state.topicSelected != topicName) { if (this.state.topicSelected != topicName) {
window.location.hash = this.state.sidePanelSelected + "/" + topicName; var parsed = parseUrlHash(window.location.hash);
parsed.path[1] = topicName;
window.location.hash = composeUrlHash(parsed.path, parsed.params);
} }
this.setState({ this.setState({
topicSelected: topicName, topicSelected: topicName,
@@ -3983,13 +3995,14 @@
showInfoPanel: false, showInfoPanel: false,
topicSelectedOnline: online, topicSelectedOnline: online,
topicSelectedAcs: acs, topicSelectedAcs: acs,
sidePanelSelected: 'contacts', sidePanelSelected: '',
infoPanelShowMembers: Tinode.getTopicType(topicName) === "grp" infoPanelShowMembers: Tinode.getTopicType(topicName) === "grp"
}); });
} else { } else {
if (this.state.topicSelected) { if (this.state.topicSelected) {
console.log("topic deleted"); var parsed = parseUrlHash(window.location.hash);
window.history.replaceState({}, "", this.state.sidePanelSelected); parsed.path[1] = '';
window.history.replaceState({}, "", composeUrlHash(parsed.path, parsed.params));
} }
// Currently selected contact deleted // Currently selected contact deleted
this.setState({ this.setState({
@@ -4039,7 +4052,9 @@
}, },
// User chose a Sign Up menu item. // User chose a Sign Up menu item.
handleNewAccount: function() { handleNewAccount: function() {
window.location.hash = "register"; var parsed = parseUrlHash(window.location.hash);
parsed.path[0] = 'register';
window.location.hash = composeUrlHash(parsed.path, parsed.params);
this.setState({sidePanelSelected: 'register'}); this.setState({sidePanelSelected: 'register'});
}, },
// Actual registration of a new account. // Actual registration of a new account.
@@ -4083,8 +4098,10 @@
// User chose Settings menu item. // User chose Settings menu item.
handleSettings: function() { handleSettings: function() {
window.location.hash = this.state.myUserId ? 'edit' : 'settings'; var parsed = parseUrlHash(window.location.hash);
this.setState({sidePanelSelected: this.state.myUserId ? 'edit-account' : 'settings'}); parsed.path[0] = this.state.myUserId ? 'edit' : 'settings';
window.location.hash = composeUrlHash(parsed.path, parsed.params);
this.setState({sidePanelSelected: this.state.myUserId ? 'edit' : 'settings'});
}, },
// User updated connection parameters - transport and server address // User updated connection parameters - transport and server address
handleConnectionSettings: function(serverAddress, transport) { handleConnectionSettings: function(serverAddress, transport) {
@@ -4095,18 +4112,17 @@
}, },
// User clicked Cancel button in Setting or Sign Up panel. // User clicked Cancel button in Setting or Sign Up panel.
handleSidepanelCancel: function() { handleSidepanelCancel: function() {
if (this.state.myUserId) { var parsed = parseUrlHash(window.location.hash);
window.location.hash = "/" + this.state.topicSelected; parsed.path[0] = '';
this.setState({sidePanelSelected: 'contacts', errorText: '', errorLevel: null}); window.location.hash = composeUrlHash(parsed.path, parsed.params);
} else { this.setState({sidePanelSelected: '', errorText: '', errorLevel: null});
window.location.hash = '';
this.setState({sidePanelSelected: 'login', errorText: '', errorLevel: null});
}
}, },
// User clicked a (+) menu item. // User clicked a (+) menu item.
handleNewTopic: function() { handleNewTopic: function() {
window.location.hash = 'new'; var parsed = parseUrlHash(window.location.hash);
this.setState({sidePanelSelected: 'new-topic'}); parsed.path[0] = 'newtpk';
window.location.hash = composeUrlHash(parsed.path, parsed.params);
this.setState({sidePanelSelected: 'newtpk'});
}, },
// Create of a new topic. New P2P topic requires peer's name. // Create of a new topic. New P2P topic requires peer's name.
handleNewTopicRequest: function(peerName, pub, priv, tags) { handleNewTopicRequest: function(peerName, pub, priv, tags) {