Browse Source

add UI logic + close poll system message

add close poll button (wip/temporarily?)
add "close poll" system message
add UI related logic to PollMainViewModel
add placeholder for pollDetails in UI

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Marcel Hibbe 3 years ago
parent
commit
b504af1cd9

+ 2 - 1
app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt

@@ -491,7 +491,8 @@ data class ChatMessage(
         REACTION,
         REACTION_DELETED,
         REACTION_REVOKED,
-        POLL_VOTED
+        POLL_VOTED,
+        POLL_CLOSED
     }
 
     companion object {

+ 3 - 0
app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt

@@ -65,6 +65,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MODERAT
 import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.OBJECT_SHARED
 import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.PASSWORD_REMOVED
 import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.PASSWORD_SET
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.POLL_CLOSED
 import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.POLL_VOTED
 import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.REACTION
 import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.REACTION_DELETED
@@ -169,6 +170,7 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.Syst
             "reaction_deleted" -> REACTION_DELETED
             "reaction_revoked" -> REACTION_REVOKED
             "poll_voted" -> POLL_VOTED
+            "poll_closed" -> POLL_CLOSED
             else -> DUMMY
         }
     }
@@ -227,6 +229,7 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.Syst
             REACTION_DELETED -> return "reaction_deleted"
             REACTION_REVOKED -> return "reaction_revoked"
             POLL_VOTED -> return "poll_voted"
+            POLL_CLOSED -> return "poll_closed"
             else -> return ""
         }
     }

+ 9 - 1
app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt

@@ -1,11 +1,13 @@
 package com.nextcloud.talk.polls.adapters
 
 import android.annotation.SuppressLint
+import android.view.View
 import androidx.recyclerview.widget.RecyclerView
 import com.nextcloud.talk.databinding.PollResultItemBinding
 
 class PollResultViewHolder(
-    private val binding: PollResultItemBinding
+    private val binding: PollResultItemBinding,
+    private val showDetails: Boolean
 ) : RecyclerView.ViewHolder(binding.root) {
 
     @SuppressLint("SetTextI18n")
@@ -14,5 +16,11 @@ class PollResultViewHolder(
         binding.pollOptionText.text = pollResultItem.pollOption
         binding.pollOptionPercentText.text = pollResultItem.pollPercent.toString() + "%"
         binding.pollOptionBar.progress = pollResultItem.pollPercent
+
+        if (showDetails) {
+            binding.pollOptionDetail.visibility = View.VISIBLE
+        } else {
+            binding.pollOptionDetail.visibility = View.GONE
+        }
     }
 }

+ 3 - 2
app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt

@@ -6,13 +6,14 @@ import androidx.recyclerview.widget.RecyclerView
 import com.nextcloud.talk.databinding.PollResultItemBinding
 
 class PollResultsAdapter(
-    private val clickListener: PollResultItemClickListener
+    private val clickListener: PollResultItemClickListener,
+    private val showDetails: Boolean
 ) : RecyclerView.Adapter<PollResultViewHolder>() {
     internal var list: MutableList<PollResultItem> = ArrayList<PollResultItem>()
 
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollResultViewHolder {
         val itemBinding = PollResultItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
-        return PollResultViewHolder(itemBinding)
+        return PollResultViewHolder(itemBinding, showDetails)
     }
 
     override fun onBindViewHolder(holder: PollResultViewHolder, position: Int) {

+ 6 - 4
app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt

@@ -5,10 +5,6 @@ import io.reactivex.Observable
 
 interface PollRepository {
 
-    fun getPoll(roomToken: String, pollId: String): Observable<Poll>?
-
-    fun vote(roomToken: String, pollId: String, option: Int): Observable<Poll>?
-
     fun createPoll(
         roomToken: String,
         question: String,
@@ -16,4 +12,10 @@ interface PollRepository {
         resultMode: Int,
         maxVotes: Int
     ): Observable<Poll>?
+
+    fun getPoll(roomToken: String, pollId: String): Observable<Poll>?
+
+    fun vote(roomToken: String, pollId: String, option: Int): Observable<Poll>?
+
+    fun closePoll(roomToken: String, pollId: String): Observable<Poll>?
 }

+ 12 - 0
app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt

@@ -102,6 +102,18 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid
         ).map { mapToPoll(it.ocs?.data!!) }
     }
 
+    override fun closePoll(roomToken: String, pollId: String): Observable<Poll> {
+
+        return ncApi.closePoll(
+            credentials,
+            ApiUtils.getUrlForPoll(
+                currentUserProvider.currentUser?.baseUrl,
+                roomToken,
+                pollId
+            ),
+        ).map { mapToPoll(it.ocs?.data!!) }
+    }
+
     companion object {
 
         private fun mapToPoll(pollResponse: PollResponse): Poll {

+ 2 - 2
app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt

@@ -60,7 +60,7 @@ class PollMainDialogFragment(
             when (state) {
                 PollMainViewModel.InitialState -> {}
 
-                is PollMainViewModel.PollVotedState -> {
+                is PollMainViewModel.PollResultState -> {
                     if (state.poll.resultMode == Poll.RESULT_MODE_HIDDEN) {
                         showVoteFragment()
                     } else {
@@ -68,7 +68,7 @@ class PollMainDialogFragment(
                     }
                 }
 
-                is PollMainViewModel.PollUnvotedState -> {
+                is PollMainViewModel.PollVoteState -> {
                     if (state.poll.status == Poll.STATUS_CLOSED) {
                         showResultsFragment()
                     } else {

+ 35 - 16
app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt

@@ -77,26 +77,23 @@ class PollResultsFragment(
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
-        adapter = PollResultsAdapter(this)
-        _binding?.pollResultsList?.adapter = adapter
-        _binding?.pollResultsList?.layoutManager = LinearLayoutManager(context)
-
         parentViewModel.viewState.observe(viewLifecycleOwner) { state ->
-            if (state is PollMainViewModel.PollVotedState &&
-                state.poll.resultMode == Poll.RESULT_MODE_PUBLIC
-            ) {
-
+            if (state is PollMainViewModel.PollResultState) {
+                initAdapter(state.showDetails)
                 initPollResults(state.poll)
                 initAmountVotersInfo(state)
-                initEditButton(state)
-            } else if (state is PollMainViewModel.PollUnvotedState &&
-                state.poll.status == Poll.STATUS_CLOSED
-            ) {
-                Log.d(TAG, "show results also if self never voted")
+                initEditButton(state.showEditButton)
+                initCloseButton(state.showCloseButton)
             }
         }
     }
 
+    private fun initAdapter(showDetails: Boolean) {
+        adapter = PollResultsAdapter(this, showDetails)
+        _binding?.pollResultsList?.adapter = adapter
+        _binding?.pollResultsList?.layoutManager = LinearLayoutManager(context)
+    }
+
     private fun initPollResults(poll: Poll) {
         if (poll.details != null) {
             val votersAmount = poll.details.size
@@ -128,15 +125,26 @@ class PollResultsFragment(
         }
     }
 
-    private fun initAmountVotersInfo(state: PollMainViewModel.PollVotedState) {
+    private fun initAmountVotersInfo(state: PollMainViewModel.PollResultState) {
         _binding?.pollAmountVoters?.text = String.format(
             resources.getString(R.string.polls_amount_voters),
             state.poll.numVoters
         )
     }
 
-    private fun initEditButton(state: PollMainViewModel.PollVotedState) {
-        if (state.poll.status == Poll.STATUS_OPEN && state.poll.resultMode == Poll.RESULT_MODE_PUBLIC) {
+    // private fun initEditButton(state: PollMainViewModel.PollResultState) {
+    //     if (state.poll.status == Poll.STATUS_OPEN && state.poll.resultMode == Poll.RESULT_MODE_PUBLIC) {
+    //         _binding?.editVoteButton?.visibility = View.VISIBLE
+    //         _binding?.editVoteButton?.setOnClickListener {
+    //             parentViewModel.edit()
+    //         }
+    //     } else {
+    //         _binding?.editVoteButton?.visibility = View.GONE
+    //     }
+    // }
+
+    private fun initEditButton(showEditButton: Boolean) {
+        if (showEditButton) {
             _binding?.editVoteButton?.visibility = View.VISIBLE
             _binding?.editVoteButton?.setOnClickListener {
                 parentViewModel.edit()
@@ -146,6 +154,17 @@ class PollResultsFragment(
         }
     }
 
+    private fun initCloseButton(showCloseButton: Boolean) {
+        if (showCloseButton) {
+            _binding?.closeVoteButton?.visibility = View.VISIBLE
+            _binding?.closeVoteButton?.setOnClickListener {
+                parentViewModel.closePoll()
+            }
+        } else {
+            _binding?.closeVoteButton?.visibility = View.GONE
+        }
+    }
+
     override fun onClick(pollResultItem: PollResultItem) {
         Log.d(TAG, "click..")
     }

+ 2 - 2
app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt

@@ -71,7 +71,7 @@ class PollVoteFragment(
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         parentViewModel.viewState.observe(viewLifecycleOwner) { state ->
-            if (state is PollMainViewModel.PollUnvotedState) {
+            if (state is PollMainViewModel.PollVoteState) {
                 val poll = state.poll
                 binding.radioGroup.removeAllViews()
                 poll.options?.map { option ->
@@ -80,7 +80,7 @@ class PollVoteFragment(
                     radioButton.id = index
                     binding.radioGroup.addView(radioButton)
                 }
-            } else if (state is PollMainViewModel.PollVotedState && state.poll.resultMode == Poll.RESULT_MODE_HIDDEN) {
+            } else if (state is PollMainViewModel.PollResultState && state.poll.resultMode == Poll.RESULT_MODE_HIDDEN) {
                 Log.d(TAG, "show vote screen also for resultMode hidden poll when already voted")
                 // TODO: other text for submit button
             }

+ 30 - 5
app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt

@@ -6,6 +6,7 @@ import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import com.nextcloud.talk.polls.model.Poll
 import com.nextcloud.talk.polls.repositories.PollRepository
+import com.nextcloud.talk.utils.database.user.UserUtils
 import io.reactivex.Observer
 import io.reactivex.android.schedulers.AndroidSchedulers
 import io.reactivex.disposables.Disposable
@@ -25,6 +26,9 @@ import javax.inject.Inject
  */
 class PollMainViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() {
 
+    @Inject
+    lateinit var userUtils: UserUtils
+
     private lateinit var roomToken: String
     private lateinit var pollId: String
 
@@ -32,8 +36,13 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito
 
     sealed interface ViewState
     object InitialState : ViewState
-    open class PollUnvotedState(val poll: Poll) : ViewState
-    open class PollVotedState(val poll: Poll) : ViewState
+    open class PollVoteState(val poll: Poll) : ViewState
+    open class PollResultState(
+        val poll: Poll,
+        val showDetails: Boolean,
+        val showEditButton: Boolean,
+        val showCloseButton: Boolean
+    ) : ViewState
 
     private val _viewState: MutableLiveData<ViewState> = MutableLiveData(InitialState)
     val viewState: LiveData<ViewState>
@@ -65,6 +74,14 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito
             ?.subscribe(PollObserver())
     }
 
+    fun closePoll() {
+        repository.closePoll(roomToken, pollId)
+            ?.doOnSubscribe { disposable = it }
+            ?.subscribeOn(Schedulers.io())
+            ?.observeOn(AndroidSchedulers.mainThread())
+            ?.subscribe(PollObserver())
+    }
+
     override fun onCleared() {
         super.onCleared()
         disposable?.dispose()
@@ -86,16 +103,24 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito
 
         override fun onComplete() {
             if (editPoll) {
-                _viewState.value = PollUnvotedState(poll)
+                _viewState.value = PollVoteState(poll)
                 editPoll = false
             } else if (poll.votedSelf.isNullOrEmpty()) {
-                _viewState.value = PollUnvotedState(poll)
+                _viewState.value = PollVoteState(poll)
             } else {
-                _viewState.value = PollVotedState(poll)
+                val showEditButton = poll.status == Poll.STATUS_OPEN && poll.resultMode == Poll.RESULT_MODE_PUBLIC
+                val showDetails = poll.status == Poll.STATUS_CLOSED && poll.resultMode == Poll.RESULT_MODE_PUBLIC
+                val showCloseButton = poll.status == Poll.STATUS_OPEN && isPollCreatedByCurrentUser(poll)
+
+                _viewState.value = PollResultState(poll, showDetails, showEditButton, showCloseButton)
             }
         }
     }
 
+    fun isPollCreatedByCurrentUser(poll: Poll): Boolean {
+        return userUtils.currentUser?.userId == poll.actorId
+    }
+
     companion object {
         private val TAG = PollMainViewModel::class.java.simpleName
     }

+ 12 - 0
app/src/main/res/layout/dialog_poll_results.xml

@@ -48,6 +48,18 @@
         app:layout_constraintTop_toBottomOf="@+id/poll_results_list_wrapper"
         tools:text="Poll results - 93 votes" />
 
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/close_vote_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:text="Close"
+        android:theme="@style/Button.Primary"
+        app:cornerRadius="@dimen/button_corner_radius"
+        app:layout_constraintEnd_toStartOf="@+id/edit_vote_button"
+        app:layout_constraintTop_toBottomOf="@+id/poll_results_list_wrapper" />
+
+
     <com.google.android.material.button.MaterialButton
         android:id="@+id/edit_vote_button"
         android:layout_width="wrap_content"

+ 15 - 0
app/src/main/res/layout/poll_result_item.xml

@@ -30,4 +30,19 @@
         app:layout_constraintTop_toBottomOf="@+id/poll_option_text"
         style="?android:attr/progressBarStyleHorizontal" />
 
+    <LinearLayout
+        android:id="@+id/poll_option_detail"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintStart_toStartOf="@+id/poll_option_text"
+        app:layout_constraintTop_toBottomOf="@+id/poll_option_bar">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="here the details..." />
+
+    </LinearLayout>
+
 </androidx.constraintlayout.widget.ConstraintLayout>