diff --git a/public/logo/lichess-thick3.svg b/public/logo/lichess-thick3.svg
new file mode 100644
index 000000000000..41cf5f4acdfe
--- /dev/null
+++ b/public/logo/lichess-thick3.svg
@@ -0,0 +1,4 @@
+
diff --git a/ui/msg/css/_convo.scss b/ui/msg/css/_convo.scss
index 7edc10adf40e..3b198f00747e 100644
--- a/ui/msg/css/_convo.scss
+++ b/ui/msg/css/_convo.scss
@@ -5,11 +5,11 @@
@extend %flex-column;
flex: 1 1 100%;
&__head {
- @extend %flex-center;
+ @extend %flex-between;
flex: 0 0 $msg-top-height;
background: $c-bg-zebra2;
border-bottom: $border;
- padding: 0 1vw;
+ padding: 0 1.5em 0 1vw;
.user-link {
@extend %flex-center-nowrap;
flex: 0 0 auto;
@@ -23,6 +23,15 @@
font-size: 1.5em;
}
}
+ &__actions {
+ @extend %flex-center;
+ }
+ }
+ &__action.button {
+ color: $c-font;
+ &.bad:hover {
+ color: $c-bad;
+ }
}
&__reply {
@extend %flex-center-nowrap;
@@ -37,6 +46,11 @@
background: $c-bg-box;
resize: none;
}
+ &__block {
+ flex: 1 1 auto;
+ text-align: center;
+ margin: .6em;
+ }
}
}
}
diff --git a/ui/msg/src/ctrl.ts b/ui/msg/src/ctrl.ts
index 835ce73d7a15..b70a2519f0fe 100644
--- a/ui/msg/src/ctrl.ts
+++ b/ui/msg/src/ctrl.ts
@@ -55,4 +55,14 @@ export default class MsgCtrl {
this.redraw();
}
}
+
+ block = () => {
+ const userId = this.data.convo?.thread.contact.id;
+ if (userId) network.block(userId).then(() => this.openConvo(userId));
+ }
+
+ unblock = () => {
+ const userId = this.data.convo?.thread.contact.id;
+ if (userId) network.unblock(userId).then(() => this.openConvo(userId));
+ }
}
diff --git a/ui/msg/src/network.ts b/ui/msg/src/network.ts
index 6d3d508917e9..3515beafa754 100644
--- a/ui/msg/src/network.ts
+++ b/ui/msg/src/network.ts
@@ -36,6 +36,20 @@ export function search(q: string) {
});
}
+export function block(u: string) {
+ return $.ajax({
+ url: `/rel/block/${u}`,
+ method: 'post'
+ });
+}
+
+export function unblock(u: string) {
+ return $.ajax({
+ url: `/rel/unblock/${u}`,
+ method: 'post'
+ });
+}
+
export function post(dest: string, text: string) {
window.lichess.pubsub.emit('socket.send', 'msgSend', { dest, text });
}
diff --git a/ui/msg/src/view/actions.ts b/ui/msg/src/view/actions.ts
new file mode 100644
index 000000000000..8ab3900feb2e
--- /dev/null
+++ b/ui/msg/src/view/actions.ts
@@ -0,0 +1,52 @@
+import { h } from 'snabbdom'
+import { VNode } from 'snabbdom/vnode'
+import { Convo } from '../interfaces'
+import { bind } from './util';
+import MsgCtrl from '../ctrl';
+
+export default function renderActions(ctrl: MsgCtrl, convo: Convo): VNode[] {
+ const user = convo.thread.contact, nodes = [];
+ const cls = 'msg-app__convo__action.button.button-empty';
+ nodes.push(
+ h(`a.${cls}`, {
+ key: 'play',
+ attrs: {
+ 'data-icon': 'U',
+ href: `/?user=${user.name}#friend`,
+ title: ctrl.trans.noarg('challengeToPlay')
+ }
+ })
+ );
+ if (convo.relations.out === false) nodes.push(
+ h(`button.${cls}.unbad.text.hover-text`, {
+ key: 'unblock',
+ attrs: {
+ 'data-icon': 'k',
+ title: ctrl.trans.noarg('blocked'),
+ 'data-hover-text': ctrl.trans.noarg('unblock')
+ },
+ hook: bind('click', ctrl.unblock)
+ })
+ );
+ else nodes.push(
+ h(`button.${cls}.bad`, {
+ key: 'block',
+ attrs: {
+ 'data-icon': 'k',
+ title: ctrl.trans.noarg('block')
+ },
+ hook: bind('click', ctrl.block)
+ })
+ );
+ nodes.push(
+ h(`button.${cls}.bad.confirm`, {
+ key: 'report',
+ attrs: {
+ 'data-icon': '!',
+ href: '/report/flag',
+ title: ctrl.trans.noarg('report')
+ }
+ })
+ );
+ return nodes;
+}
diff --git a/ui/msg/src/view/convo.ts b/ui/msg/src/view/convo.ts
index 704c6154cb4c..d513e2ee7c5e 100644
--- a/ui/msg/src/view/convo.ts
+++ b/ui/msg/src/view/convo.ts
@@ -3,7 +3,8 @@ import { VNode } from 'snabbdom/vnode'
import { Convo } from '../interfaces'
import { userName } from './util';
import renderMsgs from './msgs';
-import throttle from 'common/throttle';
+import renderActions from './actions';
+import renderTextarea from './textarea';
import MsgCtrl from '../ctrl';
export default function renderConvo(ctrl: MsgCtrl, convo: Convo): VNode {
@@ -26,109 +27,11 @@ export default function renderConvo(ctrl: MsgCtrl, convo: Convo): VNode {
]),
renderMsgs(ctrl, convo.msgs),
h('div.msg-app__convo__reply', [
- h('textarea.msg-app__convo__reply__text', {
- attrs: {
- rows: 1,
- autofocus: 1
- },
- hook: {
- insert(vnode) {
- setupTextarea(vnode.elm as HTMLTextAreaElement, user.id, ctrl.post);
- }
- }
- })
+ convo.relations.out === false || convo.relations.in === false ?
+ h('div.msg-app__convo__reply__block.text', {
+ attrs: { 'data-icon': 'k' }
+ }, 'This conversation is blocked.') :
+ renderTextarea(ctrl, user)
])
]);
}
-
-function renderActions(ctrl: MsgCtrl, convo: Convo): VNode[] {
- const user = convo.thread.contact, nodes = [];
- if (convo.relations.out) nodes.push(
- h('button.msg-app__convo__action.text.hover-text', {
- attrs: {
- 'data-icon': 'h',
- href: `/rel/unfollow/${user.id}`,
- title: ctrl.trans.noarg('following'),
- 'data-hover-text': ctrl.trans.noarg('unfollow')
- }
- })
- );
- else if (convo.relations.out === false) nodes.push(
- h('button.msg-app__convo__action.text.hover-text', {
- attrs: {
- 'data-icon': 'k',
- href: `/rel/unblock/${user.id}`,
- title: ctrl.trans.noarg('blocked'),
- 'data-hover-text': ctrl.trans.noarg('unblock')
- }
- })
- );
- else {
- nodes.push(
- h('a.msg-app__convo__action', {
- attrs: {
- 'data-icon': 'h',
- href: `/rel/follow/${user.id}`,
- title: ctrl.trans.noarg('follow')
- }
- })
- );
- nodes.push(
- h('a.msg-app__convo__action', {
- attrs: {
- 'data-icon': 'k',
- href: `/rel/block/${user.id}`,
- title: ctrl.trans.noarg('block')
- }
- })
- );
- }
- nodes.push(
- h('a.msg-app__convo__action', {
- attrs: {
- 'data-icon': 'i',
- href: '/report/flag',
- title: ctrl.trans.noarg('report')
- }
- })
- );
- return nodes;
-}
-
-function setupTextarea(area: HTMLTextAreaElement, contact: string, post: (text: string) => void) {
-
- // save the textarea content until sent
- const storage = window.lichess.storage.make(`msg:area:${contact}`);
-
- // hack to automatically resize the textarea based on content
- area.value = '';
- let baseScrollHeight = area.scrollHeight;
- area.addEventListener('input', throttle(500, () =>
- setTimeout(() => {
- const text = area.value.trim();
- area.rows = 1;
- // the resize magic
- if (text) area.rows = Math.min(10, 1 + Math.ceil((area.scrollHeight - baseScrollHeight) / 19));
- // and save content
- storage.set(text);
- })
- ));
-
- // restore previously saved content
- area.value = storage.get() || '';
- if (area.value) area.dispatchEvent(new Event('input'));
-
- // send the content on {
- if ((e.which == 10 || e.which == 13) && !e.shiftKey) {
- setTimeout(() => {
- const txt = area.value.trim();
- if (txt) post(txt);
- area.value = '';
- area.dispatchEvent(new Event('input')); // resize the textarea
- storage.remove();
- });
- }
- });
- area.focus();
-}
diff --git a/ui/msg/src/view/textarea.ts b/ui/msg/src/view/textarea.ts
new file mode 100644
index 000000000000..30242385b0c8
--- /dev/null
+++ b/ui/msg/src/view/textarea.ts
@@ -0,0 +1,57 @@
+import { h } from 'snabbdom'
+import { VNode } from 'snabbdom/vnode'
+import { User } from '../interfaces'
+import MsgCtrl from '../ctrl';
+import throttle from 'common/throttle';
+
+export default function renderTextarea(ctrl: MsgCtrl, user: User): VNode {
+ return h('textarea.msg-app__convo__reply__text', {
+ attrs: {
+ rows: 1,
+ autofocus: 1
+ },
+ hook: {
+ insert(vnode) {
+ setupTextarea(vnode.elm as HTMLTextAreaElement, user.id, ctrl.post);
+ }
+ }
+ });
+}
+
+function setupTextarea(area: HTMLTextAreaElement, contact: string, post: (text: string) => void) {
+
+ // save the textarea content until sent
+ const storage = window.lichess.storage.make(`msg:area:${contact}`);
+
+ // hack to automatically resize the textarea based on content
+ area.value = '';
+ let baseScrollHeight = area.scrollHeight;
+ area.addEventListener('input', throttle(500, () =>
+ setTimeout(() => {
+ const text = area.value.trim();
+ area.rows = 1;
+ // the resize magic
+ if (text) area.rows = Math.min(10, 1 + Math.ceil((area.scrollHeight - baseScrollHeight) / 19));
+ // and save content
+ storage.set(text);
+ })
+ ));
+
+ // restore previously saved content
+ area.value = storage.get() || '';
+ if (area.value) area.dispatchEvent(new Event('input'));
+
+ // send the content on {
+ if ((e.which == 10 || e.which == 13) && !e.shiftKey) {
+ setTimeout(() => {
+ const txt = area.value.trim();
+ if (txt) post(txt);
+ area.value = '';
+ area.dispatchEvent(new Event('input')); // resize the textarea
+ storage.remove();
+ });
+ }
+ });
+ area.focus();
+}