Render chat/messages in Vue (mostly broken)
This commit is contained in:
parent
0b269423aa
commit
1d2a11729f
@ -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
111
client/components/Chat.vue
Normal 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>
|
53
client/components/Message.vue
Normal file
53
client/components/Message.vue
Normal 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>
|
15
client/components/Username.vue
Normal file
15
client/components/Username.vue
Normal 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>
|
@ -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);
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
|
@ -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");
|
||||||
|
|
||||||
|
@ -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({
|
||||||
|
@ -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}}
|
|
@ -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>
|
|
@ -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>
|
|
Loading…
Reference in New Issue
Block a user