mirror of
https://github.com/thelounge/thelounge.git
synced 2024-06-03 14:22:19 +02:00
360 lines
7.9 KiB
Vue
360 lines
7.9 KiB
Vue
<template>
|
|
<div id="chat-container" class="window" :data-current-channel="channel.name" lang="">
|
|
<div
|
|
id="chat"
|
|
:class="{
|
|
'hide-motd': !$store.state.settings.motd,
|
|
'colored-nicks': $store.state.settings.coloredNicks,
|
|
'time-seconds': $store.state.settings.showSeconds,
|
|
'time-12h': $store.state.settings.use12hClock,
|
|
}"
|
|
>
|
|
<div
|
|
:id="'chan-' + channel.id"
|
|
class="chat-view"
|
|
:data-type="channel.type"
|
|
:aria-label="channel.name"
|
|
role="tabpanel"
|
|
>
|
|
<div class="header">
|
|
<SidebarToggle />
|
|
<span class="title" :aria-label="'Currently open ' + channel.type">{{
|
|
channel.name
|
|
}}</span>
|
|
<div v-if="channel.editTopic === true" class="topic-container">
|
|
<input
|
|
ref="topicInput"
|
|
:value="channel.topic"
|
|
class="topic-input"
|
|
placeholder="Set channel topic"
|
|
enterkeyhint="done"
|
|
@keyup.enter="saveTopic"
|
|
@keyup.esc="channel.editTopic = false"
|
|
/>
|
|
<span aria-label="Save topic" class="save-topic" @click="saveTopic">
|
|
<span type="button" aria-label="Save topic"></span>
|
|
</span>
|
|
</div>
|
|
<span
|
|
v-else
|
|
:title="channel.topic"
|
|
:class="{topic: true, empty: !channel.topic}"
|
|
@dblclick="editTopic"
|
|
><ParsedMessage
|
|
v-if="channel.topic"
|
|
:network="network"
|
|
:text="channel.topic"
|
|
/></span>
|
|
<MessageSearchForm
|
|
v-if="
|
|
$store.state.settings.searchEnabled &&
|
|
['channel', 'query'].includes(channel.type)
|
|
"
|
|
:network="network"
|
|
:channel="channel"
|
|
/>
|
|
<button
|
|
class="mentions"
|
|
aria-label="Open your mentions"
|
|
@click="openMentions"
|
|
/>
|
|
<button
|
|
class="menu"
|
|
aria-label="Open the context menu"
|
|
@click="openContextMenu"
|
|
/>
|
|
<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"
|
|
@click="$store.commit('toggleUserlist')"
|
|
/>
|
|
</span>
|
|
</div>
|
|
<div v-if="channel.type === 'special'" class="chat-content">
|
|
<div class="chat">
|
|
<div class="messages">
|
|
<div class="msg">
|
|
<component
|
|
:is="specialComponent"
|
|
:network="network"
|
|
:channel="channel"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-else class="chat-content">
|
|
<div
|
|
:class="[
|
|
'scroll-down tooltipped tooltipped-w tooltipped-no-touch',
|
|
{'scroll-down-shown': !channel.scrolledToBottom},
|
|
]"
|
|
aria-label="Jump to recent messages"
|
|
@click="$refs.messageList.jumpToBottom()"
|
|
>
|
|
<div class="scroll-down-arrow" />
|
|
</div>
|
|
<ChatUserList v-if="channel.type === 'channel'" :channel="channel" />
|
|
<MessageList
|
|
ref="messageList"
|
|
:network="network"
|
|
:channel="channel"
|
|
:focused="focused"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="$store.state.currentUserVisibleError"
|
|
id="user-visible-error"
|
|
@click="hideUserVisibleError"
|
|
>
|
|
{{ $store.state.currentUserVisibleError }}
|
|
</div>
|
|
<ChatInput :network="network" :channel="channel" />
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
@import "../css/special-channels.css";
|
|
@import "../css/colored-nicks.css";
|
|
|
|
#chat.hide-motd .msg[data-command="motd"] {
|
|
display: none !important;
|
|
}
|
|
|
|
#user-visible-error {
|
|
font-size: 14px;
|
|
line-height: 1.5;
|
|
font-weight: 600;
|
|
padding: 10px;
|
|
word-spacing: 3px;
|
|
text-transform: uppercase;
|
|
background: #e74c3c;
|
|
color: #fff;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.scroll-down {
|
|
position: absolute;
|
|
bottom: 16px;
|
|
right: 16px;
|
|
z-index: 2;
|
|
pointer-events: none;
|
|
opacity: 0;
|
|
transform: translateY(16px);
|
|
transition: transform 0.2s, opacity 0.2s;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.scroll-down-shown {
|
|
opacity: 1;
|
|
transform: none;
|
|
pointer-events: auto;
|
|
}
|
|
|
|
.scroll-down-arrow {
|
|
width: 36px;
|
|
height: 36px;
|
|
line-height: 34px;
|
|
border-radius: 50%;
|
|
background: var(--window-bg-color);
|
|
color: var(--button-color);
|
|
border: 2px solid var(--button-color);
|
|
text-align: center;
|
|
transition: background 0.2s, color 0.2s;
|
|
box-shadow: 0 6px 10px 0 rgb(0 0 0 / 15%);
|
|
}
|
|
|
|
.scroll-down:hover .scroll-down-arrow {
|
|
background: var(--button-color);
|
|
color: var(--button-text-color-hover);
|
|
}
|
|
|
|
.scroll-down-arrow::after {
|
|
content: "\f107"; /* https://fontawesome.com/icons/angle-down?style=solid */
|
|
}
|
|
|
|
#chat .chat-view[data-type="special"] table th {
|
|
word-break: normal;
|
|
}
|
|
|
|
.topic-container {
|
|
position: relative;
|
|
flex-grow: 1;
|
|
padding-left: 10px;
|
|
}
|
|
|
|
.header .topic.empty {
|
|
min-width: 0;
|
|
}
|
|
|
|
.header .topic-input {
|
|
color: inherit;
|
|
background: transparent;
|
|
border: 1px solid #cdd3da;
|
|
border-radius: 2px;
|
|
padding-right: 37px;
|
|
padding-left: 10px;
|
|
width: 100%;
|
|
height: 35px;
|
|
overflow: hidden;
|
|
font-size: 14px;
|
|
line-height: normal;
|
|
outline: none;
|
|
}
|
|
|
|
.topic-container .save-topic {
|
|
position: absolute;
|
|
top: 6px;
|
|
right: 0;
|
|
}
|
|
|
|
.topic-container .save-topic span {
|
|
font-size: 16px;
|
|
color: #607992;
|
|
width: 35px;
|
|
height: 35px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
appearance: none;
|
|
}
|
|
|
|
.topic-container .save-topic span:hover {
|
|
opacity: 0.6;
|
|
}
|
|
|
|
/deep/ table.channel-list .channel,
|
|
/deep/ table.channel-list .topic,
|
|
/deep/ table.ban-list .hostmask,
|
|
/deep/ table.ban-list .banned_by,
|
|
/deep/ table.ban-list .banned_at,
|
|
/deep/ table.ignore-list .hostmask,
|
|
/deep/ table.ignore-list .when {
|
|
text-align: left;
|
|
}
|
|
</style>
|
|
<script>
|
|
import socket from "../js/socket";
|
|
import eventbus from "../js/eventbus";
|
|
import ParsedMessage from "./ParsedMessage.vue";
|
|
import MessageList from "./MessageList.vue";
|
|
import ChatInput from "./ChatInput.vue";
|
|
import ChatUserList from "./ChatUserList.vue";
|
|
import SidebarToggle from "./SidebarToggle.vue";
|
|
import MessageSearchForm from "./MessageSearchForm.vue";
|
|
import ListBans from "./Special/ListBans.vue";
|
|
import ListInvites from "./Special/ListInvites.vue";
|
|
import ListChannels from "./Special/ListChannels.vue";
|
|
import ListIgnored from "./Special/ListIgnored.vue";
|
|
|
|
export default {
|
|
name: "Chat",
|
|
components: {
|
|
ParsedMessage,
|
|
MessageList,
|
|
ChatInput,
|
|
ChatUserList,
|
|
SidebarToggle,
|
|
MessageSearchForm,
|
|
},
|
|
props: {
|
|
network: Object,
|
|
channel: Object,
|
|
focused: String,
|
|
},
|
|
computed: {
|
|
specialComponent() {
|
|
switch (this.channel.special) {
|
|
case "list_bans":
|
|
return ListBans;
|
|
case "list_invites":
|
|
return ListInvites;
|
|
case "list_channels":
|
|
return ListChannels;
|
|
case "list_ignored":
|
|
return ListIgnored;
|
|
}
|
|
|
|
return undefined;
|
|
},
|
|
},
|
|
watch: {
|
|
channel() {
|
|
this.channelChanged();
|
|
},
|
|
"channel.editTopic"(newValue) {
|
|
if (newValue) {
|
|
this.$nextTick(() => {
|
|
this.$refs.topicInput.focus();
|
|
});
|
|
}
|
|
},
|
|
},
|
|
mounted() {
|
|
this.channelChanged();
|
|
|
|
if (this.channel.editTopic) {
|
|
this.$nextTick(() => {
|
|
this.$refs.topicInput.focus();
|
|
});
|
|
}
|
|
},
|
|
methods: {
|
|
channelChanged() {
|
|
// Triggered when active channel is set or changed
|
|
this.channel.highlight = 0;
|
|
this.channel.unread = 0;
|
|
|
|
socket.emit("open", this.channel.id);
|
|
|
|
if (this.channel.usersOutdated) {
|
|
this.channel.usersOutdated = false;
|
|
|
|
socket.emit("names", {
|
|
target: this.channel.id,
|
|
});
|
|
}
|
|
},
|
|
hideUserVisibleError() {
|
|
this.$store.commit("currentUserVisibleError", null);
|
|
},
|
|
editTopic() {
|
|
if (this.channel.type === "channel") {
|
|
this.channel.editTopic = true;
|
|
}
|
|
},
|
|
saveTopic() {
|
|
this.channel.editTopic = false;
|
|
const newTopic = this.$refs.topicInput.value;
|
|
|
|
if (this.channel.topic !== newTopic) {
|
|
const target = this.channel.id;
|
|
const text = `/raw TOPIC ${this.channel.name} :${newTopic}`;
|
|
socket.emit("input", {target, text});
|
|
}
|
|
},
|
|
openContextMenu(event) {
|
|
eventbus.emit("contextmenu:channel", {
|
|
event: event,
|
|
channel: this.channel,
|
|
network: this.network,
|
|
});
|
|
},
|
|
openMentions(event) {
|
|
eventbus.emit("mentions:toggle", {
|
|
event: event,
|
|
});
|
|
},
|
|
},
|
|
};
|
|
</script>
|