Skip to content

Instantly share code, notes, and snippets.

@Jalson1982
Last active February 2, 2023 14:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jalson1982/83f482230ef1c68ae7a775f5459aef3c to your computer and use it in GitHub Desktop.
Save Jalson1982/83f482230ef1c68ae7a775f5459aef3c to your computer and use it in GitHub Desktop.
@HiltViewModel
internal class HealthStudyQuestionsViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val getSurvey: GetSurvey,
private val updateQuestion: UpdateQuestion,
) : ViewModel() {
private val survey = savedStateHandle.get<String>("survey")
private val _state = MutableStateFlow(HealthStudyViewState())
val state = _state.asStateFlow()
init {
val parsedSurvey = survey?.let { parser.jsonToObject<SurveyResponse>(it) }
survey?.let {
parsedSurvey?.survey?.let { it1 -> setSurveyData(it1, parsedSurvey.backgroundImageUrl) }
} ?: run {
viewModelScope.launch {
getSurvey(GetSurvey.Params(parsedSurvey?.surveyId)).collectLatest { result ->
result?.let { setSurveyData(it) }
}
}
}
}
private fun setSurveyData(survey: Survey, backgroundImageUrl: URL? = null) {
survey.questions?.let { populateAnswersData(it) }
_state.value = _state.value.copy(
survey = Survey(
bookmark = survey.bookmark,
completedData = survey.completedData,
completedMessage = survey.completedMessage,
id = survey.id,
questions = survey.questions,
title = survey.title,
backgroundImageUrl = backgroundImageUrl
),
currentIndex = survey.bookmark ?: 0
)
}
fun singleSelectionQuestionSelected(id: String, questionId: String) {
val questionId = state.value.survey?.questions?.get(state.value.currentIndex)?.id ?: ""
val updatedAnswerData = _state.value.answerData.toMutableMap().apply {
put(questionId, AnswerData.SingleSelectionInputValue(id = id))
}
_state.value = _state.value.copy(answerData = updatedAnswerData)
}
fun multiSelectionQuestionSelected(id: String) {
val currentQuestionId =
state.value.survey?.questions?.get(state.value.currentIndex)?.id ?: ""
val updatedAnswerData = state.value.answerData.toMutableMap().apply {
when (val currentAnswerData = get(currentQuestionId)) {
is AnswerData.MultiSelectionInputValue -> {
val updatedIds = currentAnswerData.ids.toMutableList().apply {
if (contains(id)) {
remove(id)
} else {
add(id)
}
}
put(currentQuestionId, AnswerData.MultiSelectionInputValue(ids = updatedIds))
}
else -> {
put(currentQuestionId, AnswerData.MultiSelectionInputValue(ids = listOf(id)))
}
}
}
_state.value = _state.value.copy(answerData = updatedAnswerData)
}
fun updateFreeTextQuestion(text: String) {
val questionId = state.value.survey?.questions?.get(state.value.currentIndex)?.id ?: ""
val updatedAnswerData = _state.value.answerData.toMutableMap().apply {
put(questionId, AnswerData.FreeTextInputValue(value = text))
}
_state.value = _state.value.copy(answerData = updatedAnswerData)
}
fun updateLikertScaleQuestion(id: String) {
val questionId = state.value.survey?.questions?.get(state.value.currentIndex)?.id ?: ""
val updatedAnswerData = _state.value.answerData.toMutableMap().apply {
put(questionId, AnswerData.LikertScaleQuestion(id = id))
}
_state.value = _state.value.copy(answerData = updatedAnswerData)
}
fun updateMonthYearQuestion(month: Int, year: Int, skipped: Boolean = false) {
val questionId = state.value.survey?.questions?.get(state.value.currentIndex)?.id ?: ""
val updatedAnswerData = _state.value.answerData.toMutableMap().apply {
put(
questionId,
AnswerData.MonthYearQuestion(month = month, year = year, skipped = skipped)
)
}
_state.value = _state.value.copy(answerData = updatedAnswerData)
}
fun updateNumericQuestion(value: String, skipped: Boolean = false) {
val questionId = state.value.survey?.questions?.get(state.value.currentIndex)?.id ?: ""
val updatedAnswerData = _state.value.answerData.toMutableMap().apply {
put(questionId, AnswerData.NumericQuestionInputValue(value = value, skipped = skipped))
}
_state.value = _state.value.copy(answerData = updatedAnswerData)
}
fun updateContactFormQuestion(
email: String = "",
phoneNumber: String = "",
) {
val questionId = state.value.survey?.questions?.get(state.value.currentIndex)?.id ?: ""
val updatedAnswerData = _state.value.answerData.toMutableMap().apply {
put(
questionId,
AnswerData.ContactFormQuestion(email = email, phoneNumber = phoneNumber)
)
}
_state.value = _state.value.copy(answerData = updatedAnswerData)
}
fun updateEmojiQuestion(id: String, skipped: Boolean = false) {
val questionId = state.value.survey?.questions?.get(state.value.currentIndex)?.id ?: ""
val updatedAnswerData = _state.value.answerData.toMutableMap().apply {
put(
questionId,
AnswerData.EmojiQuestion(value = EmojiQuestionInput(id = id, skipped = skipped))
)
}
_state.value = _state.value.copy(answerData = updatedAnswerData)
}
fun updateQuestionHandler() {
viewModelScope.launch {
val currentQuestion =
_state.value.survey?.questions?.get(state.value.currentIndex) ?: return@launch
val currentAnswer = _state.value.answerData[currentQuestion.id] ?: return@launch
goNext()
val params = UpdateQuestion.Params(
id = _state.value.survey?.id ?: "",
questionId = currentQuestion.id,
body = when (currentQuestion.rawType) {
"single-selection" -> {
val answer = currentAnswer as AnswerData.SingleSelectionInputValue
AnswerBody(
value = SingleSelectionInputValue(id = answer.id),
type = currentQuestion.rawType,
lastQuestion = false
)
}
"multiple-selection" -> {
val answer = currentAnswer as AnswerData.MultiSelectionInputValue
AnswerBody(
value = MultiSelectionInputValue(ids = answer.ids),
type = currentQuestion.rawType,
lastQuestion = false
)
}
"free-text" -> {
val answer = currentAnswer as AnswerData.FreeTextInputValue
AnswerBody(
value = FreeTextInputValue(value = answer.value),
type = currentQuestion.rawType,
lastQuestion = false
)
}
"likert-scale" -> {
val answer = currentAnswer as AnswerData.LikertScaleQuestion
AnswerBody(
value = LikertScaleQuestion(id = answer.id),
type = currentQuestion.rawType,
lastQuestion = false
)
}
"month-and-year-selection" -> {
val answer = currentAnswer as AnswerData.MonthYearQuestion
AnswerBody(
value = MonthYearQuestion(
month = answer.month,
year = answer.year,
skipped = answer.skipped
),
type = currentQuestion.rawType,
lastQuestion = false
)
}
"emoji" -> {
val answer = currentAnswer as AnswerData.EmojiQuestion
AnswerBody(
value = EmojiQuestionInput(
id = answer.value.id,
skipped = answer.skipped
),
type = currentQuestion.rawType,
lastQuestion = false
)
}
"contact-form" -> {
val answer = currentAnswer as AnswerData.ContactFormQuestion
AnswerBody(
value = ContactFormQuestion(
email = answer.email,
phoneNumber = answer.phoneNumber
),
type = currentQuestion.rawType,
lastQuestion = false
)
}
else -> {
val answer = currentAnswer as AnswerData.NumericQuestionInputValue
AnswerBody(
value = NumericQuestionInputValue(value = answer.value),
type = currentQuestion.rawType,
lastQuestion = false
)
}
}
)
updateQuestion(params).collect()
}
}
private fun populateAnswersData(questions: List<Question>) {
questions.forEach { question ->
val answerData = _state.value.answerData
when (question.rawType) {
"single-selection" -> {
answerData[question.id] =
AnswerData.SingleSelectionInputValue(id = question.selected)
}
"multiple-selection" -> {
val selectedAnswers = question.answers
?.filter { it.selected == true }
?.map { it.id }
answerData[question.id] =
selectedAnswers?.let { AnswerData.MultiSelectionInputValue(ids = it) }!!
}
"free-text" -> {
val value = question.value.toString()
answerData[question.id] = AnswerData.FreeTextInputValue(value = value)
}
"likert-scale" -> {
val selected = question.selected ?: ""
answerData[question.id] = AnswerData.LikertScaleQuestion(id = selected)
}
"month-and-year-selection" -> {
val parsedData = question.value as? Map<String, Double> ?: mapOf()
val month = parsedData["month"]?.toInt() ?: 0
val year = parsedData["year"]?.toInt() ?: 0
answerData[question.id] = AnswerData.MonthYearQuestion(month = month, year = year)
}
"numeric" -> {
answerData[question.id] = AnswerData.NumericQuestionInputValue(value = "")
}
"contact-form" -> {
val parsedData = question.value as? Map<String, String> ?: mapOf()
val email = parsedData["email"].toString()
val phoneNumber = parsedData["phoneNumber"].toString()
answerData[question.id] =
AnswerData.ContactFormQuestion(email = email, phoneNumber = phoneNumber)
}
"emoji" -> {
val parsedData = question.value as? Map<String, Boolean> ?: mapOf()
val value = parsedData["id"].toString()
val skipped = parsedData["skipped"] ?: false
answerData[question.id] = AnswerData.EmojiQuestion(
value = EmojiQuestionInput(
id = value,
skipped = skipped
)
)
}
}
}
}
fun goBack() {
_state.update { it.copy(currentIndex = _state.value.currentIndex - 1) }
}
private fun goNext() {
_state.update { it.copy(currentIndex = _state.value.currentIndex + 1) }
}
fun isNextButtonEnabled(): Boolean {
val question = _state.value.survey?.questions?.get(_state.value.currentIndex)
return when (val answer = _state.value.answerData[question?.id]) {
is AnswerData.SingleSelectionInputValue -> answer.id?.isNotEmpty() ?: false
is AnswerData.MultiSelectionInputValue -> answer.ids.isNotEmpty()
is AnswerData.FreeTextInputValue -> answer.value.isNotEmpty()
is AnswerData.LikertScaleQuestion -> answer.id.isNotEmpty()
is AnswerData.MonthYearQuestion -> (answer.month != 0 && answer.year != 0) || answer.skipped
is AnswerData.NumericQuestionInputValue -> answer.value.isNotEmpty() || answer.skipped
is AnswerData.ContactFormQuestion -> answer.email.isNotEmpty() && isValidEmail(answer.email) && answer.phoneNumber.isNotEmpty()
is AnswerData.EmojiQuestion -> answer.value.id.isNotEmpty() || answer.skipped
else -> false
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment