fix: Improve plugin ui entry points, Fix chat scroll, fix notifications, fix user rights
This commit is contained in:
@@ -52,6 +52,7 @@ import {
|
||||
})
|
||||
export class ChatMessagesComponent {
|
||||
@ViewChild(ChatMessageComposerComponent) composer?: ChatMessageComposerComponent;
|
||||
@ViewChild(ChatMessageListComponent) messageList?: ChatMessageListComponent;
|
||||
|
||||
private readonly electronBridge = inject(ElectronBridgeService);
|
||||
private readonly store = inject(Store);
|
||||
@@ -98,6 +99,8 @@ export class ChatMessagesComponent {
|
||||
}
|
||||
|
||||
handleMessageSubmitted(event: ChatMessageComposerSubmitEvent): void {
|
||||
this.messageList?.scrollToBottomAfterLocalSend();
|
||||
|
||||
this.store.dispatch(
|
||||
MessagesActions.sendMessage({
|
||||
content: event.content,
|
||||
|
||||
@@ -141,9 +141,11 @@ export class ChatMessageListComponent implements AfterViewChecked, OnDestroy {
|
||||
return lookup;
|
||||
});
|
||||
|
||||
private initialScrollObserver: MutationObserver | null = null;
|
||||
private initialScrollTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
private bottomScrollObserver: MutationObserver | null = null;
|
||||
private bottomScrollTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
private boundOnImageLoad: (() => void) | null = null;
|
||||
private localSendScrollPending = false;
|
||||
private localSendScrollTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
private isAutoScrolling = false;
|
||||
private lastMessageCount = 0;
|
||||
private initialScrollPending = true;
|
||||
@@ -170,10 +172,17 @@ export class ChatMessageListComponent implements AfterViewChecked, OnDestroy {
|
||||
|
||||
const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;
|
||||
const newMessages = currentCount > this.lastMessageCount;
|
||||
const forceLocalSendScroll = this.shouldForceLocalSendScroll();
|
||||
|
||||
if (newMessages) {
|
||||
if (distanceFromBottom <= 300) {
|
||||
this.scheduleScrollToBottomSmooth();
|
||||
if (forceLocalSendScroll || distanceFromBottom <= 300) {
|
||||
if (forceLocalSendScroll) {
|
||||
this.clearLocalSendScrollPending();
|
||||
this.scheduleScrollToBottomAfterRender(true);
|
||||
} else {
|
||||
this.scheduleScrollToBottomSmooth();
|
||||
}
|
||||
|
||||
this.showNewMessagesBar.set(false);
|
||||
} else {
|
||||
queueMicrotask(() => this.showNewMessagesBar.set(true));
|
||||
@@ -198,7 +207,8 @@ export class ChatMessageListComponent implements AfterViewChecked, OnDestroy {
|
||||
this.isAutoScrolling = false;
|
||||
});
|
||||
|
||||
this.startInitialScrollWatch();
|
||||
this.clearLocalSendScrollPending();
|
||||
this.startBottomScrollWatch();
|
||||
this.showNewMessagesBar.set(false);
|
||||
this.lastMessageCount = this.messages().length;
|
||||
this.scheduleCodeHighlight();
|
||||
@@ -214,7 +224,8 @@ export class ChatMessageListComponent implements AfterViewChecked, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.stopInitialScrollWatch();
|
||||
this.stopBottomScrollWatch();
|
||||
this.clearLocalSendScrollPending();
|
||||
}
|
||||
|
||||
findRepliedMessage(messageId?: string | null): Message | undefined {
|
||||
@@ -237,8 +248,8 @@ export class ChatMessageListComponent implements AfterViewChecked, OnDestroy {
|
||||
this.showNewMessagesBar.set(false);
|
||||
}
|
||||
|
||||
if (this.initialScrollObserver) {
|
||||
this.stopInitialScrollWatch();
|
||||
if (this.bottomScrollObserver) {
|
||||
this.stopBottomScrollWatch();
|
||||
}
|
||||
|
||||
if (element.scrollTop < 150 && this.hasMoreMessages() && !this.loadingMore()) {
|
||||
@@ -275,6 +286,13 @@ export class ChatMessageListComponent implements AfterViewChecked, OnDestroy {
|
||||
this.showNewMessagesBar.set(false);
|
||||
}
|
||||
|
||||
scrollToBottomAfterLocalSend(): void {
|
||||
this.localSendScrollPending = true;
|
||||
this.showNewMessagesBar.set(false);
|
||||
this.scheduleScrollToBottomAfterRender(true);
|
||||
this.armLocalSendScrollTimeout();
|
||||
}
|
||||
|
||||
scrollToMessage(messageId: string): void {
|
||||
const container = this.messagesContainer?.nativeElement;
|
||||
|
||||
@@ -336,54 +354,42 @@ export class ChatMessageListComponent implements AfterViewChecked, OnDestroy {
|
||||
|
||||
private resetScrollingState(): void {
|
||||
this.initialScrollPending = true;
|
||||
this.stopInitialScrollWatch();
|
||||
this.stopBottomScrollWatch();
|
||||
this.clearLocalSendScrollPending();
|
||||
this.showNewMessagesBar.set(false);
|
||||
this.lastMessageCount = 0;
|
||||
this.displayLimit.set(this.PAGE_SIZE);
|
||||
}
|
||||
|
||||
private startInitialScrollWatch(): void {
|
||||
this.stopInitialScrollWatch();
|
||||
private startBottomScrollWatch(): void {
|
||||
this.stopBottomScrollWatch();
|
||||
|
||||
const element = this.messagesContainer?.nativeElement;
|
||||
|
||||
if (!element)
|
||||
return;
|
||||
|
||||
const snapToBottom = () => {
|
||||
const container = this.messagesContainer?.nativeElement;
|
||||
|
||||
if (!container)
|
||||
return;
|
||||
|
||||
this.isAutoScrolling = true;
|
||||
container.scrollTop = container.scrollHeight;
|
||||
requestAnimationFrame(() => {
|
||||
this.isAutoScrolling = false;
|
||||
});
|
||||
};
|
||||
|
||||
this.initialScrollObserver = new MutationObserver(() => {
|
||||
requestAnimationFrame(snapToBottom);
|
||||
this.bottomScrollObserver = new MutationObserver(() => {
|
||||
requestAnimationFrame(() => this.scrollToBottomInstant());
|
||||
});
|
||||
|
||||
this.initialScrollObserver.observe(element, {
|
||||
this.bottomScrollObserver.observe(element, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['src']
|
||||
});
|
||||
|
||||
this.boundOnImageLoad = () => requestAnimationFrame(snapToBottom);
|
||||
this.boundOnImageLoad = () => requestAnimationFrame(() => this.scrollToBottomInstant());
|
||||
element.addEventListener('load', this.boundOnImageLoad, true);
|
||||
|
||||
this.initialScrollTimer = setTimeout(() => this.stopInitialScrollWatch(), 5000);
|
||||
this.bottomScrollTimer = setTimeout(() => this.stopBottomScrollWatch(), 5000);
|
||||
}
|
||||
|
||||
private stopInitialScrollWatch(): void {
|
||||
if (this.initialScrollObserver) {
|
||||
this.initialScrollObserver.disconnect();
|
||||
this.initialScrollObserver = null;
|
||||
private stopBottomScrollWatch(): void {
|
||||
if (this.bottomScrollObserver) {
|
||||
this.bottomScrollObserver.disconnect();
|
||||
this.bottomScrollObserver = null;
|
||||
}
|
||||
|
||||
if (this.boundOnImageLoad && this.messagesContainer) {
|
||||
@@ -392,12 +398,41 @@ export class ChatMessageListComponent implements AfterViewChecked, OnDestroy {
|
||||
this.boundOnImageLoad = null;
|
||||
}
|
||||
|
||||
if (this.initialScrollTimer) {
|
||||
clearTimeout(this.initialScrollTimer);
|
||||
this.initialScrollTimer = null;
|
||||
if (this.bottomScrollTimer) {
|
||||
clearTimeout(this.bottomScrollTimer);
|
||||
this.bottomScrollTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private armLocalSendScrollTimeout(): void {
|
||||
if (this.localSendScrollTimer) {
|
||||
clearTimeout(this.localSendScrollTimer);
|
||||
}
|
||||
|
||||
this.localSendScrollTimer = setTimeout(() => {
|
||||
this.localSendScrollPending = false;
|
||||
this.localSendScrollTimer = null;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private clearLocalSendScrollPending(): void {
|
||||
this.localSendScrollPending = false;
|
||||
|
||||
if (this.localSendScrollTimer) {
|
||||
clearTimeout(this.localSendScrollTimer);
|
||||
this.localSendScrollTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private shouldForceLocalSendScroll(): boolean {
|
||||
if (!this.localSendScrollPending)
|
||||
return false;
|
||||
|
||||
const latestMessage = this.channelMessages().at(-1);
|
||||
|
||||
return !!latestMessage && latestMessage.senderId === this.currentUserId();
|
||||
}
|
||||
|
||||
private getMessageDateTimestamp(message: Message): number {
|
||||
return message.timestamp || getMessageTimestamp(message);
|
||||
}
|
||||
@@ -424,6 +459,31 @@ export class ChatMessageListComponent implements AfterViewChecked, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private scrollToBottomInstant(): void {
|
||||
const element = this.messagesContainer?.nativeElement;
|
||||
|
||||
if (!element)
|
||||
return;
|
||||
|
||||
this.isAutoScrolling = true;
|
||||
element.scrollTop = element.scrollHeight;
|
||||
requestAnimationFrame(() => {
|
||||
this.isAutoScrolling = false;
|
||||
});
|
||||
}
|
||||
|
||||
private scheduleScrollToBottomAfterRender(watchForLayoutChanges = false): void {
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
this.scrollToBottomInstant();
|
||||
|
||||
if (watchForLayoutChanges) {
|
||||
this.startBottomScrollWatch();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private scheduleScrollToBottomSmooth(): void {
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => this.scrollToBottomSmooth());
|
||||
|
||||
Reference in New Issue
Block a user