Merge pull request #23 from garfiec/refactor/nav-host-viewmodel_stage5
refactor: NavHostViewModel stage 5 — extract drawer models and display mapping
This commit is contained in:
commit
56fd4842be
3 changed files with 84 additions and 80 deletions
|
|
@ -0,0 +1,52 @@
|
|||
package com.garfiec.librechat.shared.navigation
|
||||
|
||||
import com.garfiec.librechat.core.common.extensions.toInstantOrNull
|
||||
import com.garfiec.librechat.core.common.extensions.formatMonthAbbrev
|
||||
import com.garfiec.librechat.core.model.Conversation
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
|
||||
internal data class DrawerDataSnapshot(
|
||||
val grouped: List<Pair<String, List<Conversation>>>,
|
||||
val activeId: String?,
|
||||
val favIds: Set<String>,
|
||||
val favConvos: List<Conversation>,
|
||||
val query: String,
|
||||
)
|
||||
|
||||
internal fun Conversation.toDrawerDisplayData(
|
||||
activeConversationId: String?,
|
||||
favoriteIds: Set<String>,
|
||||
): DrawerConversationDisplayData {
|
||||
val convId = conversationId ?: ""
|
||||
return DrawerConversationDisplayData(
|
||||
conversationId = convId,
|
||||
title = title ?: "New Chat",
|
||||
model = model,
|
||||
endpoint = endpoint,
|
||||
relativeTime = updatedAt?.toInstantOrNull()?.toRelativeTimeString() ?: "",
|
||||
isActive = convId == activeConversationId,
|
||||
isFavorite = convId in favoriteIds,
|
||||
)
|
||||
}
|
||||
|
||||
internal fun Instant.toRelativeTimeString(): String {
|
||||
val now = Clock.System.now()
|
||||
val duration = now - this
|
||||
val minutes = duration.inWholeMinutes
|
||||
val hours = duration.inWholeHours
|
||||
val days = duration.inWholeDays
|
||||
|
||||
return when {
|
||||
minutes < 1 -> "Just now"
|
||||
minutes < 60 -> "${minutes}m ago"
|
||||
hours < 24 -> "${hours}h ago"
|
||||
days < 7 -> "${days}d ago"
|
||||
else -> {
|
||||
val date = toLocalDateTime(TimeZone.currentSystemDefault()).date
|
||||
"${formatMonthAbbrev(date.monthNumber)} ${date.dayOfMonth}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.garfiec.librechat.shared.navigation
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.garfiec.librechat.core.model.EModelEndpoint
|
||||
|
||||
/**
|
||||
* Lightweight snapshot of fields DrawerConversationItem actually renders.
|
||||
* Avoids passing the full 28-field Conversation through composition.
|
||||
*/
|
||||
@Immutable
|
||||
data class DrawerConversationDisplayData(
|
||||
val conversationId: String,
|
||||
val title: String,
|
||||
val model: String?,
|
||||
val endpoint: EModelEndpoint?,
|
||||
val relativeTime: String,
|
||||
val isActive: Boolean,
|
||||
val isFavorite: Boolean,
|
||||
)
|
||||
|
||||
/**
|
||||
* Combined UI state for the drawer sidebar.
|
||||
*/
|
||||
@Immutable
|
||||
data class DrawerUiState(
|
||||
val groupedConversations: List<Pair<String, List<DrawerConversationDisplayData>>> = emptyList(),
|
||||
val favoriteConversations: List<DrawerConversationDisplayData> = emptyList(),
|
||||
val searchQuery: String = "",
|
||||
val isRefreshing: Boolean = false,
|
||||
val isLoadingMore: Boolean = false,
|
||||
val hasMore: Boolean = true,
|
||||
)
|
||||
|
|
@ -1,17 +1,12 @@
|
|||
package com.garfiec.librechat.shared.navigation
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.garfiec.librechat.core.common.extensions.toInstantOrNull
|
||||
import com.garfiec.librechat.core.common.extensions.formatMonthAbbrev
|
||||
import com.garfiec.librechat.core.data.repository.AuthRepository
|
||||
import com.garfiec.librechat.core.data.repository.BannerRepository
|
||||
import com.garfiec.librechat.core.data.repository.ConfigRepository
|
||||
import com.garfiec.librechat.core.data.repository.ConversationRepository
|
||||
import com.garfiec.librechat.core.model.Banner
|
||||
import com.garfiec.librechat.core.model.Conversation
|
||||
import com.garfiec.librechat.core.model.EModelEndpoint
|
||||
import com.garfiec.librechat.core.network.client.TokenManager
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
|
@ -23,38 +18,6 @@ import kotlinx.coroutines.flow.stateIn
|
|||
import kotlinx.coroutines.launch
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.garfiec.librechat.core.common.extensions.firstBlocking
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
|
||||
/**
|
||||
* Lightweight snapshot of fields DrawerConversationItem actually renders.
|
||||
* Avoids passing the full 28-field Conversation through composition.
|
||||
*/
|
||||
@Immutable
|
||||
data class DrawerConversationDisplayData(
|
||||
val conversationId: String,
|
||||
val title: String,
|
||||
val model: String?,
|
||||
val endpoint: EModelEndpoint?,
|
||||
val relativeTime: String,
|
||||
val isActive: Boolean,
|
||||
val isFavorite: Boolean,
|
||||
)
|
||||
|
||||
/**
|
||||
* Combined UI state for the drawer sidebar.
|
||||
*/
|
||||
@Immutable
|
||||
data class DrawerUiState(
|
||||
val groupedConversations: List<Pair<String, List<DrawerConversationDisplayData>>> = emptyList(),
|
||||
val favoriteConversations: List<DrawerConversationDisplayData> = emptyList(),
|
||||
val searchQuery: String = "",
|
||||
val isRefreshing: Boolean = false,
|
||||
val isLoadingMore: Boolean = false,
|
||||
val hasMore: Boolean = true,
|
||||
)
|
||||
|
||||
class NavHostViewModel(
|
||||
private val authRepository: AuthRepository,
|
||||
|
|
@ -205,46 +168,3 @@ class NavHostViewModel(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class DrawerDataSnapshot(
|
||||
val grouped: List<Pair<String, List<Conversation>>>,
|
||||
val activeId: String?,
|
||||
val favIds: Set<String>,
|
||||
val favConvos: List<Conversation>,
|
||||
val query: String,
|
||||
)
|
||||
|
||||
private fun Conversation.toDrawerDisplayData(
|
||||
activeConversationId: String?,
|
||||
favoriteIds: Set<String>,
|
||||
): DrawerConversationDisplayData {
|
||||
val convId = conversationId ?: ""
|
||||
return DrawerConversationDisplayData(
|
||||
conversationId = convId,
|
||||
title = title ?: "New Chat",
|
||||
model = model,
|
||||
endpoint = endpoint,
|
||||
relativeTime = updatedAt?.toInstantOrNull()?.toRelativeTimeString() ?: "",
|
||||
isActive = convId == activeConversationId,
|
||||
isFavorite = convId in favoriteIds,
|
||||
)
|
||||
}
|
||||
|
||||
private fun Instant.toRelativeTimeString(): String {
|
||||
val now = Clock.System.now()
|
||||
val duration = now - this
|
||||
val minutes = duration.inWholeMinutes
|
||||
val hours = duration.inWholeHours
|
||||
val days = duration.inWholeDays
|
||||
|
||||
return when {
|
||||
minutes < 1 -> "Just now"
|
||||
minutes < 60 -> "${minutes}m ago"
|
||||
hours < 24 -> "${hours}h ago"
|
||||
days < 7 -> "${days}d ago"
|
||||
else -> {
|
||||
val date = toLocalDateTime(TimeZone.currentSystemDefault()).date
|
||||
"${formatMonthAbbrev(date.monthNumber)} ${date.dayOfMonth}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue