mirror of
https://github.com/thelounge/thelounge.git
synced 2024-05-19 22:56:37 +02:00
add @babel/plugin-transform-runtime, fix scrolling on chan switch/history loading
This commit is contained in:
parent
027ab3bbfc
commit
a1659e1c02
|
@ -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
|
"@babel/preset-typescript", // ? babel-preset-typescript-vue should be a drop-in replacement for @babel/typescript with vue support
|
||||||
// "@vue/babel-preset-jsx",
|
// "@vue/babel-preset-jsx",
|
||||||
],
|
],
|
||||||
|
plugins: ["@babel/plugin-transform-runtime"],
|
||||||
targets: "> 0.25%, not dead",
|
targets: "> 0.25%, not dead",
|
||||||
};
|
};
|
||||||
|
|
|
@ -105,7 +105,6 @@
|
||||||
:network="network"
|
:network="network"
|
||||||
:channel="channel"
|
:channel="channel"
|
||||||
:focused="focused"
|
:focused="focused"
|
||||||
@scrolled-to-bottom="onScrolledToBottom"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -175,10 +174,6 @@ export default defineComponent({
|
||||||
return undefined;
|
return undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
const onScrolledToBottom = (data: boolean) => {
|
|
||||||
props.channel.scrolledToBottom = data;
|
|
||||||
};
|
|
||||||
|
|
||||||
const channelChanged = () => {
|
const channelChanged = () => {
|
||||||
// Triggered when active channel is set or changed
|
// Triggered when active channel is set or changed
|
||||||
emit("channel-changed", props.channel);
|
emit("channel-changed", props.channel);
|
||||||
|
@ -234,21 +229,15 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(props.channel, () => {
|
||||||
() => props.channel,
|
channelChanged();
|
||||||
() => {
|
});
|
||||||
channelChanged();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const editTopicRef = ref(props.channel.editTopic);
|
const editTopicRef = ref(props.channel.editTopic);
|
||||||
watch(editTopicRef, (newTopic) => {
|
watch(editTopicRef, (newTopic) => {
|
||||||
if (newTopic) {
|
if (newTopic) {
|
||||||
nextTick(() => {
|
void nextTick(() => {
|
||||||
topicInput.value?.focus();
|
topicInput.value?.focus();
|
||||||
}).catch((e) => {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(e);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -257,10 +246,8 @@ export default defineComponent({
|
||||||
channelChanged();
|
channelChanged();
|
||||||
|
|
||||||
if (props.channel.editTopic) {
|
if (props.channel.editTopic) {
|
||||||
nextTick(() => {
|
void nextTick(() => {
|
||||||
topicInput.value?.focus();
|
topicInput.value?.focus();
|
||||||
}).catch(() => {
|
|
||||||
// no-op
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -271,7 +258,6 @@ export default defineComponent({
|
||||||
topicInput,
|
topicInput,
|
||||||
specialComponent,
|
specialComponent,
|
||||||
editTopicRef,
|
editTopicRef,
|
||||||
onScrolledToBottom,
|
|
||||||
hideUserVisibleError,
|
hideUserVisibleError,
|
||||||
editTopic,
|
editTopic,
|
||||||
saveTopic,
|
saveTopic,
|
||||||
|
|
|
@ -102,7 +102,7 @@ export default defineComponent({
|
||||||
const autocompletionRef = ref<ReturnType<typeof autocompletion>>();
|
const autocompletionRef = ref<ReturnType<typeof autocompletion>>();
|
||||||
|
|
||||||
const setInputSize = () => {
|
const setInputSize = () => {
|
||||||
nextTick(() => {
|
void nextTick(() => {
|
||||||
if (!input.value) {
|
if (!input.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -120,8 +120,6 @@ export default defineComponent({
|
||||||
input.value.style.height = `${
|
input.value.style.height = `${
|
||||||
Math.ceil(input.value.scrollHeight / lineHeight) * lineHeight
|
Math.ceil(input.value.scrollHeight / lineHeight) * lineHeight
|
||||||
}px`;
|
}px`;
|
||||||
}).catch(() => {
|
|
||||||
// no-op
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -175,11 +175,9 @@ export default defineComponent({
|
||||||
|
|
||||||
const scrollToActiveUser = () => {
|
const scrollToActiveUser = () => {
|
||||||
// Scroll the list if needed after the active class is applied
|
// Scroll the list if needed after the active class is applied
|
||||||
nextTick(() => {
|
void nextTick(() => {
|
||||||
const el = userlist.value?.querySelector(".active");
|
const el = userlist.value?.querySelector(".active");
|
||||||
el?.scrollIntoView({block: "nearest", inline: "nearest"});
|
el?.scrollIntoView({block: "nearest", inline: "nearest"});
|
||||||
}).catch(() => {
|
|
||||||
// no-op
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,14 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import calendar from "dayjs/plugin/calendar";
|
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 eventbus from "../js/eventbus";
|
||||||
import {ClientMessage} from "../js/types";
|
import {ClientMessage} from "../js/types";
|
||||||
|
|
||||||
|
@ -33,8 +40,8 @@ export default defineComponent({
|
||||||
|
|
||||||
const dayChange = () => {
|
const dayChange = () => {
|
||||||
// TODO: this is nasty. and maybe doesnt work?
|
// TODO: this is nasty. and maybe doesnt work?
|
||||||
// const instance = Vue.getCurrentInstance();
|
const instance = getCurrentInstance();
|
||||||
// instance?.proxy?.$forceUpdate();
|
instance?.proxy?.$forceUpdate();
|
||||||
|
|
||||||
if (hoursPassed() >= 48) {
|
if (hoursPassed() >= 48) {
|
||||||
eventbus.off("daychange", dayChange);
|
eventbus.off("daychange", dayChange);
|
||||||
|
|
|
@ -179,12 +179,9 @@ export default defineComponent({
|
||||||
return messages.filter((message) => !message.channel?.channel.muted);
|
return messages.filter((message) => !message.channel?.channel.muted);
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(store.state.mentions, () => {
|
||||||
() => store.state.mentions,
|
isLoading.value = false;
|
||||||
() => {
|
});
|
||||||
isLoading.value = false;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const messageTime = (time: string) => {
|
const messageTime = (time: string) => {
|
||||||
return dayjs(time).fromNow();
|
return dayjs(time).fromNow();
|
||||||
|
|
|
@ -120,7 +120,7 @@ export default defineComponent({
|
||||||
message: {type: Object as PropType<ClientMessage>, required: true},
|
message: {type: Object as PropType<ClientMessage>, required: true},
|
||||||
channel: {type: Object as PropType<ClientChan>, required: false},
|
channel: {type: Object as PropType<ClientChan>, required: false},
|
||||||
network: {type: Object as PropType<ClientNetwork>, required: true},
|
network: {type: Object as PropType<ClientNetwork>, required: true},
|
||||||
keepScrollPosition: Function,
|
keepScrollPosition: Function as PropType<() => void>,
|
||||||
isPreviousSource: Boolean,
|
isPreviousSource: Boolean,
|
||||||
focused: Boolean,
|
focused: Boolean,
|
||||||
},
|
},
|
||||||
|
|
|
@ -66,7 +66,6 @@ import Message from "./Message.vue";
|
||||||
import MessageCondensed from "./MessageCondensed.vue";
|
import MessageCondensed from "./MessageCondensed.vue";
|
||||||
import DateMarker from "./DateMarker.vue";
|
import DateMarker from "./DateMarker.vue";
|
||||||
import {
|
import {
|
||||||
defineExpose,
|
|
||||||
computed,
|
computed,
|
||||||
defineComponent,
|
defineComponent,
|
||||||
nextTick,
|
nextTick,
|
||||||
|
@ -104,7 +103,6 @@ export default defineComponent({
|
||||||
channel: {type: Object as PropType<ClientChan>, required: true},
|
channel: {type: Object as PropType<ClientChan>, required: true},
|
||||||
focused: String,
|
focused: String,
|
||||||
},
|
},
|
||||||
emits: ["scrolled-to-bottom"],
|
|
||||||
setup(props, {emit}) {
|
setup(props, {emit}) {
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|
||||||
|
@ -117,7 +115,7 @@ export default defineComponent({
|
||||||
|
|
||||||
const jumpToBottom = () => {
|
const jumpToBottom = () => {
|
||||||
skipNextScrollEvent.value = true;
|
skipNextScrollEvent.value = true;
|
||||||
emit("scrolled-to-bottom", true);
|
props.channel.scrolledToBottom = true;
|
||||||
|
|
||||||
const el = chat.value;
|
const el = chat.value;
|
||||||
|
|
||||||
|
@ -173,8 +171,9 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
jumpToBottom();
|
jumpToBottom();
|
||||||
}).catch(() => {
|
}).catch((e) => {
|
||||||
// no-op
|
// eslint-disable-next-line no-console
|
||||||
|
console.error("Error in new IntersectionObserver", e);
|
||||||
});
|
});
|
||||||
|
|
||||||
const condensedMessages = computed(() => {
|
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,
|
// 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
|
// we have no reason to perform more checks and set it again in the next tick
|
||||||
if (isWaitingForNextTick.value) {
|
if (isWaitingForNextTick.value) {
|
||||||
|
@ -306,40 +307,44 @@ export default defineComponent({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(el);
|
||||||
|
|
||||||
if (!props.channel.scrolledToBottom) {
|
if (!props.channel.scrolledToBottom) {
|
||||||
if (props.channel.historyLoading) {
|
if (props.channel.historyLoading) {
|
||||||
const heightOld = el.scrollHeight - el.scrollTop;
|
const heightOld = el.scrollHeight - el.scrollTop;
|
||||||
|
|
||||||
isWaitingForNextTick.value = true;
|
isWaitingForNextTick.value = true;
|
||||||
|
|
||||||
nextTick(() => {
|
await nextTick();
|
||||||
isWaitingForNextTick.value = false;
|
|
||||||
skipNextScrollEvent.value = true;
|
isWaitingForNextTick.value = false;
|
||||||
el.scrollTop = el.scrollHeight - heightOld;
|
skipNextScrollEvent.value = true;
|
||||||
}).catch(() => {
|
|
||||||
// no-op
|
console.log("old top", heightOld);
|
||||||
});
|
el.scrollTop = el.scrollHeight - heightOld;
|
||||||
|
console.log("new top", el.scrollTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
isWaitingForNextTick.value = true;
|
isWaitingForNextTick.value = true;
|
||||||
nextTick(() => {
|
await nextTick();
|
||||||
isWaitingForNextTick.value = false;
|
isWaitingForNextTick.value = false;
|
||||||
jumpToBottom();
|
|
||||||
}).catch(() => {
|
console.log("HERE");
|
||||||
// no-op
|
jumpToBottom();
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onLinkPreviewToggle = (preview: ClientLinkPreview, message: ClientMessage) => {
|
const onLinkPreviewToggle = async (preview: ClientLinkPreview, message: ClientMessage) => {
|
||||||
keepScrollPosition();
|
await keepScrollPosition();
|
||||||
|
|
||||||
// Tell the server we're toggling so it remembers at page reload
|
// Tell the server we're toggling so it remembers at page reload
|
||||||
socket.emit("msg:preview:toggle", {
|
socket.emit("msg:preview:toggle", {
|
||||||
target: props.channel.id,
|
target: props.channel.id,
|
||||||
msgId: message.id,
|
msgId: message.id,
|
||||||
|
// TODO: type
|
||||||
|
// @ts-ignore
|
||||||
link: preview.link,
|
link: preview.link,
|
||||||
shown: preview.shown,
|
shown: preview.shown,
|
||||||
});
|
});
|
||||||
|
@ -359,7 +364,7 @@ export default defineComponent({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit("scrolled-to-bottom", el.scrollHeight - el.scrollTop - el.offsetHeight <= 30);
|
props.channel.scrolledToBottom = el.scrollHeight - el.scrollTop - el.offsetHeight <= 30;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
|
@ -374,19 +379,17 @@ export default defineComponent({
|
||||||
|
|
||||||
eventbus.on("resize", handleResize);
|
eventbus.on("resize", handleResize);
|
||||||
|
|
||||||
nextTick(() => {
|
void nextTick(() => {
|
||||||
if (historyObserver.value && loadMoreButton.value) {
|
if (historyObserver.value && loadMoreButton.value) {
|
||||||
historyObserver.value.observe(loadMoreButton.value);
|
historyObserver.value.observe(loadMoreButton.value);
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
|
||||||
// no-op
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.channel.id,
|
() => 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
|
// 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
|
// 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(
|
watch(
|
||||||
() => props.channel.messages,
|
() => props.channel.messages,
|
||||||
() => {
|
async () => {
|
||||||
keepScrollPosition();
|
await keepScrollPosition();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.channel.pendingMessage,
|
() => props.channel.pendingMessage,
|
||||||
() => {
|
async () => {
|
||||||
nextTick(() => {
|
// Keep the scroll stuck when input gets resized while typing
|
||||||
// Keep the scroll stuck when input gets resized while typing
|
await keepScrollPosition();
|
||||||
keepScrollPosition();
|
|
||||||
}).catch(() => {
|
|
||||||
// no-op
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -475,10 +475,8 @@ export default defineComponent({
|
||||||
|
|
||||||
watch(displayPasswordField, (newValue) => {
|
watch(displayPasswordField, (newValue) => {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
nextTick(() => {
|
void nextTick(() => {
|
||||||
publicPassword.value?.focus();
|
publicPassword.value?.focus();
|
||||||
}).catch(() => {
|
|
||||||
// no-op
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -502,10 +500,8 @@ export default defineComponent({
|
||||||
watch(
|
watch(
|
||||||
() => props.defaults?.commands,
|
() => props.defaults?.commands,
|
||||||
() => {
|
() => {
|
||||||
nextTick(() => {
|
void nextTick(() => {
|
||||||
resizeCommandsInput();
|
resizeCommandsInput();
|
||||||
}).catch((e) => {
|
|
||||||
// no-op
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -383,10 +383,8 @@ export default defineComponent({
|
||||||
sidebarWasClosed.value = store.state.sidebarOpen ? false : true;
|
sidebarWasClosed.value = store.state.sidebarOpen ? false : true;
|
||||||
store.commit("sidebarOpen", true);
|
store.commit("sidebarOpen", true);
|
||||||
|
|
||||||
nextTick(() => {
|
void nextTick(() => {
|
||||||
searchInput.value?.focus();
|
searchInput.value?.focus();
|
||||||
}).catch(() => {
|
|
||||||
// no-op
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -432,14 +430,12 @@ export default defineComponent({
|
||||||
|
|
||||||
const scrollToActive = () => {
|
const scrollToActive = () => {
|
||||||
// Scroll the list if needed after the active class is applied
|
// Scroll the list if needed after the active class is applied
|
||||||
nextTick(() => {
|
void nextTick(() => {
|
||||||
const el = networklist.value?.querySelector(".channel-list-item.active");
|
const el = networklist.value?.querySelector(".channel-list-item.active");
|
||||||
|
|
||||||
if (el) {
|
if (el) {
|
||||||
el.scrollIntoView({block: "nearest", inline: "nearest"});
|
el.scrollIntoView({block: "nearest", inline: "nearest"});
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
|
||||||
// no-op
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -265,13 +265,10 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(route.query, () => {
|
||||||
() => route.query,
|
doSearch();
|
||||||
() => {
|
setActiveChannel();
|
||||||
doSearch();
|
});
|
||||||
setActiveChannel();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(messages, () => {
|
watch(messages, () => {
|
||||||
moreResultsAvailable.value = !!(
|
moreResultsAvailable.value = !!(
|
||||||
|
@ -281,7 +278,7 @@ export default defineComponent({
|
||||||
if (!offset.value) {
|
if (!offset.value) {
|
||||||
jumpToBottom();
|
jumpToBottom();
|
||||||
} else {
|
} else {
|
||||||
nextTick(() => {
|
void nextTick(() => {
|
||||||
if (!chatRef) {
|
if (!chatRef) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -289,9 +286,6 @@ export default defineComponent({
|
||||||
const currentChatHeight = chatRef.scrollHeight;
|
const currentChatHeight = chatRef.scrollHeight;
|
||||||
chatRef.scrollTop =
|
chatRef.scrollTop =
|
||||||
oldScrollTop.value + currentChatHeight - oldChatHeight.value;
|
oldScrollTop.value + currentChatHeight - oldChatHeight.value;
|
||||||
}).catch((e) => {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error("Failed to scroll to bottom", e);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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) {
|
if (router.currentRoute.value.name) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
await router.push({name: routeName, params});
|
||||||
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
|
||||||
// This prevents invalid entries from lingering in history,
|
// This prevents invalid entries from lingering in history,
|
||||||
// and then the route guard preventing proper navigation
|
// and then the route guard preventing proper navigation
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// 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) {
|
function switchToChannel(channel: ClientChan) {
|
||||||
return navigate("RoutedChat", {id: channel.id});
|
return void navigate("RoutedChat", {id: channel.id});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("serviceWorker" in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
navigator.serviceWorker.addEventListener("message", (event) => {
|
navigator.serviceWorker.addEventListener("message", (event) => {
|
||||||
if (event.data && event.data.type === "open") {
|
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);
|
const channelTarget = store.getters.findChannel(id);
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,10 @@ socket.on("init", function (data) {
|
||||||
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) {
|
||||||
|
|
|
@ -2,8 +2,9 @@ import {nextTick} from "vue";
|
||||||
|
|
||||||
import socket from "../socket";
|
import socket from "../socket";
|
||||||
import {store} from "../store";
|
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;
|
const channel = store.getters.findChannel(data.chan)?.channel;
|
||||||
|
|
||||||
if (!channel) {
|
if (!channel) {
|
||||||
|
@ -15,13 +16,12 @@ socket.on("more", function (data) {
|
||||||
.filter((m) => m.self && m.text && m.type === "message")
|
.filter((m) => m.self && m.text && m.type === "message")
|
||||||
.map((m) => m.text)
|
.map((m) => m.text)
|
||||||
.reverse()
|
.reverse()
|
||||||
.slice(null, 100 - channel.inputHistory.length)
|
.slice(undefined, 100 - channel.inputHistory.length)
|
||||||
);
|
);
|
||||||
channel.moreHistoryAvailable =
|
channel.moreHistoryAvailable =
|
||||||
data.totalMessages > channel.messages.length + data.messages.length;
|
data.totalMessages > channel.messages.length + data.messages.length;
|
||||||
channel.messages.unshift(...data.messages);
|
channel.messages.unshift(...(data.messages as ClientMessage[]));
|
||||||
|
|
||||||
nextTick(() => {
|
await nextTick();
|
||||||
channel.historyLoading = false;
|
channel.historyLoading = false;
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -78,6 +78,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.17.10",
|
"@babel/core": "7.17.10",
|
||||||
"@babel/plugin-syntax-jsx": "7.16.7",
|
"@babel/plugin-syntax-jsx": "7.16.7",
|
||||||
|
"@babel/plugin-transform-runtime": "7.18.2",
|
||||||
"@babel/plugin-transform-typescript": "7.16.8",
|
"@babel/plugin-transform-typescript": "7.16.8",
|
||||||
"@babel/preset-env": "7.17.10",
|
"@babel/preset-env": "7.17.10",
|
||||||
"@babel/preset-typescript": "7.16.7",
|
"@babel/preset-typescript": "7.16.7",
|
||||||
|
|
|
@ -146,10 +146,6 @@ class Client {
|
||||||
for (const messageStorage of client.messageStorage) {
|
for (const messageStorage of client.messageStorage) {
|
||||||
messageStorage.enable();
|
messageStorage.enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
|
||||||
"Message storage: " + client.messageStorage.map((m) => m.isEnabled).join(", ")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_.isPlainObject(client.config.sessions)) {
|
if (!_.isPlainObject(client.config.sessions)) {
|
||||||
|
|
|
@ -306,7 +306,9 @@ class Chan {
|
||||||
requestZncPlayback(this, network, from);
|
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() {
|
isLoggable() {
|
||||||
return this.type === ChanType.CHANNEL || this.type === ChanType.QUERY;
|
return this.type === ChanType.CHANNEL || this.type === ChanType.QUERY;
|
||||||
|
|
|
@ -30,8 +30,6 @@ const toExport = {
|
||||||
initialized: false,
|
initialized: false,
|
||||||
// TODO: fix typing
|
// TODO: fix typing
|
||||||
async initialize() {
|
async initialize() {
|
||||||
log.info("Auth initializing", toExport.initialized);
|
|
||||||
|
|
||||||
if (toExport.initialized) {
|
if (toExport.initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -40,8 +38,6 @@ const toExport = {
|
||||||
const resolvedPlugins = await Promise.all(plugins);
|
const resolvedPlugins = await Promise.all(plugins);
|
||||||
|
|
||||||
for (const {default: plugin} of resolvedPlugins) {
|
for (const {default: plugin} of resolvedPlugins) {
|
||||||
log.info("Auth plugin", plugin.moduleName, "enabled", plugin.isEnabled().toString());
|
|
||||||
|
|
||||||
if (plugin.isEnabled()) {
|
if (plugin.isEnabled()) {
|
||||||
toExport.initialized = true;
|
toExport.initialized = true;
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,8 @@ function ldapAuthCommon(
|
||||||
tlsOptions: config.ldap.tlsOptions,
|
tlsOptions: config.ldap.tlsOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
ldapclient.on("error", function (err) {
|
ldapclient.on("error", function (err: Error) {
|
||||||
log.error(`Unable to connect to LDAP server: ${err}`);
|
log.error(`Unable to connect to LDAP server: ${err.toString()}`);
|
||||||
callback(false);
|
callback(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ function ldapAuthCommon(
|
||||||
ldapclient.unbind();
|
ldapclient.unbind();
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
log.error(`LDAP bind failed: ${err}`);
|
log.error(`LDAP bind failed: ${err.toString()}`);
|
||||||
callback(false);
|
callback(false);
|
||||||
} else {
|
} else {
|
||||||
callback(true);
|
callback(true);
|
||||||
|
@ -43,7 +43,7 @@ function simpleLdapAuth(user: string, password: string, callback: (success: bool
|
||||||
const config = Config.values;
|
const config = Config.values;
|
||||||
|
|
||||||
const userDN = user.replace(/([,\\/#+<>;"= ])/g, "\\$1");
|
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}`);
|
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"],
|
attributes: ["dn"],
|
||||||
} as SearchOptions;
|
} as SearchOptions;
|
||||||
|
|
||||||
ldapclient.on("error", function (err) {
|
ldapclient.on("error", function (err: Error) {
|
||||||
log.error(`Unable to connect to LDAP server: ${err}`);
|
log.error(`Unable to connect to LDAP server: ${err.toString()}`);
|
||||||
callback(false);
|
callback(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -99,15 +99,15 @@ function advancedLdapAuth(user: string, password: string, callback: (success: bo
|
||||||
res.on("searchEntry", function (entry) {
|
res.on("searchEntry", function (entry) {
|
||||||
found = true;
|
found = true;
|
||||||
const bindDN = entry.objectName;
|
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();
|
ldapclient.unbind();
|
||||||
|
|
||||||
// TODO: Fix type !
|
// TODO: Fix type !
|
||||||
ldapAuthCommon(user, bindDN!, password, callback);
|
ldapAuthCommon(user, bindDN!, password, callback);
|
||||||
});
|
});
|
||||||
|
|
||||||
res.on("error", function (err3) {
|
res.on("error", function (err3: Error) {
|
||||||
log.error(`LDAP error: ${err3}`);
|
log.error(`LDAP error: ${err3.toString()}`);
|
||||||
callback(false);
|
callback(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -116,7 +116,9 @@ function advancedLdapAuth(user: string, password: string, callback: (success: bo
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
log.warn(
|
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);
|
callback(false);
|
||||||
}
|
}
|
||||||
|
@ -138,7 +140,7 @@ const ldapAuth: AuthHandler = (manager, client, user, password, callback) => {
|
||||||
callback(valid);
|
callback(valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
let auth;
|
let auth: typeof simpleLdapAuth | typeof advancedLdapAuth;
|
||||||
|
|
||||||
if ("baseDN" in Config.values.ldap) {
|
if ("baseDN" in Config.values.ldap) {
|
||||||
auth = simpleLdapAuth;
|
auth = simpleLdapAuth;
|
||||||
|
@ -164,8 +166,8 @@ function advancedLdapLoadUsers(users: string[], callbackLoadUser) {
|
||||||
|
|
||||||
const base = config.ldap.searchDN.base;
|
const base = config.ldap.searchDN.base;
|
||||||
|
|
||||||
ldapclient.on("error", function (err) {
|
ldapclient.on("error", function (err: Error) {
|
||||||
log.error(`Unable to connect to LDAP server: ${err}`);
|
log.error(`Unable to connect to LDAP server: ${err.toString()}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
ldapclient.bind(config.ldap.searchDN.rootDN, config.ldap.searchDN.rootPassword, function (err) {
|
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) {
|
ldapclient.search(base, searchOptions, function (err2, res) {
|
||||||
if (err2) {
|
if (err2) {
|
||||||
log.error(`LDAP search error: ${err2}`);
|
log.error(`LDAP search error: ${err2?.toString()}`);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +202,7 @@ function advancedLdapLoadUsers(users: string[], callbackLoadUser) {
|
||||||
});
|
});
|
||||||
|
|
||||||
res.on("error", function (err3) {
|
res.on("error", function (err3) {
|
||||||
log.error(`LDAP error: ${err3}`);
|
log.error(`LDAP error: ${err3.toString()}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
res.on("end", function () {
|
res.on("end", function () {
|
||||||
|
|
|
@ -27,9 +27,9 @@ export type LinkPreview = {
|
||||||
thumb: string;
|
thumb: string;
|
||||||
size: number;
|
size: number;
|
||||||
link: string; // Send original matched link to the client
|
link: string; // Send original matched link to the client
|
||||||
shown: boolean | null;
|
shown?: boolean;
|
||||||
error: undefined | string;
|
error?: string;
|
||||||
message: undefined | string;
|
message?: string;
|
||||||
|
|
||||||
media: string;
|
media: string;
|
||||||
mediaType: string;
|
mediaType: string;
|
||||||
|
@ -65,7 +65,7 @@ export default function (client: Client, chan: Chan, msg: Msg, cleanText: string
|
||||||
thumb: "",
|
thumb: "",
|
||||||
size: -1,
|
size: -1,
|
||||||
link: link.link, // Send original matched link to the client
|
link: link.link, // Send original matched link to the client
|
||||||
shown: null,
|
shown: undefined,
|
||||||
error: undefined,
|
error: undefined,
|
||||||
message: undefined,
|
message: undefined,
|
||||||
media: "",
|
media: "",
|
||||||
|
|
|
@ -3,7 +3,8 @@ import LinkPrefetch from "./link";
|
||||||
import cleanIrcMessage from "../../../client/js/helpers/ircmessageparser/cleanIrcMessage";
|
import cleanIrcMessage from "../../../client/js/helpers/ircmessageparser/cleanIrcMessage";
|
||||||
import Helper from "../../helper";
|
import Helper from "../../helper";
|
||||||
import {IrcEventHandler} from "../../client";
|
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;
|
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) {
|
irc.on("notice", function (data) {
|
||||||
data.type = MessageType.NOTICE as any;
|
data.type = MessageType.NOTICE as any;
|
||||||
handleMessage(data);
|
handleMessage(data as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
irc.on("action", function (data) {
|
irc.on("action", function (data) {
|
||||||
data.type = MessageType.ACTION;
|
data.type = MessageType.ACTION;
|
||||||
handleMessage(data);
|
handleMessage(data as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
irc.on("privmsg", function (data) {
|
irc.on("privmsg", function (data) {
|
||||||
data.type = MessageType.MESSAGE;
|
data.type = MessageType.MESSAGE;
|
||||||
handleMessage(data);
|
handleMessage(data as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
irc.on("wallops", function (data) {
|
irc.on("wallops", function (data) {
|
||||||
data.from_server = true;
|
data.from_server = true;
|
||||||
data.type = MessageType.WALLOPS;
|
data.type = MessageType.WALLOPS;
|
||||||
handleMessage(data);
|
handleMessage(data as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleMessage(data: any) {
|
function handleMessage(data: {
|
||||||
let chan;
|
nick: string;
|
||||||
let from;
|
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 highlight = false;
|
||||||
let showInActive = false;
|
let showInActive = false;
|
||||||
const self = data.nick === irc.user.nick;
|
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
|
// msg is constructed down here because `from` is being copied in the constructor
|
||||||
const msg = new Msg({
|
const msg = new Msg({
|
||||||
type: data.type,
|
type: data.type,
|
||||||
time: data.time,
|
time: data.time as any,
|
||||||
text: data.message,
|
text: data.message,
|
||||||
self: self,
|
self: self,
|
||||||
from: from,
|
from: from,
|
||||||
|
|
|
@ -51,9 +51,6 @@ class SqliteMessageStorage implements ISqliteMessageStorage {
|
||||||
const logsPath = Config.getUserLogsPath();
|
const logsPath = Config.getUserLogsPath();
|
||||||
const sqlitePath = path.join(logsPath, `${this.client.name}.sqlite3`);
|
const sqlitePath = path.join(logsPath, `${this.client.name}.sqlite3`);
|
||||||
|
|
||||||
log.info("Logs path", logsPath);
|
|
||||||
log.info("Sqlite path", sqlitePath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fs.mkdirSync(logsPath, {recursive: true});
|
fs.mkdirSync(logsPath, {recursive: true});
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
|
8
src/types/socket-events.d.ts
vendored
8
src/types/socket-events.d.ts
vendored
|
@ -107,11 +107,13 @@ interface ClientToServerEvents {
|
||||||
"msg:preview:toggle": ({
|
"msg:preview:toggle": ({
|
||||||
target,
|
target,
|
||||||
messageIds,
|
messageIds,
|
||||||
|
msgId,
|
||||||
shown,
|
shown,
|
||||||
}: {
|
}: {
|
||||||
target: number;
|
target: number;
|
||||||
messageIds: number[];
|
messageIds?: number[];
|
||||||
shown: boolean;
|
msgId?: number;
|
||||||
|
shown?: boolean;
|
||||||
}) => void;
|
}) => void;
|
||||||
|
|
||||||
"network:get": (uuid: string) => void;
|
"network:get": (uuid: string) => void;
|
||||||
|
@ -133,6 +135,8 @@ interface ClientToServerEvents {
|
||||||
}) => void;
|
}) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
interface InterServerEvents {}
|
interface InterServerEvents {}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
interface SocketData {}
|
interface SocketData {}
|
||||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -821,6 +821,18 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-plugin-utils" "^7.16.7"
|
"@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":
|
"@babel/plugin-transform-shorthand-properties@^7.16.7":
|
||||||
version "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"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a"
|
||||||
|
|
Loading…
Reference in a new issue