Adding various poll UI updates.

This commit is contained in:
Michelle Tang
2025-10-17 16:57:34 -04:00
committed by Cody Henthorne
parent 7901cad90b
commit e4abc6d256
6 changed files with 56 additions and 29 deletions

View File

@@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
@@ -45,14 +44,15 @@ import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import org.signal.core.ui.compose.Buttons
import org.signal.core.ui.compose.DayNightPreviews
import org.signal.core.ui.compose.Previews
import org.signal.core.ui.compose.theme.SignalTheme
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.compose.RoundCheckbox
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.polls.PollOption
import org.thoughtcrime.securesms.polls.PollRecord
import org.thoughtcrime.securesms.polls.VoteState
@@ -79,7 +79,8 @@ fun setContent(
poll = poll,
onViewVotes = onViewVotes,
onToggleVote = onToggleVote,
pollColors = if (isOutgoing) PollColorsType.Outgoing.getColors(chatColor) else PollColorsType.Incoming.getColors(-1)
pollColors = if (isOutgoing) PollColorsType.Outgoing.getColors(chatColor) else PollColorsType.Incoming.getColors(-1),
fontSize = SignalStore.settings.messageFontSize
)
}
}
@@ -90,7 +91,8 @@ private fun Poll(
poll: PollRecord,
onViewVotes: () -> Unit = {},
onToggleVote: (PollOption, Boolean) -> Unit = { _, _ -> },
pollColors: PollColors = PollColorsType.Incoming.getColors(-1)
pollColors: PollColors = PollColorsType.Incoming.getColors(-1),
fontSize: Int = 16
) {
val totalVotes = remember(poll.pollOptions) { poll.pollOptions.sumOf { it.voters.size } }
val caption = when {
@@ -105,32 +107,38 @@ private fun Poll(
Text(
text = stringResource(caption),
color = pollColors.caption,
style = MaterialTheme.typography.bodySmall,
style = MaterialTheme.typography.bodySmall.copy(fontSize = (fontSize * .8).sp),
modifier = Modifier.padding(start = 12.dp, bottom = 4.dp)
)
poll.pollOptions.forEach {
PollOption(it, totalVotes, poll.hasEnded, onToggleVote, pollColors)
PollOption(it, totalVotes, poll.hasEnded, onToggleVote, pollColors, fontSize)
}
Spacer(Modifier.size(16.dp))
if (totalVotes == 0) {
val hasVotes = totalVotes > 0
Buttons.MediumTonal(
colors = ButtonDefaults.buttonColors(
containerColor = pollColors.buttonBackground,
contentColor = pollColors.button,
disabledContainerColor = Color.Transparent,
disabledContentColor = pollColors.text
),
onClick = onViewVotes,
enabled = hasVotes,
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
Text(
text = stringResource(R.string.Poll__no_votes),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.align(Alignment.CenterHorizontally).height(40.dp).wrapContentHeight(align = Alignment.CenterVertically),
textAlign = TextAlign.Center,
color = pollColors.text
text = if (!hasVotes) {
stringResource(R.string.Poll__no_votes)
} else if (poll.hasEnded) {
stringResource(R.string.Poll__view_results)
} else {
stringResource(R.string.Poll__view_votes)
},
style = MaterialTheme.typography.labelLarge.copy(fontSize = (fontSize * .8).sp)
)
} else {
Buttons.MediumTonal(
colors = ButtonDefaults.buttonColors(containerColor = pollColors.buttonBackground, contentColor = pollColors.button),
onClick = onViewVotes,
modifier = Modifier.align(Alignment.CenterHorizontally).height(40.dp)
) {
Text(stringResource(if (poll.hasEnded) R.string.Poll__view_results else R.string.Poll__view_votes))
}
}
Spacer(Modifier.size(4.dp))
}
@@ -142,7 +150,8 @@ private fun PollOption(
totalVotes: Int,
hasEnded: Boolean,
onToggleVote: (PollOption, Boolean) -> Unit = { _, _ -> },
pollColors: PollColors
pollColors: PollColors,
fontSize: Int
) {
val context = LocalContext.current
val haptics = LocalHapticFeedback.current
@@ -238,7 +247,7 @@ private fun PollOption(
Row(verticalAlignment = Alignment.Bottom) {
Text(
text = option.text,
style = MaterialTheme.typography.bodyLarge,
style = MaterialTheme.typography.bodyLarge.copy(fontSize = fontSize.sp),
modifier = Modifier.padding(end = 24.dp).weight(1f),
color = pollColors.text
)
@@ -260,13 +269,13 @@ private fun PollOption(
Text(
text = size.toString(),
color = pollColors.text,
style = MaterialTheme.typography.bodyMedium
style = MaterialTheme.typography.bodyMedium.copy(fontSize = (fontSize * .8).sp)
)
}
}
Box(
modifier = Modifier.height(8.dp).padding(top = 4.dp).fillMaxWidth()
modifier = Modifier.padding(top = 4.dp).height(8.dp).fillMaxWidth()
.background(
color = pollColors.progressBackground,
shape = RoundedCornerShape(18.dp)

View File

@@ -12,6 +12,7 @@ import android.app.PendingIntent
import android.content.ActivityNotFoundException
import android.content.BroadcastReceiver
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Configuration
@@ -2568,8 +2569,14 @@ class ConversationFragment :
disposables += endPoll
.subscribeBy(
// TODO(michelle): Error state when poll terminate fails
onError = { Log.w(TAG, "Error received during poll end!", it) }
onError = {
Log.w(TAG, "Error received during poll end!", it)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.Poll__couldnt_end_poll)
.setMessage(getString(R.string.Poll__check_connection))
.setPositiveButton(android.R.string.ok) { dialog: DialogInterface?, which: Int -> dialog!!.dismiss() }
.show()
}
)
}

View File

@@ -92,6 +92,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.BubbleUtil
import org.thoughtcrime.securesms.util.ConversationUtil
import org.thoughtcrime.securesms.util.NetworkUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.hasGiftBadge
import org.thoughtcrime.securesms.util.rx.RxStore
@@ -516,9 +517,13 @@ class ConversationViewModel(
}
fun endPoll(pollId: Long): Completable {
return repository
.endPoll(pollId)
.observeOn(AndroidSchedulers.mainThread())
return if (!NetworkUtil.isConnected(AppDependencies.application)) {
Completable.error(Exception("Connection required to end poll"))
} else {
repository
.endPoll(pollId)
.observeOn(AndroidSchedulers.mainThread())
}
}
fun sendMessage(

View File

@@ -235,6 +235,7 @@
android:id="@+id/poll"
android:layout_width="@dimen/media_bubble_max_width"
android:layout_height="wrap_content"
android:layout_marginTop="-4dp"
android:layout="@layout/conversation_item_poll" />
<ViewStub

View File

@@ -176,6 +176,7 @@
android:id="@+id/poll"
android:layout_width="@dimen/media_bubble_max_width"
android:layout_height="wrap_content"
android:layout_marginTop="-4dp"
android:layout="@layout/conversation_item_poll" />
<ViewStub

View File

@@ -8822,6 +8822,10 @@
<string name="Poll__end_poll">End poll</string>
<!-- Button that once pressed will show all of the voters for a poll -->
<string name="Poll__see_all">See all</string>
<!-- Dialog title when a poll fails to end -->
<string name="Poll__couldnt_end_poll">Couldn\'t end poll</string>
<!-- Dialog body when a poll fails to end and to try again -->
<string name="Poll__check_connection">Check your connection and try again.</string>
<!-- Header when creating a new poll -->
<string name="CreatePollFragment__new_poll">New poll</string>
<!-- Section text to enter in a question for a poll -->