diff --git a/vector/src/main/java/im/vector/app/features/createpoll/CreatePollAction.kt b/vector/src/main/java/im/vector/app/features/createpoll/CreatePollAction.kt index ad8da6e208..0812248487 100644 --- a/vector/src/main/java/im/vector/app/features/createpoll/CreatePollAction.kt +++ b/vector/src/main/java/im/vector/app/features/createpoll/CreatePollAction.kt @@ -19,4 +19,9 @@ package im.vector.app.features.createpoll import im.vector.app.core.platform.VectorViewModelAction sealed class CreatePollAction : VectorViewModelAction { + data class OnQuestionChanged(val question: String) : CreatePollAction() + data class OnOptionChanged(val index: Int, val option: String) : CreatePollAction() + data class OnDeleteOption(val index: Int) : CreatePollAction() + object OnAddOption : CreatePollAction() + object OnCreatePoll : CreatePollAction() } diff --git a/vector/src/main/java/im/vector/app/features/createpoll/CreatePollController.kt b/vector/src/main/java/im/vector/app/features/createpoll/CreatePollController.kt new file mode 100644 index 0000000000..cf4ec19581 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/createpoll/CreatePollController.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.createpoll + +import com.airbnb.epoxy.EpoxyController +import im.vector.app.R +import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.resources.StringProvider +import im.vector.app.core.ui.list.ItemStyle +import im.vector.app.core.ui.list.genericButtonItem +import im.vector.app.core.ui.list.genericItem +import im.vector.app.features.form.formEditTextItem +import im.vector.app.features.form.formEditTextWithDeleteItem +import javax.inject.Inject + +class CreatePollController @Inject constructor( + private val stringProvider: StringProvider, + private val colorProvider: ColorProvider +) : EpoxyController() { + + private var state: CreatePollViewState? = null + var callback: Callback? = null + + fun setData(state: CreatePollViewState) { + this.state = state + requestModelBuild() + } + + override fun buildModels() { + val currentState = state ?: return + val host = this + + genericItem { + id("question_title") + style(ItemStyle.BIG_TEXT) + title(host.stringProvider.getString(R.string.create_poll_question_title)) + } + + formEditTextItem { + id("question") + value(currentState.question) + hint(host.stringProvider.getString(R.string.create_poll_question_hint)) + singleLine(false) + maxLength(500) + onTextChange { + host.callback?.onQuestionChanged(it) + } + } + + genericItem { + id("options_title") + style(ItemStyle.BIG_TEXT) + title(host.stringProvider.getString(R.string.create_poll_options_title)) + } + + currentState.options.forEachIndexed { index, option -> + formEditTextWithDeleteItem { + id("option_$index") + value(option) + hint(host.stringProvider.getString(R.string.create_poll_options_hint, (index + 1))) + onTextChange { + host.callback?.onOptionChanged(index, it) + } + onDeleteClicked { + host.callback?.onDeleteOption(index) + } + } + } + + genericButtonItem { + id("add_option") + text(host.stringProvider.getString(R.string.create_poll_add_option)) + textColor(host.colorProvider.getColor(R.color.palette_element_green)) + buttonClickAction { + host.callback?.onAddOption() + } + } + } + + interface Callback { + fun onQuestionChanged(question: String) + fun onOptionChanged(index: Int, option: String) + fun onDeleteOption(index: Int) + fun onAddOption() + } +} diff --git a/vector/src/main/java/im/vector/app/features/createpoll/CreatePollFragment.kt b/vector/src/main/java/im/vector/app/features/createpoll/CreatePollFragment.kt index 706d58e489..ad366da62e 100644 --- a/vector/src/main/java/im/vector/app/features/createpoll/CreatePollFragment.kt +++ b/vector/src/main/java/im/vector/app/features/createpoll/CreatePollFragment.kt @@ -20,11 +20,18 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import com.airbnb.mvrx.activityViewModel +import com.airbnb.mvrx.withState +import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentCreatePollBinding import javax.inject.Inject -class CreatePollFragment @Inject constructor() : VectorBaseFragment() { +class CreatePollFragment @Inject constructor( + private val controller: CreatePollController +) : VectorBaseFragment(), CreatePollController.Callback { + + private val viewModel: CreatePollViewModel by activityViewModel() override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentCreatePollBinding { return FragmentCreatePollBinding.inflate(inflater, container, false) @@ -34,8 +41,35 @@ class CreatePollFragment @Inject constructor() : VectorBaseFragment handleOnCreatePoll() + CreatePollAction.OnAddOption -> handleOnAddOption() + is CreatePollAction.OnDeleteOption -> handleOnDeleteOption(action.index) + is CreatePollAction.OnOptionChanged -> handleOnOptionChanged(action.index, action.option) + is CreatePollAction.OnQuestionChanged -> handleOnQuestionChanged(action.question) + } + } + + private fun handleOnCreatePoll() = withState { state -> + Timber.d(state.toString()) + } + + private fun handleOnAddOption() { + setState { + val extendedOptions = options + "" + copy( + options = extendedOptions + ) + } + } + + private fun handleOnDeleteOption(index: Int) { + setState { + val filteredOptions = options.filterIndexed { ind, _ -> ind != index } + copy( + options = filteredOptions + ) + } + } + + private fun handleOnOptionChanged(index: Int, option: String) { + setState { + val changedOptions = options.mapIndexed { ind, s -> if(ind == index) option else s } + copy( + options = changedOptions + ) + } + } + + private fun handleOnQuestionChanged(question: String) { + setState { + copy( + question = question + ) + } } } diff --git a/vector/src/main/java/im/vector/app/features/createpoll/CreatePollViewState.kt b/vector/src/main/java/im/vector/app/features/createpoll/CreatePollViewState.kt index f53e7b2843..f9d46d5605 100644 --- a/vector/src/main/java/im/vector/app/features/createpoll/CreatePollViewState.kt +++ b/vector/src/main/java/im/vector/app/features/createpoll/CreatePollViewState.kt @@ -19,5 +19,6 @@ package im.vector.app.features.createpoll import com.airbnb.mvrx.MavericksState data class CreatePollViewState( - val question: String = "" + val question: String = "", + val options: List = emptyList() ) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/form/FormEditTextWithDeleteItem.kt b/vector/src/main/java/im/vector/app/features/form/FormEditTextWithDeleteItem.kt new file mode 100644 index 0000000000..a4e378a5b9 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/form/FormEditTextWithDeleteItem.kt @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.form + +import android.text.Editable +import android.widget.ImageButton +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import com.google.android.material.textfield.TextInputEditText +import com.google.android.material.textfield.TextInputLayout +import im.vector.app.R +import im.vector.app.core.epoxy.ClickListener +import im.vector.app.core.epoxy.TextListener +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.epoxy.addTextChangedListenerOnce +import im.vector.app.core.epoxy.onClick +import im.vector.app.core.extensions.setTextIfDifferent +import im.vector.app.core.platform.SimpleTextWatcher + +@EpoxyModelClass(layout = R.layout.item_form_text_input_with_delete) +abstract class FormEditTextWithDeleteItem : VectorEpoxyModel() { + + @EpoxyAttribute + var hint: String? = null + + @EpoxyAttribute + var value: String? = null + + @EpoxyAttribute + var enabled: Boolean = true + + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) + var onTextChange: TextListener? = null + + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) + var onDeleteClicked: ClickListener? = null + + private val onTextChangeListener = object : SimpleTextWatcher() { + override fun afterTextChanged(s: Editable) { + onTextChange?.invoke(s.toString()) + } + } + + override fun bind(holder: Holder) { + super.bind(holder) + holder.textInputLayout.isEnabled = enabled + holder.textInputLayout.hint = hint + + holder.textInputEditText.setTextIfDifferent(value) + + holder.textInputEditText.isEnabled = enabled + + holder.textInputEditText.addTextChangedListenerOnce(onTextChangeListener) + + holder.textInputDeleteButton.onClick(onDeleteClicked) + } + + override fun shouldSaveViewState(): Boolean { + return false + } + + override fun unbind(holder: Holder) { + super.unbind(holder) + holder.textInputEditText.removeTextChangedListener(onTextChangeListener) + } + + class Holder : VectorEpoxyHolder() { + val textInputLayout by bind(R.id.formTextInputTextInputLayout) + val textInputEditText by bind(R.id.formTextInputTextInputEditText) + val textInputDeleteButton by bind(R.id.formTextInputDeleteButton) + } +} diff --git a/vector/src/main/res/layout/fragment_create_poll.xml b/vector/src/main/res/layout/fragment_create_poll.xml index 76c744c6c5..0616b76150 100644 --- a/vector/src/main/res/layout/fragment_create_poll.xml +++ b/vector/src/main/res/layout/fragment_create_poll.xml @@ -60,4 +60,22 @@ + + +