Makes 24h the default end time for polls

This commit is contained in:
Vitor Pamplona
2026-02-17 18:41:25 -05:00
parent a6ff66b8ae
commit bb3937edb4
4 changed files with 31 additions and 37 deletions
@@ -56,11 +56,10 @@ import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.note.timeAheadNoDot import com.vitorpamplona.amethyst.ui.note.timeAheadNoDot
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.ShortNotePostViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.ShortNotePostViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.utils.TimeUtils
import java.time.Instant import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.ZoneId import java.time.ZoneId
import java.time.ZoneOffset
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@@ -69,15 +68,12 @@ fun PollDeadlinePicker(model: ShortNotePostViewModel) {
var showTimePicker by remember { mutableStateOf(false) } var showTimePicker by remember { mutableStateOf(false) }
// Get current time details // Get current time details
val currentTime = LocalDateTime.now() val currentTime = Instant.ofEpochMilli(model.closedAt * 1000).atZone(ZoneId.systemDefault()).toLocalDateTime()
val datePickerState = val datePickerState =
rememberDatePickerState( rememberDatePickerState(
initialSelectedDateMillis = initialSelectedDateMillis = model.closedAt * 1000,
currentTime yearRange = currentTime.year..2050,
.atZone(ZoneId.systemDefault())
.toInstant()
.toEpochMilli(),
selectableDates = selectableDates =
object : SelectableDates { object : SelectableDates {
override fun isSelectableDate(utcTimeMillis: Long): Boolean { override fun isSelectableDate(utcTimeMillis: Long): Boolean {
@@ -106,11 +102,11 @@ fun PollDeadlinePicker(model: ShortNotePostViewModel) {
Icon(Icons.Default.DateRange, contentDescription = null) Icon(Icons.Default.DateRange, contentDescription = null)
Spacer(Modifier.width(12.dp)) Spacer(Modifier.width(12.dp))
if (model.closedAt == null) { if (model.closedAt < TimeUtils.oneMinuteFromNow()) {
Text(stringRes(R.string.poll_closing_date_time), style = MaterialTheme.typography.bodyLarge) Text(stringRes(R.string.poll_closing_date_time) + " " + model.closedAt, style = MaterialTheme.typography.bodyLarge)
} else { } else {
Text( Text(
text = timeAheadNoDot(model.closedAt, context), text = stringRes(R.string.poll_closing_in, timeAheadNoDot(model.closedAt, context)),
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
) )
} }
@@ -140,28 +136,23 @@ fun PollDeadlinePicker(model: ShortNotePostViewModel) {
}, },
onDismissRequest = { showTimePicker = false }, onDismissRequest = { showTimePicker = false },
confirmButton = { confirmButton = {
TextButton(onClick = { TextButton(
val date = onClick = {
datePickerState.selectedDateMillis?.let { val datetimeLocalTimeZone =
Instant.ofEpochMilli(it).atZone(ZoneId.systemDefault()).toLocalDate() datePickerState.selectedDateMillis?.let { localDayAtZeroHourMillis ->
} ?: LocalDate.now() (localDayAtZeroHourMillis / 1000) +
(timePickerState.hour * TimeUtils.ONE_HOUR) +
(timePickerState.minute * TimeUtils.ONE_MINUTE)
} ?: TimeUtils.oneDayAhead()
val finalDateTime = // Get the offset from UTC for the current instant in the local time zone
LocalDateTime.of( val offset: ZoneOffset = ZoneId.systemDefault().rules.getOffset(Instant.now())
date,
LocalTime.of(timePickerState.hour, timePickerState.minute),
)
// 2. Convert to Unix Timestamp (Seconds) model.closedAt = datetimeLocalTimeZone - offset.totalSeconds
val unixTimestamp =
finalDateTime
.atZone(ZoneId.systemDefault())
.toInstant()
.epochSecond
model.closedAt = unixTimestamp showTimePicker = false
showTimePicker = false },
}) { Text(stringResource(R.string.confirm)) } ) { Text(stringResource(R.string.confirm)) }
}, },
) { ) {
TimePicker(state = timePickerState) TimePicker(state = timePickerState)
@@ -123,6 +123,7 @@ import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
import com.vitorpamplona.quartz.utils.Log import com.vitorpamplona.quartz.utils.Log
import com.vitorpamplona.quartz.utils.RandomInstance import com.vitorpamplona.quartz.utils.RandomInstance
import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -220,7 +221,7 @@ open class ShortNotePostViewModel :
var canUsePoll by mutableStateOf(false) var canUsePoll by mutableStateOf(false)
var wantsPoll by mutableStateOf(false) var wantsPoll by mutableStateOf(false)
var pollOptions: SnapshotStateMap<Int, OptionTag> = newStateMapPollOptions() var pollOptions: SnapshotStateMap<Int, OptionTag> = newStateMapPollOptions()
var closedAt by mutableStateOf<Long?>(null) var closedAt by mutableStateOf(TimeUtils.oneDayAhead())
// Invoices // Invoices
var canAddInvoice by mutableStateOf(false) var canAddInvoice by mutableStateOf(false)
@@ -539,9 +540,7 @@ open class ShortNotePostViewModel :
pollOptions[index] = tag pollOptions[index] = tag
} }
draftEvent.endsAt()?.let { closedAt = draftEvent.endsAt() ?: TimeUtils.oneDayAhead()
closedAt = it
}
message = TextFieldValue(draftEvent.content) message = TextFieldValue(draftEvent.content)
@@ -889,7 +888,7 @@ open class ShortNotePostViewModel :
wantsPoll = false wantsPoll = false
pollOptions = newStateMapPollOptions() pollOptions = newStateMapPollOptions()
closedAt = null closedAt = TimeUtils.oneDayAhead()
wantsInvoice = false wantsInvoice = false
wantsZapRaiser = false wantsZapRaiser = false
@@ -1018,7 +1017,8 @@ open class ShortNotePostViewModel :
( (
!wantsPoll || !wantsPoll ||
( (
pollOptions.isNotEmpty() && pollOptions.all { it.value.label.isNotEmpty() } pollOptions.isNotEmpty() && pollOptions.all { it.value.label.isNotEmpty() } &&
closedAt > TimeUtils.oneMinuteFromNow()
) )
) && ) &&
multiOrchestrator == null multiOrchestrator == null
+1
View File
@@ -469,6 +469,7 @@
<string name="poll_consensus_threshold">Consensus</string> <string name="poll_consensus_threshold">Consensus</string>
<string name="poll_consensus_threshold_percent">(0100)%</string> <string name="poll_consensus_threshold_percent">(0100)%</string>
<string name="poll_closing_date_time">Poll Closing Date &amp; Time</string> <string name="poll_closing_date_time">Poll Closing Date &amp; Time</string>
<string name="poll_closing_in">Poll closes in %1$s</string>
<string name="poll_closing_time">Close after</string> <string name="poll_closing_time">Close after</string>
<string name="poll_closing_time_days">days</string> <string name="poll_closing_time_days">days</string>
<string name="poll_unable_to_vote">Unable to vote</string> <string name="poll_unable_to_vote">Unable to vote</string>
@@ -55,6 +55,8 @@ object TimeUtils {
fun oneDayAgo() = now() - ONE_DAY fun oneDayAgo() = now() - ONE_DAY
fun oneDayAhead() = now() + ONE_DAY
fun eightHoursAgo() = now() - EIGHT_HOURS fun eightHoursAgo() = now() - EIGHT_HOURS
fun twoDays() = ONE_DAY * 2 fun twoDays() = ONE_DAY * 2