wip: vote on poll

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2022-06-13 14:53:48 +02:00 committed by Andy Scherzinger (Rebase PR Action)
parent 81666829b5
commit 7d8aebe234
9 changed files with 134 additions and 32 deletions

View file

@ -538,7 +538,8 @@ public interface NcApi {
@POST @POST
Observable<PollOverall> votePoll(@Header("Authorization") String authorization, Observable<PollOverall> votePoll(@Header("Authorization") String authorization,
@Url String url); @Url String url,
@Query("optionIds[]") List<Integer> optionIds);
@DELETE @DELETE
Observable<PollOverall> closePoll(@Header("Authorization") String authorization, Observable<PollOverall> closePoll(@Header("Authorization") String authorization,

View file

@ -14,4 +14,11 @@ data class Poll(
val votedSelf: List<Int>?, val votedSelf: List<Int>?,
val numVoters: Int, val numVoters: Int,
val details: List<PollDetails>? val details: List<PollDetails>?
) ) {
companion object {
const val STATUS_OPEN: Int = 0
const val STATUS_CLOSED: Int = 1
const val RESULT_MODE_PUBLIC: Int = 0
const val RESULT_MODE_HIDDEN: Int = 1
}
}

View file

@ -6,4 +6,6 @@ import io.reactivex.Observable
interface PollRepository { interface PollRepository {
fun getPoll(roomToken: String, pollId: String): Observable<Poll>? fun getPoll(roomToken: String, pollId: String): Observable<Poll>?
fun vote(roomToken: String, pollId: String, option: Int): Observable<Poll>?
} }

View file

@ -33,12 +33,12 @@ import io.reactivex.Observable
class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvider: CurrentUserProvider) : class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvider: CurrentUserProvider) :
PollRepository { PollRepository {
override fun getPoll(roomToken: String, pollId: String): Observable<Poll> { val credentials = ApiUtils.getCredentials(
currentUserProvider.currentUser?.username,
currentUserProvider.currentUser?.token
)
val credentials = ApiUtils.getCredentials( override fun getPoll(roomToken: String, pollId: String): Observable<Poll> {
currentUserProvider.currentUser?.username,
currentUserProvider.currentUser?.token
)
return ncApi.getPoll( return ncApi.getPoll(
credentials, credentials,
@ -69,6 +69,19 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid
// ) // )
} }
override fun vote(roomToken: String, pollId: String, option: Int): Observable<Poll>? {
return ncApi.votePoll(
credentials,
ApiUtils.getUrlForPoll(
currentUserProvider.currentUser?.baseUrl,
roomToken,
pollId
),
arrayOf(option).asList()
).map { mapToPoll(it.ocs?.data!!) }
}
companion object { companion object {
private fun mapToPoll(pollResponse: PollResponse): Poll { private fun mapToPoll(pollResponse: PollResponse): Poll {

View file

@ -60,7 +60,11 @@ class PollMainDialogFragment(
PollViewModel.InitialState -> {} PollViewModel.InitialState -> {}
is PollViewModel.PollClosedState -> TODO() is PollViewModel.PollClosedState -> TODO()
is PollViewModel.PollOpenState -> { is PollViewModel.PollOpenState -> {
val contentFragment = PollVoteFragment(viewModel) val contentFragment = PollVoteFragment(
viewModel,
roomToken,
pollId
)
val transaction = childFragmentManager.beginTransaction() val transaction = childFragmentManager.beginTransaction()
transaction.replace(binding.messagePollContentFragment.id, contentFragment) transaction.replace(binding.messagePollContentFragment.id, contentFragment)
transaction.commit() transaction.commit()

View file

@ -37,7 +37,11 @@ import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
class PollVoteFragment(private val parentViewModel: PollViewModel) : Fragment() { class PollVoteFragment(
private val parentViewModel: PollViewModel,
private val roomToken: String,
private val pollId: String
) : Fragment() {
@Inject @Inject
lateinit var viewModelFactory: ViewModelProvider.Factory lateinit var viewModelFactory: ViewModelProvider.Factory
@ -70,29 +74,43 @@ class PollVoteFragment(private val parentViewModel: PollViewModel) : Fragment()
val poll = state.poll val poll = state.poll
binding.radioGroup.removeAllViews() binding.radioGroup.removeAllViews()
poll.options?.map { option -> poll.options?.map { option ->
RadioButton(context) RadioButton(context).apply { text = option }
.apply { text = option } }?.forEachIndexed { index, radioButton ->
.also { radioButton.id = index
it.setOnClickListener { binding.radioGroup.addView(radioButton)
// todo
Log.d("bb", "click1")
}
}
}?.forEach {
binding.radioGroup.addView(it)
} }
} }
} }
viewModel.viewState.observe(viewLifecycleOwner) { state ->
when (state) {
PollVoteViewModel.InitialState -> {}
is PollVoteViewModel.PollVoteFailedState -> {
Log.d(TAG, "fail")
}
is PollVoteViewModel.PollVoteSuccessState -> {
parentViewModel.voted()
}
}
}
binding.radioGroup.setOnCheckedChangeListener { group, checkedId -> binding.radioGroup.setOnCheckedChangeListener { group, checkedId ->
// todo set selected in viewmodel // todo set selected in viewmodel.
Log.d("bb", "click") Log.d("bb", "click")
} }
// todo observe viewmodel checked, set view checked with it // todo observe viewmodel checked, set view checked with it
// todo listen to button click, submit
binding.submitVote.setOnClickListener {
viewModel.vote(roomToken, pollId, binding.radioGroup.checkedRadioButtonId)
}
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
} }
companion object {
private val TAG = PollVoteFragment::class.java.simpleName
}
} }

View file

@ -31,6 +31,7 @@ class PollViewModel @Inject constructor(private val repository: PollRepository)
sealed interface ViewState sealed interface ViewState
object InitialState : ViewState object InitialState : ViewState
open class PollOpenState(val poll: Poll) : ViewState open class PollOpenState(val poll: Poll) : ViewState
open class PollVotedState(val poll: Poll) : ViewState
open class PollClosedState(val poll: Poll) : ViewState open class PollClosedState(val poll: Poll) : ViewState
private val _viewState: MutableLiveData<ViewState> = MutableLiveData(InitialState) private val _viewState: MutableLiveData<ViewState> = MutableLiveData(InitialState)
@ -46,14 +47,9 @@ class PollViewModel @Inject constructor(private val repository: PollRepository)
loadPoll() loadPoll()
} }
// private fun loadPoll() { fun voted() {
// disposable = repository.getPoll(roomToken, pollId) loadPoll() // TODO load other view
// ?.subscribeOn(Schedulers.io()) }
// ?.observeOn(AndroidSchedulers.mainThread())
// ?.subscribe { poll ->
// _viewState.value = PollOpenState(poll)
// }
// }
private fun loadPoll() { private fun loadPoll() {
repository.getPoll(roomToken, pollId) repository.getPoll(roomToken, pollId)
@ -83,7 +79,12 @@ class PollViewModel @Inject constructor(private val repository: PollRepository)
} }
override fun onComplete() { override fun onComplete() {
_viewState.value = PollOpenState(poll) // TODO check attributes and decide if poll is open/closed/selfvoted...
when (poll.status) {
Poll.STATUS_OPEN -> _viewState.value = PollOpenState(poll)
Poll.STATUS_CLOSED -> _viewState.value = PollClosedState(poll)
}
} }
} }

View file

@ -21,12 +21,31 @@
package com.nextcloud.talk.polls.viewmodels package com.nextcloud.talk.polls.viewmodels
import android.util.Log
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.nextcloud.talk.polls.model.Poll
import com.nextcloud.talk.polls.repositories.PollRepository
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import javax.inject.Inject import javax.inject.Inject
class PollVoteViewModel @Inject constructor() : ViewModel() { class PollVoteViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() {
sealed interface ViewState
object InitialState : ViewState
open class PollVoteSuccessState(val poll: Poll) : ViewState
open class PollVoteFailedState() : ViewState
private val _viewState: MutableLiveData<ViewState> = MutableLiveData(InitialState)
val viewState: LiveData<ViewState>
get() = _viewState
private var disposable: Disposable? = null
private val _selectedOptions: MutableLiveData<List<String>> = MutableLiveData(emptyList()) private val _selectedOptions: MutableLiveData<List<String>> = MutableLiveData(emptyList())
val selectedOptions: LiveData<List<String>> val selectedOptions: LiveData<List<String>>
get() = _selectedOptions get() = _selectedOptions
@ -34,4 +53,41 @@ class PollVoteViewModel @Inject constructor() : ViewModel() {
fun selectOption(option: String) { fun selectOption(option: String) {
_selectedOptions.value = listOf(option) _selectedOptions.value = listOf(option)
} }
fun vote(roomToken: String, pollId: String, option: Int) {
repository.vote(roomToken, pollId, option)
?.doOnSubscribe { disposable = it }
?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(PollObserver())
}
override fun onCleared() {
super.onCleared()
disposable?.dispose()
}
inner class PollObserver : Observer<Poll> {
lateinit var poll: Poll
override fun onSubscribe(d: Disposable) = Unit
override fun onNext(response: Poll) {
poll = response
}
override fun onError(e: Throwable) {
Log.d(TAG, "An error occurred: $e")
_viewState.value = PollVoteFailedState()
}
override fun onComplete() {
_viewState.value = PollVoteSuccessState(poll)
}
}
companion object {
private val TAG = PollVoteViewModel::class.java.simpleName
}
} }

View file

@ -33,7 +33,7 @@
</RadioGroup> </RadioGroup>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/setStatus" android:id="@+id/submitVote"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:cornerRadius="@dimen/button_corner_radius" app:cornerRadius="@dimen/button_corner_radius"