feat(talk): меню сообщения — Ответить лично; рефактор оверлеев (ChatContent проще) [P1]
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
Формат: `ГГГГ-ММ-ДД | версия | изменение | контракты | риск`
|
Формат: `ГГГГ-ММ-ДД | версия | изменение | контракты | риск`
|
||||||
|
|
||||||
- 2026-07-13 | (без бампа — версия/релиз за лидом) | **Задачи — умные коллекции в UI (P1, tasks.diff).** Умные коллекции (Важное/Сегодня/Неделя/Все/Текущие/Завершённые) выведены в `TasksListNavView` над реальными списками: агрегат задач всех списков (`loadAllTasks`), счётчики-бейджи, тап открывает отфильтрованный вид; создание в коллекции идёт в список по умолчанию. 3-state видимость (Скрыта/Видима/Авто) в шите настроек с персистом в SharedPreferences (`TasksPrefsStore`, VM через `Factory(context)`). Движок `TasksSmartLists` расширен (`SmartListMode`/`visibleKeys`/`counts`), 25 юнит-тестов зелёные. Ff_app 16.7→24.6%, Fd_app 41.5→52.7% (`tasks.diff.md`). | контракты не менялись | низкий: UI изолирован в модуле, на устройстве не гонялось
|
- 2026-07-13 | (без бампа — версия/релиз за лидом) | **Задачи — умные коллекции в UI (P1, tasks.diff).** Умные коллекции (Важное/Сегодня/Неделя/Все/Текущие/Завершённые) выведены в `TasksListNavView` над реальными списками: агрегат задач всех списков (`loadAllTasks`), счётчики-бейджи, тап открывает отфильтрованный вид; создание в коллекции идёт в список по умолчанию. 3-state видимость (Скрыта/Видима/Авто) в шите настроек с персистом в SharedPreferences (`TasksPrefsStore`, VM через `Factory(context)`). Движок `TasksSmartLists` расширен (`SmartListMode`/`visibleKeys`/`counts`), 25 юнит-тестов зелёные. Ff_app 16.7→24.6%, Fd_app 41.5→52.7% (`tasks.diff.md`). | контракты не менялись | низкий: UI изолирован в модуле, на устройстве не гонялось
|
||||||
|
- 2026-07-13 | (без бампа — версия/релиз за лидом) | **Конференции — меню сообщения: «Ответить лично» + рефактор (P1).** Пункт «Ответить лично» в меню сообщения (для чужих сообщений): `TalkViewModel.replyPrivately` открывает/создаёт 1:1 с автором через существующий `createOneToOneRoom` (OCS spreed v4, сервер идемпотентен) и `openRoom`. Заодно рефактор: блок оверлеев сообщения (лист действий + диалоги правки/удаления) вынесен из `ChatContent` в отдельный `MessageActionOverlays` — `ChatContent` LongMethod 267→174, cyclomatic 29→<25 (перестал плодить baseline-churn на каждый пункт меню). Baseline пере-привязан (сигнатура ChatContent сдвинулась от нового параметра); новый код чист (компиляция+detekt зелёные). | контракты не менялись (OCS spreed v4/v1) | низкий: изолированное UI-действие + чистый рефактор, на устройстве не гонялось
|
||||||
- 2026-07-13 | (без бампа — версия/релиз за лидом) | **Конференции — меню сообщения: напоминание (P1).** Пункт «Напоминание» в `TalkMessageActionSheet` с пресетами «Сегодня» (18:00) / «Завтра» (08:00) / «Выходные» (сб 08:00) / «Неделя» (пн 08:00): `TalkRepository.setReminder` (OCS `POST /chat/{token}/{messageId}/reminder`, timestamp сек) + `TalkViewModel.setMessageReminder`/`reminderTimestamp` + enum `ReminderPreset`. Закрывает пункт «Напоминание» дыр меню сообщения (talk.diff.md). Baseline `feature/talk` пере-привязан (сигнатура ChatContent + импорт сдвинули существующие записи; новый код чист, компиляция+detekt зелёные). | контракты не менялись (OCS spreed v1) | низкий: изолированное UI-действие, на устройстве не гонялось
|
- 2026-07-13 | (без бампа — версия/релиз за лидом) | **Конференции — меню сообщения: напоминание (P1).** Пункт «Напоминание» в `TalkMessageActionSheet` с пресетами «Сегодня» (18:00) / «Завтра» (08:00) / «Выходные» (сб 08:00) / «Неделя» (пн 08:00): `TalkRepository.setReminder` (OCS `POST /chat/{token}/{messageId}/reminder`, timestamp сек) + `TalkViewModel.setMessageReminder`/`reminderTimestamp` + enum `ReminderPreset`. Закрывает пункт «Напоминание» дыр меню сообщения (talk.diff.md). Baseline `feature/talk` пере-привязан (сигнатура ChatContent + импорт сдвинули существующие записи; новый код чист, компиляция+detekt зелёные). | контракты не менялись (OCS spreed v1) | низкий: изолированное UI-действие, на устройстве не гонялось
|
||||||
- 2026-07-13 | (без бампа — версия/релиз за лидом) | **Конференции — меню сообщения, P1 итерации 1:1 (talk.diff.md).** В `TalkMessageActionSheet` добавлены пункты «Копировать» (текст → буфер), «Копировать ссылку» (`TalkUrls.messageLink` = `/call/<token>#message_<id>` → буфер) и «Переслать в «Личные заметки»» (`TalkViewModel.forwardToNoteToSelf` — шлёт текст в комнату note-to-self, spreed type 6, через существующий OCS `/chat` v1). Закрывает часть дыр меню сообщения из `docs/app-map/talk.diff.md` (Копировать/Копировать ссылку/частично Переслать). Обновлён `feature/talk/detekt-baseline.xml` — модуль был detekt-красным ДО моей правки (устаревший baseline от чужих коммитов v0.5.133+); мои новые строки чисты (компиляция+detekt зелёные), рефреш лишь пере-привязал существующие записи к сдвинутым сигнатурам. | контракты не менялись (та же OCS /chat v1) | низкий: изолированные UI-действия в своём модуле, на устройстве не гонялось
|
- 2026-07-13 | (без бампа — версия/релиз за лидом) | **Конференции — меню сообщения, P1 итерации 1:1 (talk.diff.md).** В `TalkMessageActionSheet` добавлены пункты «Копировать» (текст → буфер), «Копировать ссылку» (`TalkUrls.messageLink` = `/call/<token>#message_<id>` → буфер) и «Переслать в «Личные заметки»» (`TalkViewModel.forwardToNoteToSelf` — шлёт текст в комнату note-to-self, spreed type 6, через существующий OCS `/chat` v1). Закрывает часть дыр меню сообщения из `docs/app-map/talk.diff.md` (Копировать/Копировать ссылку/частично Переслать). Обновлён `feature/talk/detekt-baseline.xml` — модуль был detekt-красным ДО моей правки (устаревший baseline от чужих коммитов v0.5.133+); мои новые строки чисты (компиляция+detekt зелёные), рефреш лишь пере-привязал существующие записи к сдвинутым сигнатурам. | контракты не менялись (та же OCS /chat v1) | низкий: изолированные UI-действия в своём модуле, на устройстве не гонялось
|
||||||
- 2026-07-13 | (без бампа — версия/релиз за лидом) | **Задачи — умные списки: проброс DTSTART.** `DavTask.startRaw` (внёс лид, core-коммит `2cb7a9a`) пробрасывается в `TaskItem.startRaw` (`toTaskItem()`) → «Текущие»/«Сегодня»/«На неделе» теперь учитывают реальный start (раньше start был пуст, «Текущие» вырождались в «все незавершённые»). Логика/тесты уже были готовы принять непустой start. | контракты не менялись | низкий
|
- 2026-07-13 | (без бампа — версия/релиз за лидом) | **Задачи — умные списки: проброс DTSTART.** `DavTask.startRaw` (внёс лид, core-коммит `2cb7a9a`) пробрасывается в `TaskItem.startRaw` (`toTaskItem()`) → «Текущие»/«Сегодня»/«На неделе» теперь учитывают реальный start (раньше start был пуст, «Текущие» вырождались в «все незавершённые»). Логика/тесты уже были готовы принять непустой start. | контракты не менялись | низкий
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
<ID>ArgumentListWrapping:TalkComponents.kt$(message.attachmentFileName!!)</ID>
|
<ID>ArgumentListWrapping:TalkComponents.kt$(message.attachmentFileName!!)</ID>
|
||||||
<ID>CyclomaticComplexMethod:TalkFormatting.kt$fun buildChatItems( messages: List<TalkMessage>, lastReadMessageId: Long, selfActorId: String, collapseSystemMessages: Boolean, ): List<TalkChatItem></ID>
|
<ID>CyclomaticComplexMethod:TalkFormatting.kt$fun buildChatItems( messages: List<TalkMessage>, lastReadMessageId: Long, selfActorId: String, collapseSystemMessages: Boolean, ): List<TalkChatItem></ID>
|
||||||
<ID>CyclomaticComplexMethod:TalkScreen.kt$@Composable fun TalkScreen( session: AuthSession, modifier: Modifier = Modifier, openRoomToken: String? = null, scrollToMessageId: Long? = null, chatsListRequest: Int = 0, pushSyncRequest: Int = 0, pushRoomToken: String? = null, onOpenRoomConsumed: () -> Unit = {}, onRoomOpenStateChange: (Boolean) -> Unit = {}, onOpenCalendar: () -> Unit = {}, onUnauthorized: () -> Unit = {}, )</ID>
|
<ID>CyclomaticComplexMethod:TalkScreen.kt$@Composable fun TalkScreen( session: AuthSession, modifier: Modifier = Modifier, openRoomToken: String? = null, scrollToMessageId: Long? = null, chatsListRequest: Int = 0, pushSyncRequest: Int = 0, pushRoomToken: String? = null, onOpenRoomConsumed: () -> Unit = {}, onRoomOpenStateChange: (Boolean) -> Unit = {}, onOpenCalendar: () -> Unit = {}, onUnauthorized: () -> Unit = {}, )</ID>
|
||||||
<ID>CyclomaticComplexMethod:TalkScreen.kt$@Composable private fun ChatContent( session: AuthSession, state: TalkUiState, draft: String, onDraftChange: (String) -> Unit, onSend: () -> Unit, onAttach: () -> Unit, onVoiceToggle: () -> Unit, voiceRecording: Boolean, onRetry: () -> Unit, onStartCall: () -> Unit, onBackClick: () -> Unit, onOpenRoomInfo: () -> Unit, onScheduleMeeting: () -> Unit, onToggleSystemMessages: () -> Unit, onReply: (TalkMessage) -> Unit, onReaction: (TalkMessage, String) -> Unit, onDeleteMessage: (TalkMessage) -> Unit, onEditMessage: (TalkMessage, String) -> Unit, onForwardNoteToSelf: (TalkMessage) -> Unit, onSetReminder: (TalkMessage, ReminderPreset) -> Unit, onMentionQuery: (String?) -> Unit, onMentionSelected: (TalkMention) -> Unit, onClearReply: () -> Unit, onClearHighlight: () -> Unit, )</ID>
|
|
||||||
<ID>ImportOrdering:TalkComponents.kt$import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.foundation.border import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.offset import androidx.compose.foundation.lazy.LazyRow import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.IconButton import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import coil.compose.SubcomposeAsyncImage import coil.request.ImageRequest import okhttp3.Credentials import ru.forbion.f7cloud.core.auth.AuthSession import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import coil.compose.AsyncImage import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.foundation.layout.widthIn import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Popup import androidx.compose.ui.window.PopupProperties import ru.forbion.f7cloud.core.designsystem.F7Colors import ru.forbion.f7cloud.core.designsystem.F7PrimaryButton import ru.forbion.f7cloud.core.designsystem.F7TextButton import java.text.DateFormat import java.util.Calendar import java.util.Date import kotlin.math.abs</ID>
|
<ID>ImportOrdering:TalkComponents.kt$import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.foundation.border import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.offset import androidx.compose.foundation.lazy.LazyRow import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.IconButton import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import coil.compose.SubcomposeAsyncImage import coil.request.ImageRequest import okhttp3.Credentials import ru.forbion.f7cloud.core.auth.AuthSession import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import coil.compose.AsyncImage import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.foundation.layout.widthIn import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Popup import androidx.compose.ui.window.PopupProperties import ru.forbion.f7cloud.core.designsystem.F7Colors import ru.forbion.f7cloud.core.designsystem.F7PrimaryButton import ru.forbion.f7cloud.core.designsystem.F7TextButton import java.text.DateFormat import java.util.Calendar import java.util.Date import kotlin.math.abs</ID>
|
||||||
<ID>ImportOrdering:TalkScreen.kt$import android.Manifest import android.content.Context import android.content.pm.PackageManager import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.ui.draw.clip import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.material3.AlertDialog import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import kotlinx.coroutines.launch import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.viewModel import coil.compose.AsyncImage import ru.forbion.f7cloud.core.auth.AuthSession import ru.forbion.f7cloud.core.designsystem.F7Colors import ru.forbion.f7cloud.core.designsystem.F7ModuleScreen import ru.forbion.f7cloud.core.designsystem.F7OverlayDismissHandler import ru.forbion.f7cloud.core.designsystem.F7TextButton import ru.forbion.f7cloud.feature.talknative.TalkNativeCallContext import ru.forbion.f7cloud.feature.talknative.TalkNativeCallLauncher</ID>
|
<ID>ImportOrdering:TalkScreen.kt$import android.Manifest import android.content.Context import android.content.pm.PackageManager import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.ui.draw.clip import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.material3.AlertDialog import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import kotlinx.coroutines.launch import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.viewModel import coil.compose.AsyncImage import ru.forbion.f7cloud.core.auth.AuthSession import ru.forbion.f7cloud.core.designsystem.F7Colors import ru.forbion.f7cloud.core.designsystem.F7ModuleScreen import ru.forbion.f7cloud.core.designsystem.F7OverlayDismissHandler import ru.forbion.f7cloud.core.designsystem.F7TextButton import ru.forbion.f7cloud.feature.talknative.TalkNativeCallContext import ru.forbion.f7cloud.feature.talknative.TalkNativeCallLauncher</ID>
|
||||||
<ID>ImportOrdering:TalkViewModel.kt$import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch 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</ID>
|
<ID>ImportOrdering:TalkViewModel.kt$import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch 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</ID>
|
||||||
@@ -16,7 +15,7 @@
|
|||||||
<ID>LongMethod:TalkComponents.kt$@Composable fun TalkRoomRow( room: TalkRoom, session: AuthSession, modifier: Modifier = Modifier, onClick: () -> Unit, onToggleFavorite: () -> Unit = {}, onMarkUnread: () -> Unit = {}, )</ID>
|
<ID>LongMethod:TalkComponents.kt$@Composable fun TalkRoomRow( room: TalkRoom, session: AuthSession, modifier: Modifier = Modifier, onClick: () -> Unit, onToggleFavorite: () -> Unit = {}, onMarkUnread: () -> Unit = {}, )</ID>
|
||||||
<ID>LongMethod:TalkComponents.kt$@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @Composable fun TalkCreateRoomSheet( visible: Boolean, session: AuthSession, mode: CreateRoomMode, roomName: String, userSearchQuery: String, userSearchResults: List<TalkUserCandidate>, selectedUsers: List<TalkUserCandidate>, userSearchLoading: Boolean, creating: Boolean, onDismiss: () -> Unit, onModeChange: (CreateRoomMode) -> Unit, onRoomNameChange: (String) -> Unit, onUserSearchChange: (String) -> Unit, onUserToggle: (TalkUserCandidate) -> Unit, onCreate: () -> Unit, )</ID>
|
<ID>LongMethod:TalkComponents.kt$@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @Composable fun TalkCreateRoomSheet( visible: Boolean, session: AuthSession, mode: CreateRoomMode, roomName: String, userSearchQuery: String, userSearchResults: List<TalkUserCandidate>, selectedUsers: List<TalkUserCandidate>, userSearchLoading: Boolean, creating: Boolean, onDismiss: () -> Unit, onModeChange: (CreateRoomMode) -> Unit, onRoomNameChange: (String) -> Unit, onUserSearchChange: (String) -> Unit, onUserToggle: (TalkUserCandidate) -> Unit, onCreate: () -> Unit, )</ID>
|
||||||
<ID>LongMethod:TalkScreen.kt$@Composable fun TalkScreen( session: AuthSession, modifier: Modifier = Modifier, openRoomToken: String? = null, scrollToMessageId: Long? = null, chatsListRequest: Int = 0, pushSyncRequest: Int = 0, pushRoomToken: String? = null, onOpenRoomConsumed: () -> Unit = {}, onRoomOpenStateChange: (Boolean) -> Unit = {}, onOpenCalendar: () -> Unit = {}, onUnauthorized: () -> Unit = {}, )</ID>
|
<ID>LongMethod:TalkScreen.kt$@Composable fun TalkScreen( session: AuthSession, modifier: Modifier = Modifier, openRoomToken: String? = null, scrollToMessageId: Long? = null, chatsListRequest: Int = 0, pushSyncRequest: Int = 0, pushRoomToken: String? = null, onOpenRoomConsumed: () -> Unit = {}, onRoomOpenStateChange: (Boolean) -> Unit = {}, onOpenCalendar: () -> Unit = {}, onUnauthorized: () -> Unit = {}, )</ID>
|
||||||
<ID>LongMethod:TalkScreen.kt$@Composable private fun ChatContent( session: AuthSession, state: TalkUiState, draft: String, onDraftChange: (String) -> Unit, onSend: () -> Unit, onAttach: () -> Unit, onVoiceToggle: () -> Unit, voiceRecording: Boolean, onRetry: () -> Unit, onStartCall: () -> Unit, onBackClick: () -> Unit, onOpenRoomInfo: () -> Unit, onScheduleMeeting: () -> Unit, onToggleSystemMessages: () -> Unit, onReply: (TalkMessage) -> Unit, onReaction: (TalkMessage, String) -> Unit, onDeleteMessage: (TalkMessage) -> Unit, onEditMessage: (TalkMessage, String) -> Unit, onForwardNoteToSelf: (TalkMessage) -> Unit, onSetReminder: (TalkMessage, ReminderPreset) -> Unit, onMentionQuery: (String?) -> Unit, onMentionSelected: (TalkMention) -> Unit, onClearReply: () -> Unit, onClearHighlight: () -> Unit, )</ID>
|
<ID>LongMethod:TalkScreen.kt$@Composable private fun ChatContent( session: AuthSession, state: TalkUiState, draft: String, onDraftChange: (String) -> Unit, onSend: () -> Unit, onAttach: () -> Unit, onVoiceToggle: () -> Unit, voiceRecording: Boolean, onRetry: () -> Unit, onStartCall: () -> Unit, onBackClick: () -> Unit, onOpenRoomInfo: () -> Unit, onScheduleMeeting: () -> Unit, onToggleSystemMessages: () -> Unit, onReply: (TalkMessage) -> Unit, onReplyPrivately: (TalkMessage) -> Unit, onReaction: (TalkMessage, String) -> Unit, onDeleteMessage: (TalkMessage) -> Unit, onEditMessage: (TalkMessage, String) -> Unit, onForwardNoteToSelf: (TalkMessage) -> Unit, onSetReminder: (TalkMessage, ReminderPreset) -> Unit, onMentionQuery: (String?) -> Unit, onMentionSelected: (TalkMention) -> Unit, onClearReply: () -> Unit, onClearHighlight: () -> Unit, )</ID>
|
||||||
<ID>LoopWithTooManyJumpStatements:TalkRepository.kt$TalkRepository$for</ID>
|
<ID>LoopWithTooManyJumpStatements:TalkRepository.kt$TalkRepository$for</ID>
|
||||||
<ID>LoopWithTooManyJumpStatements:TalkViewModel.kt$TalkViewModel$while</ID>
|
<ID>LoopWithTooManyJumpStatements:TalkViewModel.kt$TalkViewModel$while</ID>
|
||||||
<ID>MatchingDeclarationName:TalkFilters.kt$TalkRoomFilter</ID>
|
<ID>MatchingDeclarationName:TalkFilters.kt$TalkRoomFilter</ID>
|
||||||
|
|||||||
@@ -799,8 +799,10 @@ fun TalkUnreadMarker(modifier: Modifier = Modifier) {
|
|||||||
fun TalkMessageActionSheet(
|
fun TalkMessageActionSheet(
|
||||||
message: TalkMessage?,
|
message: TalkMessage?,
|
||||||
canModify: Boolean,
|
canModify: Boolean,
|
||||||
|
canReplyPrivately: Boolean,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onReply: (TalkMessage) -> Unit,
|
onReply: (TalkMessage) -> Unit,
|
||||||
|
onReplyPrivately: (TalkMessage) -> Unit,
|
||||||
onEdit: (TalkMessage) -> Unit,
|
onEdit: (TalkMessage) -> Unit,
|
||||||
onDelete: (TalkMessage) -> Unit,
|
onDelete: (TalkMessage) -> Unit,
|
||||||
onReaction: (TalkMessage, String) -> Unit,
|
onReaction: (TalkMessage, String) -> Unit,
|
||||||
@@ -830,6 +832,9 @@ fun TalkMessageActionSheet(
|
|||||||
modifier = Modifier.padding(bottom = 8.dp),
|
modifier = Modifier.padding(bottom = 8.dp),
|
||||||
)
|
)
|
||||||
F7TextButton(text = "Ответить", onClick = { onReply(message) })
|
F7TextButton(text = "Ответить", onClick = { onReply(message) })
|
||||||
|
if (canReplyPrivately) {
|
||||||
|
F7TextButton(text = "Ответить лично", onClick = { onReplyPrivately(message) })
|
||||||
|
}
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
listOf("👍", "❤️", "😂").forEach { emoji ->
|
listOf("👍", "❤️", "😂").forEach { emoji ->
|
||||||
F7TextButton(text = emoji, onClick = { onReaction(message, emoji) })
|
F7TextButton(text = emoji, onClick = { onReaction(message, emoji) })
|
||||||
|
|||||||
@@ -292,6 +292,7 @@ fun TalkScreen(
|
|||||||
onScheduleMeeting = onOpenCalendar,
|
onScheduleMeeting = onOpenCalendar,
|
||||||
onToggleSystemMessages = vm::toggleSystemMessagesCollapse,
|
onToggleSystemMessages = vm::toggleSystemMessagesCollapse,
|
||||||
onReply = { vm.setReplyTo(it) },
|
onReply = { vm.setReplyTo(it) },
|
||||||
|
onReplyPrivately = { vm.replyPrivately(session, it) },
|
||||||
onReaction = { msg, emoji -> vm.addReaction(session, msg.id, emoji) },
|
onReaction = { msg, emoji -> vm.addReaction(session, msg.id, emoji) },
|
||||||
onDeleteMessage = { vm.deleteMessage(session, it.id) },
|
onDeleteMessage = { vm.deleteMessage(session, it.id) },
|
||||||
onEditMessage = { msg, text -> vm.editMessage(session, msg.id, text) },
|
onEditMessage = { msg, text -> vm.editMessage(session, msg.id, text) },
|
||||||
@@ -416,128 +417,76 @@ private fun RoomListContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ChatContent(
|
private fun MessageActionOverlays(
|
||||||
|
actionMessage: TalkMessage?,
|
||||||
session: AuthSession,
|
session: AuthSession,
|
||||||
state: TalkUiState,
|
roomToken: String?,
|
||||||
draft: String,
|
onClearAction: () -> Unit,
|
||||||
onDraftChange: (String) -> Unit,
|
|
||||||
onSend: () -> Unit,
|
|
||||||
onAttach: () -> Unit,
|
|
||||||
onVoiceToggle: () -> Unit,
|
|
||||||
voiceRecording: Boolean,
|
|
||||||
onRetry: () -> Unit,
|
|
||||||
onStartCall: () -> Unit,
|
|
||||||
onBackClick: () -> Unit,
|
|
||||||
onOpenRoomInfo: () -> Unit,
|
|
||||||
onScheduleMeeting: () -> Unit,
|
|
||||||
onToggleSystemMessages: () -> Unit,
|
|
||||||
onReply: (TalkMessage) -> Unit,
|
onReply: (TalkMessage) -> Unit,
|
||||||
|
onReplyPrivately: (TalkMessage) -> Unit,
|
||||||
onReaction: (TalkMessage, String) -> Unit,
|
onReaction: (TalkMessage, String) -> Unit,
|
||||||
onDeleteMessage: (TalkMessage) -> Unit,
|
|
||||||
onEditMessage: (TalkMessage, String) -> Unit,
|
|
||||||
onForwardNoteToSelf: (TalkMessage) -> Unit,
|
onForwardNoteToSelf: (TalkMessage) -> Unit,
|
||||||
onSetReminder: (TalkMessage, ReminderPreset) -> Unit,
|
onSetReminder: (TalkMessage, ReminderPreset) -> Unit,
|
||||||
onMentionQuery: (String?) -> Unit,
|
onEditMessage: (TalkMessage, String) -> Unit,
|
||||||
onMentionSelected: (TalkMention) -> Unit,
|
onDeleteMessage: (TalkMessage) -> Unit,
|
||||||
onClearReply: () -> Unit,
|
|
||||||
onClearHighlight: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
var actionMessage by remember { mutableStateOf<TalkMessage?>(null) }
|
|
||||||
var pendingDelete by remember { mutableStateOf<TalkMessage?>(null) }
|
|
||||||
var editingMessage by remember { mutableStateOf<TalkMessage?>(null) }
|
|
||||||
var editDraft by remember { mutableStateOf("") }
|
|
||||||
val listState = rememberLazyListState()
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val clipboard = LocalClipboardManager.current
|
val clipboard = LocalClipboardManager.current
|
||||||
val chatItems = remember(state.messages, state.openRoomLastReadMessageId, state.collapseSystemMessages) {
|
var editingMessage by remember { mutableStateOf<TalkMessage?>(null) }
|
||||||
buildChatItems(
|
var editDraft by remember { mutableStateOf("") }
|
||||||
messages = state.messages,
|
var pendingDelete by remember { mutableStateOf<TalkMessage?>(null) }
|
||||||
lastReadMessageId = state.openRoomLastReadMessageId,
|
|
||||||
selfActorId = session.username,
|
|
||||||
collapseSystemMessages = state.collapseSystemMessages,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val systemMessageCount = remember(state.messages) {
|
|
||||||
state.messages.count { it.isSystemMessage }
|
|
||||||
}
|
|
||||||
val showScrollDown by remember {
|
|
||||||
derivedStateOf {
|
|
||||||
if (chatItems.isEmpty()) return@derivedStateOf false
|
|
||||||
val info = listState.layoutInfo
|
|
||||||
val lastVisible = info.visibleItemsInfo.lastOrNull()?.index ?: 0
|
|
||||||
lastVisible < info.totalItemsCount - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var stickToBottom by remember { mutableStateOf(true) }
|
|
||||||
|
|
||||||
LaunchedEffect(showScrollDown) {
|
|
||||||
stickToBottom = !showScrollDown
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(chatItems.size, state.highlightMessageId) {
|
|
||||||
val targetId = state.highlightMessageId ?: return@LaunchedEffect
|
|
||||||
val idx = chatItems.indexOfFirst { item ->
|
|
||||||
item is TalkChatItem.MessageGroup && item.messages.any { it.id == targetId }
|
|
||||||
}
|
|
||||||
if (idx >= 0) {
|
|
||||||
listState.animateScrollToItem(idx)
|
|
||||||
kotlinx.coroutines.delay(2500)
|
|
||||||
onClearHighlight()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(chatItems.size) {
|
|
||||||
if (chatItems.isNotEmpty() && stickToBottom) {
|
|
||||||
listState.animateScrollToItem(chatItems.lastIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TalkMessageActionSheet(
|
TalkMessageActionSheet(
|
||||||
message = actionMessage,
|
message = actionMessage,
|
||||||
canModify = actionMessage?.let { msg ->
|
canModify = actionMessage?.let { msg ->
|
||||||
msg.actorId.isNotBlank() && msg.actorId == session.username && !msg.isSystemMessage
|
msg.actorId.isNotBlank() && msg.actorId == session.username && !msg.isSystemMessage
|
||||||
} == true,
|
} == true,
|
||||||
onDismiss = { actionMessage = null },
|
canReplyPrivately = actionMessage?.let { msg ->
|
||||||
|
msg.actorId.isNotBlank() && msg.actorId != session.username && !msg.isSystemMessage
|
||||||
|
} == true,
|
||||||
|
onDismiss = onClearAction,
|
||||||
onReply = {
|
onReply = {
|
||||||
onReply(it)
|
onReply(it)
|
||||||
actionMessage = null
|
onClearAction()
|
||||||
|
},
|
||||||
|
onReplyPrivately = {
|
||||||
|
onReplyPrivately(it)
|
||||||
|
onClearAction()
|
||||||
},
|
},
|
||||||
onEdit = {
|
onEdit = {
|
||||||
editingMessage = it
|
editingMessage = it
|
||||||
editDraft = it.text
|
editDraft = it.text
|
||||||
actionMessage = null
|
onClearAction()
|
||||||
},
|
},
|
||||||
onDelete = {
|
onDelete = {
|
||||||
pendingDelete = it
|
pendingDelete = it
|
||||||
actionMessage = null
|
onClearAction()
|
||||||
},
|
},
|
||||||
onReaction = { msg, emoji ->
|
onReaction = { msg, emoji ->
|
||||||
onReaction(msg, emoji)
|
onReaction(msg, emoji)
|
||||||
actionMessage = null
|
onClearAction()
|
||||||
},
|
},
|
||||||
onCopy = { msg ->
|
onCopy = { msg ->
|
||||||
clipboard.setText(AnnotatedString(msg.displayText))
|
clipboard.setText(AnnotatedString(msg.displayText))
|
||||||
Toast.makeText(context, "Скопировано", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Скопировано", Toast.LENGTH_SHORT).show()
|
||||||
actionMessage = null
|
onClearAction()
|
||||||
},
|
},
|
||||||
onCopyLink = { msg ->
|
onCopyLink = { msg ->
|
||||||
val token = state.selectedRoomToken
|
if (roomToken != null) {
|
||||||
if (token != null) {
|
clipboard.setText(AnnotatedString(TalkUrls.messageLink(session, roomToken, msg.id)))
|
||||||
clipboard.setText(AnnotatedString(TalkUrls.messageLink(session, token, msg.id)))
|
|
||||||
Toast.makeText(context, "Ссылка скопирована", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Ссылка скопирована", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
actionMessage = null
|
onClearAction()
|
||||||
},
|
},
|
||||||
onForwardNoteToSelf = { msg ->
|
onForwardNoteToSelf = { msg ->
|
||||||
onForwardNoteToSelf(msg)
|
onForwardNoteToSelf(msg)
|
||||||
Toast.makeText(context, "Переслано в «Личные заметки»", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Переслано в «Личные заметки»", Toast.LENGTH_SHORT).show()
|
||||||
actionMessage = null
|
onClearAction()
|
||||||
},
|
},
|
||||||
onSetReminder = { msg, preset ->
|
onSetReminder = { msg, preset ->
|
||||||
onSetReminder(msg, preset)
|
onSetReminder(msg, preset)
|
||||||
Toast.makeText(context, "Напоминание установлено", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Напоминание установлено", Toast.LENGTH_SHORT).show()
|
||||||
actionMessage = null
|
onClearAction()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -589,6 +538,95 @@ private fun ChatContent(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ChatContent(
|
||||||
|
session: AuthSession,
|
||||||
|
state: TalkUiState,
|
||||||
|
draft: String,
|
||||||
|
onDraftChange: (String) -> Unit,
|
||||||
|
onSend: () -> Unit,
|
||||||
|
onAttach: () -> Unit,
|
||||||
|
onVoiceToggle: () -> Unit,
|
||||||
|
voiceRecording: Boolean,
|
||||||
|
onRetry: () -> Unit,
|
||||||
|
onStartCall: () -> Unit,
|
||||||
|
onBackClick: () -> Unit,
|
||||||
|
onOpenRoomInfo: () -> Unit,
|
||||||
|
onScheduleMeeting: () -> Unit,
|
||||||
|
onToggleSystemMessages: () -> Unit,
|
||||||
|
onReply: (TalkMessage) -> Unit,
|
||||||
|
onReplyPrivately: (TalkMessage) -> Unit,
|
||||||
|
onReaction: (TalkMessage, String) -> Unit,
|
||||||
|
onDeleteMessage: (TalkMessage) -> Unit,
|
||||||
|
onEditMessage: (TalkMessage, String) -> Unit,
|
||||||
|
onForwardNoteToSelf: (TalkMessage) -> Unit,
|
||||||
|
onSetReminder: (TalkMessage, ReminderPreset) -> Unit,
|
||||||
|
onMentionQuery: (String?) -> Unit,
|
||||||
|
onMentionSelected: (TalkMention) -> Unit,
|
||||||
|
onClearReply: () -> Unit,
|
||||||
|
onClearHighlight: () -> Unit,
|
||||||
|
) {
|
||||||
|
var actionMessage by remember { mutableStateOf<TalkMessage?>(null) }
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val chatItems = remember(state.messages, state.openRoomLastReadMessageId, state.collapseSystemMessages) {
|
||||||
|
buildChatItems(
|
||||||
|
messages = state.messages,
|
||||||
|
lastReadMessageId = state.openRoomLastReadMessageId,
|
||||||
|
selfActorId = session.username,
|
||||||
|
collapseSystemMessages = state.collapseSystemMessages,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val systemMessageCount = remember(state.messages) {
|
||||||
|
state.messages.count { it.isSystemMessage }
|
||||||
|
}
|
||||||
|
val showScrollDown by remember {
|
||||||
|
derivedStateOf {
|
||||||
|
if (chatItems.isEmpty()) return@derivedStateOf false
|
||||||
|
val info = listState.layoutInfo
|
||||||
|
val lastVisible = info.visibleItemsInfo.lastOrNull()?.index ?: 0
|
||||||
|
lastVisible < info.totalItemsCount - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var stickToBottom by remember { mutableStateOf(true) }
|
||||||
|
|
||||||
|
LaunchedEffect(showScrollDown) {
|
||||||
|
stickToBottom = !showScrollDown
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(chatItems.size, state.highlightMessageId) {
|
||||||
|
val targetId = state.highlightMessageId ?: return@LaunchedEffect
|
||||||
|
val idx = chatItems.indexOfFirst { item ->
|
||||||
|
item is TalkChatItem.MessageGroup && item.messages.any { it.id == targetId }
|
||||||
|
}
|
||||||
|
if (idx >= 0) {
|
||||||
|
listState.animateScrollToItem(idx)
|
||||||
|
kotlinx.coroutines.delay(2500)
|
||||||
|
onClearHighlight()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(chatItems.size) {
|
||||||
|
if (chatItems.isNotEmpty() && stickToBottom) {
|
||||||
|
listState.animateScrollToItem(chatItems.lastIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageActionOverlays(
|
||||||
|
actionMessage = actionMessage,
|
||||||
|
session = session,
|
||||||
|
roomToken = state.selectedRoomToken,
|
||||||
|
onClearAction = { actionMessage = null },
|
||||||
|
onReply = onReply,
|
||||||
|
onReplyPrivately = onReplyPrivately,
|
||||||
|
onReaction = onReaction,
|
||||||
|
onForwardNoteToSelf = onForwardNoteToSelf,
|
||||||
|
onSetReminder = onSetReminder,
|
||||||
|
onEditMessage = onEditMessage,
|
||||||
|
onDeleteMessage = onDeleteMessage,
|
||||||
|
)
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
TalkChatBackground(
|
TalkChatBackground(
|
||||||
|
|||||||
@@ -625,6 +625,22 @@ class TalkViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Ответить лично: открыть (или создать) 1:1 с автором сообщения. */
|
||||||
|
fun replyPrivately(session: AuthSession, message: TalkMessage) {
|
||||||
|
val userId = message.actorId
|
||||||
|
if (userId.isBlank() || userId == session.username) return
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
runCatching { repository.createOneToOneRoom(session, userId) }
|
||||||
|
.onSuccess { room -> openRoom(session, room.token) }
|
||||||
|
.onFailure { t ->
|
||||||
|
_state.value = _state.value.copy(
|
||||||
|
error = t.message ?: "Не удалось открыть личный чат",
|
||||||
|
unauthorized = t is UnauthorizedException,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Напоминание на сообщение по пресету (сегодня/завтра/выходные/неделя). */
|
/** Напоминание на сообщение по пресету (сегодня/завтра/выходные/неделя). */
|
||||||
fun setMessageReminder(session: AuthSession, messageId: Long, preset: ReminderPreset) {
|
fun setMessageReminder(session: AuthSession, messageId: Long, preset: ReminderPreset) {
|
||||||
val token = _state.value.selectedRoomToken ?: return
|
val token = _state.value.selectedRoomToken ?: return
|
||||||
|
|||||||
Reference in New Issue
Block a user