better handling of avatars

This commit is contained in:
or-else
2017-06-16 17:44:17 -07:00
parent 895a81799f
commit 67cd659712
2 changed files with 123 additions and 23 deletions
+74 -2
View File
@@ -40,6 +40,13 @@ form {
form label {
font-size: 1rem;
}
img.avatar {
width: 100%;
height: 100%;
border-radius: 50%;
}
/* Input fields and buttons */
button {
@@ -872,6 +879,71 @@ ul.tabbar li.active > a:after {
color: #2196f3; /* blue 500 */
}
/* Letter tile base style */
.lettertile {
display: flex;
color: rgba(250, 250, 250, 0.8); /* #FAFAFA, gray 50 */
font-size: 1.8rem;
line-height: 1;
width: 3rem;
height: 3rem;
border-radius: 50%;
text-transform: uppercase;
align-items: center;
justify-content: center;
text-align: center;
}
/* Colors of letter tiles */
.lettertile.color0 {
background-color: #c5e1a5;
}
.lettertile.color1 {
background-color: #ef9a9a;
}
.lettertile.color2 {
background-color: #90caf9;
}
.lettertile.color3 {
background-color: #fff59d;
}
.lettertile.color4 {
background-color: #b0bec5;
}
.lettertile.color5 {
background-color: #f48fb1;
}
.lettertile.color6 {
background-color: #b39ddb;
}
.lettertile.color7 {
background-color: #9fa8da;
}
.lettertile.color8 {
background-color: #ffab91;
}
.lettertile.color9 {
background-color: #ffe082;
}
.lettertile.color10 {
background-color: #a5d6a7;
}
.lettertile.color11 {
background-color: #bcaaa4;
}
.lettertile.color12 {
background-color: #eeeeee;
}
.lettertile.color13 {
background-color: #80deea;
}
.lettertile.color14 {
background-color: #e6ee9c;
}
.lettertile.color15 {
background-color: #ce93d8;
}
/* Material icons resized from the default 24pt */
.material-icons {
font-size: 1.4rem;
@@ -882,10 +954,10 @@ ul.tabbar li.active > a:after {
}
#self-avatar.material-icons, .avatar-box .material-icons {
font-size: 2.8rem;
font-size: 3rem;
}
.avatar-box .material-icons {
color: rgba(250, 250, 250, 0.8); /* #fafafa, gray 50 */
background-color: #757575;
background-color: #c0c0c0;
border-radius: 50%;
}
+49 -21
View File
@@ -70,6 +70,18 @@
'data:image/' + photo.type + ';base64,' + photo.data : null;
}
// Get 32 bit integer hash value for a string. Ideally it should produce the same value
// as Java's String#hash().
function stringHash(value) {
var hash = 0;
value = "" + value;
for (var i = 0; i < value.length; i++) {
hash = ((hash<<5)-hash) + value.charCodeAt(i);
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
// Detect server address from the URL
function detectServerAddress() {
var host = DEFAULT_HOST;
@@ -203,6 +215,28 @@
};
/* END Popup/dropdown menu */
/* BEGIN Lettertile: Avatar box: either a bitmap or a letter tile or a stock icon. */
var LetterTile = React.createClass({
render: function() {
var avatar = null;
if (this.props.avatar === true) {
if (this.props.topic && this.props.title && this.props.title.trim()) {
var letter = this.props.title.trim().charAt(0);
var color = "lettertile color" + (Math.abs(stringHash(this.props.topic)) % 16);
avatar = <div className={color}>{letter}</div>
} else {
avatar = (Tinode.getTopicType(this.props.topic) === "grp") ?
<i className="material-icons">group</i> :
<i className="material-icons">person</i>;
}
} else if (this.props.avatar) {
avatar = <img className="avatar" alt="avatar" src={this.props.avatar} />;
}
return (<div>{avatar}</div>);
}
});
/* END Lettertile */
/* BEGIN Combobox for selecting host name */
var HostSelector = React.createClass({
getInitialState: function() {
@@ -307,7 +341,6 @@
/* END Login */
/* BEGIN Account registration */
var CreateAccountView = React.createClass({
getInitialState: function() {
return {
@@ -493,15 +526,10 @@
/* BEGIN Manage side panel - handle Login, Account Registration, Contacts, NewTopic views */
var SideNavbar = React.createClass({
render: function() {
var avatar = null;
if (this.props.avatar === true) {
avatar = <i id="self-avatar" className="material-icons">account_box</i>;
} else if (this.props.avatar) {
avatar = <img id="self-avatar" alt="avatar" src={this.props.avatar} />;
}
return (
<div id="side-caption-panel">
{avatar}
<div id="self-avatar"><LetterTile
avatar={this.props.avatar} topic={this.props.myUserId} title={this.props.title} /></div>
<div id="sidepanel-title" className="panel-title">{this.props.title}</div>
{this.props.state === 'login' ?
<SideNavbar.MenuStart onSignUp={this.props.onSignUp} onSettings={this.props.onSettings} /> :
@@ -591,6 +619,7 @@
<div id="sidepanel" className={this.props.hideSelf ? 'nodisplay' : null}>
<SideNavbar state={this.props.state}
title={title} avatar={avatar}
myUserId={this.props.myUserId}
onSignUp={this.props.onSignUp}
onSettings={this.props.onSettings}
onNewTopic={this.props.onNewTopic}
@@ -687,15 +716,12 @@
}
var selected = (this.props.selected === this.props.topic) ? "selected" : null;
var online = this.props.now ? "online" : "offline";
var avatar = this.props.avatar ?
<img src={this.props.avatar} className="avatar" /> :
(Tinode.getTopicType(this.props.topic) === "grp") ?
<i className="material-icons">group</i> :
<i className="material-icons">person</i>;
var avatar = this.props.avatar ? this.props.avatar : true;
return (
<li className={selected} onClick={this.handleClick}>
<div className="avatar-box">
{avatar}
<LetterTile
avatar={avatar} title={this.props.title} topic={this.props.topic} />
<span className={online} />
</div>
<div className="text-box">
@@ -1212,7 +1238,6 @@
var component = null;
if (this.state.topic) {
var messageNodes = [];
var myUid = Tinode.getCurrentUserID();
var topic = Tinode.getTopic(this.state.topic);
var previousFrom = null;
for (var i=0; i<this.state.messages.length; i++) {
@@ -1230,7 +1255,7 @@
}
previousFrom = msg.from;
var isReply = !(msg.from === myUid);
var isReply = !(msg.from === this.props.myUserId);
var msgReceived = Received.UNDEF;
if (!isReply) {
if (topic.msgReadCount(msg.seq) > 0) {
@@ -1415,7 +1440,7 @@
contextMenuVisible: false,
login: '',
password: '',
loggedIn: false,
myUserId: null,
errorText: '',
topicSelected: '',
loginDisabled: false,
@@ -1496,7 +1521,7 @@
// Logged in fine, subscribe to 'me' attaching callbacks from the contacts view.
var me = Tinode.getMeTopic();
me.onMetaDesc = instance.tnMeMetaDesc;
instance.setState({sidePanelSelected: "contacts", loggedIn: true});
instance.setState({sidePanelSelected: "contacts", myUserId: Tinode.getCurrentUserID()});
// Subscribe, fetch topic desc, the list of subscriptions, and messages (invites).
me.subscribe({get: {desc: {}, sub: {}, data: {}}});
},
@@ -1507,7 +1532,7 @@
sidePanelTitle: null,
sidePanelAvatar: null,
dialogSelected: null,
loggedIn: false,
myUserId: null,
errorText: err && err.message ? err.message : 'disconnected',
topicSelected: '',
loginDisabled: false,
@@ -1574,7 +1599,7 @@
},
// User chose Settings menu item.
handleSettings: function() {
this.setState({sidePanelSelected: this.state.loggedIn ? 'edit-account' : 'settings'});
this.setState({sidePanelSelected: this.state.myUserId ? 'edit-account' : 'settings'});
},
// User updated connection parameters - transport and server address
handleConnectionSettings: function(serverAddress, transport) {
@@ -1585,7 +1610,7 @@
},
// User clicked Cancel button in Setting or Sign Up panel.
handleSidepanelCancel: function() {
if (this.state.loggedIn) {
if (this.state.myUserId) {
this.setState({sidePanelSelected: 'contacts'});
} else {
this.setState({sidePanelSelected: 'login'});
@@ -1686,6 +1711,7 @@
avatar={this.state.sidePanelAvatar}
subscriptions={this.state.subscriptions}
login={this.state.login}
myUserId={this.state.myUserId}
loginDisabled={this.state.loginDisabled}
errorText={this.state.errorText}
topicSelected={this.state.topicSelected}
@@ -1702,6 +1728,7 @@
onCreateTopic={this.handleNewTopicRequest}
onNewTopic={this.handleNewTopic}
onCancel={this.handleSidepanelCancel}
showContextMenu={this.handleShowContextMenu} />
<MessagesView
@@ -1709,6 +1736,7 @@
displayMobile={this.state.displayMobile}
hideSelf={(this.state.displayMobile && this.state.mobilePanel !== 'topic-view')}
topic={this.state.topicSelected}
myUserId={this.state.myUserId}
serverVersion={this.state.serverVersion}
serverAddress={this.state.serverAddress}
onData={this.tnData}