Fix UI issues.
This commit is contained in:
parent
ac299d8c06
commit
db820efc3a
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package im.vector.app.core.ui.list
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.view.Gravity
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
@ -47,6 +49,12 @@ abstract class GenericButtonItem : VectorEpoxyModel<GenericButtonItem.Holder>()
|
||||
@DrawableRes
|
||||
var iconRes: Int? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var gravity: Int = Gravity.CENTER
|
||||
|
||||
@EpoxyAttribute
|
||||
var bold: Boolean = false
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.button.text = text
|
||||
@ -58,6 +66,10 @@ abstract class GenericButtonItem : VectorEpoxyModel<GenericButtonItem.Holder>()
|
||||
holder.button.icon = null
|
||||
}
|
||||
|
||||
holder.button.gravity = gravity or Gravity.CENTER_VERTICAL
|
||||
val textStyle = if (bold) Typeface.BOLD else Typeface.NORMAL
|
||||
holder.button.setTypeface(null, textStyle)
|
||||
|
||||
holder.button.onClick(buttonClickAction)
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package im.vector.app.features.createpoll
|
||||
|
||||
import android.view.Gravity
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
@ -85,6 +86,8 @@ class CreatePollController @Inject constructor(
|
||||
id("add_option")
|
||||
text(host.stringProvider.getString(R.string.create_poll_add_option))
|
||||
textColor(host.colorProvider.getColor(R.color.palette_element_green))
|
||||
gravity(Gravity.START)
|
||||
bold(true)
|
||||
buttonClickAction {
|
||||
host.callback?.onAddOption()
|
||||
}
|
||||
|
@ -21,9 +21,11 @@ import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.mvrx.activityViewModel
|
||||
import com.airbnb.mvrx.args
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.configureWith
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentCreatePollBinding
|
||||
@ -41,7 +43,6 @@ class CreatePollFragment @Inject constructor(
|
||||
) : VectorBaseFragment<FragmentCreatePollBinding>(), CreatePollController.Callback {
|
||||
|
||||
private val viewModel: CreatePollViewModel by activityViewModel()
|
||||
private val createPollArgs: CreatePollArgs by args()
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentCreatePollBinding {
|
||||
return FragmentCreatePollBinding.inflate(inflater, container, false)
|
||||
@ -61,6 +62,18 @@ class CreatePollFragment @Inject constructor(
|
||||
views.createPollButton.debouncedClicks {
|
||||
viewModel.handle(CreatePollAction.OnCreatePoll)
|
||||
}
|
||||
|
||||
viewModel.subscribe(this) {
|
||||
views.createPollButton.isEnabled = it.canCreatePoll
|
||||
}
|
||||
|
||||
viewModel.observeViewEvents {
|
||||
when (it) {
|
||||
CreatePollViewEvents.Success -> handleSuccess()
|
||||
CreatePollViewEvents.EmptyQuestionError -> handleEmptyQuestionError()
|
||||
is CreatePollViewEvents.NotEnoughOptionsError -> handleNotEnoughOptionsError(it.requiredOptionsCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) {
|
||||
@ -82,4 +95,33 @@ class CreatePollFragment @Inject constructor(
|
||||
override fun onAddOption() {
|
||||
viewModel.handle(CreatePollAction.OnAddOption)
|
||||
}
|
||||
|
||||
private fun handleSuccess() {
|
||||
requireActivity().finish()
|
||||
}
|
||||
|
||||
private fun handleEmptyQuestionError() {
|
||||
renderToast(getString(R.string.create_poll_empty_question_error))
|
||||
}
|
||||
|
||||
private fun handleNotEnoughOptionsError(requiredOptionsCount: Int) {
|
||||
renderToast(
|
||||
resources.getQuantityString(
|
||||
R.plurals.create_poll_not_enough_options_error,
|
||||
requiredOptionsCount,
|
||||
requiredOptionsCount
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun renderToast(message: String) {
|
||||
views.createPollToast.removeCallbacks(hideToastRunnable)
|
||||
views.createPollToast.text = message
|
||||
views.createPollToast.isVisible = true
|
||||
views.createPollToast.postDelayed(hideToastRunnable, 2_000)
|
||||
}
|
||||
|
||||
private val hideToastRunnable = Runnable {
|
||||
views.createPollToast.isVisible = false
|
||||
}
|
||||
}
|
||||
|
@ -18,4 +18,8 @@ package im.vector.app.features.createpoll
|
||||
|
||||
import im.vector.app.core.platform.VectorViewEvents
|
||||
|
||||
sealed class CreatePollViewEvents : VectorViewEvents
|
||||
sealed class CreatePollViewEvents : VectorViewEvents {
|
||||
object Success : CreatePollViewEvents()
|
||||
object EmptyQuestionError : CreatePollViewEvents()
|
||||
data class NotEnoughOptionsError(val requiredOptionsCount: Int) : CreatePollViewEvents()
|
||||
}
|
||||
|
@ -25,11 +25,10 @@ import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import timber.log.Timber
|
||||
|
||||
class CreatePollViewModel @AssistedInject constructor(
|
||||
@Assisted private val initialState: CreatePollViewState,
|
||||
private val session: Session
|
||||
session: Session
|
||||
) : VectorViewModel<CreatePollViewState, CreatePollAction, CreatePollViewEvents>(initialState) {
|
||||
|
||||
private val room = session.getRoom(initialState.roomId)!!
|
||||
@ -41,6 +40,8 @@ class CreatePollViewModel @AssistedInject constructor(
|
||||
|
||||
companion object : MavericksViewModelFactory<CreatePollViewModel, CreatePollViewState> {
|
||||
|
||||
private const val REQUIRED_MIN_OPTION_COUNT = 2
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: CreatePollViewState): CreatePollViewModel {
|
||||
val factory = when (viewModelContext) {
|
||||
@ -52,11 +53,11 @@ class CreatePollViewModel @AssistedInject constructor(
|
||||
}
|
||||
|
||||
init {
|
||||
// Initialize with 2 default empty options
|
||||
// Initialize with REQUIRED_MIN_OPTION_COUNT default empty options
|
||||
setState {
|
||||
copy(
|
||||
question = "",
|
||||
options = listOf("", "")
|
||||
options = List(REQUIRED_MIN_OPTION_COUNT) { "" }
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -72,14 +73,27 @@ class CreatePollViewModel @AssistedInject constructor(
|
||||
}
|
||||
|
||||
private fun handleOnCreatePoll() = withState { state ->
|
||||
|
||||
val nonEmptyOptions = state.options.filter { it.isNotEmpty() }
|
||||
when {
|
||||
state.question.isEmpty() -> {
|
||||
_viewEvents.post(CreatePollViewEvents.EmptyQuestionError)
|
||||
}
|
||||
nonEmptyOptions.size < REQUIRED_MIN_OPTION_COUNT -> {
|
||||
_viewEvents.post(CreatePollViewEvents.NotEnoughOptionsError(requiredOptionsCount = REQUIRED_MIN_OPTION_COUNT))
|
||||
}
|
||||
else -> {
|
||||
room.sendPoll(state.question, state.options)
|
||||
_viewEvents.post(CreatePollViewEvents.Success)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleOnAddOption() {
|
||||
setState {
|
||||
val extendedOptions = options + ""
|
||||
copy(
|
||||
options = extendedOptions
|
||||
options = extendedOptions,
|
||||
canCreatePoll = canCreatePoll(this.copy(options = extendedOptions))
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -88,7 +102,8 @@ class CreatePollViewModel @AssistedInject constructor(
|
||||
setState {
|
||||
val filteredOptions = options.filterIndexed { ind, _ -> ind != index }
|
||||
copy(
|
||||
options = filteredOptions
|
||||
options = filteredOptions,
|
||||
canCreatePoll = canCreatePoll(this.copy(options = filteredOptions))
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -97,7 +112,8 @@ class CreatePollViewModel @AssistedInject constructor(
|
||||
setState {
|
||||
val changedOptions = options.mapIndexed { ind, s -> if (ind == index) option else s }
|
||||
copy(
|
||||
options = changedOptions
|
||||
options = changedOptions,
|
||||
canCreatePoll = canCreatePoll(this.copy(options = changedOptions))
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -105,8 +121,14 @@ class CreatePollViewModel @AssistedInject constructor(
|
||||
private fun handleOnQuestionChanged(question: String) {
|
||||
setState {
|
||||
copy(
|
||||
question = question
|
||||
question = question,
|
||||
canCreatePoll = canCreatePoll(this.copy(question = question))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun canCreatePoll(state: CreatePollViewState): Boolean {
|
||||
return state.question.isNotEmpty() &&
|
||||
state.options.filter { it.isNotEmpty() }.size >= REQUIRED_MIN_OPTION_COUNT
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,8 @@ import com.airbnb.mvrx.MavericksState
|
||||
data class CreatePollViewState(
|
||||
val roomId: String,
|
||||
val question: String = "",
|
||||
val options: List<String> = emptyList()
|
||||
val options: List<String> = emptyList(),
|
||||
val canCreatePoll: Boolean = false
|
||||
) : MavericksState {
|
||||
|
||||
constructor(args: CreatePollArgs) : this(
|
||||
|
18
vector/src/main/res/drawable/ic_delete_10dp.xml
Normal file
18
vector/src/main/res/drawable/ic_delete_10dp.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="10dp"
|
||||
android:height="10dp"
|
||||
android:viewportWidth="10"
|
||||
android:viewportHeight="10">
|
||||
<path
|
||||
android:pathData="M0.9998,0.9997L8.9998,8.9997"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#737D8C"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M9.0005,0.9997L1.0005,8.9997"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#737D8C"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
@ -78,4 +78,18 @@
|
||||
android:text="@string/create_poll_button"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/createPollToast"
|
||||
style="@style/Widget.Vector.TextView.Caption.Toast"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginBottom="84dp"
|
||||
android:visibility="gone"
|
||||
android:accessibilityLiveRegion="polite"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="@string/voice_message_release_to_send_toast"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -28,13 +28,13 @@
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/formTextInputDeleteButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||
android:background="@drawable/circle"
|
||||
android:contentDescription="@string/delete"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_delete"
|
||||
android:src="@drawable/ic_delete_10dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/formTextInputTextInputLayout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/formTextInputTextInputLayout"
|
||||
|
@ -3634,4 +3634,9 @@
|
||||
<string name="create_poll_options_hint">Option %1$d</string>
|
||||
<string name="create_poll_add_option">ADD OPTION</string>
|
||||
<string name="create_poll_button">CREATE POLL</string>
|
||||
<string name="create_poll_empty_question_error">Question cannot be empty</string>
|
||||
<plurals name="create_poll_not_enough_options_error">
|
||||
<item quantity="one">At least %1$s option is required</item>
|
||||
<item quantity="other">At least %1$s options are required</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user