stream-chat
Version:
JS SDK for the Stream Chat API
169 lines (142 loc) • 4.96 kB
text/typescript
import type { StreamChat } from './client';
import type {
CreatePollData,
LocalMessage,
MessageResponse,
PollResponse,
PollSort,
QueryPollsFilters,
QueryPollsOptions,
} from './types';
import { Poll } from './poll';
import { formatMessage } from './utils';
import { WithSubscriptions } from './utils/WithSubscriptions';
export class PollManager extends WithSubscriptions {
private client: StreamChat;
// The pollCache contains only polls that have been created and sent as messages
// (i.e only polls that are coupled with a message, can be voted on and require a
// reactive state). It shall work as a basic look-up table for our SDK to be able
// to quickly consume poll state that will be reactive even without the polls being
// rendered within the UI.
private pollCache = new Map<string, Poll>();
constructor({ client }: { client: StreamChat }) {
super();
this.client = client;
}
get data(): Map<string, Poll> {
return this.pollCache;
}
public fromState = (id: string) => this.pollCache.get(id);
public registerSubscriptions = () => {
if (this.hasSubscriptions) {
// Already listening for events and changes
return;
}
this.addUnsubscribeFunction(this.subscribeMessageNew());
this.addUnsubscribeFunction(this.subscribePollUpdated());
this.addUnsubscribeFunction(this.subscribePollClosed());
this.addUnsubscribeFunction(this.subscribeVoteCasted());
this.addUnsubscribeFunction(this.subscribeVoteChanged());
this.addUnsubscribeFunction(this.subscribeVoteRemoved());
};
public createPoll = async (poll: CreatePollData) => {
const { poll: createdPoll } = await this.client.createPoll(poll);
if (!createdPoll.vote_counts_by_option) {
createdPoll.vote_counts_by_option = {};
}
this.setOrOverwriteInCache(createdPoll);
return this.fromState(createdPoll.id);
};
public getPoll = async (id: string) => {
const cachedPoll = this.fromState(id);
// optimistically return the cached poll if it exists and update in the background
if (cachedPoll) {
this.client.getPoll(id).then(({ poll }) => this.setOrOverwriteInCache(poll, true));
return cachedPoll;
}
// fetch it, write to the cache and return otherwise
const { poll } = await this.client.getPoll(id);
this.setOrOverwriteInCache(poll);
return this.fromState(id);
};
public queryPolls = async (
filter: QueryPollsFilters,
sort: PollSort = [],
options: QueryPollsOptions = {},
) => {
const { polls, next } = await this.client.queryPolls(filter, sort, options);
const pollInstances = polls.map((poll) => {
this.setOrOverwriteInCache(poll, true);
return this.fromState(poll.id);
});
return {
polls: pollInstances,
next,
};
};
public hydratePollCache = (
messages: LocalMessage[] | MessageResponse[],
overwriteState?: boolean,
) => {
for (const message of messages) {
if (!message.poll) {
continue;
}
const pollResponse = message.poll as PollResponse;
this.setOrOverwriteInCache(pollResponse, overwriteState);
}
};
private setOrOverwriteInCache = (
pollResponse: PollResponse,
overwriteState?: boolean,
) => {
if (!this.client._cacheEnabled()) {
return;
}
const pollFromCache = this.fromState(pollResponse.id);
if (!pollFromCache) {
const poll = new Poll({ client: this.client, poll: pollResponse });
this.pollCache.set(poll.id, poll);
} else if (overwriteState) {
pollFromCache.reinitializeState(pollResponse);
}
};
private subscribePollUpdated = () =>
this.client.on('poll.updated', (event) => {
if (event.poll?.id) {
this.fromState(event.poll.id)?.handlePollUpdated(event);
}
}).unsubscribe;
private subscribePollClosed = () =>
this.client.on('poll.closed', (event) => {
if (event.poll?.id) {
this.fromState(event.poll.id)?.handlePollClosed(event);
}
}).unsubscribe;
private subscribeVoteCasted = () =>
this.client.on('poll.vote_casted', (event) => {
if (event.poll?.id) {
this.fromState(event.poll.id)?.handleVoteCasted(event);
}
}).unsubscribe;
private subscribeVoteChanged = () =>
this.client.on('poll.vote_changed', (event) => {
if (event.poll?.id) {
this.fromState(event.poll.id)?.handleVoteChanged(event);
}
}).unsubscribe;
private subscribeVoteRemoved = () =>
this.client.on('poll.vote_removed', (event) => {
if (event.poll?.id) {
this.fromState(event.poll.id)?.handleVoteRemoved(event);
}
}).unsubscribe;
private subscribeMessageNew = () =>
this.client.on('message.new', (event) => {
const { message } = event;
if (message) {
const formattedMessage = formatMessage(message);
this.hydratePollCache([formattedMessage]);
}
}).unsubscribe;
}