diff --git a/README.md b/README.md
index 827f8eb5..43b3b4b7 100644
--- a/README.md
+++ b/README.md
@@ -18,9 +18,13 @@ Background pattern from http://subtlepatterns.com/, commercial and non-commercia
### Desktop
-
+
-
);
}
});
@@ -389,7 +466,7 @@
var AvatarUpload = React.createClass({
getInitialState: function() {
return {
- dataUrl: null
+ dataUrl: this.props.avatar
};
},
handleFileUpload(e) {
@@ -398,7 +475,7 @@
img.crossOrigin = 'Anonymous';
img.onload = function() {
var canvas = document.createElement('CANVAS');
- canvas.width = canvas.height = 64;
+ canvas.width = canvas.height = 128;
var ctx = canvas.getContext('2d');
var sx = 0, sy = 0, size;
if (this.height > this.width) {
@@ -409,7 +486,7 @@
sx = (this.width - size) / 2;
}
ctx.imageSmoothingEnabled = true;
- ctx.drawImage(this, sx, sy, size, size, 0, 0, 64, 64);
+ ctx.drawImage(this, sx, sy, size, size, 0, 0, canvas.width, canvas.height);
var bits = canvas.toDataURL("image/jpg");
canvas = null;
@@ -423,12 +500,14 @@
{this.state.dataUrl ?

:
-
64×64
}
-
+
128×128
}
+ {this.props.readOnly ? null :
+
}
+ {this.props.readOnly ? null :
+ }
);
}
@@ -492,22 +571,22 @@
var SideNavbar = React.createClass({
render: function() {
return (
-
-
+
{this.props.title}
{this.props.state === 'login' ?
:
this.props.state === 'settings' ?
- :
+ :
this.props.state === 'edit-account' ?
- :
+ :
this.props.state === 'register' ?
- :
+ :
this.props.state === 'contacts' ?
:
this.props.state === 'new-topic' ?
- :
+ :
null}
);
@@ -526,14 +605,6 @@
}
});
- SideNavbar.MenuCancel = React.createClass({
- render: function() {
- return (
-
close
- );
- }
- })
-
SideNavbar.MenuContacts = React.createClass({
render: function() {
return (
@@ -614,7 +685,10 @@
/> :
this.props.state === 'edit-account' ?
-
:
+
:
this.props.state === 'contacts' ?
- Not implemented
+
);
}
@@ -866,20 +1019,7 @@
}
},
handleGroupSubmit: function(name, dataUrl) {
- var vcard = null;
- if (name || dataUrl) {
- vcard = {
- fn: name,
- };
- if (dataUrl) {
- var dataStart = dataUrl.indexOf(",");
- vcard.photo = {
- data: dataUrl.substring(dataStart+1),
- type: "jpg"
- };
- }
- }
- this.props.onCreateTopic(undefined, vcard);
+ this.props.onCreateTopic(undefined, vcard(name, dataUrl));
},
render: function() {
return (
@@ -1026,9 +1166,118 @@
);
}
});
-
/* END Create new topic and invite users or send an invite */
+ /* BEGIN InfoView: panel with topic/user info */
+ var InfoView = React.createClass({
+ getInitialState: function() {
+ return {
+ updated: false,
+ address: null,
+ fullName: undefined,
+ avatar: null,
+ private: null
+ };
+ },
+ componentWillMount: function() {
+ var topic = Tinode.getTopic(this.props.topic);
+ if (!topic) {
+ return;
+ }
+
+ topic.onMetaDesc = this.onMetaDesc;
+ var defacs = topic.getDefaultAccess() || {};
+ this.setState({
+ fullName: topic.public ? topic.public.fn : undefined,
+ avatar: makeImageUrl(topic.public ? topic.public.photo : null),
+ private: topic.private,
+ address: topic.name,
+ auth: defacs.auth,
+ anon: defacs.anon
+ });
+ },
+ componentWillUnmount: function() {
+ var topic = Tinode.getTopic(this.props.topic);
+ if (!topic) {
+ return;
+ }
+ topic.onMetaDesc = undefined;
+ },
+
+ onMetaDesc: function(desc) {
+ // Update form with changed values
+ },
+
+ handleFullNameUpdate: function(fn) {
+ this.setState({updated: true, fullName: fn});
+ },
+ handlePrivateUpdate: function(priv) {
+ this.setState({updated: true, private: priv});
+ },
+ handleImageChanged: function(img) {
+ this.setState({updated: true, avatar: img});
+ },
+
+ handleSubmit: function(e) {
+ e.preventDefault();
+ if (this.state.updated) {
+ this.props.onSubmit(this.props.topic,
+ vcard(this.state.fullName,this.state.avatar),
+ this.state.private);
+ }
+ },
+
+ render: function() {
+ return (
+
+
+
+
+ );
+ }
+ });
+ /* END InfoView */
+
/* BEGIN Conversation panel */
var Received = {
UNDEF: -2,
@@ -1132,6 +1381,7 @@
// Unbind the previous topic from this component.
if (this.state.topic) {
var oldTopic = Tinode.getTopic(this.state.topic);
+ oldTopic.leave(false);
oldTopic.onData = undefined;
oldTopic.onInfo = undefined;
oldTopic.onMetaDesc = undefined;
@@ -1269,9 +1519,9 @@
var avatar = this.state.avatar || true;
component = (
-
+
{this.props.displayMobile ?
-
+
arrow_back
:
@@ -1287,7 +1537,7 @@
{this.state.title}
{lastSeen}
-
users online and menu buttons
+
more_vert
@@ -1438,6 +1688,7 @@
loginDisabled: false,
subscriptions: {},
displayMobile: (window.innerWidth <= MEDIA_BREAKPOINT),
+ showInfoPanel: false,
mobilePanel: 'sidepanel',
contextMenuVisible: false,
contextMenuBounds: null,
@@ -1534,6 +1785,7 @@
topicSelected: '',
loginDisabled: false,
subscriptions: {},
+ showInfoPanel: false,
mobilePanel: 'sidepanel',
contextMenuVisible: false,
contextMenuBounds: null,
@@ -1561,14 +1813,22 @@
handleTopicSelected: function(topicName) {
if (topicName) {
// Contact selected
- this.setState({topicSelected: topicName, mobilePanel: 'topic-view'});
+ this.setState({
+ topicSelected: topicName,
+ mobilePanel: 'topic-view',
+ showInfoPanel: false
+ });
} else {
// Currently selected contact deleted
- this.setState({topicSelected: null, mobilePanel: 'sidepanel'});
+ this.setState({
+ topicSelected: null,
+ mobilePanel: 'sidepanel',
+ showInfoPanel: false
+ });
}
},
// In mobile view user requested to show sidepanel
- handleHideMessageView: function() {
+ handleHideMessagesView: function() {
this.setState({mobilePanel: 'sidepanel'});
},
// User clicked on a message send button.
@@ -1585,8 +1845,8 @@
var instance = this;
Tinode.connect(this.state.serverAddress)
.then(function() {
- return Tinode.createUserBasic(login_, password_,
- {login: "basic", public: public_, private: private_});
+ return Tinode.createAccountBasic(login_, password_,
+ {login: true, public: public_, private: private_});
})
.then(function() {
instance.handleLoginSuccessful(instance);
@@ -1595,6 +1855,15 @@
instance.setState({errorText: err.message});
});
},
+
+ handleUpdateAccountRequest: function(password, pub, priv) {
+ this.handleSidepanelCancel();
+ var me = Tinode.getMeTopic();
+ me.setMeta({desc: {public: pub, private: priv}});
+ if (password) {
+ Tinode.updateAccountBasic(null, Tinode.getCurrentLogin(), password);
+ }
+ },
// User chose Settings menu item.
handleSettings: function() {
this.setState({sidePanelSelected: this.state.myUserId ? 'edit-account' : 'settings'});
@@ -1619,7 +1888,7 @@
this.setState({sidePanelSelected: 'new-topic'});
},
// Create of a new topic. New P2P topic requires peer's name.
- handleNewTopicRequest: function(peerName, vcard) {
+ handleNewTopicRequest: function(peerName, pub) {
var instance = this;
// Check if user clicked on an existing connection.
@@ -1635,7 +1904,7 @@
var params = {get: {desc: {}, data: {}}};
if (!peerName) {
params.get.sub = {}; // Fetch subs for grp topics only. Subscriptions for a p2p topic are useless.
- params.set = {desc: {public: vcard}};
+ params.set = {desc: {public: pub}};
}
topic.subscribe(params).then(function() {
instance.setState({
@@ -1649,6 +1918,11 @@
});
}
},
+
+ handleTopicUpdateRequest: function(topicName, pub, priv) {
+ var topic = Tinode.getTopic(topicName);
+ topic.setMeta({desc: {public: pub, private: priv}});
+ },
/** Called when the user enters a contact into the contact search field in the NewAccount panel
@param query {Array} is an array of contacts to search for
*/
@@ -1710,8 +1984,7 @@
switch (action) {
case "Info":
- // TODO(gene): implement topic info
- console.log("Topic info not implemented");
+ this.handleShowInfoView();
break;
case "Clear messages":
topic.delMessagesAll(false).then(function(){
@@ -1738,7 +2011,12 @@
break;
}
},
-
+ handleShowInfoView: function() {
+ this.setState({showInfoPanel: true});
+ },
+ handleHideInfoView: function() {
+ this.setState({showInfoPanel: false});
+ },
render: function() {
return (
@@ -1776,6 +2054,7 @@
onSettings={this.handleSettings}
onLoginRequest={this.handleLoginRequest}
onCreateAccount={this.handleNewAccountRequest}
+ onUpdateAccount={this.handleUpdateAccountRequest}
onTopicSelected={this.handleTopicSelected}
onSearchContacts={this.handleSearchContacts}
onCreateTopic={this.handleNewTopicRequest}
@@ -1785,7 +2064,7 @@
showContextMenu={this.handleShowContextMenu} />
+ {this.state.showInfoPanel ?
+
+ :
+ null
+ }
);
}