feat(talk): меню сообщения — Напоминание с пресетами (P1 talk.diff)

This commit is contained in:
b-mob-conf
2026-07-13 07:15:40 +00:00
parent 958e14aead
commit bbaaf7ade2
6 changed files with 104 additions and 3 deletions
@@ -807,6 +807,7 @@ fun TalkMessageActionSheet(
onCopy: (TalkMessage) -> Unit,
onCopyLink: (TalkMessage) -> Unit,
onForwardNoteToSelf: (TalkMessage) -> Unit,
onSetReminder: (TalkMessage, ReminderPreset) -> Unit,
) {
if (message == null) return
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
@@ -841,6 +842,21 @@ fun TalkMessageActionSheet(
text = "Переслать в «Личные заметки»",
onClick = { onForwardNoteToSelf(message) },
)
Text(
"Напоминание:",
style = MaterialTheme.typography.labelMedium,
color = F7Colors.TextSecondary,
)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
listOf(
"Сегодня" to ReminderPreset.TODAY,
"Завтра" to ReminderPreset.TOMORROW,
"Выходные" to ReminderPreset.WEEKEND,
"Неделя" to ReminderPreset.NEXT_WEEK,
).forEach { (label, preset) ->
F7TextButton(text = label, onClick = { onSetReminder(message, preset) })
}
}
}
if (canModify) {
F7TextButton(text = "Изменить", onClick = { onEdit(message) })
@@ -321,6 +321,24 @@ class TalkRepository {
}
}
/** Напоминание на сообщение (timestamp в секундах). OCS v1. */
suspend fun setReminder(session: AuthSession, roomToken: String, messageId: Long, timestamp: Long) {
val client = authedClient(session)
val url = spreedApi(session, "v1") + "/chat/$roomToken/$messageId/reminder?format=json"
val body = FormBody.Builder().add("timestamp", timestamp.toString()).build()
val request = Request.Builder()
.url(url)
.header("OCS-APIRequest", "true")
.header("Accept", "application/json")
.post(body)
.build()
client.newCall(request).execute().use { response ->
if (response.code != 201 && !response.isSuccessful) {
error("Talk reminder failed: HTTP ${response.code}")
}
}
}
private fun authedClient(session: AuthSession): OkHttpClient =
NetworkFactory.newAuthedClient(session.username, session.appPassword, session.trustAllCerts)
@@ -296,6 +296,7 @@ fun TalkScreen(
onDeleteMessage = { vm.deleteMessage(session, it.id) },
onEditMessage = { msg, text -> vm.editMessage(session, msg.id, text) },
onForwardNoteToSelf = { vm.forwardToNoteToSelf(session, it) },
onSetReminder = { msg, preset -> vm.setMessageReminder(session, msg.id, preset) },
onMentionQuery = { vm.setMentionQuery(session, it) },
onMentionSelected = { mention ->
draft = insertMentionIntoDraft(draft, mention)
@@ -435,6 +436,7 @@ private fun ChatContent(
onDeleteMessage: (TalkMessage) -> Unit,
onEditMessage: (TalkMessage, String) -> Unit,
onForwardNoteToSelf: (TalkMessage) -> Unit,
onSetReminder: (TalkMessage, ReminderPreset) -> Unit,
onMentionQuery: (String?) -> Unit,
onMentionSelected: (TalkMention) -> Unit,
onClearReply: () -> Unit,
@@ -532,6 +534,11 @@ private fun ChatContent(
Toast.makeText(context, "Переслано в «Личные заметки»", Toast.LENGTH_SHORT).show()
actionMessage = null
},
onSetReminder = { msg, preset ->
onSetReminder(msg, preset)
Toast.makeText(context, "Напоминание установлено", Toast.LENGTH_SHORT).show()
actionMessage = null
},
)
editingMessage?.let { message ->
@@ -13,6 +13,15 @@ import kotlinx.coroutines.isActive
import ru.forbion.f7cloud.core.auth.AuthSession
import ru.forbion.f7cloud.core.designsystem.AppForegroundTracker
import ru.forbion.f7cloud.core.network.UnauthorizedException
import java.util.Calendar
/** Пресеты напоминания по сообщению (как в веб-Talk). */
enum class ReminderPreset {
TODAY,
TOMORROW,
WEEKEND,
NEXT_WEEK,
}
data class TalkUiState(
val loading: Boolean = false,
@@ -616,6 +625,52 @@ class TalkViewModel(
}
}
/** Напоминание на сообщение по пресету (сегодня/завтра/выходные/неделя). */
fun setMessageReminder(session: AuthSession, messageId: Long, preset: ReminderPreset) {
val token = _state.value.selectedRoomToken ?: return
val timestamp = reminderTimestamp(preset)
viewModelScope.launch(Dispatchers.IO) {
runCatching { repository.setReminder(session, token, messageId, timestamp) }
.onFailure { t ->
_state.value = _state.value.copy(
error = t.message ?: "Не удалось поставить напоминание",
unauthorized = t is UnauthorizedException,
)
}
}
}
private fun reminderTimestamp(preset: ReminderPreset): Long {
val cal = Calendar.getInstance()
when (preset) {
ReminderPreset.TODAY -> cal.setTime(REMINDER_HOUR_EVENING)
ReminderPreset.TOMORROW -> {
cal.add(Calendar.DAY_OF_YEAR, 1)
cal.setTime(REMINDER_HOUR_MORNING)
}
ReminderPreset.WEEKEND -> {
while (cal.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY) {
cal.add(Calendar.DAY_OF_YEAR, 1)
}
cal.setTime(REMINDER_HOUR_MORNING)
}
ReminderPreset.NEXT_WEEK -> {
do {
cal.add(Calendar.DAY_OF_YEAR, 1)
} while (cal.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY)
cal.setTime(REMINDER_HOUR_MORNING)
}
}
return cal.timeInMillis / MILLIS_IN_SECOND
}
private fun Calendar.setTime(hour: Int) {
set(Calendar.HOUR_OF_DAY, hour)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
fun deleteMessage(session: AuthSession, messageId: Long) {
val token = _state.value.selectedRoomToken ?: return
viewModelScope.launch(Dispatchers.IO) {
@@ -774,5 +829,9 @@ class TalkViewModel(
/** spreed conversation type: note-to-self (Личные заметки). */
private const val ROOM_TYPE_NOTE_TO_SELF = 6
private const val MILLIS_IN_SECOND = 1000L
private const val REMINDER_HOUR_EVENING = 18
private const val REMINDER_HOUR_MORNING = 8
}
}