add @babel/plugin-transform-runtime, fix scrolling on chan switch/history loading

This commit is contained in:
Max Leiter 2022-05-30 21:10:11 -07:00
parent 027ab3bbfc
commit a1659e1c02
No known key found for this signature in database
GPG key ID: A3512F2F2F17EBDA
24 changed files with 147 additions and 148 deletions

View file

@ -5,5 +5,6 @@ module.exports = {
"@babel/preset-typescript", // ? babel-preset-typescript-vue should be a drop-in replacement for @babel/typescript with vue support
// "@vue/babel-preset-jsx",
],
plugins: ["@babel/plugin-transform-runtime"],
targets: "> 0.25%, not dead",
};

View file

@ -105,7 +105,6 @@
:network="network"
:channel="channel"
:focused="focused"
@scrolled-to-bottom="onScrolledToBottom"
/>
</div>
</div>
@ -175,10 +174,6 @@ export default defineComponent({
return undefined;
});
const onScrolledToBottom = (data: boolean) => {
props.channel.scrolledToBottom = data;
};
const channelChanged = () => {
// Triggered when active channel is set or changed
emit("channel-changed", props.channel);
@ -234,21 +229,15 @@ export default defineComponent({
});
};
watch(
() => props.channel,
() => {
channelChanged();
}
);
watch(props.channel, () => {
channelChanged();
});
const editTopicRef = ref(props.channel.editTopic);
watch(editTopicRef, (newTopic) => {
if (newTopic) {
nextTick(() => {
void nextTick(() => {
topicInput.value?.focus();
}).catch((e) => {
// eslint-disable-next-line no-console
console.error(e);
});
}
});
@ -257,10 +246,8 @@ export default defineComponent({
channelChanged();
if (props.channel.editTopic) {
nextTick(() => {
void nextTick(() => {
topicInput.value?.focus();
}).catch(() => {
// no-op
});
}
});
@ -271,7 +258,6 @@ export default defineComponent({
topicInput,
specialComponent,
editTopicRef,
onScrolledToBottom,
hideUserVisibleError,
editTopic,
saveTopic,

View file

@ -102,7 +102,7 @@ export default defineComponent({
const autocompletionRef = ref<ReturnType<typeof autocompletion>>();
const setInputSize = () => {
nextTick(() => {
void nextTick(() => {
if (!input.value) {
return;
}
@ -120,8 +120,6 @@ export default defineComponent({
input.value.style.height = `${
Math.ceil(input.value.scrollHeight / lineHeight) * lineHeight
}px`;
}).catch(() => {
// no-op
});
};

View file

@ -175,11 +175,9 @@ export default defineComponent({
const scrollToActiveUser = () => {
// Scroll the list if needed after the active class is applied
nextTick(() => {
void nextTick(() => {
const el = userlist.value?.querySelector(".active");
el?.scrollIntoView({block: "nearest", inline: "nearest"});
}).catch(() => {
// no-op
});
};

View file

@ -9,7 +9,14 @@
<script lang="ts">
import dayjs from "dayjs";
import calendar from "dayjs/plugin/calendar";
import {computed, defineComponent, onBeforeUnmount, onMounted, PropType} from "vue";
import {
computed,
defineComponent,
getCurrentInstance,
onBeforeUnmount,
onMounted,
PropType,
} from "vue";
import eventbus from "../js/eventbus";
import {ClientMessage} from "../js/types";
@ -33,8 +40,8 @@ export default defineComponent({
const dayChange = () => {
// TODO: this is nasty. and maybe doesnt work?
// const instance = Vue.getCurrentInstance();
// instance?.proxy?.$forceUpdate();
const instance = getCurrentInstance();
instance?.proxy?.$forceUpdate();
if (hoursPassed() >= 48) {
eventbus.off("daychange", dayChange);

View file

@ -179,12 +179,9 @@ export default defineComponent({
return messages.filter((message) => !message.channel?.channel.muted);
});
watch(
() => store.state.mentions,
() => {
isLoading.value = false;
}
);
watch(store.state.mentions, () => {
isLoading.value = false;
});
const messageTime = (time: string) => {
return dayjs(time).fromNow();

View file

@ -120,7 +120,7 @@ export default defineComponent({
message: {type: Object as PropType<ClientMessage>, required: true},
channel: {type: Object as PropType<ClientChan>, required: false},
network: {type: Object as PropType<ClientNetwork>, required: true},
keepScrollPosition: Function,
keepScrollPosition: Function as PropType<() => void>,
isPreviousSource: Boolean,
focused: Boolean,
},

View file

@ -66,7 +66,6 @@ import Message from "./Message.vue";
import MessageCondensed from "./MessageCondensed.vue";
import DateMarker from "./DateMarker.vue";
import {
defineExpose,
computed,
defineComponent,
nextTick,
@ -104,7 +103,6 @@ export default defineComponent({
channel: {type: Object as PropType<ClientChan>, required: true},
focused: String,
},
emits: ["scrolled-to-bottom"],
setup(props, {emit}) {
const store = useStore();
@ -117,7 +115,7 @@ export default defineComponent({
const jumpToBottom = () => {
skipNextScrollEvent.value = true;
emit("scrolled-to-bottom", true);
props.channel.scrolledToBottom = true;
const el = chat.value;
@ -173,8 +171,9 @@ export default defineComponent({
}
jumpToBottom();
}).catch(() => {
// no-op
}).catch((e) => {
// eslint-disable-next-line no-console
console.error("Error in new IntersectionObserver", e);
});
const condensedMessages = computed(() => {
@ -293,7 +292,9 @@ export default defineComponent({
}
};
const keepScrollPosition = () => {
const keepScrollPosition = async () => {
console.log("keepScrollPosition");
// If we are already waiting for the next tick to force scroll position,
// we have no reason to perform more checks and set it again in the next tick
if (isWaitingForNextTick.value) {
@ -306,40 +307,44 @@ export default defineComponent({
return;
}
console.log(el);
if (!props.channel.scrolledToBottom) {
if (props.channel.historyLoading) {
const heightOld = el.scrollHeight - el.scrollTop;
isWaitingForNextTick.value = true;
nextTick(() => {
isWaitingForNextTick.value = false;
skipNextScrollEvent.value = true;
el.scrollTop = el.scrollHeight - heightOld;
}).catch(() => {
// no-op
});
await nextTick();
isWaitingForNextTick.value = false;
skipNextScrollEvent.value = true;
console.log("old top", heightOld);
el.scrollTop = el.scrollHeight - heightOld;
console.log("new top", el.scrollTop);
}
return;
}
isWaitingForNextTick.value = true;
nextTick(() => {
isWaitingForNextTick.value = false;
jumpToBottom();
}).catch(() => {
// no-op
});
await nextTick();
isWaitingForNextTick.value = false;
console.log("HERE");
jumpToBottom();
};
const onLinkPreviewToggle = (preview: ClientLinkPreview, message: ClientMessage) => {
keepScrollPosition();
const onLinkPreviewToggle = async (preview: ClientLinkPreview, message: ClientMessage) => {
await keepScrollPosition();
// Tell the server we're toggling so it remembers at page reload
socket.emit("msg:preview:toggle", {
target: props.channel.id,
msgId: message.id,
// TODO: type
// @ts-ignore
link: preview.link,
shown: preview.shown,
});
@ -359,7 +364,7 @@ export default defineComponent({
return;
}
emit("scrolled-to-bottom", el.scrollHeight - el.scrollTop - el.offsetHeight <= 30);
props.channel.scrolledToBottom = el.scrollHeight - el.scrollTop - el.offsetHeight <= 30;
};
const handleResize = () => {
@ -374,19 +379,17 @@ export default defineComponent({
eventbus.on("resize", handleResize);
nextTick(() => {
void nextTick(() => {
if (historyObserver.value && loadMoreButton.value) {
historyObserver.value.observe(loadMoreButton.value);
}
}).catch(() => {
// no-op
});
});
watch(
() => props.channel.id,
() => {
emit("scrolled-to-bottom", true);
props.channel.scrolledToBottom = true;
// Re-add the intersection observer to trigger the check again on channel switch
// Otherwise if last channel had the button visible, switching to a new channel won't trigger the history
@ -399,20 +402,19 @@ export default defineComponent({
watch(
() => props.channel.messages,
() => {
keepScrollPosition();
async () => {
await keepScrollPosition();
},
{
deep: true,
}
);
watch(
() => props.channel.pendingMessage,
() => {
nextTick(() => {
// Keep the scroll stuck when input gets resized while typing
keepScrollPosition();
}).catch(() => {
// no-op
});
async () => {
// Keep the scroll stuck when input gets resized while typing
await keepScrollPosition();
}
);

View file

@ -475,10 +475,8 @@ export default defineComponent({
watch(displayPasswordField, (newValue) => {
if (newValue) {
nextTick(() => {
void nextTick(() => {
publicPassword.value?.focus();
}).catch(() => {
// no-op
});
}
});
@ -502,10 +500,8 @@ export default defineComponent({
watch(
() => props.defaults?.commands,
() => {
nextTick(() => {
void nextTick(() => {
resizeCommandsInput();
}).catch((e) => {
// no-op
});
}
);

View file

@ -383,10 +383,8 @@ export default defineComponent({
sidebarWasClosed.value = store.state.sidebarOpen ? false : true;
store.commit("sidebarOpen", true);
nextTick(() => {
void nextTick(() => {
searchInput.value?.focus();
}).catch(() => {
// no-op
});
};
@ -432,14 +430,12 @@ export default defineComponent({
const scrollToActive = () => {
// Scroll the list if needed after the active class is applied
nextTick(() => {
void nextTick(() => {
const el = networklist.value?.querySelector(".channel-list-item.active");
if (el) {
el.scrollIntoView({block: "nearest", inline: "nearest"});
}
}).catch(() => {
// no-op
});
};

View file

@ -265,13 +265,10 @@ export default defineComponent({
}
);
watch(
() => route.query,
() => {
doSearch();
setActiveChannel();
}
);
watch(route.query, () => {
doSearch();
setActiveChannel();
});
watch(messages, () => {
moreResultsAvailable.value = !!(
@ -281,7 +278,7 @@ export default defineComponent({
if (!offset.value) {
jumpToBottom();
} else {
nextTick(() => {
void nextTick(() => {
if (!chatRef) {
return;
}
@ -289,9 +286,6 @@ export default defineComponent({
const currentChatHeight = chatRef.scrollHeight;
chatRef.scrollTop =
oldScrollTop.value + currentChatHeight - oldChatHeight.value;
}).catch((e) => {
// eslint-disable-next-line no-console
console.error("Failed to scroll to bottom", e);
});
}
});

View file

@ -172,27 +172,26 @@ router.afterEach((to) => {
}
});
function navigate(routeName: string, params: any = {}) {
async function navigate(routeName: string, params: any = {}) {
if (router.currentRoute.value.name) {
// eslint-disable-next-line @typescript-eslint/no-empty-function
router.push({name: routeName, params}).catch(() => {});
await router.push({name: routeName, params});
} else {
// If current route is null, replace the history entry
// This prevents invalid entries from lingering in history,
// and then the route guard preventing proper navigation
// eslint-disable-next-line @typescript-eslint/no-empty-function
router.replace({name: routeName, params}).catch(() => {});
await router.replace({name: routeName, params}).catch(() => {});
}
}
function switchToChannel(channel: ClientChan) {
return navigate("RoutedChat", {id: channel.id});
return void navigate("RoutedChat", {id: channel.id});
}
if ("serviceWorker" in navigator) {
navigator.serviceWorker.addEventListener("message", (event) => {
if (event.data && event.data.type === "open") {
const id = parseInt(event.data.channel.substr(5), 10); // remove "chan-" prefix
const id = parseInt(event.data.channel.substring(5), 10); // remove "chan-" prefix
const channelTarget = store.getters.findChannel(id);

View file

@ -30,7 +30,10 @@ socket.on("init", function (data) {
if (!handleQueryParams()) {
// 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
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);
if (channel) {

View file

@ -2,8 +2,9 @@ import {nextTick} from "vue";
import socket from "../socket";
import {store} from "../store";
import {ClientMessage} from "../types";
socket.on("more", function (data) {
socket.on("more", async (data) => {
const channel = store.getters.findChannel(data.chan)?.channel;
if (!channel) {
@ -15,13 +16,12 @@ socket.on("more", function (data) {
.filter((m) => m.self && m.text && m.type === "message")
.map((m) => m.text)
.reverse()
.slice(null, 100 - channel.inputHistory.length)
.slice(undefined, 100 - channel.inputHistory.length)
);
channel.moreHistoryAvailable =
data.totalMessages > channel.messages.length + data.messages.length;
channel.messages.unshift(...data.messages);
channel.messages.unshift(...(data.messages as ClientMessage[]));
nextTick(() => {
channel.historyLoading = false;
});
await nextTick();
channel.historyLoading = false;
});

View file

@ -78,6 +78,7 @@
"devDependencies": {
"@babel/core": "7.17.10",
"@babel/plugin-syntax-jsx": "7.16.7",
"@babel/plugin-transform-runtime": "7.18.2",
"@babel/plugin-transform-typescript": "7.16.8",
"@babel/preset-env": "7.17.10",
"@babel/preset-typescript": "7.16.7",

View file

@ -146,10 +146,6 @@ class Client {
for (const messageStorage of client.messageStorage) {
messageStorage.enable();
}
console.log(
"Message storage: " + client.messageStorage.map((m) => m.isEnabled).join(", ")
);
}
if (!_.isPlainObject(client.config.sessions)) {

View file

@ -306,7 +306,9 @@ class Chan {
requestZncPlayback(this, network, from);
}
})
.catch((err) => log.error(`Failed to load messages for ${client.name}: ${err}`));
.catch((err: Error) =>
log.error(`Failed to load messages for ${client.name}: ${err.toString()}`)
);
}
isLoggable() {
return this.type === ChanType.CHANNEL || this.type === ChanType.QUERY;

View file

@ -30,8 +30,6 @@ const toExport = {
initialized: false,
// TODO: fix typing
async initialize() {
log.info("Auth initializing", toExport.initialized);
if (toExport.initialized) {
return;
}
@ -40,8 +38,6 @@ const toExport = {
const resolvedPlugins = await Promise.all(plugins);
for (const {default: plugin} of resolvedPlugins) {
log.info("Auth plugin", plugin.moduleName, "enabled", plugin.isEnabled().toString());
if (plugin.isEnabled()) {
toExport.initialized = true;

View file

@ -18,8 +18,8 @@ function ldapAuthCommon(
tlsOptions: config.ldap.tlsOptions,
});
ldapclient.on("error", function (err) {
log.error(`Unable to connect to LDAP server: ${err}`);
ldapclient.on("error", function (err: Error) {
log.error(`Unable to connect to LDAP server: ${err.toString()}`);
callback(false);
});
@ -27,7 +27,7 @@ function ldapAuthCommon(
ldapclient.unbind();
if (err) {
log.error(`LDAP bind failed: ${err}`);
log.error(`LDAP bind failed: ${err.toString()}`);
callback(false);
} else {
callback(true);
@ -43,7 +43,7 @@ function simpleLdapAuth(user: string, password: string, callback: (success: bool
const config = Config.values;
const userDN = user.replace(/([,\\/#+<>;"= ])/g, "\\$1");
const bindDN = `${config.ldap.primaryKey}=${userDN},${config.ldap.baseDN}`;
const bindDN = `${config.ldap.primaryKey}=${userDN},${config.ldap.baseDN || ""}`;
log.info(`Auth against LDAP ${config.ldap.url} with provided bindDN ${bindDN}`);
@ -73,8 +73,8 @@ function advancedLdapAuth(user: string, password: string, callback: (success: bo
attributes: ["dn"],
} as SearchOptions;
ldapclient.on("error", function (err) {
log.error(`Unable to connect to LDAP server: ${err}`);
ldapclient.on("error", function (err: Error) {
log.error(`Unable to connect to LDAP server: ${err.toString()}`);
callback(false);
});
@ -99,15 +99,15 @@ function advancedLdapAuth(user: string, password: string, callback: (success: bo
res.on("searchEntry", function (entry) {
found = true;
const bindDN = entry.objectName;
log.info(`Auth against LDAP ${config.ldap.url} with found bindDN ${bindDN}`);
log.info(`Auth against LDAP ${config.ldap.url} with found bindDN ${bindDN || ""}`);
ldapclient.unbind();
// TODO: Fix type !
ldapAuthCommon(user, bindDN!, password, callback);
});
res.on("error", function (err3) {
log.error(`LDAP error: ${err3}`);
res.on("error", function (err3: Error) {
log.error(`LDAP error: ${err3.toString()}`);
callback(false);
});
@ -116,7 +116,9 @@ function advancedLdapAuth(user: string, password: string, callback: (success: bo
if (!found) {
log.warn(
`LDAP Search did not find anything for: ${userDN} (${result?.status})`
`LDAP Search did not find anything for: ${userDN} (${
result?.status.toString() || "unknown"
})`
);
callback(false);
}
@ -138,7 +140,7 @@ const ldapAuth: AuthHandler = (manager, client, user, password, callback) => {
callback(valid);
}
let auth;
let auth: typeof simpleLdapAuth | typeof advancedLdapAuth;
if ("baseDN" in Config.values.ldap) {
auth = simpleLdapAuth;
@ -164,8 +166,8 @@ function advancedLdapLoadUsers(users: string[], callbackLoadUser) {
const base = config.ldap.searchDN.base;
ldapclient.on("error", function (err) {
log.error(`Unable to connect to LDAP server: ${err}`);
ldapclient.on("error", function (err: Error) {
log.error(`Unable to connect to LDAP server: ${err.toString()}`);
});
ldapclient.bind(config.ldap.searchDN.rootDN, config.ldap.searchDN.rootPassword, function (err) {
@ -185,7 +187,7 @@ function advancedLdapLoadUsers(users: string[], callbackLoadUser) {
ldapclient.search(base, searchOptions, function (err2, res) {
if (err2) {
log.error(`LDAP search error: ${err2}`);
log.error(`LDAP search error: ${err2?.toString()}`);
return true;
}
@ -200,7 +202,7 @@ function advancedLdapLoadUsers(users: string[], callbackLoadUser) {
});
res.on("error", function (err3) {
log.error(`LDAP error: ${err3}`);
log.error(`LDAP error: ${err3.toString()}`);
});
res.on("end", function () {

View file

@ -27,9 +27,9 @@ export type LinkPreview = {
thumb: string;
size: number;
link: string; // Send original matched link to the client
shown: boolean | null;
error: undefined | string;
message: undefined | string;
shown?: boolean;
error?: string;
message?: string;
media: string;
mediaType: string;
@ -65,7 +65,7 @@ export default function (client: Client, chan: Chan, msg: Msg, cleanText: string
thumb: "",
size: -1,
link: link.link, // Send original matched link to the client
shown: null,
shown: undefined,
error: undefined,
message: undefined,
media: "",

View file

@ -3,7 +3,8 @@ import LinkPrefetch from "./link";
import cleanIrcMessage from "../../../client/js/helpers/ircmessageparser/cleanIrcMessage";
import Helper from "../../helper";
import {IrcEventHandler} from "../../client";
import {ChanType} from "../../models/chan";
import Chan, {ChanType} from "../../models/chan";
import User from "../../models/user";
const nickRegExp = /(?:\x03[0-9]{1,2}(?:,[0-9]{1,2})?)?([\w[\]\\`^{|}-]+)/g;
@ -12,28 +13,39 @@ export default <IrcEventHandler>function (irc, network) {
irc.on("notice", function (data) {
data.type = MessageType.NOTICE as any;
handleMessage(data);
handleMessage(data as any);
});
irc.on("action", function (data) {
data.type = MessageType.ACTION;
handleMessage(data);
handleMessage(data as any);
});
irc.on("privmsg", function (data) {
data.type = MessageType.MESSAGE;
handleMessage(data);
handleMessage(data as any);
});
irc.on("wallops", function (data) {
data.from_server = true;
data.type = MessageType.WALLOPS;
handleMessage(data);
handleMessage(data as any);
});
function handleMessage(data: any) {
let chan;
let from;
function handleMessage(data: {
nick: string;
from_server: boolean;
hostname: string;
ident: string;
target: string;
type: MessageType;
time: number;
text: string;
message: string;
group?: string;
}) {
let chan: Chan | undefined;
let from: User;
let highlight = false;
let showInActive = false;
const self = data.nick === irc.user.nick;
@ -108,7 +120,7 @@ export default <IrcEventHandler>function (irc, network) {
// msg is constructed down here because `from` is being copied in the constructor
const msg = new Msg({
type: data.type,
time: data.time,
time: data.time as any,
text: data.message,
self: self,
from: from,

View file

@ -51,9 +51,6 @@ class SqliteMessageStorage implements ISqliteMessageStorage {
const logsPath = Config.getUserLogsPath();
const sqlitePath = path.join(logsPath, `${this.client.name}.sqlite3`);
log.info("Logs path", logsPath);
log.info("Sqlite path", sqlitePath);
try {
fs.mkdirSync(logsPath, {recursive: true});
} catch (e: any) {

View file

@ -107,11 +107,13 @@ interface ClientToServerEvents {
"msg:preview:toggle": ({
target,
messageIds,
msgId,
shown,
}: {
target: number;
messageIds: number[];
shown: boolean;
messageIds?: number[];
msgId?: number;
shown?: boolean;
}) => void;
"network:get": (uuid: string) => void;
@ -133,6 +135,8 @@ interface ClientToServerEvents {
}) => void;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface InterServerEvents {}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface SocketData {}

View file

@ -821,6 +821,18 @@
dependencies:
"@babel/helper-plugin-utils" "^7.16.7"
"@babel/plugin-transform-runtime@7.18.2":
version "7.18.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.2.tgz#04637de1e45ae8847ff14b9beead09c33d34374d"
integrity sha512-mr1ufuRMfS52ttq+1G1PD8OJNqgcTFjq3hwn8SZ5n1x1pBhi0E36rYMdTK0TsKtApJ4lDEdfXJwtGobQMHSMPg==
dependencies:
"@babel/helper-module-imports" "^7.16.7"
"@babel/helper-plugin-utils" "^7.17.12"
babel-plugin-polyfill-corejs2 "^0.3.0"
babel-plugin-polyfill-corejs3 "^0.5.0"
babel-plugin-polyfill-regenerator "^0.3.0"
semver "^6.3.0"
"@babel/plugin-transform-shorthand-properties@^7.16.7":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a"