better handling of avatars
This commit is contained in:
+74
-2
@@ -40,6 +40,13 @@ form {
|
|||||||
form label {
|
form label {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img.avatar {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
/* Input fields and buttons */
|
/* Input fields and buttons */
|
||||||
|
|
||||||
button {
|
button {
|
||||||
@@ -872,6 +879,71 @@ ul.tabbar li.active > a:after {
|
|||||||
color: #2196f3; /* blue 500 */
|
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 resized from the default 24pt */
|
||||||
.material-icons {
|
.material-icons {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
@@ -882,10 +954,10 @@ ul.tabbar li.active > a:after {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#self-avatar.material-icons, .avatar-box .material-icons {
|
#self-avatar.material-icons, .avatar-box .material-icons {
|
||||||
font-size: 2.8rem;
|
font-size: 3rem;
|
||||||
}
|
}
|
||||||
.avatar-box .material-icons {
|
.avatar-box .material-icons {
|
||||||
color: rgba(250, 250, 250, 0.8); /* #fafafa, gray 50 */
|
color: rgba(250, 250, 250, 0.8); /* #fafafa, gray 50 */
|
||||||
background-color: #757575;
|
background-color: #c0c0c0;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-21
@@ -70,6 +70,18 @@
|
|||||||
'data:image/' + photo.type + ';base64,' + photo.data : null;
|
'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
|
// Detect server address from the URL
|
||||||
function detectServerAddress() {
|
function detectServerAddress() {
|
||||||
var host = DEFAULT_HOST;
|
var host = DEFAULT_HOST;
|
||||||
@@ -203,6 +215,28 @@
|
|||||||
};
|
};
|
||||||
/* END Popup/dropdown menu */
|
/* 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 */
|
/* BEGIN Combobox for selecting host name */
|
||||||
var HostSelector = React.createClass({
|
var HostSelector = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
@@ -307,7 +341,6 @@
|
|||||||
/* END Login */
|
/* END Login */
|
||||||
|
|
||||||
/* BEGIN Account registration */
|
/* BEGIN Account registration */
|
||||||
|
|
||||||
var CreateAccountView = React.createClass({
|
var CreateAccountView = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
@@ -493,15 +526,10 @@
|
|||||||
/* BEGIN Manage side panel - handle Login, Account Registration, Contacts, NewTopic views */
|
/* BEGIN Manage side panel - handle Login, Account Registration, Contacts, NewTopic views */
|
||||||
var SideNavbar = React.createClass({
|
var SideNavbar = React.createClass({
|
||||||
render: function() {
|
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 (
|
return (
|
||||||
<div id="side-caption-panel">
|
<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>
|
<div id="sidepanel-title" className="panel-title">{this.props.title}</div>
|
||||||
{this.props.state === 'login' ?
|
{this.props.state === 'login' ?
|
||||||
<SideNavbar.MenuStart onSignUp={this.props.onSignUp} onSettings={this.props.onSettings} /> :
|
<SideNavbar.MenuStart onSignUp={this.props.onSignUp} onSettings={this.props.onSettings} /> :
|
||||||
@@ -591,6 +619,7 @@
|
|||||||
<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={this.props.state}
|
||||||
title={title} avatar={avatar}
|
title={title} avatar={avatar}
|
||||||
|
myUserId={this.props.myUserId}
|
||||||
onSignUp={this.props.onSignUp}
|
onSignUp={this.props.onSignUp}
|
||||||
onSettings={this.props.onSettings}
|
onSettings={this.props.onSettings}
|
||||||
onNewTopic={this.props.onNewTopic}
|
onNewTopic={this.props.onNewTopic}
|
||||||
@@ -687,15 +716,12 @@
|
|||||||
}
|
}
|
||||||
var selected = (this.props.selected === this.props.topic) ? "selected" : null;
|
var selected = (this.props.selected === this.props.topic) ? "selected" : null;
|
||||||
var online = this.props.now ? "online" : "offline";
|
var online = this.props.now ? "online" : "offline";
|
||||||
var avatar = this.props.avatar ?
|
var avatar = this.props.avatar ? this.props.avatar : true;
|
||||||
<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>;
|
|
||||||
return (
|
return (
|
||||||
<li className={selected} onClick={this.handleClick}>
|
<li className={selected} onClick={this.handleClick}>
|
||||||
<div className="avatar-box">
|
<div className="avatar-box">
|
||||||
{avatar}
|
<LetterTile
|
||||||
|
avatar={avatar} title={this.props.title} topic={this.props.topic} />
|
||||||
<span className={online} />
|
<span className={online} />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-box">
|
<div className="text-box">
|
||||||
@@ -1212,7 +1238,6 @@
|
|||||||
var component = null;
|
var component = null;
|
||||||
if (this.state.topic) {
|
if (this.state.topic) {
|
||||||
var messageNodes = [];
|
var messageNodes = [];
|
||||||
var myUid = Tinode.getCurrentUserID();
|
|
||||||
var topic = Tinode.getTopic(this.state.topic);
|
var topic = Tinode.getTopic(this.state.topic);
|
||||||
var previousFrom = null;
|
var previousFrom = null;
|
||||||
for (var i=0; i<this.state.messages.length; i++) {
|
for (var i=0; i<this.state.messages.length; i++) {
|
||||||
@@ -1230,7 +1255,7 @@
|
|||||||
}
|
}
|
||||||
previousFrom = msg.from;
|
previousFrom = msg.from;
|
||||||
|
|
||||||
var isReply = !(msg.from === myUid);
|
var isReply = !(msg.from === this.props.myUserId);
|
||||||
var msgReceived = Received.UNDEF;
|
var msgReceived = Received.UNDEF;
|
||||||
if (!isReply) {
|
if (!isReply) {
|
||||||
if (topic.msgReadCount(msg.seq) > 0) {
|
if (topic.msgReadCount(msg.seq) > 0) {
|
||||||
@@ -1415,7 +1440,7 @@
|
|||||||
contextMenuVisible: false,
|
contextMenuVisible: false,
|
||||||
login: '',
|
login: '',
|
||||||
password: '',
|
password: '',
|
||||||
loggedIn: false,
|
myUserId: null,
|
||||||
errorText: '',
|
errorText: '',
|
||||||
topicSelected: '',
|
topicSelected: '',
|
||||||
loginDisabled: false,
|
loginDisabled: false,
|
||||||
@@ -1496,7 +1521,7 @@
|
|||||||
// 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", loggedIn: true});
|
instance.setState({sidePanelSelected: "contacts", myUserId: Tinode.getCurrentUserID()});
|
||||||
// Subscribe, fetch topic desc, the list of subscriptions, and messages (invites).
|
// Subscribe, fetch topic desc, the list of subscriptions, and messages (invites).
|
||||||
me.subscribe({get: {desc: {}, sub: {}, data: {}}});
|
me.subscribe({get: {desc: {}, sub: {}, data: {}}});
|
||||||
},
|
},
|
||||||
@@ -1507,7 +1532,7 @@
|
|||||||
sidePanelTitle: null,
|
sidePanelTitle: null,
|
||||||
sidePanelAvatar: null,
|
sidePanelAvatar: null,
|
||||||
dialogSelected: null,
|
dialogSelected: null,
|
||||||
loggedIn: false,
|
myUserId: null,
|
||||||
errorText: err && err.message ? err.message : 'disconnected',
|
errorText: err && err.message ? err.message : 'disconnected',
|
||||||
topicSelected: '',
|
topicSelected: '',
|
||||||
loginDisabled: false,
|
loginDisabled: false,
|
||||||
@@ -1574,7 +1599,7 @@
|
|||||||
},
|
},
|
||||||
// User chose Settings menu item.
|
// User chose Settings menu item.
|
||||||
handleSettings: function() {
|
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
|
// User updated connection parameters - transport and server address
|
||||||
handleConnectionSettings: function(serverAddress, transport) {
|
handleConnectionSettings: function(serverAddress, transport) {
|
||||||
@@ -1585,7 +1610,7 @@
|
|||||||
},
|
},
|
||||||
// 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.loggedIn) {
|
if (this.state.myUserId) {
|
||||||
this.setState({sidePanelSelected: 'contacts'});
|
this.setState({sidePanelSelected: 'contacts'});
|
||||||
} else {
|
} else {
|
||||||
this.setState({sidePanelSelected: 'login'});
|
this.setState({sidePanelSelected: 'login'});
|
||||||
@@ -1686,6 +1711,7 @@
|
|||||||
avatar={this.state.sidePanelAvatar}
|
avatar={this.state.sidePanelAvatar}
|
||||||
subscriptions={this.state.subscriptions}
|
subscriptions={this.state.subscriptions}
|
||||||
login={this.state.login}
|
login={this.state.login}
|
||||||
|
myUserId={this.state.myUserId}
|
||||||
loginDisabled={this.state.loginDisabled}
|
loginDisabled={this.state.loginDisabled}
|
||||||
errorText={this.state.errorText}
|
errorText={this.state.errorText}
|
||||||
topicSelected={this.state.topicSelected}
|
topicSelected={this.state.topicSelected}
|
||||||
@@ -1702,6 +1728,7 @@
|
|||||||
onCreateTopic={this.handleNewTopicRequest}
|
onCreateTopic={this.handleNewTopicRequest}
|
||||||
onNewTopic={this.handleNewTopic}
|
onNewTopic={this.handleNewTopic}
|
||||||
onCancel={this.handleSidepanelCancel}
|
onCancel={this.handleSidepanelCancel}
|
||||||
|
|
||||||
showContextMenu={this.handleShowContextMenu} />
|
showContextMenu={this.handleShowContextMenu} />
|
||||||
|
|
||||||
<MessagesView
|
<MessagesView
|
||||||
@@ -1709,6 +1736,7 @@
|
|||||||
displayMobile={this.state.displayMobile}
|
displayMobile={this.state.displayMobile}
|
||||||
hideSelf={(this.state.displayMobile && this.state.mobilePanel !== 'topic-view')}
|
hideSelf={(this.state.displayMobile && this.state.mobilePanel !== 'topic-view')}
|
||||||
topic={this.state.topicSelected}
|
topic={this.state.topicSelected}
|
||||||
|
myUserId={this.state.myUserId}
|
||||||
serverVersion={this.state.serverVersion}
|
serverVersion={this.state.serverVersion}
|
||||||
serverAddress={this.state.serverAddress}
|
serverAddress={this.state.serverAddress}
|
||||||
onData={this.tnData}
|
onData={this.tnData}
|
||||||
|
|||||||
Reference in New Issue
Block a user