diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index bb707a9a..c1950464 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -84,6 +84,8 @@ const vueRules = defineConfig({
"vue/no-v-html": "off",
"vue/require-default-prop": "off",
"vue/v-slot-style": ["error", "longform"],
+ // Should be fixable in Vue 3 / when components use Vue.extend()
+ "@typescript-eslint/unbound-method": "off",
},
}).rules;
@@ -113,6 +115,9 @@ const tsRulesTemp = defineConfig({
module.exports = defineConfig({
root: true,
+ parserOptions: {
+ ecmaVersion: 2022,
+ },
overrides: [
{
files: [
@@ -132,7 +137,11 @@ module.exports = defineConfig({
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier",
],
- rules: {...baseRules, ...tsRules, ...tsRulesTemp},
+ rules: {
+ ...baseRules,
+ ...tsRules,
+ ...tsRulesTemp,
+ },
},
// TODO: verify
{
diff --git a/client/components/App.vue b/client/components/App.vue
index 0e6fdbb9..a84d7005 100644
--- a/client/components/App.vue
+++ b/client/components/App.vue
@@ -31,15 +31,6 @@ import ConfirmDialog from "./ConfirmDialog.vue";
import Mentions from "./Mentions.vue";
import VueApp from "vue";
-// This stops Vue from complaining about adding objects to the component context
-declare module "vue/types/vue" {
- interface Vue {
- debouncedResize: () => void;
- // TODO; type as Timeout
- dayChangeTimeout: any;
- }
-}
-
export default VueApp.extend({
name: "App",
components: {
diff --git a/client/components/Channel.vue b/client/components/Channel.vue
index 72551f9b..9ca65e56 100644
--- a/client/components/Channel.vue
+++ b/client/components/Channel.vue
@@ -27,30 +27,32 @@
-
diff --git a/client/components/ChannelWrapper.vue b/client/components/ChannelWrapper.vue
index 572881bc..4444989d 100644
--- a/client/components/ChannelWrapper.vue
+++ b/client/components/ChannelWrapper.vue
@@ -32,15 +32,17 @@
-
diff --git a/client/components/Chat.vue b/client/components/Chat.vue
index 9838d1b6..326f005a 100644
--- a/client/components/Chat.vue
+++ b/client/components/Chat.vue
@@ -145,8 +145,8 @@ export default {
MessageSearchForm,
},
props: {
- network: Object,
- channel: Object,
+ network: Object as PropType,
+ channel: Object as PropType,
focused: String,
},
computed: {
diff --git a/client/components/ChatInput.vue b/client/components/ChatInput.vue
index 401ee7ef..0db71452 100644
--- a/client/components/ChatInput.vue
+++ b/client/components/ChatInput.vue
@@ -91,8 +91,8 @@ let autocompletionRef = null;
export default {
name: "ChatInput",
props: {
- network: Object,
- channel: Object,
+ network: Object as PropType,
+ channel: Object as PropType,
},
watch: {
"channel.id"() {
diff --git a/client/components/ChatUserList.vue b/client/components/ChatUserList.vue
index 7eaa7c8b..4566ca4a 100644
--- a/client/components/ChatUserList.vue
+++ b/client/components/ChatUserList.vue
@@ -74,7 +74,7 @@ export default {
Username,
},
props: {
- channel: Object,
+ channel: Object as PropType,
},
data() {
return {
diff --git a/client/components/JoinChannel.vue b/client/components/JoinChannel.vue
index 129d5383..648b3b66 100644
--- a/client/components/JoinChannel.vue
+++ b/client/components/JoinChannel.vue
@@ -48,8 +48,8 @@ export default {
},
},
props: {
- network: Object,
- channel: Object,
+ network: Object as PropType,
+ channel: Object as PropType,
},
data() {
return {
diff --git a/client/components/LinkPreview.vue b/client/components/LinkPreview.vue
index 9ae10b74..cd8f7333 100644
--- a/client/components/LinkPreview.vue
+++ b/client/components/LinkPreview.vue
@@ -138,7 +138,7 @@ export default {
props: {
link: Object,
keepScrollPosition: Function,
- channel: Object,
+ channel: Object as PropType,
},
data() {
return {
diff --git a/client/components/Message.vue b/client/components/Message.vue
index ed172798..8df22067 100644
--- a/client/components/Message.vue
+++ b/client/components/Message.vue
@@ -113,8 +113,8 @@ export default {
components: MessageTypes,
props: {
message: Object,
- channel: Object,
- network: Object,
+ channel: Object as PropType,
+ network: Object as PropType,
keepScrollPosition: Function,
isPreviousSource: Boolean,
focused: Boolean,
diff --git a/client/components/MessageCondensed.vue b/client/components/MessageCondensed.vue
index 8d593f6a..7e32661a 100644
--- a/client/components/MessageCondensed.vue
+++ b/client/components/MessageCondensed.vue
@@ -27,7 +27,7 @@ export default {
Message,
},
props: {
- network: Object,
+ network: Object as PropType,
messages: Array,
keepScrollPosition: Function,
focused: Boolean,
diff --git a/client/components/MessageSearchForm.vue b/client/components/MessageSearchForm.vue
index 6f22fce3..587c5460 100644
--- a/client/components/MessageSearchForm.vue
+++ b/client/components/MessageSearchForm.vue
@@ -84,8 +84,8 @@ form.message-search.opened .input-wrapper {
export default {
name: "MessageSearchForm",
props: {
- network: Object,
- channel: Object,
+ network: Object as PropType,
+ channel: Object as PropType,
},
data() {
return {
diff --git a/client/components/MessageTypes/away.vue b/client/components/MessageTypes/away.vue
index 6963257c..9921d407 100644
--- a/client/components/MessageTypes/away.vue
+++ b/client/components/MessageTypes/away.vue
@@ -20,7 +20,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/back.vue b/client/components/MessageTypes/back.vue
index 4feac170..942eb72f 100644
--- a/client/components/MessageTypes/back.vue
+++ b/client/components/MessageTypes/back.vue
@@ -19,7 +19,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/chghost.vue b/client/components/MessageTypes/chghost.vue
index e54c245d..f7dd40f0 100644
--- a/client/components/MessageTypes/chghost.vue
+++ b/client/components/MessageTypes/chghost.vue
@@ -23,7 +23,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/ctcp.vue b/client/components/MessageTypes/ctcp.vue
index 4e9cc53c..5f782ea7 100644
--- a/client/components/MessageTypes/ctcp.vue
+++ b/client/components/MessageTypes/ctcp.vue
@@ -16,7 +16,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/ctcp_request.vue b/client/components/MessageTypes/ctcp_request.vue
index 592441cd..ed9c42ab 100644
--- a/client/components/MessageTypes/ctcp_request.vue
+++ b/client/components/MessageTypes/ctcp_request.vue
@@ -17,7 +17,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/error.vue b/client/components/MessageTypes/error.vue
index 0453f10a..3e5ebb5b 100644
--- a/client/components/MessageTypes/error.vue
+++ b/client/components/MessageTypes/error.vue
@@ -13,7 +13,7 @@ export default {
ParsedMessage,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
computed: {
diff --git a/client/components/MessageTypes/invite.vue b/client/components/MessageTypes/invite.vue
index e976091e..e9e16b6a 100644
--- a/client/components/MessageTypes/invite.vue
+++ b/client/components/MessageTypes/invite.vue
@@ -19,7 +19,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/join.vue b/client/components/MessageTypes/join.vue
index 9e635742..f7e4abd1 100644
--- a/client/components/MessageTypes/join.vue
+++ b/client/components/MessageTypes/join.vue
@@ -23,7 +23,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/kick.vue b/client/components/MessageTypes/kick.vue
index 14a776c8..c0a33b40 100644
--- a/client/components/MessageTypes/kick.vue
+++ b/client/components/MessageTypes/kick.vue
@@ -20,7 +20,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/mode.vue b/client/components/MessageTypes/mode.vue
index 4ca48bd2..2fcedf4b 100644
--- a/client/components/MessageTypes/mode.vue
+++ b/client/components/MessageTypes/mode.vue
@@ -17,7 +17,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/mode_channel.vue b/client/components/MessageTypes/mode_channel.vue
index 109950bf..cefd7cfe 100644
--- a/client/components/MessageTypes/mode_channel.vue
+++ b/client/components/MessageTypes/mode_channel.vue
@@ -8,7 +8,7 @@
export default {
name: "MessageChannelMode",
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/mode_user.vue b/client/components/MessageTypes/mode_user.vue
index 8c3185eb..84cec2f5 100644
--- a/client/components/MessageTypes/mode_user.vue
+++ b/client/components/MessageTypes/mode_user.vue
@@ -8,7 +8,7 @@
export default {
name: "MessageChannelMode",
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/monospace_block.vue b/client/components/MessageTypes/monospace_block.vue
index 9647335a..c72aeb33 100644
--- a/client/components/MessageTypes/monospace_block.vue
+++ b/client/components/MessageTypes/monospace_block.vue
@@ -13,7 +13,7 @@ export default {
ParsedMessage,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
computed: {
diff --git a/client/components/MessageTypes/nick.vue b/client/components/MessageTypes/nick.vue
index 53d300b6..e8f428fa 100644
--- a/client/components/MessageTypes/nick.vue
+++ b/client/components/MessageTypes/nick.vue
@@ -15,7 +15,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/part.vue b/client/components/MessageTypes/part.vue
index b2b18c2a..afa41934 100644
--- a/client/components/MessageTypes/part.vue
+++ b/client/components/MessageTypes/part.vue
@@ -20,7 +20,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/quit.vue b/client/components/MessageTypes/quit.vue
index a34cd58c..036b9c60 100644
--- a/client/components/MessageTypes/quit.vue
+++ b/client/components/MessageTypes/quit.vue
@@ -20,7 +20,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/raw.vue b/client/components/MessageTypes/raw.vue
index 520e5087..dae809c3 100644
--- a/client/components/MessageTypes/raw.vue
+++ b/client/components/MessageTypes/raw.vue
@@ -6,7 +6,7 @@
export default {
name: "MessageTypeRaw",
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/topic.vue b/client/components/MessageTypes/topic.vue
index 7ec96e95..e77d5716 100644
--- a/client/components/MessageTypes/topic.vue
+++ b/client/components/MessageTypes/topic.vue
@@ -21,7 +21,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
};
diff --git a/client/components/MessageTypes/topic_set_by.vue b/client/components/MessageTypes/topic_set_by.vue
index 66559880..b6b4e9f0 100644
--- a/client/components/MessageTypes/topic_set_by.vue
+++ b/client/components/MessageTypes/topic_set_by.vue
@@ -16,7 +16,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
computed: {
diff --git a/client/components/MessageTypes/whois.vue b/client/components/MessageTypes/whois.vue
index 5a080788..c67bc640 100644
--- a/client/components/MessageTypes/whois.vue
+++ b/client/components/MessageTypes/whois.vue
@@ -123,7 +123,7 @@ export default {
Username,
},
props: {
- network: Object,
+ network: Object as PropType,
message: Object,
},
methods: {
diff --git a/client/components/NetworkList.vue b/client/components/NetworkList.vue
index 915cda1b..4cd2fe11 100644
--- a/client/components/NetworkList.vue
+++ b/client/components/NetworkList.vue
@@ -209,7 +209,7 @@ import isIgnoredKeybind from "../js/helpers/isIgnoredKeybind";
import distance from "../js/helpers/distance";
import eventbus from "../js/eventbus";
-export default Vue.extend({
+export default {
name: "NetworkList",
components: {
JoinChannel,
@@ -481,5 +481,5 @@ export default Vue.extend({
});
},
},
-});
+};
diff --git a/client/components/NetworkLobby.vue b/client/components/NetworkLobby.vue
index 1b94c22d..a4fc35ce 100644
--- a/client/components/NetworkLobby.vue
+++ b/client/components/NetworkLobby.vue
@@ -56,7 +56,7 @@ export default {
ChannelWrapper,
},
props: {
- network: Object,
+ network: Object as PropType,
isJoinChannelShown: Boolean,
active: Boolean,
isFiltering: Boolean,
diff --git a/client/components/ParsedMessage.vue b/client/components/ParsedMessage.vue
index 842cbb34..cbfe38a9 100644
--- a/client/components/ParsedMessage.vue
+++ b/client/components/ParsedMessage.vue
@@ -7,7 +7,7 @@ export default {
props: {
text: String,
message: Object,
- network: Object,
+ network: Object as PropType,
},
render(createElement, context) {
return parse(
diff --git a/client/components/Special/ListBans.vue b/client/components/Special/ListBans.vue
index 781be944..82d2b202 100644
--- a/client/components/Special/ListBans.vue
+++ b/client/components/Special/ListBans.vue
@@ -27,8 +27,8 @@ export default {
ParsedMessage,
},
props: {
- network: Object,
- channel: Object,
+ network: Object as PropType,
+ channel: Object as PropType,
},
methods: {
localetime(date) {
diff --git a/client/components/Special/ListChannels.vue b/client/components/Special/ListChannels.vue
index bc415346..16f7d906 100644
--- a/client/components/Special/ListChannels.vue
+++ b/client/components/Special/ListChannels.vue
@@ -27,8 +27,8 @@ export default {
ParsedMessage,
},
props: {
- network: Object,
- channel: Object,
+ network: Object as PropType,
+ channel: Object as PropType,
},
};
diff --git a/client/components/Special/ListIgnored.vue b/client/components/Special/ListIgnored.vue
index be672266..21317e53 100644
--- a/client/components/Special/ListIgnored.vue
+++ b/client/components/Special/ListIgnored.vue
@@ -25,8 +25,8 @@ export default {
ParsedMessage,
},
props: {
- network: Object,
- channel: Object,
+ network: Object as PropType,
+ channel: Object as PropType,
},
methods: {
localetime(date) {
diff --git a/client/components/Special/ListInvites.vue b/client/components/Special/ListInvites.vue
index 9f59645e..8d7cd04e 100644
--- a/client/components/Special/ListInvites.vue
+++ b/client/components/Special/ListInvites.vue
@@ -29,8 +29,8 @@ export default {
ParsedMessage,
},
props: {
- network: Object,
- channel: Object,
+ network: Object as PropType,
+ channel: Object as PropType,
},
methods: {
localetime(date) {
diff --git a/client/components/Username.vue b/client/components/Username.vue
index 92ebb60f..6d6aadce 100644
--- a/client/components/Username.vue
+++ b/client/components/Username.vue
@@ -20,8 +20,8 @@ export default {
user: Object,
active: Boolean,
onHover: Function,
- channel: Object,
- network: Object,
+ channel: Object as PropType,
+ network: Object as PropType,
},
computed: {
mode() {
diff --git a/client/js/socket-events/init.ts b/client/js/socket-events/init.ts
index 79d5d38a..397f07cc 100644
--- a/client/js/socket-events/init.ts
+++ b/client/js/socket-events/init.ts
@@ -4,6 +4,7 @@ import storage from "../localStorage";
import {router, switchToChannel, navigate} from "../router";
import store from "../store";
import parseIrcUri from "../helpers/parseIrcUri";
+import {ClientChan, ClientNetwork, InitClientChan} from "../types";
socket.on("init", function (data) {
store.commit("networks", mergeNetworkData(data.networks));
@@ -47,8 +48,9 @@ socket.on("init", function (data) {
}
});
-function mergeNetworkData(newNetworks) {
- const collapsedNetworks = new Set(JSON.parse(storage.get("thelounge.networks.collapsed")));
+function mergeNetworkData(newNetworks: ClientNetwork[]) {
+ const stored = storage.get("thelounge.networks.collapsed");
+ const collapsedNetworks = stored ? new Set(JSON.parse(stored)) : new Set();
for (let n = 0; n < newNetworks.length; n++) {
const network = newNetworks[n];
@@ -74,7 +76,7 @@ function mergeNetworkData(newNetworks) {
if (key === "channels") {
currentNetwork.channels = mergeChannelData(
currentNetwork.channels,
- network.channels
+ network.channels as InitClientChan[]
);
} else {
currentNetwork[key] = network[key];
@@ -87,7 +89,7 @@ function mergeNetworkData(newNetworks) {
return newNetworks;
}
-function mergeChannelData(oldChannels, newChannels) {
+function mergeChannelData(oldChannels: InitClientChan[], newChannels: InitClientChan[]) {
for (let c = 0; c < newChannels.length; c++) {
const channel = newChannels[c];
const currentChannel = oldChannels.find((chan) => chan.id === channel.id);
@@ -131,7 +133,7 @@ function mergeChannelData(oldChannels, newChannels) {
// on the client, and decide whether theres more messages to load from server
if (key === "totalMessages") {
currentChannel.moreHistoryAvailable =
- channel.totalMessages > currentChannel.messages.length;
+ channel.totalMessages! > currentChannel.messages.length;
continue;
}
@@ -167,10 +169,12 @@ function handleQueryParams() {
if (params.has("uri")) {
// Set default connection settings from IRC protocol links
const uri = params.get("uri");
- const queryParams = parseIrcUri(uri);
+ const queryParams = parseIrcUri(uri as string);
cleanParams();
- router.push({name: "Connect", query: queryParams});
+ router.push({name: "Connect", query: queryParams}).catch(() => {
+ // Ignore errors
+ });
return true;
} else if (document.body.classList.contains("public") && document.location.search) {
@@ -178,7 +182,9 @@ function handleQueryParams() {
const queryParams = Object.fromEntries(params.entries());
cleanParams();
- router.push({name: "Connect", query: queryParams});
+ router.push({name: "Connect", query: queryParams}).catch(() => {
+ // Ignore errors
+ });
return true;
}
diff --git a/client/js/store.ts b/client/js/store.ts
index 9df4b138..9fd27e63 100644
--- a/client/js/store.ts
+++ b/client/js/store.ts
@@ -1,8 +1,8 @@
import Vue from "vue";
-import Vuex from "vuex";
+import Vuex, {GetterTree, Store} from "vuex";
import {createSettingsStore} from "./store-settings";
import storage from "./localStorage";
-import {ClientChan, ClientNetwork} from "./types";
+import type {ClientChan, ClientNetwork, InitClientChan} from "./types";
const appName = document.title;
@@ -20,7 +20,7 @@ function detectDesktopNotificationState() {
return "blocked";
}
-export type State = {
+export interface State {
appLoaded: boolean;
activeChannel: {
network: ClientNetwork;
@@ -54,13 +54,15 @@ export type State = {
} | null;
messageSearchInProgress: boolean;
searchEnabled: boolean;
-};
+}
-export type SettingsState = {};
-const store = new Vuex.Store>({
+const store = new Store({
state: {
appLoaded: false,
- activeChannel: null,
+ activeChannel: {
+ network: {} as ClientNetwork,
+ channel: {} as ClientChan,
+ },
currentUserVisibleError: null,
desktopNotificationState: detectDesktopNotificationState(),
isAutoCompleting: false,
@@ -162,14 +164,16 @@ const store = new Vuex.Store>({
state.messageSearchResults = value;
},
addMessageSearchResults(state, value) {
- if (state.messageSearchResults!.results) {
- // Append the search results and add networks and channels to new messages
- value.results = [...state.messageSearchResults!.results, ...value.results];
- } else {
- value.results = value.results;
+ // Append the search results and add networks and channels to new messages
+ if (!state.messageSearchResults) {
+ state.messageSearchResults = {results: []};
}
- state.messageSearchResults = value;
+ const results = [...state.messageSearchResults.results, ...value.results];
+
+ state.messageSearchResults = {
+ results,
+ };
},
},
actions: {
@@ -179,11 +183,11 @@ const store = new Vuex.Store>({
},
},
getters: {
- findChannelOnCurrentNetwork: (state) => (name) => {
+ findChannelOnCurrentNetwork: (state) => (name: string) => {
name = name.toLowerCase();
return state.activeChannel?.network.channels.find((c) => c.name.toLowerCase() === name);
},
- findChannelOnNetwork: (state) => (networkUuid, channelName) => {
+ findChannelOnNetwork: (state) => (networkUuid: string, channelName: string) => {
for (const network of state.networks) {
if (network.uuid !== networkUuid) {
continue;
@@ -198,7 +202,7 @@ const store = new Vuex.Store>({
return null;
},
- findChannel: (state) => (id) => {
+ findChannel: (state) => (id: number) => {
for (const network of state.networks) {
for (const channel of network.channels) {
if (channel.id === id) {
@@ -209,7 +213,7 @@ const store = new Vuex.Store>({
return null;
},
- findNetwork: (state) => (uuid) => {
+ findNetwork: (state) => (uuid: string) => {
for (const network of state.networks) {
if (network.uuid === uuid) {
return network;
@@ -233,14 +237,16 @@ const store = new Vuex.Store>({
return highlightCount;
},
+ // TODO: type
title(state, getters) {
- const alertEventCount = getters.highlightCount ? `(${getters.highlightCount}) ` : "";
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ const alertEventCount = getters?.highlightCount ? `(${getters.highlightCount}) ` : "";
const channelname = state.activeChannel ? `${state.activeChannel.channel.name} — ` : "";
return alertEventCount + channelname + appName;
},
- initChannel: () => (channel) => {
+ initChannel: () => (channel: InitClientChan) => {
// TODO: This should be a mutation
channel.pendingMessage = "";
channel.inputHistoryPosition = 0;
@@ -250,20 +256,20 @@ const store = new Vuex.Store>({
.filter((m) => m.self && m.text && m.type === "message")
.map((m) => m.text)
.reverse()
- .slice(null, 99)
+ .slice(0, 99)
);
channel.historyLoading = false;
channel.scrolledToBottom = true;
channel.editTopic = false;
- channel.moreHistoryAvailable = channel.totalMessages > channel.messages.length;
+ channel.moreHistoryAvailable = channel.totalMessages! > channel.messages.length;
delete channel.totalMessages;
if (channel.type === "channel") {
channel.usersOutdated = true;
}
- return channel;
+ return channel as ClientChan;
},
},
});
diff --git a/client/js/types.d.ts b/client/js/types.d.ts
index 23947726..84d2c9d1 100644
--- a/client/js/types.d.ts
+++ b/client/js/types.d.ts
@@ -5,6 +5,7 @@ declare module "*.vue" {
import Vue from "vue";
export default Vue;
}
+
interface LoungeWindow extends Window {
g_TheLoungeRemoveLoading?: () => void;
}
@@ -12,8 +13,22 @@ interface LoungeWindow extends Window {
type ClientChan = Chan & {
moreHistoryAvailable: boolean;
editTopic: boolean;
+
+ // these are added in store/initChannel
+ pendingMessage: string;
+ inputHistoryPosition: number;
+ inputHistory: string[];
+ historyLoading: boolean;
+ scrolledToBottom: boolean;
+ usersOutdated: boolean;
+};
+
+type InitClientChan = ClientChan & {
+ // total messages is deleted after its use when init event is sent/handled
+ totalMessages?: number;
};
type ClientNetwork = Network & {
isJoinChannelShown: boolean;
+ isCollapsed: boolean;
};
diff --git a/client/js/vue.ts b/client/js/vue.ts
index 80833412..cee2afaa 100644
--- a/client/js/vue.ts
+++ b/client/js/vue.ts
@@ -2,7 +2,7 @@ import constants from "./constants";
import "../css/style.css";
import Vue from "vue";
-import store from "./store";
+import store, {State} from "./store";
import App from "../components/App.vue";
import storage from "./localStorage";
import {router, navigate} from "./router";
@@ -13,20 +13,26 @@ import "./socket-events";
import "./webpush";
import "./keybinds";
import {ClientChan} from "./types";
+import {Store} from "vuex";
const favicon = document.getElementById("favicon");
const faviconNormal = favicon?.getAttribute("href") || "";
const faviconAlerted = favicon?.dataset.other || "";
-type Data = {};
-export type Methods = {
- switchToChannel: (channel: ClientChan) => void;
- closeChannel: (channel: ClientChan) => void;
-};
-type Computed = {};
-type Props = {};
+declare module "vue/types/vue" {
+ interface Vue {
+ debouncedResize: () => void;
+ // TODO; type as Timeout
+ dayChangeTimeout: any;
-new Vue({
+ switchToChannel: (channel: ClientChan) => void;
+ closeChannel: (channel: ClientChan) => void;
+
+ $store: Store;
+ }
+}
+
+new Vue({
el: "#viewport",
router,
mounted() {
diff --git a/package.json b/package.json
index f1fbc3e4..6a12eefa 100644
--- a/package.json
+++ b/package.json
@@ -111,6 +111,7 @@
"@typescript-eslint/parser": "5.22.0",
"@vue/babel-helper-vue-jsx-merge-props": "1.2.1",
"@vue/babel-preset-jsx": "1.2.4",
+ "@vue/runtime-core": "3.2.35",
"@vue/runtime-dom": "3.2.33",
"@vue/server-test-utils": "1.3.0",
"@vue/test-utils": "1.3.0",
diff --git a/src/plugins/dev-server.ts b/src/plugins/dev-server.ts
index 3aa073c0..e9e55981 100644
--- a/src/plugins/dev-server.ts
+++ b/src/plugins/dev-server.ts
@@ -4,11 +4,22 @@ import express from "express";
import log from "../log";
+import webpack from "webpack";
+import config from "../../webpack.config";
+
export default (app: express.Application) => {
log.debug("Starting server in development mode");
- const webpack = require("webpack");
- const webpackConfig = require("../../webpack.config.js")(undefined, { mode: "production" });
+ const webpackConfig = config(undefined, {mode: "production"});
+
+ if (
+ !webpackConfig ||
+ !webpackConfig.plugins?.length ||
+ !webpackConfig.entry ||
+ !webpackConfig.entry["js/bundle.js"]
+ ) {
+ throw new Error("No valid production webpack config found");
+ }
webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
webpackConfig.entry["js/bundle.js"].push(
diff --git a/test/client/components/ParsedMessageTestWrapper.vue b/test/client/components/ParsedMessageTestWrapper.vue
index 10e4333a..7913f41d 100644
--- a/test/client/components/ParsedMessageTestWrapper.vue
+++ b/test/client/components/ParsedMessageTestWrapper.vue
@@ -15,7 +15,7 @@ export default {
props: {
text: String,
message: Object,
- network: Object,
+ network: Object as PropType,
},
};
diff --git a/webpack.config.js b/webpack.config.js
deleted file mode 100644
index 018533a5..00000000
--- a/webpack.config.js
+++ /dev/null
@@ -1,189 +0,0 @@
-"use strict";
-
-const webpack = require("webpack");
-const path = require("path");
-const CopyPlugin = require("copy-webpack-plugin");
-const MiniCssExtractPlugin = require("mini-css-extract-plugin");
-const VueLoaderPlugin = require("vue-loader/lib/plugin");
-const Helper = require("./src/helper.js");
-const babelConfig = require("./babel.config.cjs");
-
-const isProduction = process.env.NODE_ENV === "production";
-const config = {
- mode: isProduction ? "production" : "development",
- entry: {
- "js/bundle.js": [path.resolve(__dirname, "client/js/vue.js")],
- },
- devtool: "source-map",
- output: {
- clean: true, // Clean the output directory before emit.
- path: path.resolve(__dirname, "public"),
- filename: "[name]",
- publicPath: "/",
- },
- performance: {
- hints: false,
- },
- module: {
- rules: [
- {
- test: /\.vue$/,
- use: {
- loader: "vue-loader",
- options: {
- compilerOptions: {
- preserveWhitespace: false,
- },
- },
- },
- },
- {
- test: /\.css$/,
- use: [
- {
- loader: MiniCssExtractPlugin.loader,
- options: {
- esModule: false,
- },
- },
- {
- loader: "css-loader",
- options: {
- url: false,
- importLoaders: 1,
- sourceMap: true,
- },
- },
- {
- loader: "postcss-loader",
- options: {
- sourceMap: true,
- },
- },
- ],
- },
- {
- test: /\.js$/,
- include: [path.resolve(__dirname, "client")],
- use: {
- loader: "babel-loader",
- options: babelConfig,
- },
- },
- ],
- },
- optimization: {
- splitChunks: {
- cacheGroups: {
- commons: {
- test: /[\\/]node_modules[\\/]/,
- name: "js/bundle.vendor.js",
- chunks: "all",
- },
- },
- },
- },
- externals: {
- json3: "JSON", // socket.io uses json3.js, but we do not target any browsers that need it
- },
- plugins: [
- new VueLoaderPlugin(),
- new MiniCssExtractPlugin({
- filename: "css/style.css",
- }),
- new CopyPlugin({
- patterns: [
- {
- from: "./node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff*",
- to: "fonts/[name][ext]",
- },
- {
- from: "./client/js/loading-error-handlers.js",
- to: "js/[name][ext]",
- },
- {
- from: "./client/*",
- to: "[name][ext]",
- globOptions: {
- ignore: ["**/index.html.tpl", "**/service-worker.js"],
- },
- },
- {
- from: "./client/service-worker.js",
- to: "[name][ext]",
- transform(content) {
- return content
- .toString()
- .replace(
- "__HASH__",
- isProduction ? Helper.getVersionCacheBust() : "dev"
- );
- },
- },
- {
- from: "./client/audio/*",
- to: "audio/[name][ext]",
- },
- {
- from: "./client/img/*",
- to: "img/[name][ext]",
- },
- {
- from: "./client/themes/*",
- to: "themes/[name][ext]",
- },
- ],
- }),
- // socket.io uses debug, we don't need it
- new webpack.NormalModuleReplacementPlugin(
- /debug/,
- path.resolve(__dirname, "scripts/noop.js")
- ),
- ],
-};
-
-module.exports = (env, argv) => {
- if (argv.mode === "development") {
- config.target = "node";
- config.devtool = "eval";
- config.stats = "errors-only";
- config.output.path = path.resolve(__dirname, "test/public");
- config.entry = {
- "testclient.js": [path.resolve(__dirname, "test/client/index.js")],
- };
-
- // Add the istanbul plugin to babel-loader options
- for (const rule of config.module.rules) {
- if (rule.use.loader === "babel-loader") {
- rule.use.options.plugins = ["istanbul"];
- }
- }
-
- // `optimization.splitChunks` is incompatible with a `target` of `node`. See:
- // - https://github.com/zinserjan/mocha-webpack/issues/84
- // - https://github.com/webpack/webpack/issues/6727#issuecomment-372589122
- config.optimization.splitChunks = false;
-
- // Disable plugins like copy files, it is not required
- config.plugins = [
- new VueLoaderPlugin(),
- new MiniCssExtractPlugin({
- filename: "css/style.css",
- }),
- // Client tests that require Vue may end up requireing socket.io
- new webpack.NormalModuleReplacementPlugin(
- /js(\/|\\)socket\.js/,
- path.resolve(__dirname, "scripts/noop.js")
- ),
-
- // "Fixes" Critical dependency: the request of a dependency is an expression
- new webpack.ContextReplacementPlugin(/vue-server-renderer$/),
- ];
- }
-
- if (argv.mode === "production") {
- // ...
- }
-
- return config;
-};
diff --git a/yarn.lock b/yarn.lock
index 11c14dc5..c02bfa86 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1874,6 +1874,13 @@
dependencies:
"@vue/shared" "3.2.33"
+"@vue/reactivity@3.2.35":
+ version "3.2.35"
+ resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.35.tgz#c66af289f3beda6aba63c264db9c6acd607d1c73"
+ integrity sha512-6j9N9R1SwHVcJas4YqAzwdRS/cgmj3Z9aUert5Mv1jk5B9H9ivN/zot/fgMUbseWXigkkmX60OsfRbz49o8kCw==
+ dependencies:
+ "@vue/shared" "3.2.35"
+
"@vue/runtime-core@3.2.33":
version "3.2.33"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.33.tgz#2df8907c85c37c3419fbd1bdf1a2df097fa40df2"
@@ -1882,6 +1889,14 @@
"@vue/reactivity" "3.2.33"
"@vue/shared" "3.2.33"
+"@vue/runtime-core@3.2.35":
+ version "3.2.35"
+ resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.35.tgz#a87bd5214ff31f9dc6542f5c498d4f3543c6ea8f"
+ integrity sha512-P8AeGPRGyIiYdOdvLc/7KR8VSdbUGG8Jxdx6Xlj5okEjyV9IYxeHRIQIoye85K0lZXBH4zuh1syD1mX+oZ0KqQ==
+ dependencies:
+ "@vue/reactivity" "3.2.35"
+ "@vue/shared" "3.2.35"
+
"@vue/runtime-dom@3.2.33":
version "3.2.33"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.33.tgz#123b8969247029ea0d9c1983676d4706a962d848"
@@ -1904,6 +1919,11 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.33.tgz#69a8c99ceb37c1b031d5cc4aec2ff1dc77e1161e"
integrity sha512-UBc1Pg1T3yZ97vsA2ueER0F6GbJebLHYlEi4ou1H5YL4KWvMOOWwpYo9/QpWq93wxKG6Wo13IY74Hcn/f7c7Bg==
+"@vue/shared@3.2.35":
+ version "3.2.35"
+ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.35.tgz#fb60530fa009dc21473386a7639eed833877cb0f"
+ integrity sha512-/sxDqMcy0MsfQ3LQixKYDxIinDYNy1dXTsF2Am0pv0toImWabymFQ8cFmPJnPt+gh5ElKwwn7KzQcDbLHar60A==
+
"@vue/test-utils@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.3.0.tgz#d563decdcd9c68a7bca151d4179a2bfd6d5c3e15"