Skip to content

[MMB-179] New Location Methods #95

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,4 @@ The above listener will receive a `CursorUpdate` event:
"position": { "x": 864, "y": 32 },
"data": { "color": "red" }
}
```
```
23 changes: 21 additions & 2 deletions docs/class-definitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Create a new instance of the Spaces library.

The Spaces library constructor is overloaded allowing it to be instantiated using a [ClientOptions](https://ably.com/docs/api/realtime-sdk?lang=javascript#client-options) object:

_**Depracated: the ClientOptions option will be removed in the next release. Use the Ably client instance method described underneath.**_
_**Deprecated: the ClientOptions option will be removed in the next release. Use the Ably client instance method described underneath.**_

```ts
import Spaces from '@ably-labs/spaces';
Expand Down Expand Up @@ -279,6 +279,25 @@ Set your current location. [Location](#location-1) can be any JSON-serializable
```ts
type set = (update: Location) => void;
```
### getSelf
Get location for self
```ts
space.locations.getSelf()
```
### getAll
Get location for all members

```ts
space.locations.getAll()
```
### getOthers
Get location for other members

```ts
space.locations.getOthers()
```



### on

Expand Down Expand Up @@ -343,7 +362,7 @@ type set = (update: { position: CursorPosition, data?: CursorData })
Example usage:

```ts
window.addEventListner('mousemove', ({ clientX, clientY }) => {
window.addEventListener('mousemove', ({ clientX, clientY }) => {
space.cursors.set({ position: { x: clientX, y: clientY }, data: { color: "red" } });
});
```
Expand Down
18 changes: 9 additions & 9 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ Spaces use an [Ably promise-based realtime client](https://github.com/ably/ably-

To instantiate with options, you will need at minimum an [Ably API key](#ably-api-key) and a [clientId](https://ably.com/docs/auth/identified-clients?lang=javascripts). A clientId represents an identity of an connection. In most cases this will something like the id of a user:

_**Depracated: the ClientOptions option will be removed in the next release. Use the Ably client instance method described underneath.**_
_**Deprecated: the ClientOptions option will be removed in the next release. Use the Ably client instance method described underneath.**_

```ts
import Spaces from '@ably-labs/spaces';

const spaces = new Spaces({ key: "<API-key>", clientId: "<client-ID>" });
```

If you already have an ably client in your application, you can just pass it directly to Spaces (the client will still need to instatiated with a clientId):
If you already have an ably client in your application, you can just pass it directly to Spaces (the client will still need to instantiated with a clientId):

```ts
import { Realtime } from 'ably/promise';
Expand Down Expand Up @@ -110,7 +110,7 @@ See [SpaceMember](/docs/class-definitions.md#spacemember) for details on propert

### Listen to members updates

The `space` instance is an `EventEmitter`. Events will be emitted for updates to members (includeing self). You can listen to the following events:
The `space` instance is an `EventEmitter`. Events will be emitted for updates to members (including self). You can listen to the following events:

#### enter

Expand Down Expand Up @@ -189,7 +189,7 @@ Each member can set a location for themselves:
space.locations.set({ slide: '3', component: 'slide-title' });
```

A location does not have a prescribed shape. In your UI it can represent a single location (an id of a field in form), multiple locations (id's of multiple cells in a spredsheet) or a hierarchy (a field in one of the multiple forms visible on screen).
A location does not have a prescribed shape. In your UI it can represent a single location (an id of a field in form), multiple locations (id's of multiple cells in a spreadsheet) or a hierarchy (a field in one of the multiple forms visible on screen).

The location property will be set on the [member](#members).

Expand Down Expand Up @@ -275,20 +275,20 @@ space.enter();
Then call `.set`:

```ts
window.addEventListner('mousemove', ({ clientX, clientY }) => {
window.addEventListener('mousemove', ({ clientX, clientY }) => {
space.cursors.set({ position: { x: clientX, y: clientY } });
});
```

A `CursorUpdate` is an object with 2 properties. `position` is an object with 2 required properties, `x` and `y`. These represent the position of the cursor on a 2D plane. A second optional property, `data` can also be passed. This is an object of any shape and is meant for data associated with the cursor movement (like drag or hover calculation results):

```ts
window.addEventListner('mousemove', ({ clientX, clientY }) => {
window.addEventListener('mousemove', ({ clientX, clientY }) => {
space.cursors.set({ position: { x: clientX, y: clientY }, data: { color: 'red' } });
});
```

### Inital cursor position and data
### Initial cursor position and data

To retrieve the initial position and data of all cursors within a space, you can use the `space.cursors.getAll()` method. This returns an object keyed by `connectionId`. The value is the last `cursorUpdate` set by the given `connectionId`.

Expand Down Expand Up @@ -336,7 +336,7 @@ Calling `on` with an array of named events and a function argument will subscrib
space.on([`membersUpdate`], () => {});
```

Calling `off` with no argumnets will remove all registered listeners.
Calling `off` with no arguments will remove all registered listeners.

```ts
space.off();
Expand All @@ -362,4 +362,4 @@ space.off(`membersUpdate`, listener);
space.off([`membersUpdate`], listener);
```

As with the native DOM API, this only works if the listener is the same reference as the one passed to `on`.
As with the native DOM API, this only works if the listener is the same reference as the one passed to `on`.
44 changes: 41 additions & 3 deletions src/Locations.mockClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ describe('Locations (mockClient)', () => {
const spy = vi.fn();
await space.enter();
space.locations.on(LOCATION_UPDATE, spy);
space.locations['onPresenceUpdate'](
createPresenceMessage('update', { clientId: '2', connectionId: '2', data: { location: 'location2' } }),
);
space.locations['onPresenceUpdate'](createPresenceMessage('update', { clientId: '2', connectionId: '2' }));
expect(spy).toHaveBeenCalledOnce();
});

Expand Down Expand Up @@ -82,4 +80,44 @@ describe('Locations (mockClient)', () => {
});
});
});

describe('location getters', () => {
it<SpaceTestContext>('getSelf returns the location only for self', async ({ space }) => {
await space.enter();
space.locations['onPresenceUpdate'](
createPresenceMessage('update', { data: { currentLocation: 'location33', previousLocation: null } }),
);
expect(space.locations.getSelf()).toEqual('location33');
});

it<SpaceTestContext>('getOthers returns the locations only for others', async ({ space }) => {
await space.enter();
space.locations['onPresenceUpdate'](
createPresenceMessage('update', { data: { currentLocation: '23', previousLocation: null } }),
);
space.locations['onPresenceUpdate'](
createPresenceMessage('update', {
connectionId: '2',
data: { currentLocation: 'location22', previousLocation: null },
}),
);
const othersLocations = space.locations.getOthers();
expect(othersLocations).toEqual({ '2': 'location22' });
});

it<SpaceTestContext>('getAll returns the locations for self and others', async ({ space }) => {
await space.enter();
space.locations['onPresenceUpdate'](
createPresenceMessage('update', { data: { currentLocation: 'location11', previousLocation: null } }),
);
space.locations['onPresenceUpdate'](
createPresenceMessage('update', {
connectionId: '2',
data: { currentLocation: 'location22', previousLocation: null },
}),
);
const allLocations = space.locations.getAll();
expect(allLocations).toEqual({ '1': 'location11', '2': 'location22' });
});
});
});
24 changes: 24 additions & 0 deletions src/Locations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,28 @@ export default class Locations extends EventEmitter<LocationEventMap> {
currentLocation: location,
});
}

getSelf(): Location | undefined {
const self = this.space.getSelf();
return self ? self.location : undefined;
}

getOthers(): Record<string, Location> {
const self = this.space.getSelf();

return this.space
.getMembers()
.filter((member) => member.connectionId !== self?.connectionId)
.reduce((acc, member) => {
acc[member.connectionId] = member.location;
return acc;
}, {});
}

getAll(): Record<string, Location> {
return this.space.getMembers().reduce((acc, member) => {
acc[member.connectionId] = member.location;
return acc;
}, {});
}
}