add avatar and sender user colors

This commit is contained in:
Bruno Windels 2020-08-13 12:41:00 +02:00
parent d33e7b2a8b
commit 44cc691c79
10 changed files with 102 additions and 21 deletions

View file

@ -15,6 +15,42 @@ limitations under the License.
*/ */
export function avatarInitials(name) { export function avatarInitials(name) {
const words = name.split(" ").slice(0, 2); let words = name.split(" ");
return words.reduce((i, w) => i + w.charAt(0).toUpperCase(), ""); if (words.length === 1) {
words = words[0].split("-");
}
words = words.slice(0, 2);
return words.reduce((i, w) => {
let firstChar = w.charAt(0);
if (firstChar === "!" || firstChar === "@" || firstChar === "#") {
firstChar = w.charAt(1);
}
return i + firstChar.toUpperCase();
}, "");
}
/**
* calculates a numeric hash for a given string
*
* @param {string} str string to hash
*
* @return {number}
*/
function hashCode(str) {
let hash = 0;
let i;
let chr;
if (str.length === 0) {
return hash;
}
for (i = 0; i < str.length; i++) {
chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
return Math.abs(hash);
}
export function getIdentifierColorNumber(id) {
return (hashCode(id) % 8) + 1;
} }

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import {TimelineViewModel} from "./timeline/TimelineViewModel.js"; import {TimelineViewModel} from "./timeline/TimelineViewModel.js";
import {avatarInitials} from "../avatar.js"; import {avatarInitials, getIdentifierColorNumber} from "../avatar.js";
import {ViewModel} from "../../ViewModel.js"; import {ViewModel} from "../../ViewModel.js";
export class RoomViewModel extends ViewModel { export class RoomViewModel extends ViewModel {
@ -90,7 +90,9 @@ export class RoomViewModel extends ViewModel {
return avatarInitials(this._room.name); return avatarInitials(this._room.name);
} }
get avatarColorNumber() {
return getIdentifierColorNumber(this._room.id)
}
async _sendMessage(message) { async _sendMessage(message) {
if (message) { if (message) {

View file

@ -15,6 +15,7 @@ limitations under the License.
*/ */
import {SimpleTile} from "./SimpleTile.js"; import {SimpleTile} from "./SimpleTile.js";
import {getIdentifierColorNumber} from "../../../avatar.js";
export class MessageTile extends SimpleTile { export class MessageTile extends SimpleTile {
constructor(options) { constructor(options) {
@ -32,6 +33,10 @@ export class MessageTile extends SimpleTile {
return this._entry.sender; return this._entry.sender;
} }
get senderColorNumber() {
return getIdentifierColorNumber(this._entry.sender);
}
get date() { get date() {
return this._date.toLocaleDateString({}, {month: "numeric", day: "numeric"}); return this._date.toLocaleDateString({}, {month: "numeric", day: "numeric"});
} }

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {avatarInitials} from "../avatar.js"; import {avatarInitials, getIdentifierColorNumber} from "../avatar.js";
import {ViewModel} from "../../ViewModel.js"; import {ViewModel} from "../../ViewModel.js";
export class RoomTileViewModel extends ViewModel { export class RoomTileViewModel extends ViewModel {
@ -60,4 +60,8 @@ export class RoomTileViewModel extends ViewModel {
get avatarInitials() { get avatarInitials() {
return avatarInitials(this._room.name); return avatarInitials(this._room.name);
} }
get avatarColorNumber() {
return getIdentifierColorNumber(this._room.id)
}
} }

View file

@ -21,6 +21,15 @@ limitations under the License.
font-family: 'Inter', sans-serif, 'emoji'; font-family: 'Inter', sans-serif, 'emoji';
background-color: white; background-color: white;
color: #2e2f32; color: #2e2f32;
--usercolor1: #368BD6;
--usercolor2: #AC3BA8;
--usercolor3: #03B381;
--usercolor4: #E64F7A;
--usercolor5: #FF812D;
--usercolor6: #2DC2C5;
--usercolor7: #5C56F5;
--usercolor8: #74D12C;
} }
.avatar { .avatar {
@ -30,6 +39,15 @@ limitations under the License.
color: white; color: white;
} }
.hydrogen .avatar.usercolor1 { background-color: var(--usercolor1); }
.hydrogen .avatar.usercolor2 { background-color: var(--usercolor2); }
.hydrogen .avatar.usercolor3 { background-color: var(--usercolor3); }
.hydrogen .avatar.usercolor4 { background-color: var(--usercolor4); }
.hydrogen .avatar.usercolor5 { background-color: var(--usercolor5); }
.hydrogen .avatar.usercolor6 { background-color: var(--usercolor6); }
.hydrogen .avatar.usercolor7 { background-color: var(--usercolor7); }
.hydrogen .avatar.usercolor8 { background-color: var(--usercolor8); }
.LeftPanel { .LeftPanel {
background: rgba(245, 245, 245, 0.90); background: rgba(245, 245, 245, 0.90);
} }
@ -135,6 +153,15 @@ a {
font-weight: bold; font-weight: bold;
} }
.hydrogen .sender.usercolor1 { color: var(--usercolor1); }
.hydrogen .sender.usercolor2 { color: var(--usercolor2); }
.hydrogen .sender.usercolor3 { color: var(--usercolor3); }
.hydrogen .sender.usercolor4 { color: var(--usercolor4); }
.hydrogen .sender.usercolor5 { color: var(--usercolor5); }
.hydrogen .sender.usercolor6 { color: var(--usercolor6); }
.hydrogen .sender.usercolor7 { color: var(--usercolor7); }
.hydrogen .sender.usercolor8 { color: var(--usercolor8); }
.TextMessageView .message-container time { .TextMessageView .message-container time {
padding: 2px 0 0px 20px; padding: 2px 0 0px 20px;
font-size: 0.9em; font-size: 0.9em;

View file

@ -17,9 +17,9 @@ limitations under the License.
import {TemplateView} from "../general/TemplateView.js"; import {TemplateView} from "../general/TemplateView.js";
export class RoomTile extends TemplateView { export class RoomTile extends TemplateView {
render(t) { render(t, vm) {
return t.li({"className": {"active": vm => vm.isOpen}}, [ return t.li({"className": {"active": vm => vm.isOpen}}, [
t.div({className: "avatar medium"}, vm => vm.avatarInitials), t.div({className: `avatar medium usercolor${vm.avatarColorNumber}`}, vm => vm.avatarInitials),
t.div({className: "description"}, t.div({className: "name"}, vm => vm.name)) t.div({className: "description"}, t.div({className: "name"}, vm => vm.name))
]); ]);
} }

View file

@ -30,7 +30,7 @@ export class RoomView extends TemplateView {
t.div({className: "TimelinePanel"}, [ t.div({className: "TimelinePanel"}, [
t.div({className: "RoomHeader"}, [ t.div({className: "RoomHeader"}, [
t.button({className: "back", onClick: () => vm.close()}), t.button({className: "back", onClick: () => vm.close()}),
t.div({className: "avatar large"}, vm => vm.avatarInitials), t.div({className: `avatar large usercolor${vm.avatarColorNumber}`}, vm => vm.avatarInitials),
t.div({className: "room-description"}, [ t.div({className: "room-description"}, [
t.h2(vm => vm.name), t.h2(vm => vm.name),
]), ]),

View file

@ -15,6 +15,7 @@ limitations under the License.
*/ */
import {TemplateView} from "../../../general/TemplateView.js"; import {TemplateView} from "../../../general/TemplateView.js";
import {renderMessage} from "./common.js";
export class ImageView extends TemplateView { export class ImageView extends TemplateView {
render(t, vm) { render(t, vm) {
@ -33,13 +34,8 @@ export class ImageView extends TemplateView {
style: `padding-top: ${heightRatioPercent}%; width: ${vm.thumbnailWidth}px;` style: `padding-top: ${heightRatioPercent}%; width: ${vm.thumbnailWidth}px;`
}, image); }, image);
return t.li( return renderMessage(t, vm,
{className: {"TextMessageView": true, own: vm.isOwn, pending: vm.isPending}}, [t.div(linkContainer), t.p(t.time(vm.date + " " + vm.time))]
t.div({className: "message-container"}, [
t.div({className: "sender"}, vm => vm.isContinuation ? "" : vm.sender),
t.div(linkContainer),
t.p(t.time(vm.date + " " + vm.time)),
])
); );
} }
} }

View file

@ -15,15 +15,12 @@ limitations under the License.
*/ */
import {TemplateView} from "../../../general/TemplateView.js"; import {TemplateView} from "../../../general/TemplateView.js";
import {renderMessage} from "./common.js";
export class TextMessageView extends TemplateView { export class TextMessageView extends TemplateView {
render(t, vm) { render(t, vm) {
return t.li( return renderMessage(t, vm,
{className: {"TextMessageView": true, own: vm.isOwn, pending: vm.isPending}}, [t.p([vm.text, t.time(vm.date + " " + vm.time)])]
t.div({className: "message-container"}, [
t.div({className: "sender"}, vm => vm.isContinuation ? "" : vm.sender),
t.p([vm.text, t.time(vm.date + " " + vm.time)]),
])
); );
} }
} }

View file

@ -0,0 +1,14 @@
export function renderMessage(t, vm, children) {
const classes = {
"TextMessageView": true,
own: vm.isOwn,
pending: vm.isPending,
continuation: vm.isContinuation,
};
const sender = t.div({className: `sender usercolor${vm.senderColorNumber}`}, vm => vm.isContinuation ? "" : vm.sender);
children = [sender].concat(children);
return t.li(
{className: classes},
t.div({className: "message-container"}, children)
);
}