feat(SearchPost): 吧内搜索记录
This commit is contained in:
parent
e24649efe9
commit
d86f0d9c41
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<litepal>
|
||||
<dbname value="tblite" />
|
||||
<version value="35" />
|
||||
<version value="36" />
|
||||
<list>
|
||||
<mapping class="com.huanchengfly.tieba.post.models.database.Account" />
|
||||
<mapping class="com.huanchengfly.tieba.post.models.database.Draft" />
|
||||
|
|
|
|||
|
|
@ -75,9 +75,9 @@ class SearchPostActivity : BaseActivity() {
|
|||
} else {
|
||||
State.SEARCH
|
||||
}
|
||||
if (value != null) {
|
||||
if (value != null && forumName != null) {
|
||||
refreshLayout.autoRefresh()
|
||||
SearchPostHistory(value, forumName)
|
||||
SearchPostHistory(value, forumName!!)
|
||||
.saveOrUpdate("content = ?", value)
|
||||
}
|
||||
}
|
||||
|
|
@ -170,7 +170,7 @@ class SearchPostActivity : BaseActivity() {
|
|||
state = State.INPUT
|
||||
editText.showSoftInput()
|
||||
} else {
|
||||
finish()
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
package com.huanchengfly.tieba.post.models.database;
|
||||
|
||||
import org.litepal.crud.LitePalSupport;
|
||||
|
||||
public class SearchPostHistory extends LitePalSupport {
|
||||
private int id;
|
||||
private long timestamp;
|
||||
private String content;
|
||||
private String forumName;
|
||||
|
||||
public SearchPostHistory(String content, String forumName) {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
this.content = content;
|
||||
this.forumName = forumName;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getForumName() {
|
||||
return forumName;
|
||||
}
|
||||
|
||||
public void setForumName(String forumName) {
|
||||
this.forumName = forumName;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.huanchengfly.tieba.post.models.database
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import org.litepal.crud.LitePalSupport
|
||||
|
||||
@Immutable
|
||||
class SearchPostHistory(
|
||||
val content: String,
|
||||
val forumName: String,
|
||||
val timestamp: Long = System.currentTimeMillis(),
|
||||
) : LitePalSupport() {
|
||||
val id: Long = 0
|
||||
}
|
||||
|
|
@ -1,27 +1,38 @@
|
|||
package com.huanchengfly.tieba.post.ui.page.forum.searchpost
|
||||
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
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.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.ContentAlpha
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
||||
import androidx.compose.material.icons.rounded.ArrowDropDown
|
||||
import androidx.compose.material.icons.rounded.ExpandLess
|
||||
import androidx.compose.material.icons.rounded.ExpandMore
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
|
|
@ -38,6 +49,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -45,9 +57,11 @@ import androidx.compose.ui.semantics.Role
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.huanchengfly.tieba.post.R
|
||||
import com.huanchengfly.tieba.post.arch.collectPartialAsState
|
||||
import com.huanchengfly.tieba.post.arch.pageViewModel
|
||||
import com.huanchengfly.tieba.post.models.database.SearchPostHistory
|
||||
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
|
||||
import com.huanchengfly.tieba.post.ui.common.theme.compose.pullRefreshIndicator
|
||||
import com.huanchengfly.tieba.post.ui.page.ProvideNavigator
|
||||
|
|
@ -55,6 +69,7 @@ import com.huanchengfly.tieba.post.ui.page.destinations.ForumPageDestination
|
|||
import com.huanchengfly.tieba.post.ui.page.destinations.SubPostsPageDestination
|
||||
import com.huanchengfly.tieba.post.ui.page.destinations.ThreadPageDestination
|
||||
import com.huanchengfly.tieba.post.ui.page.destinations.UserProfilePageDestination
|
||||
import com.huanchengfly.tieba.post.ui.widgets.compose.Button
|
||||
import com.huanchengfly.tieba.post.ui.widgets.compose.ClickMenu
|
||||
import com.huanchengfly.tieba.post.ui.widgets.compose.ErrorScreen
|
||||
import com.huanchengfly.tieba.post.ui.widgets.compose.HorizontalDivider
|
||||
|
|
@ -68,9 +83,121 @@ import com.huanchengfly.tieba.post.ui.widgets.compose.rememberMenuState
|
|||
import com.huanchengfly.tieba.post.ui.widgets.compose.states.StateScreen
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class, ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun SearchHistoryList(
|
||||
searchHistories: ImmutableList<SearchPostHistory>,
|
||||
onSearchHistoryClick: (SearchPostHistory) -> Unit,
|
||||
expanded: Boolean = false,
|
||||
onToggleExpand: () -> Unit = {},
|
||||
onDelete: (SearchPostHistory) -> Unit = {},
|
||||
onClear: () -> Unit = {},
|
||||
) {
|
||||
val hasItem = remember(searchHistories) {
|
||||
searchHistories.isNotEmpty()
|
||||
}
|
||||
val hasMore = remember(searchHistories) {
|
||||
searchHistories.size > 6
|
||||
}
|
||||
val showItem = remember(expanded, hasMore, searchHistories) {
|
||||
if (!expanded && hasMore) searchHistories.take(6) else searchHistories
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.title_search_history),
|
||||
modifier = Modifier
|
||||
.weight(1f),
|
||||
style = MaterialTheme.typography.subtitle1
|
||||
)
|
||||
if (hasItem) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.button_clear_all),
|
||||
modifier = Modifier.clickable(onClick = onClear),
|
||||
style = MaterialTheme.typography.button
|
||||
)
|
||||
}
|
||||
}
|
||||
FlowRow(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.animateContentSize(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
showItem.fastForEach { searchHistory ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 8.dp)
|
||||
.clip(RoundedCornerShape(100))
|
||||
.combinedClickable(
|
||||
onClick = { onSearchHistoryClick(searchHistory) },
|
||||
onLongClick = { onDelete(searchHistory) }
|
||||
)
|
||||
.background(ExtendedTheme.colors.chip)
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = searchHistory.content
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasMore) {
|
||||
Button(
|
||||
onClick = onToggleExpand,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
backgroundColor = Color.Transparent,
|
||||
contentColor = ExtendedTheme.colors.text
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (expanded) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = if (expanded) R.string.button_expand_less_history else R.string.button_expand_more_history
|
||||
),
|
||||
style = MaterialTheme.typography.button,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasItem) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.tip_empty),
|
||||
color = ExtendedTheme.colors.textDisabled,
|
||||
fontSize = 16.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
|
||||
@Destination
|
||||
@Composable
|
||||
|
|
@ -78,7 +205,9 @@ fun ForumSearchPostPage(
|
|||
forumName: String,
|
||||
forumId: Long,
|
||||
navigator: DestinationsNavigator,
|
||||
viewModel: ForumSearchPostViewModel = pageViewModel(),
|
||||
viewModel: ForumSearchPostViewModel = pageViewModel<ForumSearchPostUiIntent, ForumSearchPostViewModel>(
|
||||
listOf(ForumSearchPostUiIntent.Init)
|
||||
),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val currentKeyword by viewModel.uiState.collectPartialAsState(
|
||||
|
|
@ -117,6 +246,11 @@ fun ForumSearchPostPage(
|
|||
prop1 = ForumSearchPostUiState::hasMore,
|
||||
initial = true
|
||||
)
|
||||
val searchHistories by viewModel.uiState.collectPartialAsState(
|
||||
prop1 = ForumSearchPostUiState::searchHistories,
|
||||
initial = persistentListOf()
|
||||
)
|
||||
|
||||
val isEmpty by remember {
|
||||
derivedStateOf { data.isEmpty() }
|
||||
}
|
||||
|
|
@ -453,6 +587,27 @@ fun ForumSearchPostPage(
|
|||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SearchHistoryList(
|
||||
searchHistories = searchHistories,
|
||||
onSearchHistoryClick = {
|
||||
viewModel.send(
|
||||
ForumSearchPostUiIntent.Refresh(
|
||||
it.content,
|
||||
forumName,
|
||||
forumId,
|
||||
currentSortType,
|
||||
currentFilterType
|
||||
)
|
||||
)
|
||||
},
|
||||
onDelete = {
|
||||
viewModel.send(ForumSearchPostUiIntent.DeleteHistory(it.id))
|
||||
},
|
||||
onClear = {
|
||||
viewModel.send(ForumSearchPostUiIntent.ClearHistory)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
package com.huanchengfly.tieba.post.ui.page.forum.searchpost
|
||||
|
||||
import com.huanchengfly.tieba.post.App
|
||||
import com.huanchengfly.tieba.post.R
|
||||
import com.huanchengfly.tieba.post.api.TiebaApi
|
||||
import com.huanchengfly.tieba.post.api.models.SearchThreadBean
|
||||
import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorMessage
|
||||
import com.huanchengfly.tieba.post.arch.BaseViewModel
|
||||
import com.huanchengfly.tieba.post.arch.CommonUiEvent
|
||||
import com.huanchengfly.tieba.post.arch.ImmutableHolder
|
||||
import com.huanchengfly.tieba.post.arch.PartialChange
|
||||
import com.huanchengfly.tieba.post.arch.PartialChangeProducer
|
||||
|
|
@ -10,18 +14,29 @@ import com.huanchengfly.tieba.post.arch.UiEvent
|
|||
import com.huanchengfly.tieba.post.arch.UiIntent
|
||||
import com.huanchengfly.tieba.post.arch.UiState
|
||||
import com.huanchengfly.tieba.post.arch.wrapImmutable
|
||||
import com.huanchengfly.tieba.post.models.database.SearchPostHistory
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.flatMapConcat
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import org.litepal.LitePal
|
||||
import org.litepal.extension.delete
|
||||
import org.litepal.extension.deleteAll
|
||||
import org.litepal.extension.find
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
|
|
@ -32,20 +47,69 @@ class ForumSearchPostViewModel @Inject constructor() :
|
|||
override fun createPartialChangeProducer(): PartialChangeProducer<ForumSearchPostUiIntent, ForumSearchPostPartialChange, ForumSearchPostUiState> =
|
||||
ForumSearchPostPartialChangeProducer
|
||||
|
||||
override fun dispatchEvent(partialChange: ForumSearchPostPartialChange): UiEvent? =
|
||||
when (partialChange) {
|
||||
is ForumSearchPostPartialChange.DeleteHistory.Failure -> CommonUiEvent.Toast(
|
||||
App.INSTANCE.getString(
|
||||
R.string.toast_delete_failure,
|
||||
partialChange.error.getErrorMessage()
|
||||
)
|
||||
)
|
||||
|
||||
is ForumSearchPostPartialChange.ClearHistory.Success -> CommonUiEvent.Toast(
|
||||
App.INSTANCE.getString(R.string.toast_clear_success)
|
||||
)
|
||||
|
||||
is ForumSearchPostPartialChange.ClearHistory.Failure -> CommonUiEvent.Toast(
|
||||
App.INSTANCE.getString(
|
||||
R.string.toast_clear_failure,
|
||||
partialChange.error.getErrorMessage()
|
||||
)
|
||||
)
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
private object ForumSearchPostPartialChangeProducer :
|
||||
PartialChangeProducer<ForumSearchPostUiIntent, ForumSearchPostPartialChange, ForumSearchPostUiState> {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun toPartialChangeFlow(intentFlow: Flow<ForumSearchPostUiIntent>): Flow<ForumSearchPostPartialChange> =
|
||||
merge(
|
||||
intentFlow.filterIsInstance<ForumSearchPostUiIntent.Init>()
|
||||
.flatMapConcat { produceInitPartialChange() },
|
||||
intentFlow.filterIsInstance<ForumSearchPostUiIntent.Refresh>()
|
||||
.flatMapConcat { it.producePartialChange() },
|
||||
intentFlow.filterIsInstance<ForumSearchPostUiIntent.LoadMore>()
|
||||
.flatMapConcat { it.producePartialChange() },
|
||||
intentFlow.filterIsInstance<ForumSearchPostUiIntent.DeleteHistory>()
|
||||
.flatMapConcat { it.producePartialChange() },
|
||||
intentFlow.filterIsInstance<ForumSearchPostUiIntent.ClearHistory>()
|
||||
.flatMapConcat { produceClearHistoryPartialChange() },
|
||||
)
|
||||
|
||||
private fun produceInitPartialChange(): Flow<ForumSearchPostPartialChange.Init> =
|
||||
flow<ForumSearchPostPartialChange.Init> {
|
||||
val searchHistories = LitePal
|
||||
.order("timestamp DESC")
|
||||
.find<SearchPostHistory>()
|
||||
emit(ForumSearchPostPartialChange.Init.Success(searchHistories))
|
||||
}.catch {
|
||||
emit(ForumSearchPostPartialChange.Init.Failure(it))
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun ForumSearchPostUiIntent.Refresh.producePartialChange(): Flow<ForumSearchPostPartialChange.Refresh> =
|
||||
TiebaApi.getInstance()
|
||||
.searchPostFlow(keyword, forumName, forumId, sortType, filterType)
|
||||
flowOf(keyword.trim())
|
||||
.filter { it.isNotBlank() }
|
||||
.onEach {
|
||||
runCatching {
|
||||
SearchPostHistory(it, forumName).saveOrUpdate("content = ?", it)
|
||||
}
|
||||
}
|
||||
.flatMapConcat {
|
||||
TiebaApi.getInstance()
|
||||
.searchPostFlow(it, forumName, forumId, sortType, filterType)
|
||||
}
|
||||
.map<SearchThreadBean, ForumSearchPostPartialChange.Refresh> {
|
||||
val postList = it.data.postList.toImmutableList()
|
||||
ForumSearchPostPartialChange.Refresh.Success(
|
||||
|
|
@ -60,12 +124,14 @@ class ForumSearchPostViewModel @Inject constructor() :
|
|||
emit(
|
||||
ForumSearchPostPartialChange.Refresh.Start(
|
||||
keyword,
|
||||
forumName,
|
||||
sortType,
|
||||
filterType
|
||||
)
|
||||
)
|
||||
}
|
||||
.catch { emit(ForumSearchPostPartialChange.Refresh.Failure(it)) }
|
||||
.flowOn(Dispatchers.IO)
|
||||
|
||||
private fun ForumSearchPostUiIntent.LoadMore.producePartialChange(): Flow<ForumSearchPostPartialChange.LoadMore> =
|
||||
TiebaApi.getInstance()
|
||||
|
|
@ -83,10 +149,28 @@ class ForumSearchPostViewModel @Inject constructor() :
|
|||
}
|
||||
.onStart { emit(ForumSearchPostPartialChange.LoadMore.Start) }
|
||||
.catch { emit(ForumSearchPostPartialChange.LoadMore.Failure(it)) }
|
||||
|
||||
private fun ForumSearchPostUiIntent.DeleteHistory.producePartialChange(): Flow<ForumSearchPostPartialChange.DeleteHistory> =
|
||||
flow<ForumSearchPostPartialChange.DeleteHistory> {
|
||||
LitePal.delete<SearchPostHistory>(id)
|
||||
emit(ForumSearchPostPartialChange.DeleteHistory.Success(id))
|
||||
}.catch {
|
||||
emit(ForumSearchPostPartialChange.DeleteHistory.Failure(it))
|
||||
}
|
||||
|
||||
private fun produceClearHistoryPartialChange(): Flow<ForumSearchPostPartialChange.ClearHistory> =
|
||||
flow<ForumSearchPostPartialChange.ClearHistory> {
|
||||
LitePal.deleteAll<SearchPostHistory>()
|
||||
emit(ForumSearchPostPartialChange.ClearHistory.Success)
|
||||
}.catch {
|
||||
emit(ForumSearchPostPartialChange.ClearHistory.Failure(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface ForumSearchPostUiIntent : UiIntent {
|
||||
data object Init : ForumSearchPostUiIntent
|
||||
|
||||
data class Refresh(
|
||||
val keyword: String,
|
||||
val forumName: String,
|
||||
|
|
@ -103,20 +187,52 @@ sealed interface ForumSearchPostUiIntent : UiIntent {
|
|||
val sortType: Int,
|
||||
val filterType: Int,
|
||||
) : ForumSearchPostUiIntent
|
||||
|
||||
data class DeleteHistory(val id: Long) : ForumSearchPostUiIntent
|
||||
|
||||
data object ClearHistory : ForumSearchPostUiIntent
|
||||
}
|
||||
|
||||
sealed interface ForumSearchPostPartialChange : PartialChange<ForumSearchPostUiState> {
|
||||
sealed class Init : ForumSearchPostPartialChange {
|
||||
override fun reduce(oldState: ForumSearchPostUiState): ForumSearchPostUiState =
|
||||
when (this) {
|
||||
is Success -> oldState.copy(
|
||||
searchHistories = searchHistories.toImmutableList()
|
||||
)
|
||||
|
||||
is Failure -> oldState
|
||||
}
|
||||
|
||||
data class Success(
|
||||
val searchHistories: List<SearchPostHistory>,
|
||||
) : Init()
|
||||
|
||||
data class Failure(
|
||||
val error: Throwable,
|
||||
) : Init()
|
||||
}
|
||||
|
||||
sealed class Refresh : ForumSearchPostPartialChange {
|
||||
override fun reduce(oldState: ForumSearchPostUiState): ForumSearchPostUiState =
|
||||
when (this) {
|
||||
is Start -> oldState.copy(
|
||||
isRefreshing = true,
|
||||
isLoadingMore = false,
|
||||
error = null,
|
||||
keyword = keyword,
|
||||
sortType = sortType,
|
||||
filterType = filterType,
|
||||
)
|
||||
is Start -> {
|
||||
val newSearchHistories = (oldState.searchHistories
|
||||
.filterNot { it.content == keyword } + SearchPostHistory(
|
||||
keyword,
|
||||
forumName
|
||||
))
|
||||
.sortedByDescending { it.timestamp }
|
||||
oldState.copy(
|
||||
isRefreshing = true,
|
||||
isLoadingMore = false,
|
||||
error = null,
|
||||
searchHistories = newSearchHistories.toImmutableList(),
|
||||
keyword = keyword,
|
||||
sortType = sortType,
|
||||
filterType = filterType,
|
||||
)
|
||||
}
|
||||
|
||||
is Success -> oldState.copy(
|
||||
isRefreshing = false,
|
||||
|
|
@ -139,6 +255,7 @@ sealed interface ForumSearchPostPartialChange : PartialChange<ForumSearchPostUiS
|
|||
|
||||
data class Start(
|
||||
val keyword: String,
|
||||
val forumName: String,
|
||||
val sortType: Int,
|
||||
val filterType: Int,
|
||||
) : Refresh()
|
||||
|
|
@ -196,12 +313,48 @@ sealed interface ForumSearchPostPartialChange : PartialChange<ForumSearchPostUiS
|
|||
val error: Throwable,
|
||||
) : LoadMore()
|
||||
}
|
||||
|
||||
sealed class DeleteHistory : ForumSearchPostPartialChange {
|
||||
override fun reduce(oldState: ForumSearchPostUiState): ForumSearchPostUiState =
|
||||
when (this) {
|
||||
is Success -> oldState.copy(
|
||||
searchHistories = oldState.searchHistories.filterNot { it.id == id }
|
||||
.toImmutableList()
|
||||
)
|
||||
|
||||
is Failure -> oldState
|
||||
}
|
||||
|
||||
data class Success(val id: Long) : DeleteHistory()
|
||||
|
||||
data class Failure(
|
||||
val error: Throwable,
|
||||
) : DeleteHistory()
|
||||
}
|
||||
|
||||
sealed class ClearHistory : ForumSearchPostPartialChange {
|
||||
override fun reduce(oldState: ForumSearchPostUiState): ForumSearchPostUiState =
|
||||
when (this) {
|
||||
is Success -> oldState.copy(
|
||||
searchHistories = persistentListOf()
|
||||
)
|
||||
|
||||
is Failure -> oldState
|
||||
}
|
||||
|
||||
data object Success : ClearHistory()
|
||||
|
||||
data class Failure(
|
||||
val error: Throwable,
|
||||
) : ClearHistory()
|
||||
}
|
||||
}
|
||||
|
||||
data class ForumSearchPostUiState(
|
||||
val isRefreshing: Boolean = true,
|
||||
val isLoadingMore: Boolean = false,
|
||||
val error: ImmutableHolder<Throwable>? = null,
|
||||
val searchHistories: ImmutableList<SearchPostHistory> = persistentListOf(),
|
||||
val currentPage: Int = 1,
|
||||
val hasMore: Boolean = true,
|
||||
val keyword: String = "",
|
||||
|
|
|
|||
|
|
@ -104,16 +104,15 @@ class SearchViewModel :
|
|||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
private fun SearchUiIntent.SubmitKeyword.producePartialChange() =
|
||||
flowOf(SearchPartialChange.SubmitKeyword(keyword.trim()))
|
||||
flowOf(keyword.trim())
|
||||
.onEach {
|
||||
runCatching {
|
||||
val trimKeyword = keyword.trim()
|
||||
if (trimKeyword.isNotBlank()) SearchHistory(trimKeyword).saveOrUpdate(
|
||||
"content = ?",
|
||||
trimKeyword
|
||||
)
|
||||
if (it.isNotBlank()) {
|
||||
runCatching {
|
||||
SearchHistory(it).saveOrUpdate("content = ?", it)
|
||||
}
|
||||
}
|
||||
}
|
||||
.map { SearchPartialChange.SubmitKeyword(it) }
|
||||
|
||||
private fun SearchUiIntent.KeywordInputChanged.producePartialChange() =
|
||||
if (keyword.isNotBlank()) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue