1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

DROID-2734 Global search | Save search query, related to state (#1541)

This commit is contained in:
Konstantin Ivanov 2024-09-16 16:51:22 +02:00 committed by GitHub
parent 0544fb7cf9
commit 6f6c08b0bb
Signed by: github
GPG key ID: B5690EEEBB952194
37 changed files with 591 additions and 419 deletions

View file

@ -82,7 +82,6 @@ import com.anytypeio.anytype.domain.page.bookmark.CreateBookmarkBlock
import com.anytypeio.anytype.domain.page.bookmark.SetupBookmark
import com.anytypeio.anytype.domain.relations.AddRelationToObject
import com.anytypeio.anytype.domain.relations.SetRelationKey
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.search.SearchObjects
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.table.CreateTable
@ -295,9 +294,6 @@ open class EditorTestSetup {
@Mock
lateinit var spaceSyncAndP2PStatusProvider: SpaceSyncAndP2PStatusProvider
@Mock
lateinit var getLastSearchQuery: GetLastSearchQuery
lateinit var interceptFileLimitEvents: InterceptFileLimitEvents
lateinit var addRelationToObject: AddRelationToObject
@ -502,8 +498,7 @@ open class EditorTestSetup {
syncStatusProvider = spaceSyncAndP2PStatusProvider,
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
clearLastOpenedObject = clearLastOpenedObject,
getNetworkMode = getNetworkMode,
getLastSearchQuery = getLastSearchQuery
getNetworkMode = getNetworkMode
)
}

View file

@ -48,7 +48,6 @@ import com.anytypeio.anytype.domain.page.CloseBlock
import com.anytypeio.anytype.domain.page.CreateObject
import com.anytypeio.anytype.domain.search.CancelSearchSubscription
import com.anytypeio.anytype.domain.search.DataViewSubscriptionContainer
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.domain.sets.OpenObjectSet
import com.anytypeio.anytype.domain.sets.SetQueryToObjectSet
@ -186,9 +185,6 @@ abstract class TestObjectSetSetup {
@Mock
lateinit var clearLastOpenedObject: ClearLastOpenedObject
@Mock
lateinit var getLastSearchQuery: GetLastSearchQuery
private lateinit var getTemplates: GetTemplates
private lateinit var getDefaultObjectType: GetDefaultObjectType
@ -316,8 +312,7 @@ abstract class TestObjectSetSetup {
permissions = permissions,
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
spaceSyncAndP2PStatusProvider = spaceSyncAndP2PStatusProvider,
clearLastOpenedObject = clearLastOpenedObject,
getLastSearchQuery = getLastSearchQuery
clearLastOpenedObject = clearLastOpenedObject
)
}

View file

@ -80,7 +80,6 @@ import com.anytypeio.anytype.domain.page.bookmark.SetupBookmark
import com.anytypeio.anytype.domain.relations.AddFileToObject
import com.anytypeio.anytype.domain.relations.AddRelationToObject
import com.anytypeio.anytype.domain.relations.SetRelationKey
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.search.SearchObjects
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.table.CreateTable
@ -293,8 +292,7 @@ object EditorSessionModule {
analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
syncStatusProvider: SpaceSyncAndP2PStatusProvider,
getNetworkMode: GetNetworkMode,
clearLastOpenedObject: ClearLastOpenedObject,
getLastSearchQuery: GetLastSearchQuery
clearLastOpenedObject: ClearLastOpenedObject
): EditorViewModelFactory = EditorViewModelFactory(
params = params,
permissions = permissions,
@ -340,8 +338,7 @@ object EditorSessionModule {
getNetworkMode = getNetworkMode,
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
clearLastOpenedObject = clearLastOpenedObject,
syncStatusProvider = syncStatusProvider,
getLastSearchQuery = getLastSearchQuery
syncStatusProvider = syncStatusProvider
)
@JvmStatic
@ -1204,14 +1201,6 @@ object EditorUseCaseModule {
spaceManager = spaceManager
)
@JvmStatic
@Provides
@PerScreen
fun provideGetLastSearchQueryUseCase(
repo: UserSettingsRepository,
dispatchers: AppCoroutineDispatchers
): GetLastSearchQuery = GetLastSearchQuery(repo, dispatchers)
@Module
interface Bindings {

View file

@ -57,7 +57,6 @@ import com.anytypeio.anytype.domain.relations.AddRelationToObject
import com.anytypeio.anytype.domain.relations.DeleteRelationFromDataView
import com.anytypeio.anytype.domain.search.CancelSearchSubscription
import com.anytypeio.anytype.domain.search.DataViewSubscriptionContainer
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.search.SearchObjects
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.domain.sets.OpenObjectSet
@ -246,8 +245,7 @@ object ObjectSetModule {
permissions: UserPermissionProvider,
clearLastOpenedObject: ClearLastOpenedObject,
analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
spaceSyncAndP2PStatusProvider: SpaceSyncAndP2PStatusProvider,
getLastSearchQuery: GetLastSearchQuery
spaceSyncAndP2PStatusProvider: SpaceSyncAndP2PStatusProvider
): ObjectSetViewModelFactory = ObjectSetViewModelFactory(
params = params,
openObjectSet = openObjectSet,
@ -289,18 +287,9 @@ object ObjectSetModule {
permissions = permissions,
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
spaceSyncAndP2PStatusProvider = spaceSyncAndP2PStatusProvider,
clearLastOpenedObject = clearLastOpenedObject,
getLastSearchQuery = getLastSearchQuery
clearLastOpenedObject = clearLastOpenedObject
)
@JvmStatic
@Provides
@PerScreen
fun provideGetLastSearchQuery(
repo: UserSettingsRepository,
dispatchers: AppCoroutineDispatchers
): GetLastSearchQuery = GetLastSearchQuery(repo, dispatchers)
@JvmStatic
@Provides
@PerScreen

View file

@ -135,14 +135,10 @@ class Navigator : AppNavigation {
}
}
override fun openPageSearch(initialQuery: String, space: Id) {
// Old search
// navController?.navigate(R.id.pageSearchFragment)
// Uncomment to use new search
override fun openGlobalSearch(space: Id) {
navController?.navigate(
R.id.globalSearchScreen,
GlobalSearchFragment.args(
initialQuery = initialQuery,
resId = R.id.globalSearchScreen,
args = GlobalSearchFragment.args(
space = space
)
)

View file

@ -38,8 +38,7 @@ class NavigationRouter(
)
is AppNavigation.Command.Exit -> navigation.exit()
is AppNavigation.Command.ExitToDesktop -> navigation.exitToDesktop()
is AppNavigation.Command.OpenPageSearch -> navigation.openPageSearch(
initialQuery = command.initialQuery,
is AppNavigation.Command.OpenGlobalSearch -> navigation.openGlobalSearch(
space = command.space
)
is AppNavigation.Command.OpenUpdateAppScreen -> navigation.openUpdateAppScreen()

View file

@ -312,8 +312,7 @@ class HomeScreenFragment : BaseComposeFragment() {
}
is Command.OpenGlobalSearchScreen -> {
runCatching {
navigation().openPageSearch(
initialQuery = command.initialQuery,
navigation().openGlobalSearch(
space = command.space
)
}.onFailure {

View file

@ -37,7 +37,6 @@ class GlobalSearchFragment : BaseBottomSheetComposeFragment() {
private val vm by viewModels<GlobalSearchViewModel> { factory }
private val initialQuery get() = argString(ARG_INITIAL_STATE)
private val space get() = argString(ARG_SPACE)
override fun onCreateView(
@ -107,7 +106,6 @@ class GlobalSearchFragment : BaseBottomSheetComposeFragment() {
override fun injectDependencies() {
val params = GlobalSearchViewModel.VmParams(
initialQuery = initialQuery,
space = SpaceId(space)
)
componentManager().globalSearchComponent.get(params).inject(this)
@ -120,9 +118,7 @@ class GlobalSearchFragment : BaseBottomSheetComposeFragment() {
companion object {
const val KEYBOARD_HIDE_DELAY = 300L
const val ARG_INITIAL_STATE = "arg.global.search.initial_state"
const val ARG_SPACE = "arg.global.search.space"
fun args(initialQuery: String, space: Id): Bundle =
bundleOf(ARG_INITIAL_STATE to initialQuery, ARG_SPACE to space)
fun args(space: Id): Bundle = bundleOf(ARG_SPACE to space)
}
}

View file

@ -27,6 +27,8 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
@ -35,6 +37,7 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -115,6 +118,13 @@ fun GlobalSearchScreen(
onClearRelatedClicked: () -> Unit
) {
val selectionColors = TextSelectionColors(
backgroundColor = colorResource(id = R.color.cursor_color).copy(
alpha = 0.2f
),
handleColor = colorResource(id = R.color.cursor_color),
)
var showLoading by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(state.isLoading) {
@ -130,7 +140,17 @@ fun GlobalSearchScreen(
var query by remember { mutableStateOf(TextFieldValue()) }
if (state is GlobalSearchViewModel.ViewState.Init) {
query = TextFieldValue(text = state.query, selection = TextRange(start = 0, end = state.query.length))
query = TextFieldValue(
text = state.query,
selection = TextRange(start = 0, end = state.query.length)
)
}
if (state is GlobalSearchViewModel.ViewState.RelatedInit) {
query = TextFieldValue(
text = state.query,
selection = TextRange(start = 0, end = state.query.length)
)
}
Column(
@ -175,54 +195,57 @@ fun GlobalSearchScreen(
start = 11.dp
)
)
BasicTextField(
value = query,
modifier = Modifier
.weight(1.0f)
.padding(start = 6.dp)
.align(Alignment.CenterVertically)
.focusRequester(focusRequester)
,
textStyle = BodyRegular.copy(
color = colorResource(id = R.color.text_primary)
),
onValueChange = { input ->
query = input.also {
onQueryChanged(input.text)
}
},
singleLine = true,
maxLines = 1,
keyboardActions = KeyboardActions(
onDone = {
focus.clearFocus(true)
}
),
decorationBox = @Composable { innerTextField ->
TextFieldDefaults.OutlinedTextFieldDecorationBox(
value = query.text,
innerTextField = innerTextField,
enabled = true,
singleLine = true,
visualTransformation = VisualTransformation.None,
interactionSource = interactionSource,
placeholder = {
Text(
text = stringResource(id = R.string.search),
style = BodyRegular.copy(
color = colorResource(id = R.color.text_tertiary)
CompositionLocalProvider(value = LocalTextSelectionColors provides selectionColors) {
BasicTextField(
value = query,
modifier = Modifier
.weight(1.0f)
.padding(start = 6.dp)
.align(Alignment.CenterVertically)
.focusRequester(focusRequester),
textStyle = BodyRegular.copy(
color = colorResource(id = R.color.text_primary)
),
onValueChange = { input ->
query = input.also {
onQueryChanged(input.text)
}
},
singleLine = true,
maxLines = 1,
keyboardActions = KeyboardActions(
onDone = {
focus.clearFocus(true)
}
),
decorationBox = @Composable { innerTextField ->
TextFieldDefaults.OutlinedTextFieldDecorationBox(
value = query.text,
innerTextField = innerTextField,
enabled = true,
singleLine = true,
visualTransformation = VisualTransformation.None,
interactionSource = interactionSource,
placeholder = {
Text(
text = stringResource(id = R.string.search),
style = BodyRegular.copy(
color = colorResource(id = R.color.text_tertiary)
)
)
)
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = colorResource(id = R.color.shape_transparent)
),
border = {},
contentPadding = PaddingValues()
)
},
cursorBrush = SolidColor(colorResource(id = R.color.palette_system_blue)),
)
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = colorResource(id = R.color.shape_transparent),
cursorColor = colorResource(id = R.color.cursor_color),
),
border = {},
contentPadding = PaddingValues()
)
},
cursorBrush = SolidColor(colorResource(id = R.color.palette_system_blue)),
)
}
Box(
modifier = Modifier.size(16.dp)
) {
@ -260,55 +283,18 @@ fun GlobalSearchScreen(
) {
if (state is GlobalSearchViewModel.ViewState.Related) {
stickyHeader {
Row(
modifier = Modifier
.height(48.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.Bottom
) {
Text(
text = buildAnnotatedString {
append(stringResource(R.string.global_search_related_to))
withStyle(
style = SpanStyle(
fontWeight = FontWeight.Bold
)
) {
append(state.target.title)
}
},
style = Caption1Regular,
color = colorResource(id = R.color.text_secondary),
modifier = Modifier
.weight(1.0f)
.padding(
start = 20.dp,
bottom = 8.dp
),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = stringResource(id = R.string.clear),
style = Caption1Regular,
color = colorResource(id = R.color.text_secondary),
modifier = Modifier
.padding(
start = 20.dp,
end = 20.dp,
bottom = 8.dp
)
.clickable {
onClearRelatedClicked().also {
query = TextFieldValue()
}
}
)
RelatedHeader(title = state.target.title) {
onClearRelatedClicked()
query = TextFieldValue()
}
}
}
if (state is GlobalSearchViewModel.ViewState.RelatedInit) {
stickyHeader {
RelatedHeader(title = state.target.title) {
onClearRelatedClicked()
query = TextFieldValue()
}
Divider(
paddingStart = 20.dp,
paddingEnd = 20.dp
)
}
}
items(
@ -393,6 +379,60 @@ fun GlobalSearchScreen(
}
}
@Composable
private fun RelatedHeader(
title: String,
onClearRelatedClicked: () -> Unit
) {
Row(
modifier = Modifier
.height(48.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.Bottom
) {
Text(
text = buildAnnotatedString {
append(stringResource(R.string.global_search_related_to))
withStyle(
style = SpanStyle(
fontWeight = FontWeight.Bold
)
) {
append(title)
}
},
style = Caption1Regular,
color = colorResource(id = R.color.text_secondary),
modifier = Modifier
.weight(1.0f)
.padding(
start = 20.dp,
bottom = 8.dp
),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = stringResource(id = R.string.clear),
style = Caption1Regular,
color = colorResource(id = R.color.text_secondary),
modifier = Modifier
.padding(
start = 20.dp,
end = 20.dp,
bottom = 8.dp
)
.clickable {
onClearRelatedClicked()
}
)
}
Divider(
paddingStart = 20.dp,
paddingEnd = 20.dp
)
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun GlobalSearchItem(
@ -461,7 +501,7 @@ private fun GlobalSearchItem(
text = globalSearchItemView.title,
nameMeta = globalSearchItemView.nameMeta
)
when(val meta = globalSearchItemView.meta) {
when (val meta = globalSearchItemView.meta) {
is GlobalSearchItemView.Meta.Block -> {
if (meta.highlights.isEmpty()) {
Text(
@ -476,6 +516,7 @@ private fun GlobalSearchItem(
)
}
}
is GlobalSearchItemView.Meta.Default -> {
if (meta.highlights.isEmpty()) {
Text(
@ -491,6 +532,7 @@ private fun GlobalSearchItem(
)
}
}
is GlobalSearchItemView.Meta.Status -> {
DefaultMetaStatusRelation(
title = meta.name,
@ -498,6 +540,7 @@ private fun GlobalSearchItem(
color = meta.color
)
}
is GlobalSearchItemView.Meta.Tag -> {
DefaultMetaTagRelation(
title = meta.name,
@ -505,6 +548,7 @@ private fun GlobalSearchItem(
color = meta.color
)
}
is GlobalSearchItemView.Meta.None -> {
// Draw nothing.
}
@ -715,6 +759,7 @@ fun GlobalSearchObjectIcon(
avatarFontSize = avatarFontSize,
avatarBackgroundColor = avatarBackgroundColor
)
is ObjectIcon.Profile.Image -> DefaultProfileIconImage(icon, modifier, iconSize)
is ObjectIcon.Basic.Emoji -> DefaultEmojiObjectIcon(modifier, iconSize, icon)
is ObjectIcon.Basic.Image -> DefaultObjectImageIcon(icon.hash, modifier, iconSize)
@ -729,6 +774,7 @@ fun GlobalSearchObjectIcon(
iconSize = iconSize
)
}
else -> {
// Draw nothing.
}

View file

@ -104,8 +104,7 @@ class CollectionFragment : BaseComposeFragment() {
space = space
)
is Command.ToDesktop -> navigation.exitToDesktop()
is Command.ToSearch -> navigation.openPageSearch(
initialQuery = command.initialQuery,
is Command.ToSearch -> navigation.openGlobalSearch(
space = command.space
)
is Command.SelectSpace -> {

View file

@ -514,14 +514,14 @@ sealed class Command {
}
data class SearchWithMeta(
val query: String,
val limit: Int,
val offset: Int,
val query: String = EMPTY_QUERY,
val limit: Int = 0,
val offset: Int = 0,
val keys: List<Key>,
val sorts: List<DVSort>,
val sorts: List<DVSort> = emptyList(),
val filters: List<DVFilter>,
val withMeta: Boolean,
val withMetaRelationDetails: Boolean,
val withMeta: Boolean = false,
val withMetaRelationDetails: Boolean = false,
val space: SpaceId
) {
data class Result(

View file

@ -0,0 +1,6 @@
package com.anytypeio.anytype.core_models
data class GlobalSearchHistory(
val query: String,
val relatedObject: Id? = null
)

View file

@ -248,4 +248,6 @@
<color name="object_number_background">#F2F2F2</color>
<color name="object_search_cursor_color">#007AFF</color>
</resources>

View file

@ -1,5 +1,6 @@
package com.anytypeio.anytype.data.auth.repo
import com.anytypeio.anytype.core_models.GlobalSearchHistory
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ThemeMode
import com.anytypeio.anytype.core_models.Wallpaper
@ -22,9 +23,9 @@ interface UserSettingsCache {
suspend fun getLastOpenedObject(space: SpaceId) : Id?
suspend fun clearLastOpenedObject(space: SpaceId)
suspend fun setLastSearchQuery(query: String, space: SpaceId)
suspend fun getLastSearchQuery(space: SpaceId): String
suspend fun clearLastSearchQuery(space: SpaceId)
suspend fun setGlobalSearchHistory(globalSearchHistory: GlobalSearchHistory, space: SpaceId)
suspend fun getGlobalSearchHistory(space: SpaceId): GlobalSearchHistory?
suspend fun clearGlobalSearchHistory(space: SpaceId)
suspend fun setWallpaper(space: Id, wallpaper: Wallpaper)
suspend fun getWallpaper(space: Id) : Wallpaper

View file

@ -1,5 +1,6 @@
package com.anytypeio.anytype.data.auth.repo
import com.anytypeio.anytype.core_models.GlobalSearchHistory
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ThemeMode
import com.anytypeio.anytype.core_models.Wallpaper
@ -76,15 +77,15 @@ class UserSettingsDataRepository(private val cache: UserSettingsCache) : UserSet
cache.clearLastOpenedObject(space)
}
override suspend fun setLastSearchQuery(query: String, space: SpaceId) {
cache.setLastSearchQuery(query, space)
override suspend fun setGlobalSearchHistory(globalSearchHistory: GlobalSearchHistory, space: SpaceId) {
cache.setGlobalSearchHistory(globalSearchHistory, space)
}
override suspend fun getLastSearchQuery(space: SpaceId): String {
return cache.getLastSearchQuery(space)
override suspend fun setGlobalSearchHistory(space: SpaceId): GlobalSearchHistory? {
return cache.getGlobalSearchHistory(space)
}
override suspend fun clearLastSearchQuery(space: SpaceId) {
cache.clearLastSearchQuery(space)
override suspend fun clearGlobalSearchHistory(space: SpaceId) {
cache.clearGlobalSearchHistory(space)
}
}

View file

@ -1,6 +1,7 @@
package com.anytypeio.anytype.domain.config
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.GlobalSearchHistory
import com.anytypeio.anytype.core_models.ThemeMode
import com.anytypeio.anytype.core_models.Wallpaper
import com.anytypeio.anytype.core_models.WidgetSession
@ -28,9 +29,9 @@ interface UserSettingsRepository {
suspend fun getLastOpenedObject(space: SpaceId) : Id?
suspend fun clearLastOpenedObject(space: SpaceId)
suspend fun setLastSearchQuery(query: String, space: SpaceId)
suspend fun getLastSearchQuery(space: SpaceId): String
suspend fun clearLastSearchQuery(space: SpaceId)
suspend fun setGlobalSearchHistory(globalSearchHistory: GlobalSearchHistory, space: SpaceId)
suspend fun setGlobalSearchHistory(space: SpaceId): GlobalSearchHistory?
suspend fun clearGlobalSearchHistory(space: SpaceId)
suspend fun setThemeMode(mode: ThemeMode)
suspend fun getThemeMode(): ThemeMode

View file

@ -1,19 +0,0 @@
package com.anytypeio.anytype.domain.search
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import javax.inject.Inject
class GetLastSearchQuery @Inject constructor(
private val settings: UserSettingsRepository,
dispatchers: AppCoroutineDispatchers
) : ResultInteractor<GetLastSearchQuery.Params, String>(dispatchers.io) {
override suspend fun doWork(params: Params): String {
return settings.getLastSearchQuery(space = params.space)
}
data class Params(val space: SpaceId)
}

View file

@ -0,0 +1,32 @@
package com.anytypeio.anytype.domain.search
import com.anytypeio.anytype.core_models.GlobalSearchHistory
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import javax.inject.Inject
class RestoreGlobalSearchHistory @Inject constructor(
private val settings: UserSettingsRepository,
dispatchers: AppCoroutineDispatchers
) : ResultInteractor<RestoreGlobalSearchHistory.Params, RestoreGlobalSearchHistory.Response>(
dispatchers.io
) {
override suspend fun doWork(params: Params): Response {
return Response(
globalSearchHistory = settings.setGlobalSearchHistory(
space = params.spaceId
)
)
}
data class Params(
val spaceId: SpaceId
)
data class Response(
val globalSearchHistory: GlobalSearchHistory?
)
}

View file

@ -1,6 +1,8 @@
package com.anytypeio.anytype.domain.search
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.GlobalSearchHistory
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.block.repo.BlockRepository
@ -9,12 +11,26 @@ import javax.inject.Inject
class SearchWithMeta @Inject constructor(
private val repo: BlockRepository,
private val dispatchers: AppCoroutineDispatchers,
private val settings: UserSettingsRepository
) : ResultInteractor<Command.SearchWithMeta, List<Command.SearchWithMeta.Result>>(dispatchers.io) {
override suspend fun doWork(params: Command.SearchWithMeta): List<Command.SearchWithMeta.Result> {
return repo.searchObjectWithMeta(command = params).also {
settings.setLastSearchQuery(query = params.query, space = params.space)
private val settings: UserSettingsRepository,
dispatchers: AppCoroutineDispatchers
) : ResultInteractor<SearchWithMeta.Params, List<Command.SearchWithMeta.Result>>(dispatchers.io) {
override suspend fun doWork(params: SearchWithMeta.Params): List<Command.SearchWithMeta.Result> {
return repo.searchObjectWithMeta(command = params.command).also {
if (params.saveSearch) saveSearch(params)
}
}
private suspend fun saveSearch(params: Params) {
val search = GlobalSearchHistory(
query = params.command.query,
relatedObject = params.relatedObjectId
)
settings.setGlobalSearchHistory(globalSearchHistory = search, space = params.command.space)
}
data class Params(
val command: Command.SearchWithMeta,
val relatedObjectId: Id?,
val saveSearch: Boolean = false
)
}

View file

@ -0,0 +1,31 @@
package com.anytypeio.anytype.domain.search
import com.anytypeio.anytype.core_models.GlobalSearchHistory
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import javax.inject.Inject
class UpdateGlobalSearchHistory @Inject constructor(
private val settings: UserSettingsRepository,
dispatchers: AppCoroutineDispatchers
) : ResultInteractor<UpdateGlobalSearchHistory.Params, Unit>(dispatchers.io) {
override suspend fun doWork(params: Params) {
settings.setGlobalSearchHistory(
globalSearchHistory = GlobalSearchHistory(
query = params.query,
relatedObject = params.relatedObjectId
),
space = params.spaceId
)
}
data class Params(
val spaceId: SpaceId,
val query: String,
val relatedObjectId: Id?
)
}

View file

@ -4,6 +4,7 @@ import android.content.Context
import android.content.SharedPreferences
import androidx.datastore.core.DataStore
import androidx.datastore.dataStore
import com.anytypeio.anytype.core_models.GlobalSearchHistory
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.NO_VALUE
import com.anytypeio.anytype.core_models.ThemeMode
@ -12,6 +13,7 @@ import com.anytypeio.anytype.core_models.WidgetSession
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_models.primitives.TypeId
import com.anytypeio.anytype.data.auth.repo.UserSettingsCache
import com.anytypeio.anytype.persistence.GlobalSearchHistoryProto
import com.anytypeio.anytype.persistence.SpacePreference
import com.anytypeio.anytype.persistence.SpacePreferences
import com.anytypeio.anytype.persistence.common.JsonString
@ -318,7 +320,7 @@ class DefaultUserSettingsCache(
}
}
override suspend fun setLastSearchQuery(query: String, space: SpaceId) {
override suspend fun setGlobalSearchHistory(globalSearchHistory: GlobalSearchHistory, space: SpaceId) {
context.spacePrefsStore.updateData { existingPreferences ->
val givenSpacePreference = existingPreferences
.preferences
@ -327,7 +329,10 @@ class DefaultUserSettingsCache(
defaultValue = SpacePreference()
)
val updated = givenSpacePreference.copy(
lastSearchQuery = query
globalSearchHistory = GlobalSearchHistoryProto(
lastSearchQuery = globalSearchHistory.query,
lastSearchRelatedObjectId = globalSearchHistory.relatedObject
)
)
val result = buildMap {
putAll(existingPreferences.preferences)
@ -337,24 +342,33 @@ class DefaultUserSettingsCache(
}
}
override suspend fun getLastSearchQuery(space: SpaceId): String {
override suspend fun getGlobalSearchHistory(space: SpaceId): GlobalSearchHistory? {
return context.spacePrefsStore
.data
.map { preferences ->
preferences
val result = preferences
.preferences[space.id]
?.lastSearchQuery.orEmpty()
?.globalSearchHistory
if (result == null) {
return@map null
} else {
GlobalSearchHistory(
query = result.lastSearchQuery.orEmpty(),
relatedObject = result.lastSearchRelatedObjectId
)
}
}
.first()
}
override suspend fun clearLastSearchQuery(space: SpaceId) {
override suspend fun clearGlobalSearchHistory(space: SpaceId) {
context.spacePrefsStore.updateData { existingPreferences ->
val givenSpacePreference = existingPreferences
.preferences
.getOrDefault(key = space.id, defaultValue = SpacePreference())
val updated = givenSpacePreference.copy(
lastSearchQuery = null
globalSearchHistory = null
)
val result = buildMap {
putAll(existingPreferences.preferences)

View file

@ -12,5 +12,10 @@ message SpacePreference {
optional string defaultObjectTypeKey = 1;
repeated string pinnedObjectTypeIds = 2;
optional string lastOpenedObject = 3;
optional string lastSearchQuery = 4;
optional GlobalSearchHistoryProto globalSearchHistory = 5;
}
message GlobalSearchHistoryProto {
optional string lastSearchQuery = 1;
optional string lastSearchRelatedObjectId = 2;
}

View file

@ -3,6 +3,7 @@ package com.anytypeio.anytype.persistence
import android.content.Context
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.test.core.app.ApplicationProvider
import com.anytypeio.anytype.core_models.GlobalSearchHistory
import com.anytypeio.anytype.core_models.Wallpaper
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_models.primitives.TypeId
@ -270,4 +271,47 @@ class UserSettingsCacheTest {
actual = cache.getDefaultObjectType(space2)
)
}
@Test
fun `should save global search for given space and then clear it`() = runTest {
val cache = DefaultUserSettingsCache(
prefs = defaultPrefs,
context = ApplicationProvider.getApplicationContext()
)
val space = SpaceId(MockDataFactory.randomUuid())
val globalSearch = GlobalSearchHistory(
query = MockDataFactory.randomString(),
relatedObject = MockDataFactory.randomUuid()
)
// Settings are empty before we save anything
assertEquals(
expected = null,
actual = cache.getDefaultObjectType(space)
)
// Saving global search for given space
cache.setGlobalSearchHistory(
globalSearchHistory = globalSearch,
space = space
)
// Making sure global search is saved
assertEquals(
expected = globalSearch,
actual = cache.getGlobalSearchHistory(space)
)
cache.clearGlobalSearchHistory(space)
assertEquals(
expected = null,
actual = cache.getGlobalSearchHistory(space)
)
}
}

View file

@ -98,7 +98,6 @@ import com.anytypeio.anytype.domain.page.CreateObject
import com.anytypeio.anytype.domain.page.CreateObjectAsMentionOrLink
import com.anytypeio.anytype.domain.page.OpenPage
import com.anytypeio.anytype.domain.relations.AddRelationToObject
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.search.SearchObjects
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.templates.ApplyTemplate
@ -332,8 +331,7 @@ class EditorViewModel(
private val getNetworkMode: GetNetworkMode,
private val clearLastOpenedObject: ClearLastOpenedObject,
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
private val spaceSyncAndP2PStatusProvider: SpaceSyncAndP2PStatusProvider,
private val getLastSearchQuery: GetLastSearchQuery
private val spaceSyncAndP2PStatusProvider: SpaceSyncAndP2PStatusProvider
) : ViewStateViewModel<ViewState>(),
PickerListener,
SupportNavigation<EventWrapper<AppNavigation.Command>>,
@ -4305,29 +4303,12 @@ class EditorViewModel(
)
viewModelScope.launch {
val params = GetLastSearchQuery.Params(space = vmParams.space)
getLastSearchQuery.async(params).fold(
onSuccess = { query ->
navigation.postValue(
EventWrapper(
AppNavigation.Command.OpenPageSearch(
initialQuery = query,
space = vmParams.space.id
)
)
navigation.postValue(
EventWrapper(
AppNavigation.Command.OpenGlobalSearch(
space = vmParams.space.id
)
},
onFailure = {
Timber.e(it, "Error while getting last search query")
navigation.postValue(
EventWrapper(
AppNavigation.Command.OpenPageSearch(
initialQuery = "",
space = vmParams.space.id
)
)
)
}
)
)
}
}

View file

@ -34,7 +34,6 @@ import com.anytypeio.anytype.domain.page.CreateObject
import com.anytypeio.anytype.domain.page.CreateObjectAsMentionOrLink
import com.anytypeio.anytype.domain.page.OpenPage
import com.anytypeio.anytype.domain.relations.AddRelationToObject
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.search.SearchObjects
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.templates.ApplyTemplate
@ -98,8 +97,7 @@ open class EditorViewModelFactory @Inject constructor(
private val getNetworkMode: GetNetworkMode,
private val clearLastOpenedObject: ClearLastOpenedObject,
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
private val syncStatusProvider: SpaceSyncAndP2PStatusProvider,
private val getLastSearchQuery: GetLastSearchQuery
private val syncStatusProvider: SpaceSyncAndP2PStatusProvider
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@ -149,8 +147,7 @@ open class EditorViewModelFactory @Inject constructor(
spaceSyncAndP2PStatusProvider = syncStatusProvider,
getNetworkMode = getNetworkMode,
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
clearLastOpenedObject = clearLastOpenedObject,
getLastSearchQuery = getLastSearchQuery
clearLastOpenedObject = clearLastOpenedObject
) as T
}
}

View file

@ -58,7 +58,6 @@ import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.page.CloseBlock
import com.anytypeio.anytype.domain.page.CreateObject
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.search.SearchObjects
import com.anytypeio.anytype.domain.spaces.ClearLastOpenedSpace
import com.anytypeio.anytype.domain.spaces.GetSpaceView
@ -197,7 +196,6 @@ class HomeScreenViewModel(
private val createBlock: CreateBlock,
private val dateProvider: DateProvider,
private val addObjectToCollection: AddObjectToCollection,
private val getLastSearchQuery: GetLastSearchQuery,
private val clearLastOpenedSpace: ClearLastOpenedSpace
) : NavigationViewModel<HomeScreenViewModel.Navigation>(),
Reducer<ObjectView, Payload>,
@ -1697,26 +1695,8 @@ class HomeScreenViewModel(
fun onSearchIconClicked() {
viewModelScope.launch {
val space = spaceManager.get()
val params = GetLastSearchQuery.Params(space = SpaceId(space))
getLastSearchQuery.async(params).fold(
onSuccess = { query ->
commands.emit(
Command.OpenGlobalSearchScreen(
initialQuery = query,
space = space
)
)
},
onFailure = {
Timber.e(it, "Error while getting last search query")
commands.emit(
Command.OpenGlobalSearchScreen(
initialQuery = "",
space = space
)
)
}
commands.emit(
Command.OpenGlobalSearchScreen(space = spaceManager.get())
)
}
viewModelScope.sendEvent(
@ -2097,7 +2077,6 @@ class HomeScreenViewModel(
private val dateProvider: DateProvider,
private val coverImageHashProvider: CoverImageHashProvider,
private val addObjectToCollection: AddObjectToCollection,
private val getLastSearchQuery: GetLastSearchQuery,
private val clearLastOpenedSpace: ClearLastOpenedSpace
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@ -2146,7 +2125,6 @@ class HomeScreenViewModel(
createBlock = createBlock,
dateProvider = dateProvider,
addObjectToCollection = addObjectToCollection,
getLastSearchQuery = getLastSearchQuery,
clearLastOpenedSpace = clearLastOpenedSpace
) as T
}
@ -2190,7 +2168,7 @@ sealed class Command {
data class OpenObjectCreateDialog(val space: SpaceId) : Command()
data class OpenGlobalSearchScreen(val initialQuery: String, val space: Id) : Command()
data class OpenGlobalSearchScreen(val space: Id) : Command()
data object OpenVault: Command()

View file

@ -36,7 +36,7 @@ interface AppNavigation {
fun exit()
fun exitToDesktop()
fun openPageSearch(initialQuery: String, space: Id)
fun openGlobalSearch(space: Id)
fun openUpdateAppScreen()
fun openRemoteFilesManageScreen(subscription: Id)
@ -69,8 +69,7 @@ interface AppNavigation {
object OpenSettings : Command()
object MigrationErrorScreen: Command()
data class OpenPageSearch(
val initialQuery: String,
data class OpenGlobalSearch(
val space: Id
) : Command()

View file

@ -30,10 +30,13 @@ import com.anytypeio.anytype.core_models.ThemeColor
import com.anytypeio.anytype.core_models.ext.EMPTY_STRING_VALUE
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.domain.base.Resultat
import com.anytypeio.anytype.domain.base.fold
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.search.RestoreGlobalSearchHistory
import com.anytypeio.anytype.domain.search.SearchWithMeta
import com.anytypeio.anytype.domain.search.UpdateGlobalSearchHistory
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
import com.anytypeio.anytype.presentation.common.BaseViewModel
import com.anytypeio.anytype.presentation.extension.sendAnalyticsSearchBacklinksEvent
@ -42,10 +45,13 @@ import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
import com.anytypeio.anytype.presentation.home.navigation
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.objects.getProperName
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.filterObjectsByIds
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.filterSearchObjects
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
@ -55,7 +61,6 @@ import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch
import timber.log.Timber
@ -68,9 +73,11 @@ class GlobalSearchViewModel(
private val urlBuilder: UrlBuilder,
private val analytics: Analytics,
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
private val restoreGlobalSearchHistory: RestoreGlobalSearchHistory,
private val updateGlobalSearchHistory: UpdateGlobalSearchHistory
) : BaseViewModel(), AnalyticSpaceHelperDelegate by analyticSpaceHelperDelegate {
private val userInput = MutableStateFlow(vmParams.initialQuery)
private val userInput = MutableStateFlow("")
private val searchQuery = userInput
.take(1)
.onCompletion {
@ -81,51 +88,129 @@ class GlobalSearchViewModel(
val navigation = MutableSharedFlow<OpenObjectNavigation>()
val state = combine(
mode,
searchQuery
) { mode, query ->
mode to query
}.flatMapLatest { (mode, query) ->
when(mode) {
is Mode.Default -> {
buildDefaultSearchFlow(query = query, space = vmParams.space)
}
is Mode.Related -> {
buildRelatedSearchFlow(query = query, mode = mode, space = vmParams.space)
}
private val _state: MutableStateFlow<ViewState> = MutableStateFlow(ViewState.Init())
val state = _state.asStateFlow()
init {
Timber.d("GlobalSearchViewModel, init")
proceedRestoreGlobalSearch(space = vmParams.space)
}
private fun proceedRestoreGlobalSearch(space: SpaceId) {
Timber.d("restoreGlobalSearch, space $space")
viewModelScope.launch {
val params = RestoreGlobalSearchHistory.Params(spaceId = space)
restoreGlobalSearchHistory.async(params = params).fold(
onSuccess = { response ->
val globalSearchHistory = response.globalSearchHistory
Timber.d("restoreGlobalSearchHistory, onSuccess $globalSearchHistory")
userInput.value = globalSearchHistory?.query ?: EMPTY_STRING_VALUE
val relatedObjectId = globalSearchHistory?.relatedObject
if (!relatedObjectId.isNullOrEmpty()) {
proceedRelatedObjectSearch(
query = globalSearchHistory.query,
relatedObjectId = relatedObjectId
)
} else {
val initialState =
ViewState.Init(query = globalSearchHistory?.query ?: EMPTY_STRING_VALUE)
proceedWithInitialState(initialState)
}
},
onFailure = {
Timber.e(it, "restoreGlobalSearch, onFailure")
userInput.value = EMPTY_STRING_VALUE
proceedWithInitialState(ViewState.Init(query = EMPTY_STRING_VALUE))
}
)
}
}.scan<ViewState, ViewState>(
initial = ViewState.Init(
query = vmParams.initialQuery
}
private suspend fun proceedRelatedObjectSearch(query: String, relatedObjectId: Id) {
val params = SearchWithMeta.Params(
relatedObjectId = relatedObjectId,
command = Command.SearchWithMeta(
limit = 1,
keys = DEFAULT_KEYS,
filters = filterObjectsByIds(
ids = listOf(relatedObjectId),
spaces = listOf(vmParams.space.id)
),
space = vmParams.space
)
)
) { curr, new ->
when(new) {
is ViewState.Default -> {
if (new.isLoading) {
new.copy(
views = curr.views
searchWithMeta.async(params).fold(
onSuccess = { result ->
Timber.d("proceedRelatedObjectSearch, onSuccess $result")
val relatedGlobalSearchItemView = result.firstOrNull()?.view(
storeOfRelations = storeOfRelations,
storeOfObjectTypes = storeOfObjectTypes,
urlBuilder = urlBuilder
)
if (relatedGlobalSearchItemView != null) {
mode.value = Mode.Related(target = relatedGlobalSearchItemView)
proceedWithInitialState(
ViewState.RelatedInit(
query = query,
target = relatedGlobalSearchItemView,
isLoading = false
)
)
} else {
new
proceedWithInitialState(ViewState.Init(query = query))
}
},
onFailure = {
Timber.e(it, "proceedRelatedObjectSearch, onFailure")
proceedWithInitialState(ViewState.Init(query = query))
}
)
}
@OptIn(ExperimentalCoroutinesApi::class)
private suspend fun proceedWithInitialState(initial: ViewState) {
combine(
mode,
searchQuery
) { mode, query ->
mode to query
}.flatMapLatest { (mode, query) ->
when(mode) {
is Mode.Default -> {
buildDefaultSearchFlow(query = query, space = vmParams.space)
}
is Mode.Related -> {
buildRelatedSearchFlow(query = query, mode = mode, space = vmParams.space)
}
}
is ViewState.Related -> {
if (new.isLoading) {
new.copy(
views = curr.views
)
} else {
new
}.scan(
initial = initial
) { curr, new ->
when(new) {
is ViewState.Default -> {
if (new.isLoading) {
new.copy(
views = curr.views
)
} else {
new
}
}
is ViewState.Related -> {
if (new.isLoading) {
new.copy(
views = curr.views
)
} else {
new
}
}
else -> new
}
else -> new
}.collect {
_state.value = it
}
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
initialValue = ViewState.Init("")
)
}
private suspend fun buildRelatedSearchFlow(
query: String,
@ -133,32 +218,12 @@ class GlobalSearchViewModel(
space: SpaceId
) = searchWithMeta
.stream(
Command.SearchWithMeta(
relatedSearchFlowParams(
query = query,
limit = DEFAULT_SEARCH_LIMIT,
offset = 0,
keys = DEFAULT_KEYS,
filters = buildList {
addAll(
ObjectSearchConstants.filterSearchObjects(
spaces = listOf(vmParams.space.id)
)
)
add(
DVFilter(
relation = Relations.ID,
value = buildSet {
addAll(mode.target.links)
addAll(mode.target.backlinks)
}.toList(),
condition = DVFilterCondition.IN
)
)
},
sorts = ObjectSearchConstants.sortsSearchObjects,
withMetaRelationDetails = false,
withMeta = false,
space = space
links = mode.target.links,
backlinks = mode.target.backlinks,
space = space,
relatedObjectId = mode.target.id
)
).map { result ->
when (result) {
@ -191,22 +256,67 @@ class GlobalSearchViewModel(
}
}
private suspend fun buildDefaultSearchFlow(query: String, space: SpaceId) = searchWithMeta
.stream(
Command.SearchWithMeta(
private fun relatedSearchFlowParams(
query: String,
links: List<Id>,
backlinks: List<Id>,
space: SpaceId,
relatedObjectId: Id?
): SearchWithMeta.Params {
return SearchWithMeta.Params(
saveSearch = true,
relatedObjectId = relatedObjectId,
command = Command.SearchWithMeta(
query = query,
limit = DEFAULT_SEARCH_LIMIT,
offset = 0,
keys = DEFAULT_KEYS,
filters = ObjectSearchConstants.filterSearchObjects(
// TODO add tech space?
spaces = listOf(space.id)
),
filters = buildList {
addAll(
filterSearchObjects(
spaces = listOf(vmParams.space.id)
)
)
add(
DVFilter(
relation = Relations.ID,
value = buildSet {
addAll(links)
addAll(backlinks)
}.toList(),
condition = DVFilterCondition.IN
)
)
},
sorts = ObjectSearchConstants.sortsSearchObjects,
withMetaRelationDetails = true,
withMeta = true,
withMetaRelationDetails = false,
withMeta = false,
space = space
)
)
}
private suspend fun buildDefaultSearchFlow(query: String, space: SpaceId) = searchWithMeta
.stream(
SearchWithMeta.Params(
saveSearch = true,
relatedObjectId = null,
command = Command.SearchWithMeta(
query = query,
limit = DEFAULT_SEARCH_LIMIT,
offset = 0,
keys = DEFAULT_KEYS,
filters = ObjectSearchConstants.filterSearchObjects(
// TODO add tech space?
spaces = listOf(space.id)
),
sorts = ObjectSearchConstants.sortsSearchObjects,
withMetaRelationDetails = true,
withMeta = true,
space = space
)
)
).map { result ->
when (result) {
is Resultat.Failure -> {
@ -272,6 +382,10 @@ class GlobalSearchViewModel(
viewModelScope.launch {
userInput.value = EMPTY_STRING_VALUE
mode.value = Mode.Related(globalSearchItemView)
proceedUpdateGlobalSearch(
query = EMPTY_STRING_VALUE,
relatedObjectId = globalSearchItemView.id
)
}
viewModelScope.launch {
sendAnalyticsSearchBacklinksEvent(
@ -281,7 +395,23 @@ class GlobalSearchViewModel(
}
}
data class VmParams(val initialQuery: String, val space: SpaceId)
private suspend fun proceedUpdateGlobalSearch(query: String, relatedObjectId: Id?) {
val params = UpdateGlobalSearchHistory.Params(
spaceId = vmParams.space,
query = query,
relatedObjectId = relatedObjectId
)
updateGlobalSearchHistory.async(params).fold(
onSuccess = {
Timber.i("updateGlobalSearch, onSuccess")
},
onFailure = {
Timber.e(it, "updateGlobalSearch, onFailure")
}
)
}
data class VmParams(val space: SpaceId)
class Factory @Inject constructor(
private val vmParams: VmParams,
@ -290,7 +420,9 @@ class GlobalSearchViewModel(
private val storeOfRelations: StoreOfRelations,
private val urlBuilder: UrlBuilder,
private val analytics: Analytics,
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
private val restoreGlobalSearchHistory: RestoreGlobalSearchHistory,
private val updateGlobalSearchHistory: UpdateGlobalSearchHistory
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@ -301,7 +433,9 @@ class GlobalSearchViewModel(
storeOfRelations = storeOfRelations,
urlBuilder = urlBuilder,
analytics = analytics,
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
restoreGlobalSearchHistory = restoreGlobalSearchHistory,
updateGlobalSearchHistory = updateGlobalSearchHistory
) as T
}
}
@ -343,6 +477,14 @@ class GlobalSearchViewModel(
override val isLoading: Boolean
): ViewState()
//ToDo: remove this state, and make Related sealed class
data class RelatedInit (
val query: String = EMPTY_STRING_VALUE,
val target: GlobalSearchItemView,
override val views: List<GlobalSearchItemView> = emptyList(),
override val isLoading: Boolean
): ViewState()
fun isEmptyState() : Boolean {
return this !is Init && !this.isLoading && views.isEmpty()
}

View file

@ -649,7 +649,9 @@ object ObjectSearchConstants {
Relations.RESTRICTIONS,
Relations.SIZE_IN_BYTES,
Relations.FILE_MIME_TYPE,
Relations.FILE_EXT
Relations.FILE_EXT,
Relations.LAST_OPENED_DATE,
Relations.LAST_MODIFIED_DATE
)
val defaultOptionKeys = listOf(

View file

@ -57,7 +57,6 @@ import com.anytypeio.anytype.domain.page.CloseBlock
import com.anytypeio.anytype.domain.page.CreateObject
import com.anytypeio.anytype.domain.search.DataViewState
import com.anytypeio.anytype.domain.search.DataViewSubscriptionContainer
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.sets.OpenObjectSet
import com.anytypeio.anytype.domain.sets.SetQueryToObjectSet
import com.anytypeio.anytype.domain.templates.CreateTemplate
@ -181,7 +180,6 @@ class ObjectSetViewModel(
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
private val spaceSyncAndP2PStatusProvider: SpaceSyncAndP2PStatusProvider,
private val clearLastOpenedObject: ClearLastOpenedObject,
private val getLastSearchQuery: GetLastSearchQuery
) : ViewModel(), SupportNavigation<EventWrapper<AppNavigation.Command>>,
ViewerDelegate by viewerDelegate,
AnalyticSpaceHelperDelegate by analyticSpaceHelperDelegate
@ -1655,25 +1653,10 @@ class ObjectSetViewModel(
props = Props(mapOf(EventsPropertiesKey.route to EventsDictionary.Routes.navigation))
)
viewModelScope.launch {
val params = GetLastSearchQuery.Params(space = vmParams.space)
getLastSearchQuery.async(params).fold(
onSuccess = { query ->
dispatch(
AppNavigation.Command.OpenPageSearch(
initialQuery = query,
space = vmParams.space.id
)
)
},
onFailure = {
Timber.e(it, "Error while getting last search query")
dispatch(
AppNavigation.Command.OpenPageSearch(
initialQuery = "",
space = vmParams.space.id
)
)
}
dispatch(
AppNavigation.Command.OpenGlobalSearch(
space = vmParams.space.id
)
)
}
}

View file

@ -27,7 +27,6 @@ import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.page.CloseBlock
import com.anytypeio.anytype.domain.page.CreateObject
import com.anytypeio.anytype.domain.search.DataViewSubscriptionContainer
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.sets.OpenObjectSet
import com.anytypeio.anytype.domain.sets.SetQueryToObjectSet
import com.anytypeio.anytype.domain.templates.CreateTemplate
@ -84,8 +83,7 @@ class ObjectSetViewModelFactory(
private val dateProvider: DateProvider,
private val spaceSyncAndP2PStatusProvider: SpaceSyncAndP2PStatusProvider,
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
private val clearLastOpenedObject: ClearLastOpenedObject,
private val getLastSearchQuery: GetLastSearchQuery
private val clearLastOpenedObject: ClearLastOpenedObject
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@ -130,8 +128,7 @@ class ObjectSetViewModelFactory(
dateProvider = dateProvider,
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
spaceSyncAndP2PStatusProvider = spaceSyncAndP2PStatusProvider,
clearLastOpenedObject = clearLastOpenedObject,
getLastSearchQuery = getLastSearchQuery
clearLastOpenedObject = clearLastOpenedObject
) as T
}
}

View file

@ -16,7 +16,6 @@ import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_models.Position
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.ext.process
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_utils.ext.cancel
import com.anytypeio.anytype.core_utils.ext.replace
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
@ -39,7 +38,6 @@ import com.anytypeio.anytype.domain.objects.DeleteObjects
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.page.CreateObject
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.spaces.GetSpaceView
import com.anytypeio.anytype.domain.workspace.SpaceManager
import com.anytypeio.anytype.domain.workspace.getSpaceWithTechSpace
@ -47,7 +45,6 @@ import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
import com.anytypeio.anytype.presentation.extension.sendAnalyticsObjectCreateEvent
import com.anytypeio.anytype.presentation.extension.sendDeletionWarning
import com.anytypeio.anytype.presentation.extension.sendScreenHomeEvent
import com.anytypeio.anytype.presentation.home.Command
import com.anytypeio.anytype.presentation.home.HomeScreenViewModel.Companion.HOME_SCREEN_PROFILE_OBJECT_SUBSCRIPTION
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
import com.anytypeio.anytype.presentation.home.navigation
@ -110,8 +107,7 @@ class CollectionViewModel(
private val spaceManager: SpaceManager,
private val getSpaceView: GetSpaceView,
private val dateTypeNameProvider: DateTypeNameProvider,
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
private val getLastSearchQuery: GetLastSearchQuery
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate
) : ViewModel(), Reducer<CoreObjectView, Payload>, AnalyticSpaceHelperDelegate by analyticSpaceHelperDelegate {
val payloads: Flow<Payload>
@ -834,25 +830,8 @@ class CollectionViewModel(
props = Props(mapOf(EventsPropertiesKey.route to EventsDictionary.Routes.navigation))
)
viewModelScope.launch {
val params = GetLastSearchQuery.Params(space = SpaceId(space))
getLastSearchQuery.async(params).fold(
onSuccess = { query ->
commands.emit(
Command.ToSearch(
initialQuery = query,
space = space
)
)
},
onFailure = {
Timber.e(it, "Error while getting last search query")
commands.emit(
Command.ToSearch(
initialQuery = "",
space = space
)
)
}
commands.emit(
Command.ToSearch(space = space)
)
}
}
@ -967,8 +946,7 @@ class CollectionViewModel(
private val spaceManager: SpaceManager,
private val getSpaceView: GetSpaceView,
private val dateTypeNameProvider: DateTypeNameProvider,
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
private val getLastSearchQuery: GetLastSearchQuery
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@ -994,8 +972,7 @@ class CollectionViewModel(
spaceManager = spaceManager,
getSpaceView = getSpaceView,
dateTypeNameProvider = dateTypeNameProvider,
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
getLastSearchQuery = getLastSearchQuery
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate
) as T
}
}
@ -1007,7 +984,7 @@ class CollectionViewModel(
data class LaunchObjectSet(val target: Id, val space: Id) : Command()
data object ToDesktop : Command()
data class ToSearch(val initialQuery: String, val space: Id) : Command()
data class ToSearch(val space: Id) : Command()
data object SelectSpace : Command()
data object Exit : Command()
}

View file

@ -124,8 +124,7 @@ class CollectionCreateAndAddObjectTest: ObjectSetViewModelTestSetup() {
permissions = permissions,
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
spaceSyncAndP2PStatusProvider = spaceSyncAndP2PStatusProvider,
clearLastOpenedObject = clearLastOpenedObject,
getLastSearchQuery = getLastSearchQuery
clearLastOpenedObject = clearLastOpenedObject
)
stubNetworkMode()
stubObservePermissions()

View file

@ -87,7 +87,6 @@ import com.anytypeio.anytype.domain.page.bookmark.CreateBookmarkBlock
import com.anytypeio.anytype.domain.page.bookmark.SetupBookmark
import com.anytypeio.anytype.domain.relations.AddRelationToObject
import com.anytypeio.anytype.domain.relations.SetRelationKey
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.search.SearchObjects
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.table.CreateTable
@ -365,9 +364,6 @@ open class EditorViewModelTest {
@Mock
lateinit var spaceSyncAndP2PStatusProvider: SpaceSyncAndP2PStatusProvider
@Mock
lateinit var getLastSearchQuery: GetLastSearchQuery
lateinit var vm: EditorViewModel
private lateinit var builder: UrlBuilder
@ -3964,8 +3960,7 @@ open class EditorViewModelTest {
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
spaceSyncAndP2PStatusProvider = spaceSyncAndP2PStatusProvider,
clearLastOpenedObject = clearLastOpenedObject,
getNetworkMode = getNetworkMode,
getLastSearchQuery = getLastSearchQuery
getNetworkMode = getNetworkMode
)
}

View file

@ -78,7 +78,6 @@ import com.anytypeio.anytype.domain.page.bookmark.CreateBookmarkBlock
import com.anytypeio.anytype.domain.page.bookmark.SetupBookmark
import com.anytypeio.anytype.domain.relations.AddRelationToObject
import com.anytypeio.anytype.domain.relations.SetRelationKey
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.search.SearchObjects
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.table.CreateTable
@ -293,9 +292,6 @@ open class EditorPresentationTestSetup {
@Mock
lateinit var convertObjectToCollection: ConvertObjectToCollection
@Mock
lateinit var getLastSearchQuery: GetLastSearchQuery
lateinit var tableDelegate: EditorTableDelegate
lateinit var dispatcher: Dispatcher<Payload>
@ -503,8 +499,7 @@ open class EditorPresentationTestSetup {
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
clearLastOpenedObject = clearLastOpenedObject,
spaceSyncAndP2PStatusProvider = spaceSyncAndP2PStatusProvider,
getNetworkMode = getNetworkMode,
getLastSearchQuery = getLastSearchQuery
getNetworkMode = getNetworkMode
)
}

View file

@ -56,7 +56,6 @@ import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.page.CloseBlock
import com.anytypeio.anytype.domain.page.CreateObject
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.search.SearchObjects
import com.anytypeio.anytype.domain.spaces.ClearLastOpenedSpace
import com.anytypeio.anytype.domain.spaces.GetSpaceView
@ -245,9 +244,6 @@ class HomeScreenViewModelTest {
@Mock
lateinit var addObjectToCollection: AddObjectToCollection
@Mock
lateinit var getLastSearchQuery: GetLastSearchQuery
@Mock
lateinit var clearLastOpenedSpace: ClearLastOpenedSpace
@ -2948,7 +2944,6 @@ class HomeScreenViewModelTest {
createDataViewObject = createDataViewObject,
dateProvider = dateProvider,
addObjectToCollection = addObjectToCollection,
getLastSearchQuery = getLastSearchQuery,
clearLastOpenedSpace = clearLastOpenedSpace
)

View file

@ -57,7 +57,6 @@ import com.anytypeio.anytype.domain.page.CloseBlock
import com.anytypeio.anytype.domain.page.CreateObject
import com.anytypeio.anytype.domain.search.CancelSearchSubscription
import com.anytypeio.anytype.domain.search.DataViewSubscriptionContainer
import com.anytypeio.anytype.domain.search.GetLastSearchQuery
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.domain.sets.OpenObjectSet
import com.anytypeio.anytype.domain.sets.SetQueryToObjectSet
@ -213,9 +212,6 @@ open class ObjectSetViewModelTestSetup {
@Mock
lateinit var spaceSyncAndP2PStatusProvider: SpaceSyncAndP2PStatusProvider
@Mock
lateinit var getLastSearchQuery: GetLastSearchQuery
var permissions: UserPermissionProvider = UserPermissionProviderStub()
lateinit var spaceConfig: Config
@ -307,8 +303,7 @@ open class ObjectSetViewModelTestSetup {
permissions = permissions,
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
spaceSyncAndP2PStatusProvider = spaceSyncAndP2PStatusProvider,
clearLastOpenedObject = clearLastOpenedObject,
getLastSearchQuery = getLastSearchQuery
clearLastOpenedObject = clearLastOpenedObject
)
}