From 00de92e45c079da33d7b641eaf299df3b7d83252 Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Thu, 17 Aug 2023 11:29:19 +0100 Subject: [PATCH] Make get and set functions async In preparation for the Members class using the async presence.get rather than maintaining a spearate members array (see https://ably.atlassian.net/browse/MMB-182). Signed-off-by: Lewis Marshall --- demo/src/App.tsx | 2 +- demo/src/hooks/useMembers.ts | 42 +++++++++++++++++--------------- docs/class-definitions.md | 6 ++--- examples/live-cursors.ts | 5 ++-- examples/location.ts | 2 +- examples/locking.ts | 4 ++-- src/Cursors.test.ts | 8 +++---- src/Cursors.ts | 8 +++---- src/Locations.test.ts | 26 ++++++++++---------- src/Locations.ts | 34 +++++++++++++------------- src/Locks.test.ts | 22 ++++++++--------- src/Locks.ts | 8 +++---- src/Members.ts | 22 +++++++++-------- src/Space.test.ts | 46 ++++++++++++++++++------------------ src/Space.ts | 23 +++++++++--------- src/utilities/test/fakes.ts | 8 +++++-- 16 files changed, 138 insertions(+), 128 deletions(-) diff --git a/demo/src/App.tsx b/demo/src/App.tsx index afb23742..a58874d6 100644 --- a/demo/src/App.tsx +++ b/demo/src/App.tsx @@ -14,7 +14,7 @@ const App = () => { const enter = async () => { const name = getRandomName(); await space.enter({ name, color: getRandomColor() }); - space.locations.set({ slide: `${0}`, element: null }); + await space.locations.set({ slide: `${0}`, element: null }); }; enter(); diff --git a/demo/src/hooks/useMembers.ts b/demo/src/hooks/useMembers.ts index 47e34cd1..31442505 100644 --- a/demo/src/hooks/useMembers.ts +++ b/demo/src/hooks/useMembers.ts @@ -24,32 +24,36 @@ export const useMembers: () => Partial<{ self?: Member; others: Member[]; member useEffect(() => { if (!space) return; - const initSelf = space.members.getSelf(); - const initMembers = space.members.getAll(); + const init = async () => { + const initSelf = await space.members.getSelf(); + const initMembers = await space.members.getAll(); - if (isMember(initSelf)) { - setSelf(initSelf); - } + if (isMember(initSelf)) { + setSelf(initSelf); + } - if (areMembers(initMembers)) { - setMembers(initMembers); - setOthers(membersToOthers(initMembers, initSelf)); - } + if (areMembers(initMembers)) { + setMembers(initMembers); + setOthers(membersToOthers(initMembers, initSelf)); + } - const handler = ({ members }: { members: SpaceMember[] }) => { - const self = space.members.getSelf(); + const handler = async ({ members }: { members: SpaceMember[] }) => { + const self = await space.members.getSelf(); - if (isMember(self)) { - setSelf(self); - } + if (isMember(self)) { + setSelf(self); + } - if (areMembers(members)) { - setMembers([...members]); - setOthers(membersToOthers([...members], self)); - } + if (areMembers(members)) { + setMembers([...members]); + setOthers(membersToOthers([...members], self)); + } + }; + + space.subscribe('update', handler); }; - space.subscribe('update', handler); + init(); return () => { space.unsubscribe('update', handler); diff --git a/docs/class-definitions.md b/docs/class-definitions.md index 7c400d8a..54b74427 100644 --- a/docs/class-definitions.md +++ b/docs/class-definitions.md @@ -282,19 +282,19 @@ type set = (update: Location) => void; ### getSelf Get location for self ```ts -space.locations.getSelf() +await space.locations.getSelf() ``` ### getAll Get location for all members ```ts -space.locations.getAll() +await space.locations.getAll() ``` ### getOthers Get location for other members ```ts -space.locations.getOthers() +await space.locations.getOthers() ``` diff --git a/examples/live-cursors.ts b/examples/live-cursors.ts index 6e8525b6..438f2b6c 100644 --- a/examples/live-cursors.ts +++ b/examples/live-cursors.ts @@ -16,8 +16,9 @@ const space = await spaces.get('slide-deck-224'); space.enter({ name: 'Helmut' }); // Listen to all changes to all members within a space -space.cursors.subscribe('update', (cursorUpdate) => { - const member = space.members.getAll().find((member) => member.connectionId === cursorUpdate.connectionId); +space.cursors.subscribe('update', async (cursorUpdate) => { + const members = await space.members.getAll(); + const member = members.find((member) => member.connectionId === cursorUpdate.connectionId); renderCursor(cursorUpdate, member); }); diff --git a/examples/location.ts b/examples/location.ts index ae415daa..ba04835c 100644 --- a/examples/location.ts +++ b/examples/location.ts @@ -22,4 +22,4 @@ space.locations.subscribe('update', ({ member, currentLocation, previousLocation }); // Set your location -space.locations.set({ slide: 0, elementId: 'title' }); +await space.locations.set({ slide: 0, elementId: 'title' }); diff --git a/examples/locking.ts b/examples/locking.ts index 855bcc0c..d41c8b91 100644 --- a/examples/locking.ts +++ b/examples/locking.ts @@ -22,8 +22,8 @@ if (!isLocked) { } // Update UI when parts of the UI are locked -space.locks.subscribe('update', (lock) => { - const self = space.members.getSelf(); +space.locks.subscribe('update', async (lock) => { + const self = await space.members.getSelf(); if (lock.request.status === LockStatus.LOCKED && self.connectionId === lock.member.connectionId) { const location = { diff --git a/src/Cursors.test.ts b/src/Cursors.test.ts index efad9f01..e4312fbd 100644 --- a/src/Cursors.test.ts +++ b/src/Cursors.test.ts @@ -464,7 +464,7 @@ describe('Cursors', () => { selfStub, }) => { vi.spyOn(space.cursors, 'getAll').mockImplementation(async () => lastCursorPositionsStub); - vi.spyOn(space.members, 'getSelf').mockReturnValue(selfStub); + vi.spyOn(space.members, 'getSelf').mockResolvedValue(selfStub); const selfCursor = await space.cursors.getSelf(); expect(selfCursor).toEqual(lastCursorPositionsStub['connectionId1']); @@ -472,7 +472,7 @@ describe('Cursors', () => { it('returns an empty object if self is not present in cursors', async ({ space }) => { vi.spyOn(space.cursors, 'getAll').mockResolvedValue({}); - vi.spyOn(space.members, 'getSelf').mockReturnValue(undefined); + vi.spyOn(space.members, 'getSelf').mockResolvedValue(undefined); const others = await space.cursors.getOthers(); expect(others).toEqual({}); @@ -495,7 +495,7 @@ describe('Cursors', () => { }; vi.spyOn(space.cursors, 'getAll').mockResolvedValue(onlyMyCursor); - vi.spyOn(space.members, 'getSelf').mockReturnValue(selfStub); + vi.spyOn(space.members, 'getSelf').mockResolvedValue(selfStub); const others = await space.cursors.getOthers(); expect(others).toEqual({}); @@ -507,7 +507,7 @@ describe('Cursors', () => { lastCursorPositionsStub, }) => { vi.spyOn(space.cursors, 'getAll').mockResolvedValue(lastCursorPositionsStub); - vi.spyOn(space.members, 'getSelf').mockReturnValue(selfStub); + vi.spyOn(space.members, 'getSelf').mockResolvedValue(selfStub); const others = await space.cursors.getOthers(); expect(others).toEqual({ diff --git a/src/Cursors.ts b/src/Cursors.ts index 2bcf04c8..5bef1fba 100644 --- a/src/Cursors.ts +++ b/src/Cursors.ts @@ -44,8 +44,8 @@ export default class Cursors extends EventEmitter { * @param {CursorUpdate} cursor * @return {void} */ - set(cursor: Pick): void { - const self = this.space.members.getSelf(); + async set(cursor: Pick) { + const self = await this.space.members.getSelf(); if (!self) { throw new Error('Must enter a space before setting a cursor update'); @@ -149,7 +149,7 @@ export default class Cursors extends EventEmitter { } async getSelf(): Promise { - const self = this.space.members.getSelf(); + const self = await this.space.members.getSelf(); if (!self) return; const allCursors = await this.getAll(); @@ -157,7 +157,7 @@ export default class Cursors extends EventEmitter { } async getOthers(): Promise> { - const self = this.space.members.getSelf(); + const self = await this.space.members.getSelf(); if (!self) return {}; const allCursors = await this.getAll(); diff --git a/src/Locations.test.ts b/src/Locations.test.ts index 0f3ada4a..5637cb84 100644 --- a/src/Locations.test.ts +++ b/src/Locations.test.ts @@ -31,20 +31,20 @@ describe('Locations', () => { describe('set', () => { it('errors if setting location before entering the space', ({ space }) => { - expect(() => space.locations.set('location1')).toThrowError(); + expect(() => space.locations.set('location1')).rejects.toThrowError(); }); it('sends a presence update on location set', async ({ space, presence }) => { const spy = vi.spyOn(presence, 'update'); await space.enter(); - space.locations.set('location1'); + await space.locations.set('location1'); expect(spy).toHaveBeenCalledWith(createLocationUpdate({ current: 'location1' })); }); it('fires an event when a location is set', async ({ space }) => { const spy = vi.fn(); space.locations.subscribe('update', spy); - space['onPresenceUpdate']( + await space['onPresenceUpdate']( createPresenceMessage('update', { data: createLocationUpdate({ current: 'location1' }), }), @@ -56,13 +56,13 @@ describe('Locations', () => { const spy = vi.fn(); space.locations.subscribe('update', spy); - space['onPresenceUpdate']( + await space['onPresenceUpdate']( createPresenceMessage('update', { data: createLocationUpdate({ current: 'location1' }), }), ); - space['onPresenceUpdate']( + await space['onPresenceUpdate']( createPresenceMessage('update', { data: createLocationUpdate({ current: 'location2', previous: 'location1', id: 'newId' }), }), @@ -78,43 +78,43 @@ describe('Locations', () => { describe('location getters', () => { it('getSelf returns the location only for self', async ({ space }) => { - space['onPresenceUpdate']( + await space['onPresenceUpdate']( createPresenceMessage('update', { data: createLocationUpdate({ current: 'location1' }), }), ); - expect(space.locations.getSelf()).toEqual('location1'); + expect(space.locations.getSelf()).resolves.toEqual('location1'); }); it('getOthers returns the locations only for others', async ({ space }) => { - space['onPresenceUpdate']( + await space['onPresenceUpdate']( createPresenceMessage('update', { data: createLocationUpdate({ current: 'location1' }) }), ); - space['onPresenceUpdate']( + await space['onPresenceUpdate']( createPresenceMessage('update', { connectionId: '2', data: createLocationUpdate({ current: 'location2' }), }), ); - const othersLocations = space.locations.getOthers(); + const othersLocations = await space.locations.getOthers(); expect(othersLocations).toEqual({ '2': 'location2' }); }); it('getAll returns the locations for self and others', async ({ space }) => { - space['onPresenceUpdate']( + await space['onPresenceUpdate']( createPresenceMessage('update', { data: createLocationUpdate({ current: 'location1' }) }), ); - space['onPresenceUpdate']( + await space['onPresenceUpdate']( createPresenceMessage('update', { connectionId: '2', data: createLocationUpdate({ current: 'location2' }), }), ); - const allLocations = space.locations.getAll(); + const allLocations = await space.locations.getAll(); expect(allLocations).toEqual({ '1': 'location1', '2': 'location2' }); }); }); diff --git a/src/Locations.ts b/src/Locations.ts index 41eec662..1cf17278 100644 --- a/src/Locations.ts +++ b/src/Locations.ts @@ -22,7 +22,7 @@ export default class Locations extends EventEmitter { super(); } - processPresenceMessage(message: PresenceMember) { + async processPresenceMessage(message: PresenceMember) { // Only an update action is currently a valid location update. if (message.action !== 'update') return; @@ -37,7 +37,7 @@ export default class Locations extends EventEmitter { const update = message.data.locationUpdate; const { previous } = update; - const member = this.space.members.getByConnectionId(message.connectionId); + const member = await this.space.members.getByConnectionId(message.connectionId); if (member) { this.emit('update', { @@ -50,8 +50,8 @@ export default class Locations extends EventEmitter { } } - set(location: unknown) { - const self = this.space.members.getSelf(); + async set(location: unknown) { + const self = await this.space.members.getSelf(); if (!self) { throw new Error('You must enter a space before setting a location.'); @@ -69,7 +69,7 @@ export default class Locations extends EventEmitter { }, }; - return this.presenceUpdate(update); + await this.presenceUpdate(update); } subscribe>( @@ -106,25 +106,23 @@ export default class Locations extends EventEmitter { } } - getSelf(): unknown { - const self = this.space.members.getSelf(); + async getSelf(): Promise { + const self = await this.space.members.getSelf(); return self ? self.location : null; } - getOthers(): Record { - const self = this.space.members.getSelf(); + async getOthers(): Promise> { + const members = await this.space.members.getOthers(); - return this.space.members - .getAll() - .filter((member) => member.connectionId !== self?.connectionId) - .reduce((acc: Record, member: SpaceMember) => { - acc[member.connectionId] = member.location; - return acc; - }, {}); + return members.reduce((acc: Record, member: SpaceMember) => { + acc[member.connectionId] = member.location; + return acc; + }, {}); } - getAll(): Record { - return this.space.members.getAll().reduce((acc: Record, member: SpaceMember) => { + async getAll(): Promise> { + const members = await this.space.members.getAll(); + return members.reduce((acc: Record, member: SpaceMember) => { acc[member.connectionId] = member.location; return acc; }, {}); diff --git a/src/Locks.test.ts b/src/Locks.test.ts index 001339d7..d4ce047c 100644 --- a/src/Locks.test.ts +++ b/src/Locks.test.ts @@ -104,7 +104,7 @@ describe('Locks (mockClient)', () => { it('sets a PENDING request to LOCKED', async ({ space }) => { await space.enter(); - const member = space.members.getSelf()!; + const member = (await space.members.getSelf())!; const emitSpy = vi.spyOn(space.locks, 'emit'); @@ -120,7 +120,7 @@ describe('Locks (mockClient)', () => { ], }, }); - space.locks.processPresenceMessage(msg); + await space.locks.processPresenceMessage(msg); const lock = space.locks.getLockRequest(lockID, member.connectionId)!; expect(lock.status).toBe('locked'); @@ -183,7 +183,7 @@ describe('Locks (mockClient)', () => { ], }, }); - space.locks.processPresenceMessage(msg); + await space.locks.processPresenceMessage(msg); const lock = space.locks.get(lockID)!; expect(lock.member.connectionId).toBe(otherConnId); @@ -202,11 +202,11 @@ describe('Locks (mockClient)', () => { ], }, }); - space.locks.processPresenceMessage(msg); - const selfMember = space.members.getByConnectionId(client.connection.id!)!; + await space.locks.processPresenceMessage(msg); + const selfMember = (await space.members.getByConnectionId(client.connection.id!))!; const selfLock = space.locks.getLockRequest(lockID, selfMember.connectionId)!; expect(selfLock.status).toBe(expectedSelfStatus); - const otherMember = space.members.getByConnectionId(otherConnId)!; + const otherMember = (await space.members.getByConnectionId(otherConnId))!; const otherLock = space.locks.getLockRequest(lockID, otherMember.connectionId)!; expect(otherLock.status).toBe(expectedOtherStatus); @@ -223,7 +223,7 @@ describe('Locks (mockClient)', () => { it('sets a released request to UNLOCKED', async ({ space }) => { await space.enter(); - const member = space.members.getSelf()!; + const member = (await space.members.getSelf())!; let msg = Realtime.PresenceMessage.fromValues({ connectionId: member.connectionId, @@ -237,7 +237,7 @@ describe('Locks (mockClient)', () => { ], }, }); - space.locks.processPresenceMessage(msg); + await space.locks.processPresenceMessage(msg); const emitSpy = vi.spyOn(space.locks, 'emit'); @@ -245,7 +245,7 @@ describe('Locks (mockClient)', () => { connectionId: member.connectionId, extras: undefined, }); - space.locks.processPresenceMessage(msg); + await space.locks.processPresenceMessage(msg); const lock = space.locks.getLockRequest(lockID, member.connectionId); expect(lock).not.toBeDefined(); @@ -260,7 +260,7 @@ describe('Locks (mockClient)', () => { it('removes the identified lock request from presence extras', async ({ space, presence }) => { await space.enter(); - const member = space.members.getSelf()!; + const member = (await space.members.getSelf())!; const lockID = 'test'; const msg = Realtime.PresenceMessage.fromValues({ @@ -275,7 +275,7 @@ describe('Locks (mockClient)', () => { ], }, }); - space.locks.processPresenceMessage(msg); + await space.locks.processPresenceMessage(msg); expect(space.locks.get(lockID)).toBeDefined(); const presenceUpdate = vi.spyOn(presence, 'update'); diff --git a/src/Locks.ts b/src/Locks.ts index a14e69ee..486881ff 100644 --- a/src/Locks.ts +++ b/src/Locks.ts @@ -53,7 +53,7 @@ export default class Locks extends EventEmitter { } async acquire(id: string, opts?: LockOptions): Promise { - const self = this.space.members.getSelf(); + const self = await this.space.members.getSelf(); if (!self) { throw new Error('Must enter a space before acquiring a lock'); } @@ -83,7 +83,7 @@ export default class Locks extends EventEmitter { } async release(id: string): Promise { - const self = this.space.members.getSelf(); + const self = await this.space.members.getSelf(); if (!self) { throw new Error('Must enter a space before acquiring a lock'); } @@ -127,8 +127,8 @@ export default class Locks extends EventEmitter { } } - processPresenceMessage(message: Types.PresenceMessage) { - const member = this.space.members.getByConnectionId(message.connectionId); + async processPresenceMessage(message: Types.PresenceMessage) { + const member = await this.space.members.getByConnectionId(message.connectionId); if (!member) return; if (!Array.isArray(message?.extras?.locks)) { diff --git a/src/Members.ts b/src/Members.ts index 646d2adb..54a34855 100644 --- a/src/Members.ts +++ b/src/Members.ts @@ -28,7 +28,7 @@ class Members extends EventEmitter { this.leavers = new Leavers(this.space.options.offlineTimeout); } - processPresenceMessage(message: PresenceMember) { + async processPresenceMessage(message: PresenceMember) { const { action, connectionId } = message; const isLeaver = !!this.leavers.getByConnectionId(connectionId); const isMember = this.members.some((m) => m.connectionId === connectionId); @@ -60,16 +60,17 @@ class Members extends EventEmitter { } } - getSelf(): SpaceMember | undefined { - return this.space.connectionId ? this.getByConnectionId(this.space.connectionId) : undefined; + async getSelf(): Promise { + return this.space.connectionId ? await this.getByConnectionId(this.space.connectionId) : undefined; } - getAll(): SpaceMember[] { + async getAll(): Promise { return this.members.concat(this.leavers.getAll().map((l) => l.member)); } - getOthers(): SpaceMember[] { - return this.getAll().filter((m) => m.connectionId !== this.space.connectionId); + async getOthers(): Promise { + const members = await this.getAll(); + return members.filter((m) => m.connectionId !== this.space.connectionId); } subscribe>( @@ -112,8 +113,9 @@ class Members extends EventEmitter { return members; } - getByConnectionId(connectionId: string): SpaceMember | undefined { - return this.getAll().find((m) => m.connectionId === connectionId); + async getByConnectionId(connectionId: string): Promise { + const members = await this.getAll(); + return members.find((m) => m.connectionId === connectionId); } createMember(message: PresenceMember): SpaceMember { @@ -130,7 +132,7 @@ class Members extends EventEmitter { }; } - onMemberOffline(member: SpaceMember) { + async onMemberOffline(member: SpaceMember) { this.leavers.removeLeaver(member.connectionId); this.emit('remove', member); @@ -143,7 +145,7 @@ class Members extends EventEmitter { }); } - this.space.emit('update', { members: this.getAll() }); + this.space.emit('update', { members: await this.getAll() }); } } diff --git a/src/Space.test.ts b/src/Space.test.ts index b7e1c04e..0ebac5f0 100644 --- a/src/Space.test.ts +++ b/src/Space.test.ts @@ -67,10 +67,10 @@ describe('Space', () => { vi.spyOn(presence, 'get').mockImplementationOnce(async () => [createPresenceMessage('update')]); await space.enter(); - const member = space.members.getByConnectionId('1'); + const member = await space.members.getByConnectionId('1'); expect(member).toEqual(createSpaceMember()); - const noMember = space.members.getByConnectionId('nonExistentConnectionId'); + const noMember = await space.members.getByConnectionId('nonExistentConnectionId'); expect(noMember).toBe(undefined); }); @@ -97,8 +97,8 @@ describe('Space', () => { ]); await space.enter(); - const member1 = space.members.getByConnectionId('1')!; - const member2 = space.members.getByConnectionId('2')!; + const member1 = await space.members.getByConnectionId('1')!; + const member2 = await space.members.getByConnectionId('2')!; const lock1 = space.locks.get('lock1')!; expect(lock1.member).toEqual(member2); @@ -176,14 +176,14 @@ describe('Space', () => { it('adds new members', async ({ space }) => { const callbackSpy = vi.fn(); space.subscribe('update', callbackSpy); - createPresenceEvent(space, 'enter'); + await createPresenceEvent(space, 'enter'); const member1 = createSpaceMember({ lastEvent: { name: 'enter', timestamp: 1 } }); expect(callbackSpy).toHaveBeenNthCalledWith(1, { members: [member1], }); - createPresenceEvent(space, 'enter', { + await createPresenceEvent(space, 'enter', { clientId: '2', connectionId: '2', data: createProfileUpdate({ current: { name: 'Betty' } }), @@ -206,12 +206,12 @@ describe('Space', () => { const callbackSpy = vi.fn(); space.subscribe('update', callbackSpy); - createPresenceEvent(space, 'enter'); + await createPresenceEvent(space, 'enter'); expect(callbackSpy).toHaveBeenNthCalledWith(1, { members: [createSpaceMember({ lastEvent: { name: 'enter', timestamp: 1 } })], }); - createPresenceEvent(space, 'update', { + await createPresenceEvent(space, 'update', { data: createProfileUpdate({ current: { name: 'Betty' } }), }); @@ -224,12 +224,12 @@ describe('Space', () => { const callbackSpy = vi.fn(); space.subscribe('update', callbackSpy); - createPresenceEvent(space, 'enter'); + await createPresenceEvent(space, 'enter'); expect(callbackSpy).toHaveBeenNthCalledWith(1, { members: [createSpaceMember({ lastEvent: { name: 'enter', timestamp: 1 } })], }); - createPresenceEvent(space, 'leave'); + await createPresenceEvent(space, 'leave'); expect(callbackSpy).toHaveBeenNthCalledWith(2, { members: [createSpaceMember({ isConnected: false, lastEvent: { name: 'leave', timestamp: 1 } })], }); @@ -248,17 +248,17 @@ describe('Space', () => { const callbackSpy = vi.fn(); space.subscribe('update', callbackSpy); - createPresenceEvent(space, 'enter'); + await createPresenceEvent(space, 'enter'); expect(callbackSpy).toHaveBeenNthCalledWith(1, { members: [createSpaceMember({ lastEvent: { name: 'enter', timestamp: 1 } })], }); - createPresenceEvent(space, 'leave'); + await createPresenceEvent(space, 'leave'); expect(callbackSpy).toHaveBeenNthCalledWith(2, { members: [createSpaceMember({ isConnected: false, lastEvent: { name: 'leave', timestamp: 1 } })], }); - vi.advanceTimersByTime(130_000); + await vi.advanceTimersByTimeAsync(130_000); expect(callbackSpy).toHaveBeenNthCalledWith(3, { members: [] }); expect(callbackSpy).toHaveBeenCalledTimes(3); @@ -268,8 +268,8 @@ describe('Space', () => { const callbackSpy = vi.fn(); space.subscribe('update', callbackSpy); - createPresenceEvent(space, 'enter'); - createPresenceEvent(space, 'enter', { clientId: '2', connectionId: '2' }); + await createPresenceEvent(space, 'enter'); + await createPresenceEvent(space, 'enter', { clientId: '2', connectionId: '2' }); expect(callbackSpy).toHaveBeenNthCalledWith(2, { members: [ createSpaceMember({ lastEvent: { name: 'enter', timestamp: 1 } }), @@ -277,7 +277,7 @@ describe('Space', () => { ], }); - createPresenceEvent(space, 'leave'); + await createPresenceEvent(space, 'leave'); expect(callbackSpy).toHaveBeenNthCalledWith(3, { members: [ createSpaceMember({ clientId: '2', connectionId: '2', lastEvent: { name: 'enter', timestamp: 1 } }), @@ -285,8 +285,8 @@ describe('Space', () => { ], }); - vi.advanceTimersByTime(60_000); - createPresenceEvent(space, 'enter'); + await vi.advanceTimersByTimeAsync(60_000); + await createPresenceEvent(space, 'enter'); expect(callbackSpy).toHaveBeenNthCalledWith(4, { members: [ createSpaceMember({ clientId: '2', connectionId: '2', lastEvent: { name: 'enter', timestamp: 1 } }), @@ -294,16 +294,16 @@ describe('Space', () => { ], }); - vi.advanceTimersByTime(130_000); // 2:10 passed, default timeout is 2 min + await vi.advanceTimersByTimeAsync(130_000); // 2:10 passed, default timeout is 2 min expect(callbackSpy).toHaveBeenCalledTimes(4); }); it('unsubscribes when unsubscribe is called', async ({ space }) => { const spy = vi.fn(); space.subscribe('update', spy); - createPresenceEvent(space, 'enter', { clientId: '2' }); + await createPresenceEvent(space, 'enter', { clientId: '2' }); space.unsubscribe('update', spy); - createPresenceEvent(space, 'enter', { clientId: '2' }); + await createPresenceEvent(space, 'enter', { clientId: '2' }); expect(spy).toHaveBeenCalledOnce(); }); @@ -311,9 +311,9 @@ describe('Space', () => { it('unsubscribes when unsubscribe is called with no arguments', async ({ space }) => { const spy = vi.fn(); space.subscribe('update', spy); - createPresenceEvent(space, 'enter', { clientId: '2' }); + await createPresenceEvent(space, 'enter', { clientId: '2' }); space.unsubscribe(); - createPresenceEvent(space, 'enter', { clientId: '2' }); + await createPresenceEvent(space, 'enter', { clientId: '2' }); expect(spy).toHaveBeenCalledOnce(); }); diff --git a/src/Space.ts b/src/Space.ts index 9962b23c..0c3659c2 100644 --- a/src/Space.ts +++ b/src/Space.ts @@ -94,11 +94,11 @@ class Space extends EventEmitter { }; } - private onPresenceUpdate(message: PresenceMember) { - this.members.processPresenceMessage(message); - this.locations.processPresenceMessage(message); - this.locks.processPresenceMessage(message); - this.emit('update', { members: this.members.getAll() }); + private async onPresenceUpdate(message: PresenceMember) { + await this.members.processPresenceMessage(message); + await this.locations.processPresenceMessage(message); + await this.locks.processPresenceMessage(message); + this.emit('update', { members: await this.members.getAll() }); } async enter(profileData: ProfileData = null): Promise { @@ -137,7 +137,7 @@ class Space extends EventEmitter { | Record | ((update: Record | null) => Record), ): Promise { - const self = this.members.getSelf(); + const self = await this.members.getSelf(); if (!isObject(profileDataOrUpdateFn) && !isFunction(profileDataOrUpdateFn)) { throw new Error('Space.updateProfileData(): Invalid arguments: ' + inspect([profileDataOrUpdateFn])); @@ -163,8 +163,8 @@ class Space extends EventEmitter { return this.presenceUpdate(update); } - leave(profileData: ProfileData = null) { - const self = this.members.getSelf(); + async leave(profileData: ProfileData = null) { + const self = await this.members.getSelf(); if (!self) { throw new Error('You must enter a space before attempting to leave it'); @@ -182,11 +182,12 @@ class Space extends EventEmitter { }, }; - return this.presenceLeave(update); + await this.presenceLeave(update); } - getState(): { members: SpaceMember[] } { - return { members: this.members.getAll() }; + async getState(): Promise<{ members: SpaceMember[] }> { + const members = await this.members.getAll(); + return { members }; } subscribe>( diff --git a/src/utilities/test/fakes.ts b/src/utilities/test/fakes.ts index 83755921..c803444b 100644 --- a/src/utilities/test/fakes.ts +++ b/src/utilities/test/fakes.ts @@ -58,8 +58,12 @@ const createPresenceMessage = (type: T, override?: P } }; -const createPresenceEvent = (space: Space, type: T, override?: Partial) => { - space['onPresenceUpdate'](createPresenceMessage(type, override)); +const createPresenceEvent = async ( + space: Space, + type: T, + override?: Partial, +) => { + await space['onPresenceUpdate'](createPresenceMessage(type, override)); }; const createLocationUpdate = (update?: Partial): PresenceMember['data'] => {