@100mslive/react-native-room-kit
Version:
100ms Room Kit provides simple & easy to use UI components to build Live Streaming & Video Conferencing experiences in your apps.
457 lines (442 loc) • 13.7 kB
text/typescript
import { HMSPollQuestionType, HMSPollType } from '@100mslive/react-native-hms';
import type {
HMSPoll,
PollLeaderboardResponse,
} from '@100mslive/react-native-hms';
import {
PollsStateActionTypes,
CreatePollStages,
HmsStateActionTypes,
} from '../actionTypes';
import type {
PollConfig,
PollQuestionUI,
PollsActionType,
} from '../actionTypes';
function getDefaultQuestionObj(): PollQuestionUI {
return {
title: '',
responseEditable: false,
saved: false,
skippable: false,
pointWeightage: '10',
type: HMSPollQuestionType.singleChoice,
options: [
[false, ''],
[false, ''],
],
};
}
type IntialStateType = {
pollName: string;
pollConfig: PollConfig;
navigationStack: CreatePollStages[];
questions: PollQuestionUI[];
deleteConfirmationVisible: boolean;
selectedPollQuestionIndex: number | null;
launchingPoll: boolean;
selectedPollId: string | null;
cuedPollIds: HMSPoll['pollId'][]; // In case of HLSViewer, pollIds should be aligned with onCue event
polls: Record<string, HMSPoll>;
pollsResponses: Record<string, Record<number, number | number[]>>;
leaderboards: Record<string, PollLeaderboardResponse>;
};
const INITIAL_STATE: IntialStateType = {
pollName: '',
pollConfig: {
type: HMSPollType.poll,
voteCountHidden: false,
resultsAnonymous: false,
},
navigationStack: [CreatePollStages.POLL_CONFIG],
questions: [getDefaultQuestionObj()],
deleteConfirmationVisible: false,
selectedPollQuestionIndex: null,
launchingPoll: false,
selectedPollId: null,
cuedPollIds: [],
polls: {},
pollsResponses: {},
leaderboards: {},
};
const hmsStatesReducer = (
state = INITIAL_STATE,
action: PollsActionType
): IntialStateType => {
switch (action.type) {
case PollsStateActionTypes.SET_DELETE_CONFIRMATION_VISIBLE:
return {
...state,
deleteConfirmationVisible: action.deleteConfirmationVisible,
};
case PollsStateActionTypes.SET_POLL_NAME:
return {
...state,
pollName: action.pollName,
};
case PollsStateActionTypes.SET_POLL_CONFIG:
return {
...state,
pollConfig: {
...state.pollConfig,
...action.pollConfig,
},
questions:
'type' in action.pollConfig
? state.questions.map((ques) => ({ ...ques, saved: false }))
: state.questions,
selectedPollQuestionIndex: null,
};
case PollsStateActionTypes.PUSH_TO_NAVIGATION_STACK:
return {
...state,
navigationStack: [...state.navigationStack, action.screen],
};
case PollsStateActionTypes.RESET_NAVIGATION_STACK:
return {
...state,
navigationStack: INITIAL_STATE.navigationStack,
};
case PollsStateActionTypes.POP_FROM_NAVIGATION_STACK: {
const updatedNavigationStack = [...state.navigationStack];
updatedNavigationStack.pop();
return {
...state,
navigationStack: updatedNavigationStack,
};
}
case PollsStateActionTypes.REPLACE_TOP_OF_NAVIGATION_STACK:
const updatedNavigationStack = [...state.navigationStack];
updatedNavigationStack[updatedNavigationStack.length - 1] = action.screen;
return {
...state,
navigationStack: updatedNavigationStack,
};
case PollsStateActionTypes.ADD_POLL_QUESTION:
return {
...state,
questions: [...state.questions, getDefaultQuestionObj()],
selectedPollQuestionIndex: null,
};
case PollsStateActionTypes.DELETE_POLL_QUESTION:
let updatedQuestions = state.questions;
if (state.selectedPollQuestionIndex === null) {
return state;
}
if (updatedQuestions.length > state.selectedPollQuestionIndex) {
updatedQuestions = updatedQuestions.filter(
(_, idx) => idx !== state.selectedPollQuestionIndex
);
}
if (updatedQuestions === state.questions) {
return state;
}
return {
...state,
questions: updatedQuestions,
selectedPollQuestionIndex: null,
};
case PollsStateActionTypes.SET_SELECTED_QUESTION_INDEX:
return {
...state,
selectedPollQuestionIndex: action.index,
};
case PollsStateActionTypes.SET_QUESTION_TYPE:
return {
...state,
questions: state.questions.map((question, idx) =>
idx === action.questionIndex
? {
...question,
options:
action.questionType === HMSPollQuestionType.shortAnswer ||
action.questionType === HMSPollQuestionType.longAnswer
? undefined
: question.options,
type: action.questionType,
}
: question
),
};
case PollsStateActionTypes.SET_QUESTION_TITLE:
return {
...state,
questions: state.questions.map((question, idx) =>
idx === action.questionIndex
? {
...question,
title: action.title,
}
: question
),
};
case PollsStateActionTypes.SET_POINT_WEIGHTAGE:
return {
...state,
questions: state.questions.map((question, idx) =>
idx === action.questionIndex
? {
...question,
pointWeightage: action.pointWeightage,
}
: question
),
};
case PollsStateActionTypes.ADD_QUESTION_OPTION:
return {
...state,
questions: state.questions.map((question, idx) =>
idx === action.questionIndex
? {
...question,
options: [...(question.options || []), [false, '']],
}
: question
),
};
case PollsStateActionTypes.DELETE_QUESTION_OPTION:
return {
...state,
questions: state.questions.map((question, idx) =>
idx === action.questionIndex
? {
...question,
options:
question.options &&
question.options.filter((_, idx) => idx !== action.index),
}
: question
),
};
case PollsStateActionTypes.EDIT_QUESTION_OPTION:
return {
...state,
questions: state.questions.map((question, idx) =>
idx === action.questionIndex
? {
...question,
options:
question.options &&
question.options.map((option, idx) =>
idx === action.optionIndex
? [option[0], action.option]
: option
),
}
: question
),
};
case PollsStateActionTypes.SET_QUESTION_CORRECT_OPTION:
return {
...state,
questions: state.questions.map((question, idx) =>
idx === action.questionIndex
? {
...question,
options:
question.options &&
question.options.map((option, idx) => {
if (
action.correctOption === false ||
question.type === HMSPollQuestionType.multipleChoice
) {
return idx === action.optionIndex
? [action.correctOption, option[1]]
: option;
}
return [
idx === action.optionIndex ? true : false,
option[1],
];
}),
}
: question
),
};
case PollsStateActionTypes.SET_QUESTION_SKIPPABLE:
return {
...state,
questions: state.questions.map((question, idx) =>
idx === action.questionIndex
? {
...question,
skippable: action.skippable,
}
: question
),
};
case PollsStateActionTypes.SET_QUESTION_RES_EDITABLE:
return {
...state,
questions: state.questions.map((question, idx) =>
idx === action.questionIndex
? {
...question,
responseEditable: action.responseEditable,
}
: question
),
};
case PollsStateActionTypes.SET_QUESTION_SAVED:
return {
...state,
questions: state.questions.map((question, idx) =>
idx === action.questionIndex
? {
...question,
saved: action.saved,
}
: question
),
};
case PollsStateActionTypes.SET_LAUNCHING_POLL:
return {
...state,
launchingPoll: action.launching,
questions: action.launching
? state.questions.map((question) => ({ ...question, saved: true }))
: state.questions,
};
case PollsStateActionTypes.SET_SELECTED_POLL_ID:
return {
...state,
selectedPollId: action.pollId,
};
case PollsStateActionTypes.ADD_POLL:
const prevPoll = state.polls[action.poll.pollId];
// Hack: Restore previous state of poll if current poll has missing myResponses and voteCount
if (
prevPoll &&
Array.isArray(prevPoll.questions) &&
prevPoll.questions.length > 0
) {
action.poll.questions?.forEach((question) => {
const prevQuestion = prevPoll.questions?.find(
(prevQuestion) => prevQuestion.index === question.index
);
//#region Restore previous responses on question if current question has no responses
const prevResponsesOnQuestion = prevQuestion?.myResponses;
if (
Array.isArray(prevResponsesOnQuestion) &&
prevResponsesOnQuestion.length > 0 &&
(!question.myResponses || question.myResponses.length <= 0)
) {
question.myResponses = prevResponsesOnQuestion;
}
//#endregion
//#region Restore previous voteCount on question options if current question options has no voteCount
const prevOptions = prevQuestion?.options;
question.options?.forEach((option) => {
const prevOption = prevOptions?.find(
(prevOption) => prevOption.index === option.index
);
// Edge Case: User changes response on question, due to which new vountCount becomes 0, and we are treating as invalid value
if (
option.voteCount <= 0 &&
prevOption &&
prevOption?.voteCount > 0
) {
option.voteCount = prevOption.voteCount;
}
});
//#endregion
});
}
return {
...state,
polls: {
...state.polls,
[action.poll.pollId]: action.poll,
},
};
case PollsStateActionTypes.UPDATE_POLL:
return {
...state,
polls: {
...state.polls,
[action.poll.pollId]: {
...state.polls[action.poll.pollId],
...action.poll,
},
},
};
case PollsStateActionTypes.SET_POLL_QUESTION_RESPONSE:
return {
...state,
pollsResponses: {
...state.pollsResponses,
[action.pollId]: {
...state.pollsResponses[action.pollId],
[action.questionIndex]: action.response,
},
},
};
case PollsStateActionTypes.ADD_POLL_QUESTION_RESPONSE:
const prevResponses =
state.pollsResponses[action.pollId]?.[action.questionIndex];
const newResponses = prevResponses
? Array.isArray(prevResponses)
? [...prevResponses, action.response]
: [prevResponses, action.response]
: [action.response];
return {
...state,
pollsResponses: {
...state.pollsResponses,
[action.pollId]: {
...state.pollsResponses[action.pollId],
[action.questionIndex]: newResponses,
},
},
};
case PollsStateActionTypes.REMOVE_POLL_QUESTION_RESPONSE: {
const prevResponses =
state.pollsResponses[action.pollId]?.[action.questionIndex];
const newResponses = prevResponses
? Array.isArray(prevResponses)
? prevResponses.filter((res) => res !== action.response)
: prevResponses === action.response
? []
: action.response
: [];
return {
...state,
pollsResponses: {
...state.pollsResponses,
[action.pollId]: {
...state.pollsResponses[action.pollId],
[action.questionIndex]: newResponses,
},
},
};
}
case PollsStateActionTypes.ADD_CUED_POLL_ID: {
return {
...state,
cuedPollIds: [...state.cuedPollIds, action.pollId],
};
}
case PollsStateActionTypes.ADD_LEADERBOARD: {
return {
...state,
leaderboards: {
...state.leaderboards,
[action.pollId]: action.leaderboard,
},
};
}
case PollsStateActionTypes.CLEAR_POLL_FORM_STATE: {
return {
...INITIAL_STATE,
polls: state.polls,
selectedPollId: state.selectedPollId,
};
}
case PollsStateActionTypes.CLEAR_POLLS_STATE:
case HmsStateActionTypes.CLEAR_STATES:
return INITIAL_STATE;
default:
return state;
}
};
export default hmsStatesReducer;