diff --git a/src/app/services/chat-message-handler.ts b/src/app/services/chat-message-handler.ts new file mode 100644 index 0000000..056b721 --- /dev/null +++ b/src/app/services/chat-message-handler.ts @@ -0,0 +1,47 @@ +import { Observable, Subject } from 'rxjs'; +import { MatrixClient } from 'matrix-js-sdk/src/client'; +import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; +import { EventTimeline } from 'matrix-js-sdk/src/models/event-timeline'; +import { Room } from 'matrix-js-sdk/src/models/room'; + +export class ChatMessageHandler { + constructor( + private matrixClientPromise: Promise, + ) { } + + getRoomMessages = (roomId: string) => { + const subject = new Subject(); + + this.publishRoomOldMessages(roomId, subject); + this.publishRoomNewMessages(roomId, subject); + + return subject; + } + + private publishRoomOldMessages(roomId: string, subject: Subject): void { + this.matrixClientPromise.then(client => { + const room = client.getRoom(roomId) as Room; + room?.timeline + .filter((t: MatrixEvent) => (t.getType() === 'm.room.message')) + .forEach(message => subject.next(message)); + }); + } + + private publishRoomNewMessages(roomId: string, subject: Subject): void { + this.matrixClientPromise.then(client => { + client.on('Room.timeline', (event: EventTimeline, room: Room, toStartOfTimeline: boolean) => { + if (room.roomId !== roomId) { + return; // only listen to specified room + } + if (toStartOfTimeline) { + return; // don't print paginated results + } + if (event.getType() !== 'm.room.message') { + return; // only print messages + } + + subject.next(event); + }); + }); + } +} diff --git a/src/app/services/chat.service.ts b/src/app/services/chat.service.ts index 021a75c..6610470 100644 --- a/src/app/services/chat.service.ts +++ b/src/app/services/chat.service.ts @@ -3,17 +3,23 @@ import { Injectable } from '@angular/core'; import * as olm from 'olm'; global.Olm = olm; import * as matrix from 'matrix-js-sdk'; +import { MatrixClient } from 'matrix-js-sdk/src/client'; import { RoomMember } from 'matrix-js-sdk/src/models/room'; import { Room } from 'matrix-js-sdk/src/models/room'; import { EventTimeline } from 'matrix-js-sdk/src/models/event-timeline'; import { MatrixLoginDto } from '../entities/matrix-login-dto'; import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; +import { Observable, Subject } from 'rxjs'; +import { ChatMessageHandler } from './chat-message-handler'; @Injectable({ providedIn: 'root' }) export class ChatService { - private client: any; + private matrixClient: MatrixClient; + private chatMessageHandler: ChatMessageHandler; + + getRoomMessages: (roomId: string) => (Observable); constructor( private http: HttpClient @@ -23,7 +29,15 @@ export class ChatService { window.location.href = 'http://synapse:8008/_matrix/client/r0/login/sso/redirect?redirectUrl=http://localhost:4200/'; } - async login(loginToken: string): Promise { + login(loginToken: string): Promise { + const matrixClientPromise = this.loginWithToken(loginToken); + this.chatMessageHandler = new ChatMessageHandler(matrixClientPromise); + this.getRoomMessages = this.chatMessageHandler.getRoomMessages; + + return matrixClientPromise; + } + + private async loginWithToken(loginToken: string): Promise { const matrixLogin = await this.http.post( 'http://synapse:8008/_matrix/client/r0/login', { @@ -33,50 +47,28 @@ export class ChatService { } ).toPromise(); console.log('LOGGED TO CHAT'); - console.log(matrixLogin); - this.client = matrix.createClient({ + console.log({ matrixLogin }); + this.matrixClient = matrix.createClient({ baseUrl: 'http://localhost:8008', accessToken: matrixLogin.access_token, userId: matrixLogin.user_id }); - await this.client.startClient({ initialSyncLimit: 100 }); + await this.matrixClient.startClient({ initialSyncLimit: 100 }); - this.client.once('sync', (state: string, prevState: string, _: any) => { + this.matrixClient.once('sync', (state: string, prevState: string, _: any) => { if (state === 'PREPARED') { console.log('CHAT SYNC FINISHED'); } else { console.log(state); } }); - } - getRoomMessages(roomId: string): MatrixEvent[] { - console.log('roomId=' + roomId); - const room: Room = this.client?.getRoom(roomId) as Room; - console.log({room}); - return room?.timeline - .filter((t: MatrixEvent) => (t.getType() === 'm.room.message')); + return this.matrixClient; } getRoomMembers(roomId: string): RoomMember[] { - const room: Room = this.client?.getRoom(roomId) as Room; + const room: Room = this.matrixClient?.getRoom(roomId) as Room; return room?.getJoinedMembers(); } - - listemRoomMessages(roomId: string, handle: (event: EventTimeline) => void): void { - this.client?.on('Room.timeline', (event: EventTimeline, room: Room, toStartOfTimeline: boolean) => { - if (room.roomId !== roomId) { - return; // only listen to specified room - } - if (toStartOfTimeline) { - return; // don't print paginated results - } - if (event.getType() !== 'm.room.message') { - return; // only print messages - } - - handle(event); - }); - } } diff --git a/src/app/views/chat/chat.component.ts b/src/app/views/chat/chat.component.ts index 5e157e3..ca3a392 100644 --- a/src/app/views/chat/chat.component.ts +++ b/src/app/views/chat/chat.component.ts @@ -1,10 +1,9 @@ -import { Component, Input, OnInit, AfterViewInit, ElementRef, ViewChildren } from '@angular/core'; +import { Component, Input, OnInit, AfterViewInit, ElementRef, ViewChildren, ChangeDetectorRef } from '@angular/core'; import { ChatService } from 'src/app/services/chat.service'; -import { RoomMember } from 'matrix-js-sdk/src/models/room'; -import { Room } from 'matrix-js-sdk/src/models/room'; import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; import { QueryList } from '@angular/core'; +import { Observable } from 'rxjs'; @Component({ selector: 'app-chat', @@ -14,27 +13,33 @@ import { QueryList } from '@angular/core'; export class ChatComponent implements OnInit, AfterViewInit { @Input() roomId: string; @ViewChildren('messageItem', { read: ElementRef }) messageItems: QueryList; - messages: MatrixEvent[]; + messages: MatrixEvent[] = new Array(); - constructor(private chatService: ChatService) { } + constructor( + private chatService: ChatService, + private changeDetection: ChangeDetectorRef + ) { } ngOnInit(): void { - this.messages = this.chatService.getRoomMessages(this.roomId); + if (this.chatService.getRoomMessages !== undefined) { + const messages$ = this.chatService.getRoomMessages(this.roomId); - this.chatService.listemRoomMessages(this.roomId, (event: any) => { - console.log( - // the room name will update with m.room.name events automatically - '%s: %s', event.getSender(), event.getContent().body - ); - }); + messages$.subscribe({ + next: (event: MatrixEvent) => { + this.messages.push(event); + this.changeDetection.detectChanges(); + } + }); + } } ngAfterViewInit(): void { // Always scroll to last chat message. - this.messageItems?.last.nativeElement?.scrollIntoView(false); - this.messageItems.changes.subscribe((c) => { - console.log({ c }); - this.messageItems?.last.nativeElement?.scrollIntoView(false); + this.messageItems.last?.nativeElement?.scrollIntoView(false); + this.messageItems.changes.subscribe({ + next: (c) => { + this.messageItems.last?.nativeElement?.scrollIntoView(false); + } }); }