mirror of
https://github.com/thelounge/thelounge.git
synced 2024-06-08 08:42:17 +02:00
Vue 3 upgrade
This commit is contained in:
parent
5329483a40
commit
0bd65e5f19
|
@ -71,6 +71,6 @@ plugins:
|
||||||
|
|
||||||
extends:
|
extends:
|
||||||
- eslint:recommended
|
- eslint:recommended
|
||||||
- plugin:vue/recommended
|
- plugin:vue/vue3-recommended
|
||||||
- prettier
|
- prettier
|
||||||
- prettier/vue
|
- prettier/vue
|
||||||
|
|
|
@ -18,6 +18,9 @@ import Mousetrap from "mousetrap";
|
||||||
import throttle from "lodash/throttle";
|
import throttle from "lodash/throttle";
|
||||||
import storage from "../js/localStorage";
|
import storage from "../js/localStorage";
|
||||||
import isIgnoredKeybind from "../js/helpers/isIgnoredKeybind";
|
import isIgnoredKeybind from "../js/helpers/isIgnoredKeybind";
|
||||||
|
import socket from "../js/socket";
|
||||||
|
|
||||||
|
import {navigate} from "../js/router";
|
||||||
|
|
||||||
import Sidebar from "./Sidebar.vue";
|
import Sidebar from "./Sidebar.vue";
|
||||||
import ImageViewer from "./ImageViewer.vue";
|
import ImageViewer from "./ImageViewer.vue";
|
||||||
|
@ -67,8 +70,10 @@ export default {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dayChangeTimeout = setTimeout(emitDayChange, this.msUntilNextDay());
|
this.dayChangeTimeout = setTimeout(emitDayChange, this.msUntilNextDay());
|
||||||
|
|
||||||
|
socket.open();
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeUnmount() {
|
||||||
Mousetrap.unbind("esc", this.escapeKey);
|
Mousetrap.unbind("esc", this.escapeKey);
|
||||||
Mousetrap.unbind("alt+u", this.toggleUserList);
|
Mousetrap.unbind("alt+u", this.toggleUserList);
|
||||||
Mousetrap.unbind("alt+s", this.toggleSidebar);
|
Mousetrap.unbind("alt+s", this.toggleSidebar);
|
||||||
|
@ -124,6 +129,41 @@ export default {
|
||||||
|
|
||||||
this.$store.commit("userlistOpen", isUserlistOpen === "true");
|
this.$store.commit("userlistOpen", isUserlistOpen === "true");
|
||||||
},
|
},
|
||||||
|
switchToChannel(channel) {
|
||||||
|
navigate("RoutedChat", {id: channel.id});
|
||||||
|
},
|
||||||
|
closeChannel(channel) {
|
||||||
|
if (channel.type === "lobby") {
|
||||||
|
eventbus.emit(
|
||||||
|
"confirm-dialog",
|
||||||
|
{
|
||||||
|
title: "Remove network",
|
||||||
|
text: `Are you sure you want to quit and remove ${channel.name}? This cannot be undone.`,
|
||||||
|
button: "Remove network",
|
||||||
|
},
|
||||||
|
(result) => {
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.closed = true;
|
||||||
|
socket.emit("input", {
|
||||||
|
target: Number(channel.id),
|
||||||
|
text: "/quit",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.closed = true;
|
||||||
|
|
||||||
|
socket.emit("input", {
|
||||||
|
target: Number(channel.id),
|
||||||
|
text: "/close",
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -103,7 +103,7 @@ export default {
|
||||||
this.setInputSize();
|
this.setInputSize();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
beforeUnmount() {
|
||||||
eventbus.on("escapekey", this.blurInput);
|
eventbus.on("escapekey", this.blurInput);
|
||||||
|
|
||||||
if (this.$store.state.settings.autocomplete) {
|
if (this.$store.state.settings.autocomplete) {
|
||||||
|
@ -165,7 +165,7 @@ export default {
|
||||||
upload.mounted();
|
upload.mounted();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
destroyed() {
|
unmounted() {
|
||||||
eventbus.off("escapekey", this.blurInput);
|
eventbus.off("escapekey", this.blurInput);
|
||||||
|
|
||||||
if (autocompletionRef) {
|
if (autocompletionRef) {
|
||||||
|
|
|
@ -65,7 +65,7 @@ export default {
|
||||||
eventbus.on("escapekey", this.close);
|
eventbus.on("escapekey", this.close);
|
||||||
eventbus.on("confirm-dialog", this.open);
|
eventbus.on("confirm-dialog", this.open);
|
||||||
},
|
},
|
||||||
destroyed() {
|
unmounted() {
|
||||||
eventbus.off("escapekey", this.close);
|
eventbus.off("escapekey", this.close);
|
||||||
eventbus.off("confirm-dialog", this.open);
|
eventbus.off("confirm-dialog", this.open);
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,9 +18,8 @@
|
||||||
@mouseleave="activeItem = -1"
|
@mouseleave="activeItem = -1"
|
||||||
@keydown.enter.prevent="clickActiveItem"
|
@keydown.enter.prevent="clickActiveItem"
|
||||||
>
|
>
|
||||||
<template v-for="(item, id) of items">
|
<template v-for="(item, id) of items" :key="item.name">
|
||||||
<li
|
<li
|
||||||
:key="item.name"
|
|
||||||
:class="[
|
:class="[
|
||||||
'context-menu-' + item.type,
|
'context-menu-' + item.type,
|
||||||
item.class ? 'context-menu-' + item.class : null,
|
item.class ? 'context-menu-' + item.class : null,
|
||||||
|
@ -63,7 +62,7 @@ export default {
|
||||||
eventbus.on("contextmenu:user", this.openUserContextMenu);
|
eventbus.on("contextmenu:user", this.openUserContextMenu);
|
||||||
eventbus.on("contextmenu:channel", this.openChannelContextMenu);
|
eventbus.on("contextmenu:channel", this.openChannelContextMenu);
|
||||||
},
|
},
|
||||||
destroyed() {
|
unmounted() {
|
||||||
eventbus.off("escapekey", this.close);
|
eventbus.off("escapekey", this.close);
|
||||||
eventbus.off("contextmenu:user", this.openUserContextMenu);
|
eventbus.off("contextmenu:user", this.openUserContextMenu);
|
||||||
eventbus.off("contextmenu:channel", this.openChannelContextMenu);
|
eventbus.off("contextmenu:channel", this.openChannelContextMenu);
|
||||||
|
|
|
@ -28,7 +28,7 @@ export default {
|
||||||
eventbus.on("daychange", this.dayChange);
|
eventbus.on("daychange", this.dayChange);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeUnmount() {
|
||||||
eventbus.off("daychange", this.dayChange);
|
eventbus.off("daychange", this.dayChange);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -42,7 +42,7 @@ export default {
|
||||||
name: "JoinChannel",
|
name: "JoinChannel",
|
||||||
directives: {
|
directives: {
|
||||||
focus: {
|
focus: {
|
||||||
inserted(el) {
|
mounted(el) {
|
||||||
el.focus();
|
el.focus();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -51,6 +51,7 @@ export default {
|
||||||
network: Object,
|
network: Object,
|
||||||
channel: Object,
|
channel: Object,
|
||||||
},
|
},
|
||||||
|
emits: ["toggle-join-channel"],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
inputChannel: "",
|
inputChannel: "",
|
||||||
|
|
|
@ -172,10 +172,10 @@ export default {
|
||||||
|
|
||||||
this.onPreviewUpdate();
|
this.onPreviewUpdate();
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeUnmount() {
|
||||||
eventbus.off("resize", this.handleResize);
|
eventbus.off("resize", this.handleResize);
|
||||||
},
|
},
|
||||||
destroyed() {
|
unmounted() {
|
||||||
// Let this preview go through load/canplay events again,
|
// Let this preview go through load/canplay events again,
|
||||||
// Otherwise the browser can cause a resize on video elements
|
// Otherwise the browser can cause a resize on video elements
|
||||||
this.link.sourceLoaded = false;
|
this.link.sourceLoaded = false;
|
||||||
|
@ -199,8 +199,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPreviewReady() {
|
onPreviewReady() {
|
||||||
this.$set(this.link, "sourceLoaded", true);
|
|
||||||
|
|
||||||
this.keepScrollPosition();
|
this.keepScrollPosition();
|
||||||
|
|
||||||
if (this.link.type === "link") {
|
if (this.link.type === "link") {
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
<p v-if="isLoading">Loading…</p>
|
<p v-if="isLoading">Loading…</p>
|
||||||
<p v-else>You have no recent mentions.</p>
|
<p v-else>You have no recent mentions.</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-for="message in resolvedMessages" v-else>
|
<template v-for="message in resolvedMessages" v-else :key="message.msgId">
|
||||||
<div :key="message.msgId" :class="['msg', message.type]">
|
<div :class="['msg', message.type]">
|
||||||
<div class="mentions-info">
|
<div class="mentions-info">
|
||||||
<div>
|
<div>
|
||||||
<span class="from">
|
<span class="from">
|
||||||
|
@ -177,14 +177,17 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
"$store.state.mentions"() {
|
"$store.state.mentions": {
|
||||||
this.isLoading = false;
|
handler() {
|
||||||
|
this.isLoading = false;
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
eventbus.on("mentions:toggle", this.openPopup);
|
eventbus.on("mentions:toggle", this.openPopup);
|
||||||
},
|
},
|
||||||
destroyed() {
|
unmounted() {
|
||||||
eventbus.off("mentions:toggle", this.openPopup);
|
eventbus.off("mentions:toggle", this.openPopup);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<template v-if="message.type === 'unhandled'">
|
<template v-if="message.type === 'unhandled'">
|
||||||
<span class="from">[{{ message.command }}]</span>
|
<span class="from">[{{ message.command }}]</span>
|
||||||
<span class="content">
|
<span class="content">
|
||||||
<span v-for="(param, id) in message.params" :key="id">{{ param }} </span>
|
<span class="content">{{ message.params.join(" ") }}</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="isAction()">
|
<template v-else-if="isAction()">
|
||||||
|
|
|
@ -18,12 +18,8 @@
|
||||||
aria-relevant="additions"
|
aria-relevant="additions"
|
||||||
@copy="onCopy"
|
@copy="onCopy"
|
||||||
>
|
>
|
||||||
<template v-for="(message, id) in condensedMessages">
|
<template v-for="(message, id) in condensedMessages" :key="message.id + '-date'">
|
||||||
<DateMarker
|
<DateMarker v-if="shouldDisplayDateMarker(message, id)" :message="message" />
|
||||||
v-if="shouldDisplayDateMarker(message, id)"
|
|
||||||
:key="message.id + '-date'"
|
|
||||||
:message="message"
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
v-if="shouldDisplayUnreadMarker(message.id)"
|
v-if="shouldDisplayUnreadMarker(message.id)"
|
||||||
:key="message.id + '-unread'"
|
:key="message.id + '-unread'"
|
||||||
|
@ -148,8 +144,11 @@ export default {
|
||||||
this.historyObserver.observe(this.$refs.loadMoreButton);
|
this.historyObserver.observe(this.$refs.loadMoreButton);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"channel.messages"() {
|
"channel.messages": {
|
||||||
this.keepScrollPosition();
|
handler() {
|
||||||
|
this.keepScrollPosition();
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
},
|
},
|
||||||
"channel.pendingMessage"() {
|
"channel.pendingMessage"() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
@ -187,11 +186,11 @@ export default {
|
||||||
beforeUpdate() {
|
beforeUpdate() {
|
||||||
unreadMarkerShown = false;
|
unreadMarkerShown = false;
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeUnmount() {
|
||||||
eventbus.off("resize", this.handleResize);
|
eventbus.off("resize", this.handleResize);
|
||||||
this.$refs.chat.removeEventListener("scroll", this.handleScroll);
|
this.$refs.chat.removeEventListener("scroll", this.handleScroll);
|
||||||
},
|
},
|
||||||
destroyed() {
|
unmounted() {
|
||||||
if (this.historyObserver) {
|
if (this.historyObserver) {
|
||||||
this.historyObserver.disconnect();
|
this.historyObserver.disconnect();
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,9 +50,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="message.whois.special">
|
<template v-if="message.whois.special">
|
||||||
<template v-for="special in message.whois.special">
|
<template v-for="special in message.whois.special" :key="special">
|
||||||
<dt :key="special">Special:</dt>
|
<dt>Special:</dt>
|
||||||
<dd :key="special">{{ special }}</dd>
|
<dd>{{ special }}</dd>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -104,10 +104,9 @@
|
||||||
@start="onDragStart"
|
@start="onDragStart"
|
||||||
@end="onDragEnd"
|
@end="onDragEnd"
|
||||||
>
|
>
|
||||||
<template v-for="(channel, index) in network.channels">
|
<template v-for="(channel, index) in network.channels" :key="channel.id">
|
||||||
<Channel
|
<Channel
|
||||||
v-if="index > 0"
|
v-if="index > 0"
|
||||||
:key="channel.id"
|
|
||||||
:channel="channel"
|
:channel="channel"
|
||||||
:network="network"
|
:network="network"
|
||||||
:active="
|
:active="
|
||||||
|
@ -245,7 +244,7 @@ export default {
|
||||||
Mousetrap.bind("alt+shift+left", this.collapseNetwork);
|
Mousetrap.bind("alt+shift+left", this.collapseNetwork);
|
||||||
Mousetrap.bind("alt+j", this.toggleSearch);
|
Mousetrap.bind("alt+j", this.toggleSearch);
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeUnmount() {
|
||||||
Mousetrap.unbind("alt+shift+right", this.expandNetwork);
|
Mousetrap.unbind("alt+shift+right", this.expandNetwork);
|
||||||
Mousetrap.unbind("alt+shift+left", this.collapseNetwork);
|
Mousetrap.unbind("alt+shift+left", this.collapseNetwork);
|
||||||
Mousetrap.unbind("alt+j", this.toggleSearch);
|
Mousetrap.unbind("alt+j", this.toggleSearch);
|
||||||
|
|
|
@ -61,6 +61,7 @@ export default {
|
||||||
active: Boolean,
|
active: Boolean,
|
||||||
isFiltering: Boolean,
|
isFiltering: Boolean,
|
||||||
},
|
},
|
||||||
|
emits: ["toggle-join-channel"],
|
||||||
computed: {
|
computed: {
|
||||||
channel() {
|
channel() {
|
||||||
return this.network.channels[0];
|
return this.network.channels[0];
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
<script>
|
<script>
|
||||||
import parse from "../js/helpers/parse";
|
import parse from "../js/helpers/parse";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ParsedMessage",
|
name: "ParsedMessage",
|
||||||
functional: true,
|
|
||||||
props: {
|
props: {
|
||||||
text: String,
|
text: String,
|
||||||
message: Object,
|
message: Object,
|
||||||
network: Object,
|
network: Object,
|
||||||
},
|
},
|
||||||
render(createElement, context) {
|
setup(props) {
|
||||||
return parse(
|
return () =>
|
||||||
createElement,
|
parse(
|
||||||
typeof context.props.text !== "undefined"
|
typeof props.text !== "undefined" ? props.text : props.message.text,
|
||||||
? context.props.text
|
props.message,
|
||||||
: context.props.message.text,
|
props.network
|
||||||
context.props.message,
|
);
|
||||||
context.props.network
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
aria-label="Connect to network"
|
aria-label="Connect to network"
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-controls="connect"
|
aria-controls="connect"
|
||||||
:aria-selected="$route.name === 'Connect'"
|
:aria-selected="doesRouteMatch('Connect')"
|
||||||
/></span>
|
/></span>
|
||||||
<span class="tooltipped tooltipped-n tooltipped-no-touch" aria-label="Settings"
|
<span class="tooltipped tooltipped-n tooltipped-no-touch" aria-label="Settings"
|
||||||
><router-link
|
><router-link
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
aria-label="Settings"
|
aria-label="Settings"
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-controls="settings"
|
aria-controls="settings"
|
||||||
:aria-selected="$route.name === 'Settings'"
|
:aria-selected="doesRouteMatch('Settings')"
|
||||||
/></span>
|
/></span>
|
||||||
<span
|
<span
|
||||||
class="tooltipped tooltipped-n tooltipped-no-touch"
|
class="tooltipped tooltipped-n tooltipped-no-touch"
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
aria-label="Help"
|
aria-label="Help"
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-controls="help"
|
aria-controls="help"
|
||||||
:aria-selected="$route.name === 'Help'"
|
:aria-selected="doesRouteMatch('Help')"
|
||||||
/></span>
|
/></span>
|
||||||
</footer>
|
</footer>
|
||||||
</aside>
|
</aside>
|
||||||
|
@ -195,6 +195,9 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isPublic: () => document.body.classList.contains("public"),
|
isPublic: () => document.body.classList.contains("public"),
|
||||||
|
doesRouteMatch(route) {
|
||||||
|
return this.$route.name === route ? true : null;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
246
client/components/Windows/SearchResults.vue
Normal file
246
client/components/Windows/SearchResults.vue
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
<template>
|
||||||
|
<div id="chat-container" class="window">
|
||||||
|
<div
|
||||||
|
id="chat"
|
||||||
|
:class="{
|
||||||
|
'colored-nicks': $store.state.settings.coloredNicks,
|
||||||
|
'time-seconds': $store.state.settings.showSeconds,
|
||||||
|
'time-12h': $store.state.settings.use12hClock,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="chat-view"
|
||||||
|
data-type="search-results"
|
||||||
|
aria-label="Search results"
|
||||||
|
role="tabpanel"
|
||||||
|
>
|
||||||
|
<div class="header">
|
||||||
|
<SidebarToggle />
|
||||||
|
<span class="title"
|
||||||
|
>Searching in <span class="channel-name">{{ channel.name }}</span> for</span
|
||||||
|
>
|
||||||
|
<span class="topic">{{ $route.query.q }}</span>
|
||||||
|
<MessageSearchForm :network="network" :channel="channel" />
|
||||||
|
<button
|
||||||
|
class="close"
|
||||||
|
aria-label="Close search window"
|
||||||
|
title="Close search window"
|
||||||
|
@click="closeSearch"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="chat-content">
|
||||||
|
<div ref="chat" class="chat" tabindex="-1">
|
||||||
|
<div v-show="moreResultsAvailable" class="show-more">
|
||||||
|
<button
|
||||||
|
ref="loadMoreButton"
|
||||||
|
:disabled="
|
||||||
|
$store.state.messageSearchInProgress ||
|
||||||
|
!$store.state.isConnected
|
||||||
|
"
|
||||||
|
class="btn"
|
||||||
|
@click="onShowMoreClick"
|
||||||
|
>
|
||||||
|
<span v-if="$store.state.messageSearchInProgress">Loading…</span>
|
||||||
|
<span v-else>Show older messages</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="$store.state.messageSearchInProgress && !offset"
|
||||||
|
class="search-status"
|
||||||
|
>
|
||||||
|
Searching…
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!messages.length && !offset" class="search-status">
|
||||||
|
No results found.
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="messages"
|
||||||
|
role="log"
|
||||||
|
aria-live="polite"
|
||||||
|
aria-relevant="additions"
|
||||||
|
>
|
||||||
|
<template v-for="(message, id) in messages" :key="message.id">
|
||||||
|
<div class="result" @:click="jump(message, id)">
|
||||||
|
<DateMarker
|
||||||
|
v-if="shouldDisplayDateMarker(message, id)"
|
||||||
|
:key="message.date"
|
||||||
|
:message="message"
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
:key="message.id"
|
||||||
|
:channel="channel"
|
||||||
|
:network="network"
|
||||||
|
:message="message"
|
||||||
|
:data-id="message.id"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.channel-name {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import socket from "../../js/socket";
|
||||||
|
|
||||||
|
import SidebarToggle from "../SidebarToggle.vue";
|
||||||
|
import Message from "../Message.vue";
|
||||||
|
import MessageSearchForm from "../MessageSearchForm.vue";
|
||||||
|
import DateMarker from "../DateMarker.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "SearchResults",
|
||||||
|
components: {
|
||||||
|
SidebarToggle,
|
||||||
|
Message,
|
||||||
|
DateMarker,
|
||||||
|
MessageSearchForm,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
offset: 0,
|
||||||
|
moreResultsAvailable: false,
|
||||||
|
oldScrollTop: 0,
|
||||||
|
oldChatHeight: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
search() {
|
||||||
|
return this.$store.state.messageSearchResults;
|
||||||
|
},
|
||||||
|
messages() {
|
||||||
|
if (!this.search) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.search.results;
|
||||||
|
},
|
||||||
|
chan() {
|
||||||
|
const chanId = parseInt(this.$route.params.id, 10);
|
||||||
|
return this.$store.getters.findChannel(chanId);
|
||||||
|
},
|
||||||
|
network() {
|
||||||
|
if (!this.chan) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.chan.network;
|
||||||
|
},
|
||||||
|
channel() {
|
||||||
|
if (!this.chan) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.chan.channel;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
"$route.params.id"() {
|
||||||
|
this.doSearch();
|
||||||
|
this.setActiveChannel();
|
||||||
|
},
|
||||||
|
"$route.query.q"() {
|
||||||
|
this.doSearch();
|
||||||
|
this.setActiveChannel();
|
||||||
|
},
|
||||||
|
messages() {
|
||||||
|
this.moreResultsAvailable = this.messages.length && !(this.messages.length % 100);
|
||||||
|
|
||||||
|
if (!this.offset) {
|
||||||
|
this.jumpToBottom();
|
||||||
|
} else {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const currentChatHeight = this.$refs.chat.scrollHeight;
|
||||||
|
this.$refs.chat.scrollTop =
|
||||||
|
this.oldScrollTop + currentChatHeight - this.oldChatHeight;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.setActiveChannel();
|
||||||
|
this.doSearch();
|
||||||
|
this.$root.$on("re-search", this.doSearch); // Enable MessageSearchForm to search for the same query again
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
this.$root.$off("re-search");
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setActiveChannel() {
|
||||||
|
this.$store.commit("activeChannel", this.chan);
|
||||||
|
},
|
||||||
|
closeSearch() {
|
||||||
|
this.$root.switchToChannel(this.channel);
|
||||||
|
},
|
||||||
|
shouldDisplayDateMarker(message, id) {
|
||||||
|
const previousMessage = this.messages[id - 1];
|
||||||
|
|
||||||
|
if (!previousMessage) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Date(previousMessage.time).getDay() !== new Date(message.time).getDay();
|
||||||
|
},
|
||||||
|
doSearch() {
|
||||||
|
this.offset = 0;
|
||||||
|
this.$store.commit("messageSearchInProgress", true);
|
||||||
|
|
||||||
|
if (!this.offset) {
|
||||||
|
this.$store.commit("messageSearchResults", null); // Only reset if not getting offset
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.emit("search", {
|
||||||
|
networkUuid: this.network.uuid,
|
||||||
|
channelName: this.channel.name,
|
||||||
|
searchTerm: this.$route.query.q,
|
||||||
|
offset: this.offset,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onShowMoreClick() {
|
||||||
|
this.offset += 100;
|
||||||
|
this.$store.commit("messageSearchInProgress", true);
|
||||||
|
|
||||||
|
this.oldScrollTop = this.$refs.chat.scrollTop;
|
||||||
|
this.oldChatHeight = this.$refs.chat.scrollHeight;
|
||||||
|
|
||||||
|
socket.emit("search", {
|
||||||
|
networkUuid: this.network.uuid,
|
||||||
|
channelName: this.channel.name,
|
||||||
|
searchTerm: this.$route.query.q,
|
||||||
|
offset: this.offset + 1,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
jumpToBottom() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const el = this.$refs.chat;
|
||||||
|
el.scrollTop = el.scrollHeight;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
jump(message, id) {
|
||||||
|
// TODO: Implement jumping to messages!
|
||||||
|
// This is difficult because it means client will need to handle a potentially nonlinear message set
|
||||||
|
// (loading IntersectionObserver both before AND after the messages)
|
||||||
|
this.$router.push({
|
||||||
|
name: "MessageList",
|
||||||
|
params: {
|
||||||
|
id: this.chan.id,
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
focused: id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -74,7 +74,7 @@ export default {
|
||||||
mounted() {
|
mounted() {
|
||||||
socket.on("auth:failed", this.onAuthFailed);
|
socket.on("auth:failed", this.onAuthFailed);
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeUnmount() {
|
||||||
socket.off("auth:failed", this.onAuthFailed);
|
socket.off("auth:failed", this.onAuthFailed);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -555,6 +555,10 @@ p {
|
||||||
|
|
||||||
/* End icons */
|
/* End icons */
|
||||||
|
|
||||||
|
#viewport-mount {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
#viewport {
|
#viewport {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body class="<%- public ? " public" : "" %>" data-transports="<%- JSON.stringify(transports) %>">
|
<body class="<%- public ? " public" : "" %>" data-transports="<%- JSON.stringify(transports) %>">
|
||||||
<div id="viewport"></div>
|
<div id="viewport-mount"></div>
|
||||||
<div id="loading">
|
<div id="loading">
|
||||||
<div class="window">
|
<div class="window">
|
||||||
<div id="loading-status-container">
|
<div id="loading-status-container">
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
import {h as createElement} from "vue";
|
||||||
import parseStyle from "./ircmessageparser/parseStyle";
|
import parseStyle from "./ircmessageparser/parseStyle";
|
||||||
import findChannels from "./ircmessageparser/findChannels";
|
import findChannels from "./ircmessageparser/findChannels";
|
||||||
import {findLinks} from "./ircmessageparser/findLinks";
|
import {findLinks} from "./ircmessageparser/findLinks";
|
||||||
|
@ -15,7 +16,7 @@ import Username from "../../components/Username.vue";
|
||||||
const emojiModifiersRegex = /[\u{1f3fb}-\u{1f3ff}]|\u{fe0f}/gu;
|
const emojiModifiersRegex = /[\u{1f3fb}-\u{1f3ff}]|\u{fe0f}/gu;
|
||||||
|
|
||||||
// Create an HTML `span` with styling information for a given fragment
|
// Create an HTML `span` with styling information for a given fragment
|
||||||
function createFragment(fragment, createElement) {
|
function createFragment(fragment) {
|
||||||
const classes = [];
|
const classes = [];
|
||||||
|
|
||||||
if (fragment.bold) {
|
if (fragment.bold) {
|
||||||
|
@ -70,7 +71,7 @@ function createFragment(fragment, createElement) {
|
||||||
|
|
||||||
// Transform an IRC message potentially filled with styling control codes, URLs,
|
// Transform an IRC message potentially filled with styling control codes, URLs,
|
||||||
// nicknames, and channels into a string of HTML elements to display on the client.
|
// nicknames, and channels into a string of HTML elements to display on the client.
|
||||||
function parse(createElement, text, message = undefined, network = undefined) {
|
function parse(text, message = undefined, network = undefined) {
|
||||||
// Extract the styling information and get the plain text version from it
|
// Extract the styling information and get the plain text version from it
|
||||||
const styleFragments = parseStyle(text);
|
const styleFragments = parseStyle(text);
|
||||||
const cleanText = styleFragments.map((fragment) => fragment.text).join("");
|
const cleanText = styleFragments.map((fragment) => fragment.text).join("");
|
||||||
|
@ -90,9 +91,7 @@ function parse(createElement, text, message = undefined, network = undefined) {
|
||||||
// Merge the styling information with the channels / URLs / nicks / text objects and
|
// Merge the styling information with the channels / URLs / nicks / text objects and
|
||||||
// generate HTML strings with the resulting fragments
|
// generate HTML strings with the resulting fragments
|
||||||
return merge(parts, styleFragments, cleanText).map((textPart) => {
|
return merge(parts, styleFragments, cleanText).map((textPart) => {
|
||||||
const fragments = textPart.fragments.map((fragment) =>
|
const fragments = textPart.fragments.map((fragment) => createFragment(fragment));
|
||||||
createFragment(fragment, createElement)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Wrap these potentially styled fragments with links and channel buttons
|
// Wrap these potentially styled fragments with links and channel buttons
|
||||||
if (textPart.link) {
|
if (textPart.link) {
|
||||||
|
@ -103,12 +102,10 @@ function parse(createElement, text, message = undefined, network = undefined) {
|
||||||
const link = createElement(
|
const link = createElement(
|
||||||
"a",
|
"a",
|
||||||
{
|
{
|
||||||
attrs: {
|
href: textPart.link,
|
||||||
href: textPart.link,
|
dir: preview ? null : "auto",
|
||||||
dir: preview ? null : "auto",
|
target: "_blank",
|
||||||
target: "_blank",
|
rel: "noopener",
|
||||||
rel: "noopener",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
fragments
|
fragments
|
||||||
);
|
);
|
||||||
|
@ -122,18 +119,14 @@ function parse(createElement, text, message = undefined, network = undefined) {
|
||||||
if (preview.size > 0) {
|
if (preview.size > 0) {
|
||||||
linkEls.push(
|
linkEls.push(
|
||||||
createElement(LinkPreviewFileSize, {
|
createElement(LinkPreviewFileSize, {
|
||||||
props: {
|
size: preview.size,
|
||||||
size: preview.size,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
linkEls.push(
|
linkEls.push(
|
||||||
createElement(LinkPreviewToggle, {
|
createElement(LinkPreviewToggle, {
|
||||||
props: {
|
link: preview,
|
||||||
link: preview,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -142,9 +135,7 @@ function parse(createElement, text, message = undefined, network = undefined) {
|
||||||
return createElement(
|
return createElement(
|
||||||
"span",
|
"span",
|
||||||
{
|
{
|
||||||
attrs: {
|
dir: "auto",
|
||||||
dir: "auto",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
linkEls
|
linkEls
|
||||||
);
|
);
|
||||||
|
@ -152,11 +143,11 @@ function parse(createElement, text, message = undefined, network = undefined) {
|
||||||
return createElement(
|
return createElement(
|
||||||
InlineChannel,
|
InlineChannel,
|
||||||
{
|
{
|
||||||
props: {
|
channel: textPart.channel,
|
||||||
channel: textPart.channel,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
fragments
|
{
|
||||||
|
default: () => fragments,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
} else if (textPart.emoji) {
|
} else if (textPart.emoji) {
|
||||||
const emojiWithoutModifiers = textPart.emoji.replace(emojiModifiersRegex, "");
|
const emojiWithoutModifiers = textPart.emoji.replace(emojiModifiersRegex, "");
|
||||||
|
@ -168,11 +159,9 @@ function parse(createElement, text, message = undefined, network = undefined) {
|
||||||
"span",
|
"span",
|
||||||
{
|
{
|
||||||
class: ["emoji"],
|
class: ["emoji"],
|
||||||
attrs: {
|
role: "img",
|
||||||
role: "img",
|
"aria-label": title,
|
||||||
"aria-label": title,
|
title: title,
|
||||||
title: title,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
fragments
|
fragments
|
||||||
);
|
);
|
||||||
|
@ -180,16 +169,14 @@ function parse(createElement, text, message = undefined, network = undefined) {
|
||||||
return createElement(
|
return createElement(
|
||||||
Username,
|
Username,
|
||||||
{
|
{
|
||||||
props: {
|
user: {
|
||||||
user: {
|
nick: textPart.nick,
|
||||||
nick: textPart.nick,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
attrs: {
|
|
||||||
dir: "auto",
|
|
||||||
},
|
},
|
||||||
|
dir: "auto",
|
||||||
},
|
},
|
||||||
fragments
|
{
|
||||||
|
default: () => fragments,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,7 @@
|
||||||
|
|
||||||
const constants = require("./constants");
|
const constants = require("./constants");
|
||||||
|
|
||||||
import Vue from "vue";
|
import {createRouter, createWebHashHistory} from "vue-router";
|
||||||
import VueRouter from "vue-router";
|
|
||||||
|
|
||||||
Vue.use(VueRouter);
|
|
||||||
|
|
||||||
import SignIn from "../components/Windows/SignIn.vue";
|
import SignIn from "../components/Windows/SignIn.vue";
|
||||||
import Connect from "../components/Windows/Connect.vue";
|
import Connect from "../components/Windows/Connect.vue";
|
||||||
|
@ -16,7 +13,8 @@ import NetworkEdit from "../components/Windows/NetworkEdit.vue";
|
||||||
import RoutedChat from "../components/RoutedChat.vue";
|
import RoutedChat from "../components/RoutedChat.vue";
|
||||||
import store from "./store";
|
import store from "./store";
|
||||||
|
|
||||||
const router = new VueRouter({
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
name: "SignIn",
|
name: "SignIn",
|
||||||
|
@ -100,19 +98,19 @@ router.beforeEach((to, from, next) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle closing image viewer with the browser back button
|
// // Handle closing image viewer with the browser back button
|
||||||
if (!router.app.$refs.app) {
|
// if (router.app.$refs.app) {
|
||||||
next();
|
// next();
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
const imageViewer = router.app.$root.$refs.app.$refs.imageViewer;
|
// const imageViewer = router.app.$refs.app.$refs.imageViewer;
|
||||||
|
|
||||||
if (imageViewer && imageViewer.link) {
|
// if (imageViewer && imageViewer.link) {
|
||||||
imageViewer.closeViewer();
|
// imageViewer.closeViewer();
|
||||||
next(false);
|
// next(false);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
@ -144,7 +142,7 @@ router.afterEach((to) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function navigate(routeName, params = {}) {
|
function navigate(routeName, params = {}) {
|
||||||
if (router.currentRoute.name) {
|
if (router.currentRoute.value.name) {
|
||||||
router.push({name: routeName, params}).catch(() => {});
|
router.push({name: routeName, params}).catch(() => {});
|
||||||
} else {
|
} else {
|
||||||
// If current route is null, replace the history entry
|
// If current route is null, replace the history entry
|
||||||
|
|
|
@ -80,7 +80,7 @@ function showSignIn() {
|
||||||
window.g_TheLoungeRemoveLoading();
|
window.g_TheLoungeRemoveLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (router.currentRoute.name !== "SignIn") {
|
if (router.currentRoute.value.name !== "SignIn") {
|
||||||
navigate("SignIn");
|
navigate("SignIn");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import Vue from "vue";
|
import {nextTick} from "vue";
|
||||||
import socket from "../socket";
|
import socket from "../socket";
|
||||||
import storage from "../localStorage";
|
import storage from "../localStorage";
|
||||||
import {router, switchToChannel, navigate} from "../router";
|
import {router, switchToChannel, navigate} from "../router";
|
||||||
|
@ -25,13 +25,16 @@ socket.on("init", function (data) {
|
||||||
window.g_TheLoungeRemoveLoading();
|
window.g_TheLoungeRemoveLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
nextTick(() => {
|
||||||
// If we handled query parameters like irc:// links or just general
|
// If we handled query parameters like irc:// links or just general
|
||||||
// connect parameters in public mode, then nothing to do here
|
// connect parameters in public mode, then nothing to do here
|
||||||
if (!handleQueryParams()) {
|
if (!handleQueryParams()) {
|
||||||
// If we are on an unknown route or still on SignIn component
|
// If we are on an unknown route or still on SignIn component
|
||||||
// then we can open last known channel on server, or Connect window if none
|
// then we can open last known channel on server, or Connect window if none
|
||||||
if (!router.currentRoute.name || router.currentRoute.name === "SignIn") {
|
if (
|
||||||
|
!router.currentRoute.value.name ||
|
||||||
|
router.currentRoute.value.name === "SignIn"
|
||||||
|
) {
|
||||||
const channel = store.getters.findChannel(data.active);
|
const channel = store.getters.findChannel(data.active);
|
||||||
|
|
||||||
if (channel) {
|
if (channel) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import Vue from "vue";
|
import {nextTick} from "vue";
|
||||||
|
|
||||||
import socket from "../socket";
|
import socket from "../socket";
|
||||||
import store from "../store";
|
import store from "../store";
|
||||||
|
|
||||||
|
@ -16,7 +15,7 @@ socket.on("more", function (data) {
|
||||||
data.totalMessages > channel.channel.messages.length + data.messages.length;
|
data.totalMessages > channel.channel.messages.length + data.messages.length;
|
||||||
channel.channel.messages.unshift(...data.messages);
|
channel.channel.messages.unshift(...data.messages);
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
nextTick(() => {
|
||||||
channel.channel.historyLoading = false;
|
channel.channel.historyLoading = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import Vue from "vue";
|
|
||||||
|
|
||||||
import socket from "../socket";
|
import socket from "../socket";
|
||||||
import store from "../store";
|
import store from "../store";
|
||||||
|
|
||||||
|
@ -16,6 +14,7 @@ socket.on("msg:preview", function (data) {
|
||||||
const previewIndex = message.previews.findIndex((m) => m.link === data.preview.link);
|
const previewIndex = message.previews.findIndex((m) => m.link === data.preview.link);
|
||||||
|
|
||||||
if (previewIndex > -1) {
|
if (previewIndex > -1) {
|
||||||
Vue.set(message.previews, previewIndex, data.preview);
|
// TODO: Does this work?
|
||||||
|
message.previews[previewIndex] = data.preview;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import Vue from "vue";
|
|
||||||
|
|
||||||
import socket from "../socket";
|
import socket from "../socket";
|
||||||
import store from "../store";
|
import store from "../store";
|
||||||
import {switchToChannel} from "../router";
|
import {switchToChannel} from "../router";
|
||||||
|
@ -61,7 +59,7 @@ socket.on("network:info", function (data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key in data) {
|
for (const key in data) {
|
||||||
Vue.set(network, key, data[key]);
|
network[key] = data[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import Vue from "vue";
|
import {createStore} from "vuex";
|
||||||
import Vuex from "vuex";
|
|
||||||
import {createSettingsStore} from "./store-settings";
|
import {createSettingsStore} from "./store-settings";
|
||||||
import storage from "./localStorage";
|
import storage from "./localStorage";
|
||||||
|
|
||||||
const appName = document.title;
|
const appName = document.title;
|
||||||
|
|
||||||
Vue.use(Vuex);
|
|
||||||
|
|
||||||
function detectDesktopNotificationState() {
|
function detectDesktopNotificationState() {
|
||||||
if (!("Notification" in window)) {
|
if (!("Notification" in window)) {
|
||||||
return "unsupported";
|
return "unsupported";
|
||||||
|
@ -17,7 +14,7 @@ function detectDesktopNotificationState() {
|
||||||
return "blocked";
|
return "blocked";
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
const store = createStore({
|
||||||
state: {
|
state: {
|
||||||
appLoaded: false,
|
appLoaded: false,
|
||||||
activeChannel: null,
|
activeChannel: null,
|
||||||
|
|
|
@ -3,12 +3,11 @@
|
||||||
const constants = require("./constants");
|
const constants = require("./constants");
|
||||||
|
|
||||||
import "../css/style.css";
|
import "../css/style.css";
|
||||||
import Vue from "vue";
|
import {createApp} from "vue";
|
||||||
import store from "./store";
|
import store from "./store";
|
||||||
import App from "../components/App.vue";
|
import App from "../components/App.vue";
|
||||||
import storage from "./localStorage";
|
import storage from "./localStorage";
|
||||||
import {router, navigate} from "./router";
|
import {router} from "./router";
|
||||||
import socket from "./socket";
|
|
||||||
import eventbus from "./eventbus";
|
import eventbus from "./eventbus";
|
||||||
|
|
||||||
import "./socket-events";
|
import "./socket-events";
|
||||||
|
@ -19,57 +18,13 @@ const favicon = document.getElementById("favicon");
|
||||||
const faviconNormal = favicon.getAttribute("href");
|
const faviconNormal = favicon.getAttribute("href");
|
||||||
const faviconAlerted = favicon.dataset.other;
|
const faviconAlerted = favicon.dataset.other;
|
||||||
|
|
||||||
new Vue({
|
const vueApp = createApp(App);
|
||||||
el: "#viewport",
|
vueApp.use(store);
|
||||||
router,
|
vueApp.use(router);
|
||||||
mounted() {
|
// https://next.router.vuejs.org/guide/migration/#removal-of-router-app
|
||||||
socket.open();
|
router.app = vueApp;
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
switchToChannel(channel) {
|
|
||||||
navigate("RoutedChat", {id: channel.id});
|
|
||||||
},
|
|
||||||
closeChannel(channel) {
|
|
||||||
if (channel.type === "lobby") {
|
|
||||||
eventbus.emit(
|
|
||||||
"confirm-dialog",
|
|
||||||
{
|
|
||||||
title: "Remove network",
|
|
||||||
text: `Are you sure you want to quit and remove ${channel.name}? This cannot be undone.`,
|
|
||||||
button: "Remove network",
|
|
||||||
},
|
|
||||||
(result) => {
|
|
||||||
if (!result) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.closed = true;
|
vueApp.mount("#viewport-mount");
|
||||||
socket.emit("input", {
|
|
||||||
target: Number(channel.id),
|
|
||||||
text: "/quit",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.closed = true;
|
|
||||||
|
|
||||||
socket.emit("input", {
|
|
||||||
target: Number(channel.id),
|
|
||||||
text: "/close",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
render(createElement) {
|
|
||||||
return createElement(App, {
|
|
||||||
ref: "app",
|
|
||||||
props: this,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
store,
|
|
||||||
});
|
|
||||||
|
|
||||||
store.watch(
|
store.watch(
|
||||||
(state) => state.sidebarOpen,
|
(state) => state.sidebarOpen,
|
||||||
|
@ -112,7 +67,11 @@ store.watch(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
Vue.config.errorHandler = function (e) {
|
vueApp.config.errorHandler = function (e) {
|
||||||
store.commit("currentUserVisibleError", `Vue error: ${e.message}`);
|
store.commit("currentUserVisibleError", `Vue error: ${e.message}`);
|
||||||
console.error(e); // eslint-disable-line
|
console.error(e); // eslint-disable-line
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
app: vueApp,
|
||||||
|
};
|
||||||
|
|
21
package.json
21
package.json
|
@ -40,6 +40,7 @@
|
||||||
"node": ">=10.15.0"
|
"node": ">=10.15.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vue/compat": "3.2.23",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"busboy": "0.3.1",
|
"busboy": "0.3.1",
|
||||||
"chalk": "4.1.0",
|
"chalk": "4.1.0",
|
||||||
|
@ -71,11 +72,12 @@
|
||||||
"sqlite3": "5.0.2"
|
"sqlite3": "5.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.13.8",
|
"@babel/core": "7.15.5",
|
||||||
"@babel/preset-env": "7.13.9",
|
"@babel/preset-env": "7.15.6",
|
||||||
"@fortawesome/fontawesome-free": "5.15.2",
|
"@fortawesome/fontawesome-free": "5.15.4",
|
||||||
"@vue/server-test-utils": "1.1.3",
|
"@vue/compiler-sfc": "3.2.22",
|
||||||
"@vue/test-utils": "1.1.3",
|
"@vue/server-test-utils": "1.3.0",
|
||||||
|
"@vue/test-utils": "2.0.0-rc.16",
|
||||||
"babel-loader": "8.2.2",
|
"babel-loader": "8.2.2",
|
||||||
"babel-plugin-istanbul": "6.0.0",
|
"babel-plugin-istanbul": "6.0.0",
|
||||||
"chai": "4.3.0",
|
"chai": "4.3.0",
|
||||||
|
@ -108,13 +110,12 @@
|
||||||
"stylelint-config-standard": "20.0.0",
|
"stylelint-config-standard": "20.0.0",
|
||||||
"textcomplete": "0.18.2",
|
"textcomplete": "0.18.2",
|
||||||
"undate": "0.3.0",
|
"undate": "0.3.0",
|
||||||
"vue": "2.6.12",
|
"vue": "3.0.9",
|
||||||
"vue-loader": "15.9.6",
|
"vue-loader": "16.8.3",
|
||||||
"vue-router": "3.5.1",
|
"vue-router": "4.0.12",
|
||||||
"vue-server-renderer": "2.6.12",
|
"vue-server-renderer": "2.6.12",
|
||||||
"vue-template-compiler": "2.6.12",
|
|
||||||
"vuedraggable": "2.24.3",
|
"vuedraggable": "2.24.3",
|
||||||
"vuex": "3.6.2",
|
"vuex": "4.0.2",
|
||||||
"webpack": "5.21.2",
|
"webpack": "5.21.2",
|
||||||
"webpack-cli": "4.5.0",
|
"webpack-cli": "4.5.0",
|
||||||
"webpack-dev-middleware": "4.1.0",
|
"webpack-dev-middleware": "4.1.0",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
const webpack = require("webpack");
|
const webpack = require("webpack");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const VueLoaderPlugin = require("vue-loader/lib/plugin");
|
const {VueLoaderPlugin} = require("vue-loader");
|
||||||
const config = require("./webpack.config.js");
|
const config = require("./webpack.config.js");
|
||||||
|
|
||||||
const testFile = path.resolve(__dirname, "test/public/testclient.js");
|
const testFile = path.resolve(__dirname, "test/public/testclient.js");
|
||||||
|
|
|
@ -4,7 +4,7 @@ const webpack = require("webpack");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const CopyPlugin = require("copy-webpack-plugin");
|
const CopyPlugin = require("copy-webpack-plugin");
|
||||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||||
const VueLoaderPlugin = require("vue-loader/lib/plugin");
|
const {VueLoaderPlugin} = require("vue-loader");
|
||||||
const Helper = require("./src/helper.js");
|
const Helper = require("./src/helper.js");
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV === "production";
|
const isProduction = process.env.NODE_ENV === "production";
|
||||||
|
@ -22,6 +22,11 @@ const config = {
|
||||||
performance: {
|
performance: {
|
||||||
hints: false,
|
hints: false,
|
||||||
},
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
vue: "@vue/compat",
|
||||||
|
},
|
||||||
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
|
@ -31,6 +36,9 @@ const config = {
|
||||||
options: {
|
options: {
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
preserveWhitespace: false,
|
preserveWhitespace: false,
|
||||||
|
compatConfig: {
|
||||||
|
MODE: 3,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -87,6 +95,10 @@ const config = {
|
||||||
json3: "JSON", // socket.io uses json3.js, but we do not target any browsers that need it
|
json3: "JSON", // socket.io uses json3.js, but we do not target any browsers that need it
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
__VUE_OPTIONS_API__: true,
|
||||||
|
__VUE_PROD_DEVTOOLS__: false,
|
||||||
|
}),
|
||||||
new VueLoaderPlugin(),
|
new VueLoaderPlugin(),
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
filename: "css/style.css",
|
filename: "css/style.css",
|
||||||
|
|
Loading…
Reference in a new issue