Skip to content
This repository has been archived by the owner on Oct 18, 2024. It is now read-only.

feat: improve type and autocompletion in stores #643

Merged
merged 3 commits into from
Apr 18, 2024
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
17 changes: 11 additions & 6 deletions src/common/types/stores.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ export enum StoreType {
WHO_IS_ONLINE = 'who-is-online-store',
}

type StoreApi<T extends (...args: any[]) => any> = {
type Subject<T extends (...args: any[]) => any, K extends keyof ReturnType<T>> = ReturnType<T>[K]

type StoreApiWithoutDestroy<T extends (...args: any[]) => any> = {
[K in keyof ReturnType<T>]: {
subscribe(callback?: (value: keyof T) => void): void;
subject: PublicSubject<keyof T>;
publish<T>(value: T): void;
value: any;
subscribe(callback?: (value: Subject<T, K>['value']) => void): void;
subject: Subject<T, K>;
publish(value: Subject<T, K>['value']): void;
value: Subject<T, K>['value'];
};
};
}

type StoreApi<T extends (...args: any[]) => any> = Omit<StoreApiWithoutDestroy<T>, 'destroy'> & { destroy(): void };


// When creating new Stores, expand the ternary with the new Store. For example:
// ...T extends StoreType.GLOBAL ? StoreApi<typeof useGlobalStore> : T extends StoreType.WHO_IS_ONLINE ? StoreApi<typeof useWhoIsOnlineStore> : never;
Expand Down
11 changes: 6 additions & 5 deletions src/common/utils/use-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,22 @@ function subscribeTo<T>(
* @description Returns a proxy of the global store data and a subscribe function to be used in the components
*/
export function useStore<T extends StoreType>(name: T): Store<T> {
// @TODO - Improve types to get better sugestions when writing code
const storeData = stores[name as StoreType]();
const bindedSubscribeTo = subscribeTo.bind(this);

const proxy = new Proxy(storeData, {
get(store: Store<T>, valueName: string) {
get(store, valueName: string) {
if (valueName === 'destroy') return store.destroy;

return {
subscribe<K extends Store<T>>(callback?: (value: K) => void) {
subscribe(callback?) {
bindedSubscribeTo(valueName, store[valueName], callback);
},
subject: store[valueName] as typeof storeData,
subject: store[valueName],
get value() {
return this.subject.value;
},
publish(newValue: keyof Store<T>) {
publish(newValue) {
this.subject.value = newValue;
},
};
Expand Down
1 change: 1 addition & 0 deletions src/components/base/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { AblyRealtimeService } from '../../services/realtime';
import { ComponentNames } from '../types';

import { DefaultAttachComponentOptions } from './types';
import { useGlobalStore } from '../../services/stores';

export abstract class BaseComponent extends Observable {
public abstract name: ComponentNames;
Expand Down
2 changes: 1 addition & 1 deletion src/components/comments/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class Comments extends BaseComponent {
const { group, localParticipant } = this.useStore(StoreType.GLOBAL);
group.subscribe();

localParticipant.subscribe((participant: Participant) => {
localParticipant.subscribe((participant) => {
this.localParticipantId = participant.id;
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/form-elements/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,7 @@ describe('form elements', () => {
instance['publishTypedEvent']({ presence, data });

expect(instance['publish']).toHaveBeenCalledWith(
`${FieldEvents.KEYBOARD_INTERACTION}-${fieldId}`,
FieldEvents.KEYBOARD_INTERACTION,
{
fieldId,
userId: '123',
Expand Down
4 changes: 2 additions & 2 deletions src/components/form-elements/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ export class FormElements extends BaseComponent {
}: SocketEvent<InputPayload>) => {
if (presence.id === this.localParticipant.id) return;

this.publish(`${FieldEvents.INPUT}-${fieldId}`, {
this.publish(FieldEvents.INPUT, {
value,
fieldId,
attribute,
Expand All @@ -627,7 +627,7 @@ export class FormElements extends BaseComponent {
}: SocketEvent<FocusPayload>) => {
if (presence.id === this.localParticipant.id) return;

this.publish(`${FieldEvents.KEYBOARD_INTERACTION}-${fieldId}`, {
this.publish(FieldEvents.KEYBOARD_INTERACTION, {
fieldId,
userId: presence.id,
userName: presence.name,
Expand Down
25 changes: 15 additions & 10 deletions src/components/who-is-online/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ describe('Who Is Online', () => {
name: MOCK_ABLY_PARTICIPANT_DATA_2.name,
};
const { following } = whoIsOnlineComponent['useStore'](StoreType.WHO_IS_ONLINE);
following.publish<Following>(followingData);
following.publish(followingData);

whoIsOnlineComponent['setFollow'](MOCK_ABLY_PARTICIPANT);

Expand All @@ -294,7 +294,7 @@ describe('Who Is Online', () => {
};

const { following } = whoIsOnlineComponent['useStore'](StoreType.WHO_IS_ONLINE);
following.publish<Following>(followingData);
following.publish(followingData);

whoIsOnlineComponent['setFollow']({
...MOCK_ABLY_PARTICIPANT,
Expand Down Expand Up @@ -330,7 +330,7 @@ describe('Who Is Online', () => {
describe('stopFollowing', () => {
test('should do nothing if participant leaving is not being followed', () => {
const { following } = whoIsOnlineComponent['useStore'](StoreType.WHO_IS_ONLINE);
following.publish<Following>({
following.publish({
color: MOCK_ABLY_PARTICIPANT_DATA_2.color,
id: MOCK_ABLY_PARTICIPANT_DATA_2.id,
name: MOCK_ABLY_PARTICIPANT_DATA_2.name,
Expand All @@ -339,12 +339,12 @@ describe('Who Is Online', () => {
whoIsOnlineComponent['stopFollowing'](MOCK_ABLY_PARTICIPANT);

expect(following.value).toBeDefined();
expect(following.value.id).toBe(MOCK_ABLY_PARTICIPANT_DATA_2.id);
expect(following.value!.id).toBe(MOCK_ABLY_PARTICIPANT_DATA_2.id);
});

test('should set following to undefined if following the participant who is leaving', () => {
const { following } = whoIsOnlineComponent['useStore'](StoreType.WHO_IS_ONLINE);
following.publish<Following>({
following.publish({
color: MOCK_ABLY_PARTICIPANT_DATA_1.color,
id: MOCK_ABLY_PARTICIPANT_DATA_1.id,
name: MOCK_ABLY_PARTICIPANT_DATA_1.name,
Expand Down Expand Up @@ -406,7 +406,7 @@ describe('Who Is Online', () => {
name: MOCK_ABLY_PARTICIPANT_DATA_2.name,
};
const { following } = whoIsOnlineComponent['useStore'](StoreType.WHO_IS_ONLINE);
following.publish<Following>(followingData);
following.publish(followingData);

whoIsOnlineComponent['followMousePointer']({
detail: { id: 'unit-test-id' },
Expand Down Expand Up @@ -438,7 +438,7 @@ describe('Who Is Online', () => {

test('should publish "stop following" event when stopFollowing is called', () => {
const { following } = whoIsOnlineComponent['useStore'](StoreType.WHO_IS_ONLINE);
following.publish<Following>({
following.publish({
color: MOCK_ABLY_PARTICIPANT_DATA_2.color,
id: MOCK_ABLY_PARTICIPANT_DATA_2.id,
name: MOCK_ABLY_PARTICIPANT_DATA_2.name,
Expand Down Expand Up @@ -481,7 +481,7 @@ describe('Who Is Online', () => {
name: MOCK_ABLY_PARTICIPANT_DATA_2.name,
};

following.publish<Following>(followingData);
following.publish(followingData);

whoIsOnlineComponent['follow']({
detail: { id: 'unit-test-id' },
Expand Down Expand Up @@ -546,6 +546,7 @@ describe('Who Is Online', () => {
disableGoToParticipant: { value: false },
disableGatherAll: { value: false },
disablePrivateMode: { value: false },
destroy: jest.fn(),
});

expect(
Expand All @@ -565,6 +566,7 @@ describe('Who Is Online', () => {
disableGoToParticipant: { value: false },
disableGatherAll: { value: false },
disablePrivateMode: { value: false },
destroy: jest.fn(),
});

expect(
Expand All @@ -584,6 +586,7 @@ describe('Who Is Online', () => {
disableGatherAll: { value: true },
disableFollowParticipant: { value: true },
disableGoToParticipant: { value: true },
destroy: jest.fn(),
});

expect(
Expand All @@ -603,6 +606,7 @@ describe('Who Is Online', () => {
disableGoToParticipant: { value: false },
disableGatherAll: { value: false },
disablePrivateMode: { value: false },
destroy: jest.fn(),
});

expect(
Expand All @@ -622,6 +626,7 @@ describe('Who Is Online', () => {
disableGoToParticipant: { value: false },
disableGatherAll: { value: false },
disablePrivateMode: { value: false },
destroy: jest.fn(),
});

expect(
Expand Down Expand Up @@ -865,7 +870,7 @@ describe('Who Is Online', () => {
](StoreType.WHO_IS_ONLINE);
disableGoToParticipant.publish(false);
disableFollowParticipant.publish(false);
following.publish<Following>({ color: 'red', id: 'participant123', name: 'name' });
following.publish({ color: 'red', id: 'participant123', name: 'name' });

const controls = whoIsOnlineComponent['getOtherParticipantsControls']('participant123');

Expand Down Expand Up @@ -1053,7 +1058,7 @@ describe('Who Is Online', () => {

participants.publish(participantsList);
extras.publish([participant5]);
following.publish<Following>({
following.publish({
color: 'red',
id: 'test id 5',
name: 'participant 5',
Expand Down
43 changes: 23 additions & 20 deletions src/components/who-is-online/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ export class WhoIsOnline extends BaseComponent {
*/
protected start(): void {
const { localParticipant } = this.useStore(StoreType.GLOBAL);
localParticipant.subscribe((value: { id: string }) => {
this.localParticipantId = value.id;
localParticipant.subscribe((participant) => {
this.localParticipantId = participant.id;
});

this.subscribeToRealtimeEvents();
Expand All @@ -94,6 +94,9 @@ export class WhoIsOnline extends BaseComponent {
this.removeListeners();
this.element.remove();
this.element = null;

const { destroy } = this.useStore(StoreType.WHO_IS_ONLINE);
destroy();
}

/**
Expand Down Expand Up @@ -623,24 +626,24 @@ export class WhoIsOnline extends BaseComponent {
private updateParticipantsControls(participantId: string | undefined): void {
const { participants } = this.useStore(StoreType.WHO_IS_ONLINE);

participants.publish(
participants.value.map((participant: Participant) => {
if (participantId && participant.id !== participantId) return participant;

const { id } = participant;
const disableDropdown = this.shouldDisableDropdown({
activeComponents: participant.activeComponents,
participantId: id,
});
const presenceEnabled = !disableDropdown;
const controls = this.getControls({ participantId: id, presenceEnabled }) ?? [];

return {
...participant,
controls,
};
}),
);
const newParticipantsList = participants.value.map((participant: Participant) => {
if (participantId && participant.id !== participantId) return participant;

const { id } = participant;
const disableDropdown = this.shouldDisableDropdown({
activeComponents: participant.activeComponents,
participantId: id,
});
const presenceEnabled = !disableDropdown;
const controls = this.getControls({ participantId: id, presenceEnabled }) ?? [];

return {
...participant,
controls,
};
})

participants.publish(newParticipantsList);
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/services/stores/global/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export class GlobalStore {

public destroy() {
this.localParticipant.destroy();
this.participants.destroy();
this.group.destroy();
instance.value = null;
}
}
Expand Down
17 changes: 13 additions & 4 deletions src/services/stores/who-is-online/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,11 @@ export class WhoIsOnlineStore {
public disablePrivateMode = subject<boolean>(false);
public disableGatherAll = subject<boolean>(false);
public disableFollowMe = subject<boolean>(false);

public participants = subject<Participant[]>([]);
public extras = subject<Participant[]>([]);

public joinedPresence = subject<boolean | undefined>(undefined);
public everyoneFollowsMe = subject<boolean>(false);
public privateMode = subject<boolean>(false);

public following = subject<Following | undefined>(undefined);

constructor() {
Expand All @@ -33,7 +30,19 @@ export class WhoIsOnlineStore {
}

public destroy() {
this.disablePresenceControls.destroy();
this.disableGoToParticipant.destroy()
this.disablePresenceControls.destroy()
this.disableFollowParticipant.destroy()
this.disablePrivateMode.destroy()
this.disableGatherAll.destroy()
this.disableFollowMe.destroy()
this.participants.destroy()
this.extras.destroy()
this.joinedPresence.destroy()
this.everyoneFollowsMe.destroy()
this.privateMode.destroy()
this.following.destroy()

instance.value = null;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/web-components/comments/components/annotation-pin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export class CommentsAnnotationPin extends WebComponentsBaseElement {
if (this.type !== PinMode.ADD) return;

const { localParticipant } = this.useStore(StoreType.GLOBAL);
localParticipant.subscribe((participant: Participant) => {
localParticipant.subscribe((participant) => {
this.localAvatar = participant?.avatar?.imageUrl;
this.localName = participant?.name;
});
Expand Down
Loading
Loading