diff --git a/ts/session/messages/outgoing/content/TypingMessage.ts b/ts/session/messages/outgoing/content/TypingMessage.ts index a338c7f4b..d21b0a6c2 100644 --- a/ts/session/messages/outgoing/content/TypingMessage.ts +++ b/ts/session/messages/outgoing/content/TypingMessage.ts @@ -1,7 +1,27 @@ import { ContentMessage } from './ContentMessage'; import { SignalService } from '../../../../protobuf'; +import { TextEncoder } from 'util'; + +interface TypingMessageParams { + timestamp: number; + identifier: string; + isTyping: boolean; + typingTimestamp: number | null; + groupId: string | null; +} + +export class TypingMessage extends ContentMessage { + private readonly isTyping: boolean; + private readonly typingTimestamp: number | null; + private readonly groupId: string | null; + + constructor(params: TypingMessageParams) { + super({timestamp: params.timestamp, identifier: params.identifier}); + this.isTyping = params.isTyping; + this.typingTimestamp = params.typingTimestamp; + this.groupId = params.groupId; + } -export abstract class TypingMessage extends ContentMessage { public ttl(): number { return 60 * 1000; // 1 minute for typing indicators @@ -13,6 +33,19 @@ export abstract class TypingMessage extends ContentMessage { }); } - protected abstract typingProto(): SignalService.TypingMessage; + protected typingProto(): SignalService.TypingMessage { + const ACTION_ENUM = SignalService.TypingMessage.Action; + + const action = this.isTyping ? ACTION_ENUM.STARTED : ACTION_ENUM.STOPPED; + const finalTimestamp = this.typingTimestamp || Date.now(); + const typingMessage = new SignalService.TypingMessage(); + if (this.groupId) { + typingMessage.groupId = new TextEncoder().encode(this.groupId); + } + typingMessage.action = action; + typingMessage.timestamp = finalTimestamp; + + return typingMessage; + } } diff --git a/ts/test/session/messages/TypingMessage_test.ts b/ts/test/session/messages/TypingMessage_test.ts new file mode 100644 index 000000000..312ca0cc0 --- /dev/null +++ b/ts/test/session/messages/TypingMessage_test.ts @@ -0,0 +1,88 @@ +import { expect } from 'chai'; + +import { TypingMessage } from '../../../session/messages/outgoing'; +import { SignalService } from '../../../protobuf'; +import { TextEncoder } from 'util'; + +describe('TypingMessage', () => { + it('has Action.STARTED is isTyping = true', () => { + const message = new TypingMessage({ + timestamp: Date.now(), + identifier: '123456', + isTyping: true, + typingTimestamp: null, + groupId: null, + }); + const plainText = message.plainTextBuffer(); + const decoded = SignalService.Content.toObject(SignalService.Content.decode(plainText)); + expect(decoded.typingMessage).to.have.property('action', SignalService.TypingMessage.Action.STARTED); + }); + + it('has Action.STOPPED is isTyping = false', () => { + const message = new TypingMessage({ + timestamp: Date.now(), + identifier: '123456', + isTyping: false, + typingTimestamp: null, + groupId: null, + }); + const plainText = message.plainTextBuffer(); + const decoded = SignalService.Content.toObject(SignalService.Content.decode(plainText)); + expect(decoded.typingMessage).to.have.property('action', SignalService.TypingMessage.Action.STOPPED); + }); + + it('has typingTimestamp set if value passed', () => { + const message = new TypingMessage({ + timestamp: Date.now(), + identifier: '123456', + isTyping: true, + typingTimestamp: 111111111, + groupId: null, + }); + const plainText = message.plainTextBuffer(); + const decoded = SignalService.Content.toObject(SignalService.Content.decode(plainText)); + const typingTimestamp = decoded.typingMessage.timestamp.toNumber(); + expect(typingTimestamp).to.be.equal(111111111); + }); + + it('has typingTimestamp set with Date.now() if value not passed', () => { + const message = new TypingMessage({ + timestamp: Date.now(), + identifier: '123456', + isTyping: true, + typingTimestamp: null, + groupId: null, + }); + const plainText = message.plainTextBuffer(); + const decoded = SignalService.Content.toObject(SignalService.Content.decode(plainText)); + const typingTimestamp = decoded.typingMessage.timestamp.toNumber(); + expect(typingTimestamp).to.be.equal(Date.now()); + }); + + it('has groupId set if a value given', () => { + const groupId = '6666666666'; + const message = new TypingMessage({ + timestamp: Date.now(), + identifier: '123456', + isTyping: true, + typingTimestamp: null, + groupId, + }); + const plainText = message.plainTextBuffer(); + const decoded = SignalService.Content.toObject(SignalService.Content.decode(plainText)); + const manuallyEncodedGroupId = new TextEncoder().encode(groupId); + + expect(decoded.typingMessage.groupId).to.be.deep.equal(manuallyEncodedGroupId); + }); + + it('ttl of 1 minute', () => { + const message = new TypingMessage({ + timestamp: Date.now(), + identifier: '123456', + isTyping: true, + typingTimestamp: null, + groupId: null, + }); + expect(message.ttl()).to.equal(60 * 1000); + }); +});