Render chat/messages in Vue (mostly broken)

This commit is contained in:
Pavel Djundik 2018-07-08 13:50:11 +03:00 committed by Pavel Djundik
parent 0b269423aa
commit 1d2a11729f
12 changed files with 203 additions and 93 deletions

View File

@ -62,7 +62,15 @@
<div <div
id="chat-container" id="chat-container"
class="window"> class="window">
<div id="chat"/> <div id="chat">
<!--Chat v-if="activeChannel" :channel="activeChannel.channel"/-->
<template v-for="network in networks">
<Chat
v-for="channel in network.channels"
:key="channel.id"
:channel="channel"/>
</template>
</div>
<div id="connection-error"/> <div id="connection-error"/>
<form <form
id="form" id="form"
@ -113,11 +121,13 @@
<script> <script>
import Network from "./Network.vue"; import Network from "./Network.vue";
import Chat from "./Chat.vue";
export default { export default {
name: "App", name: "App",
components: { components: {
Network, Network,
Chat,
}, },
props: { props: {
activeChannel: Object, activeChannel: Object,

111
client/components/Chat.vue Normal file
View File

@ -0,0 +1,111 @@
<template>
<div
:id="'chan-' + channel.id"
:class="[channel.type, 'chan']"
:data-id="channel.id"
:data-type="channel.type"
:aria-label="channel.name"
role="tabpanel"
>
<div class="header">
<button
class="lt"
aria-label="Toggle channel list"/>
<span class="title">{{ channel.name }}</span>
<span
:title="channel.topic"
class="topic"
v-html="$options.filters.parse(channel.topic)"/>
<button
class="menu"
aria-label="Open the context menu"
/>
<span
v-if="channel.type === 'channel'"
class="rt-tooltip tooltipped tooltipped-w"
aria-label="Toggle user list">
<button
class="rt"
aria-label="Toggle user list"/>
</span>
</div>
<div class="chat-content">
<div class="chat">
<div
v-if="channel.messages.length > 0"
class="show-more">
<button
:data-id="channel.id"
class="btn"
data-alt-text="Loading…">Show older messages</button>
</div>
<div
class="messages"
role="log"
aria-live="polite"
aria-relevant="additions"
>
<template v-for="(message, id) in channel.messages">
<div
v-if="shouldDisplayDateMarker(id)"
:key="message.id + '-date'"
:data-time="message.time"
:aria-label="message.time | localedate"
class="date-marker-container tooltipped tooltipped-s"
>
<div class="date-marker">
<span
:data-label="message.time | friendlydate"
class="date-marker-text"/>
</div>
</div>
<Message
:message="message"
:key="message.id"/>
</template>
</div>
</div>
<aside
v-if="channel.type === 'channel'"
class="userlist">
<div class="count">
<input
type="search"
class="search"
aria-label="Search among the user list"
tabindex="-1">
</div>
<div class="names names-filtered"/>
<div class="names names-original"/>
</aside>
</div>
</div>
</template>
<script>
import Message from "./Message.vue";
export default {
name: "Chat",
components: {
Message,
},
props: {
channel: Object,
},
methods: {
shouldDisplayDateMarker(id) {
const previousTime = this.channel.messages[id - 1];
if (!previousTime) {
return true;
}
const currentTime = this.channel.messages[id];
return (new Date(previousTime.time)).getDay() !== (new Date(currentTime.time)).getDay();
},
},
};
</script>

View File

@ -0,0 +1,53 @@
<template>
<div
:id="'msg-' + message.id"
:class="['msg', message.type, {self: message.self, highlight: message.highlight}]"
:data-time="message.time"
:data-from="message.from && message.from.nick"
>
<span
:aria-label="message.time | localetime"
class="time tooltipped tooltipped-e">{{ message.time | tz }}</span>
<template v-if="message.type === 'unhandled'">
<span class="from">[{{ message.command }}]</span>
<span class="content">
<span
v-for="(param, id) in message.params"
:key="id">{{ param }}</span>
</span>
</template>
<template v-elseif="message.type === 'message'">
<span class="from">
<template v-if="message.from && message.from.nick">
<Username :user="message.from"/>
</template>
</span>
<span class="content">
<span
class="text"
v-html="$options.filters.parse(message.text, message.users)"/>
<div
v-for="preview in message.previews"
:key="preview.link"
:data-url="preview.link"
class="preview"/>
</span>
</template>
</div>
</template>
<script>
import Username from "./Username.vue";
export default {
name: "Message",
components: {
Username,
},
props: {
message: Object,
},
};
</script>

View File

@ -0,0 +1,15 @@
<template>
<span
:class="['user', $options.filters.colorClass(user.nick)]"
:data-name="user.nick"
role="button">{{ user.mode }}{{ user.nick }}</span>
</template>
<script>
export default {
name: "Username",
props: {
user: Object,
},
};
</script>

View File

@ -148,7 +148,7 @@ function renderChannel(data) {
function renderChannelMessages(data) { function renderChannelMessages(data) {
const documentFragment = buildChannelMessages($(document.createDocumentFragment()), data.id, data.type, data.messages); const documentFragment = buildChannelMessages($(document.createDocumentFragment()), data.id, data.type, data.messages);
const channel = chat.find("#chan-" + data.id + " .messages").append(documentFragment); const channel = chat.find("#chan-" + data.id + " .messages");
renderUnreadMarker($(templates.unread_marker()), data.firstUnread, channel); renderUnreadMarker($(templates.unread_marker()), data.firstUnread, channel);
} }
@ -264,12 +264,6 @@ function renderNetworks(data, singleNetwork) {
} }
if (newChannels.length > 0) { if (newChannels.length > 0) {
chat.append(
templates.chat({
channels: newChannels,
})
);
newChannels.forEach((channel) => { newChannels.forEach((channel) => {
renderChannel(channel); renderChannel(channel);

View File

@ -3,7 +3,6 @@
const $ = require("jquery"); const $ = require("jquery");
const socket = require("../socket"); const socket = require("../socket");
const render = require("../render"); const render = require("../render");
const chat = $("#chat");
const templates = require("../../views"); const templates = require("../../views");
const sidebar = $("#sidebar"); const sidebar = $("#sidebar");
const {Vue, vueApp} = require("../vue"); const {Vue, vueApp} = require("../vue");
@ -12,12 +11,6 @@ socket.on("join", function(data) {
vueApp.networks.find((n) => n.uuid === data.network) vueApp.networks.find((n) => n.uuid === data.network)
.channels.splice(data.index || -1, 0, data.chan); .channels.splice(data.index || -1, 0, data.chan);
chat.append(
templates.chat({
channels: [data.chan],
})
);
Vue.nextTick(() => render.renderChannel(data.chan)); Vue.nextTick(() => render.renderChannel(data.chan));
// Queries do not automatically focus, unless the user did a whois // Queries do not automatically focus, unless the user did a whois

View File

@ -13,8 +13,6 @@ socket.on("part", function(data) {
.trigger("click"); .trigger("click");
} }
$("#chan-" + data.chan).remove();
const network = vueApp.networks.find((n) => n.uuid === data.network); const network = vueApp.networks.find((n) => n.uuid === data.network);
network.channels.splice(network.channels.findIndex((c) => c.id === data.chan), 1); network.channels.splice(network.channels.findIndex((c) => c.id === data.chan), 1);
}); });

View File

@ -1,7 +1,6 @@
"use strict"; "use strict";
const $ = require("jquery"); const $ = require("jquery");
const chat = $("#chat");
const socket = require("../socket"); const socket = require("../socket");
const sidebar = $("#sidebar"); const sidebar = $("#sidebar");
const {Vue, vueApp} = require("../vue"); const {Vue, vueApp} = require("../vue");
@ -9,14 +8,6 @@ const {Vue, vueApp} = require("../vue");
socket.on("quit", function(data) { socket.on("quit", function(data) {
vueApp.networks.splice(vueApp.networks.findIndex((n) => n.uuid === data.network), 1); vueApp.networks.splice(vueApp.networks.findIndex((n) => n.uuid === data.network), 1);
const id = data.network;
const network = sidebar.find(`.network[data-uuid="${id}"]`);
network.children(".chan").each(function() {
// this = child
chat.find($(this).attr("data-target")).remove();
});
Vue.nextTick(() => { Vue.nextTick(() => {
const chan = sidebar.find(".chan"); const chan = sidebar.find(".chan");

View File

@ -3,7 +3,19 @@
const Vue = require("vue").default; const Vue = require("vue").default;
const App = require("../components/App.vue").default; const App = require("../components/App.vue").default;
const roundBadgeNumber = require("./libs/handlebars/roundBadgeNumber"); const roundBadgeNumber = require("./libs/handlebars/roundBadgeNumber");
const parse = require("./libs/handlebars/parse");
const tz = require("./libs/handlebars/tz");
const localetime = require("./libs/handlebars/localetime");
const localedate = require("./libs/handlebars/localedate");
const friendlydate = require("./libs/handlebars/friendlydate");
const colorClass = require("./libs/handlebars/colorClass");
Vue.filter("parse", parse);
Vue.filter("tz", tz);
Vue.filter("localetime", localetime);
Vue.filter("localedate", localedate);
Vue.filter("friendlydate", friendlydate);
Vue.filter("colorClass", colorClass);
Vue.filter("roundBadgeNumber", roundBadgeNumber); Vue.filter("roundBadgeNumber", roundBadgeNumber);
const vueApp = new Vue({ const vueApp = new Vue({

View File

@ -1,39 +0,0 @@
{{#each channels}}
<div
id="chan-{{id}}"
class="chan {{type}}"
data-id="{{id}}"
data-type="{{type}}"
role="tabpanel"
aria-label="{{name}}"
>
<div class="header">
<button class="lt" aria-label="Toggle channel list"></button>
<span class="title">{{name}}</span>
<span title="{{topic}}" class="topic">{{{parse topic}}}</span>
<button class="menu" aria-label="Open the context menu"></button>
{{#equal type "channel"}}
<span class="rt-tooltip tooltipped tooltipped-w" aria-label="Toggle user list">
<button class="rt" aria-label="Toggle user list"></button>
</span>
{{/equal}}
</div>
<div class="chat-content">
<div class="chat">
<div class="show-more{{#if messages.length}} show{{/if}}">
<button class="btn" data-id="{{id}}" data-alt-text="Loading…">Show older messages</button>
</div>
<div class="messages" role="log" aria-live="polite" aria-relevant="additions"></div>
</div>
{{#equal type "channel"}}
<aside class="userlist">
<div class="count">
<input type="search" class="search" aria-label="Search among the user list" tabindex="-1">
</div>
<div class="names names-filtered"></div>
<div class="names names-original"></div>
</aside>
{{/equal}}
</div>
</div>
{{/each}}

View File

@ -1,17 +0,0 @@
<div class="msg {{type}}{{#if self}} self{{/if}}{{#if highlight}} highlight{{/if}}" id="msg-{{id}}" data-time="{{time}}"{{#if from.nick}} data-from="{{from.nick}}"{{/if}}>
<span class="time tooltipped tooltipped-e" aria-label="{{localetime time}}">
{{tz time}}
</span>
<span class="from">
{{#if from.nick}}
{{> user_name from}}
{{/if}}
</span>
<span class="content">
<span class="text">{{{parse text users}}}</span>
{{#each previews}}
<div class="preview" data-url="{{link}}"></div>
{{/each}}
</span>
</div>

View File

@ -1,11 +0,0 @@
<div class="msg msg-{{slugify command}} {{type}}{{#if self}} self{{/if}}{{#if highlight}} highlight{{/if}}" id="msg-{{id}}" data-time="{{time}}">
<span class="time tooltipped tooltipped-e" aria-label="{{localetime time}}">
{{tz time}}
</span>
<span class="from">[{{command}}]</span>
<span class="content">
{{#each params}}
<span>{{this}}</span>
{{/each}}
</span>
</div>