forked from mystiq/hydrogen-web
add avatar and sender user colors
This commit is contained in:
parent
d33e7b2a8b
commit
44cc691c79
10 changed files with 102 additions and 21 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
]),
|
]),
|
||||||
|
|
|
@ -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)),
|
|
||||||
])
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)]),
|
|
||||||
])
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
src/ui/web/session/room/timeline/common.js
Normal file
14
src/ui/web/session/room/timeline/common.js
Normal 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)
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in a new issue