UNPKG

stream-chat

Version:

JS SDK for the Stream Chat API

170 lines (154 loc) 5.2 kB
import { PollComposerCompositionMiddlewareExecutor, PollComposerStateMiddlewareExecutor, VALID_MAX_VOTES_VALUE_REGEX, } from './middleware/pollComposer'; import { StateStore } from '../store'; import { VotingVisibility } from '../types'; import { generateUUIDv4 } from '../utils'; import type { MessageComposer } from './messageComposer'; import type { PollComposerFieldErrors, PollComposerState, UpdateFieldsData, } from './middleware/pollComposer'; export type PollComposerOptions = { composer: MessageComposer; }; export class PollComposer { readonly state: StateStore<PollComposerState>; readonly composer: MessageComposer; readonly compositionMiddlewareExecutor: PollComposerCompositionMiddlewareExecutor; readonly stateMiddlewareExecutor: PollComposerStateMiddlewareExecutor; constructor({ composer }: PollComposerOptions) { this.composer = composer; this.state = new StateStore<PollComposerState>(this.initialState); this.compositionMiddlewareExecutor = new PollComposerCompositionMiddlewareExecutor({ composer, }); this.stateMiddlewareExecutor = new PollComposerStateMiddlewareExecutor(); } get initialState(): PollComposerState { return { data: { allow_answers: false, allow_user_suggested_options: false, description: '', enforce_unique_vote: true, id: generateUUIDv4(), max_votes_allowed: '', name: '', options: [{ id: generateUUIDv4(), text: '' }], user_id: this.composer.client.user?.id, voting_visibility: VotingVisibility.public, }, errors: {}, }; } get allow_answers() { return this.state.getLatestValue().data.allow_answers; } get allow_user_suggested_options() { return this.state.getLatestValue().data.allow_user_suggested_options; } get description() { return this.state.getLatestValue().data.description; } get enforce_unique_vote() { return this.state.getLatestValue().data.enforce_unique_vote; } get id() { return this.state.getLatestValue().data.id; } get max_votes_allowed() { return this.state.getLatestValue().data.max_votes_allowed; } get name() { return this.state.getLatestValue().data.name; } get options() { return this.state.getLatestValue().data.options; } get user_id() { return this.state.getLatestValue().data.user_id; } get voting_visibility() { return this.state.getLatestValue().data.voting_visibility; } get canCreatePoll() { const { data, errors } = this.state.getLatestValue(); const hasAtLeastOneNonEmptyOption = data.options.filter((o) => !!o.text.trim()).length > 0; const hasName = !!data.name; const maxVotesAllowedNumber = parseInt( data.max_votes_allowed?.match(VALID_MAX_VOTES_VALUE_REGEX)?.[0] || '', ); const validMaxVotesAllowed = data.max_votes_allowed === '' || (!!maxVotesAllowedNumber && (2 <= maxVotesAllowedNumber || maxVotesAllowedNumber <= 10)); return ( hasAtLeastOneNonEmptyOption && hasName && validMaxVotesAllowed && Object.values(errors).filter((errorText) => !!errorText).length === 0 ); } initState = () => { this.state.next(this.initialState); }; /** * Updates specified fields and generates relevant errors * @param data * @param injectedFieldErrors - errors produced externally that will take precedence over the errors generated in the middleware chaing */ // FIXME: change method params to a single object with the next major release updateFields = async ( data: UpdateFieldsData, injectedFieldErrors?: PollComposerFieldErrors, ) => { const { state, status } = await this.stateMiddlewareExecutor.execute({ eventName: 'handleFieldChange', initialValue: { nextState: { ...this.state.getLatestValue() }, previousState: { ...this.state.getLatestValue() }, targetFields: data, injectedFieldErrors, }, }); if (status === 'discard') return; this.state.next(state.nextState); }; handleFieldBlur = async (field: keyof PollComposerState['data']) => { const result = await this.stateMiddlewareExecutor.execute({ eventName: 'handleFieldBlur', initialValue: { nextState: { ...this.state.getLatestValue() }, previousState: { ...this.state.getLatestValue() }, targetFields: { [field]: this.state.getLatestValue().data[field] }, }, }); if (result.status === 'discard') return; this.state.next(result.state.nextState); }; compose = async () => { const { data, errors } = this.state.getLatestValue(); const result = await this.compositionMiddlewareExecutor.execute({ eventName: 'compose', initialValue: { data: { ...data, max_votes_allowed: data.max_votes_allowed ? parseInt(data.max_votes_allowed) : undefined, options: data.options ?.filter((o) => o.text.trim()) .map((o) => ({ text: o.text })), }, errors, }, }); if (result.status === 'discard') return; return result.state; }; }