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

DROID-2853 All content | Enhancement | Menu, cache, empty states (#1613)

This commit is contained in:
Konstantin Ivanov 2024-10-01 21:02:58 +02:00 committed by GitHub
parent 97fa5232b9
commit 8878d0760b
Signed by: github
GPG key ID: B5690EEEBB952194
18 changed files with 287 additions and 110 deletions

View file

@ -27,9 +27,11 @@ import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModel
import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModelFactory
import com.anytypeio.anytype.feature_allcontent.ui.AllContentWrapperScreen
import com.anytypeio.anytype.feature_allcontent.ui.AllContentNavigation.ALL_CONTENT_MAIN
import com.anytypeio.anytype.presentation.widgets.collection.Subscription
import com.anytypeio.anytype.ui.base.navigation
import com.anytypeio.anytype.ui.settings.typography
import javax.inject.Inject
import timber.log.Timber
class AllContentFragment : BaseComposeFragment() {
@ -62,6 +64,9 @@ class AllContentFragment : BaseComposeFragment() {
target = command.id,
space = command.space
)
}.onFailure {
toast("Failed to open document")
Timber.e(it, "Failed to open document")
}
}
is AllContentViewModel.Command.NavigateToSetOrCollection -> {
@ -70,11 +75,25 @@ class AllContentFragment : BaseComposeFragment() {
target = command.id,
space = command.space,
)
}.onFailure {
toast("Failed to open object set")
Timber.e(it, "Failed to open object set")
}
}
is AllContentViewModel.Command.SendToast -> {
toast(command.message)
}
is AllContentViewModel.Command.NavigateToBin -> {
runCatching {
navigation().launchCollections(
subscription = Subscription.Bin,
space = command.space
)
}.onFailure {
toast("Failed to open bin")
Timber.e(it, "Failed to open bin")
}
}
}
}
}
@ -96,7 +115,8 @@ class AllContentFragment : BaseComposeFragment() {
uiMenuState = vm.uiMenu.collectAsStateWithLifecycle().value,
onSortClick = vm::onSortClicked,
onModeClick = vm::onAllContentModeClicked,
onItemClicked = vm::onItemClicked
onItemClicked = vm::onItemClicked,
onBinClick = vm::onViewBinClicked
)
}
}

View file

@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height

View file

@ -42,4 +42,7 @@ interface UserSettingsCache {
suspend fun getWidgetSession() : WidgetSession
suspend fun saveWidgetSession(session: WidgetSession)
suspend fun clear()
suspend fun getAllContentSort(space: SpaceId): Id
suspend fun setAllContentSort(space: SpaceId, sort: Id)
}

View file

@ -105,4 +105,12 @@ class UserSettingsDataRepository(private val cache: UserSettingsCache) : UserSet
order = order
)
}
override suspend fun getAllContentSort(space: SpaceId): Id {
return cache.getAllContentSort(space)
}
override suspend fun setAllContentSort(space: SpaceId, sort: Id) {
cache.setAllContentSort(space, sort)
}
}

View file

@ -1,6 +1,5 @@
package com.anytypeio.anytype.domain.all_content
import com.anytypeio.anytype.core_models.DVSort
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
@ -15,8 +14,8 @@ class RestoreAllContentState @Inject constructor(
) {
override suspend fun doWork(params: Params): Response {
//todo: implement
return Response(activeSort = null)
val sort = settings.getAllContentSort(params.spaceId)
return Response(activeSort = sort)
}
data class Params(

View file

@ -13,12 +13,14 @@ class UpdateAllContentState @Inject constructor(
) : ResultInteractor<UpdateAllContentState.Params, Unit>(dispatchers.io) {
override suspend fun doWork(params: Params) {
//todo: implement
settings.setAllContentSort(
space = params.spaceId,
sort = params.sort
)
}
data class Params(
val spaceId: SpaceId,
val query: String,
val relatedObjectId: Id?
val sort: Id
)
}

View file

@ -45,5 +45,8 @@ interface UserSettingsRepository {
suspend fun getWidgetSession() : WidgetSession
suspend fun saveWidgetSession(session: WidgetSession)
suspend fun getAllContentSort(space: SpaceId): Id
suspend fun setAllContentSort(space: SpaceId, sort: Id)
suspend fun clear()
}

View file

@ -31,7 +31,7 @@ sealed class AllContentState {
@Immutable
enum class AllContentTab {
PAGES, LISTS, MEDIA, BOOKMARKS, FILES, TYPES, RELATIONS
PAGES, LISTS, MEDIA, BOOKMARKS, FILES, TYPES
}
sealed class AllContentMode {
@ -164,7 +164,8 @@ data class UiMenuState(
val mode: List<AllContentMenuMode>,
val container: MenuSortsItem.Container,
val sorts: List<MenuSortsItem.Sort>,
val types: List<MenuSortsItem.SortType>
val types: List<MenuSortsItem.SortType>,
val showBin: Boolean = true
) {
companion object {
fun empty(): UiMenuState {
@ -208,7 +209,8 @@ fun AllContentMode.view(): UiTitleState {
fun Key?.mapRelationKeyToSort(): AllContentSort {
return when (this) {
Relations.CREATED_DATE -> AllContentSort.ByDateCreated()
Relations.LAST_OPENED_DATE -> AllContentSort.ByDateUpdated()
Relations.LAST_MODIFIED_DATE -> AllContentSort.ByDateUpdated()
Relations.NAME -> AllContentSort.ByName()
else -> DEFAULT_INITIAL_SORT
}
}
@ -249,7 +251,7 @@ fun ObjectWrapper.Basic.toAllContentItem(
layout = layout,
builder = urlBuilder
),
lastModifiedDate = DateParser.parseInMillis(obj.lastModifiedDate) ?: 0L,
lastModifiedDate = DateParser.parse(obj.getValue(Relations.LAST_MODIFIED_DATE)) ?: 0L,
createdDate = DateParser.parse(obj.getValue(Relations.CREATED_DATE)) ?: 0L
)
}

View file

@ -118,7 +118,6 @@ fun AllContentTab.filtersForSubscribe(
}
AllContentTab.TYPES -> TODO()
AllContentTab.RELATIONS -> TODO()
}
}
@ -144,7 +143,6 @@ fun AllContentTab.filtersForSearch(
}
AllContentTab.TYPES -> TODO()
AllContentTab.RELATIONS -> TODO()
}
}

View file

@ -10,6 +10,7 @@ import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.domain.all_content.RestoreAllContentState
import com.anytypeio.anytype.domain.all_content.UpdateAllContentState
import com.anytypeio.anytype.domain.base.fold
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.LocaleProvider
import com.anytypeio.anytype.domain.misc.UrlBuilder
@ -40,7 +41,6 @@ import java.time.LocalDate
import java.time.ZoneId
import java.time.format.TextStyle
import java.time.temporal.ChronoUnit
import javax.inject.Named
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
@ -159,7 +159,7 @@ class AllContentViewModel(
val initialParams = restoreAllContentState.run(
RestoreAllContentState.Params(vmParams.spaceId)
)
if (initialParams.activeSort != null) {
if (!initialParams.activeSort.isNullOrEmpty()) {
_sortState.value = initialParams.activeSort.mapRelationKeyToSort()
}
}.onFailure { e ->
@ -418,7 +418,7 @@ class AllContentViewModel(
fun onTabClicked(tab: AllContentTab) {
Timber.d("onTabClicked: $tab")
if (tab == AllContentTab.TYPES || tab == AllContentTab.RELATIONS) {
if (tab == AllContentTab.TYPES) {
viewModelScope.launch {
_commands.emit(Command.SendToast("Not implemented yet"))
}
@ -438,6 +438,24 @@ class AllContentViewModel(
fun onSortClicked(sort: AllContentSort) {
Timber.d("onSortClicked: $sort")
_sortState.value = sort
proceedWithSortSaving(sort)
}
private fun proceedWithSortSaving(sort: AllContentSort) {
viewModelScope.launch {
val params = UpdateAllContentState.Params(
spaceId = vmParams.spaceId,
sort = sort.relationKey.key
)
updateAllContentState.async(params).fold(
onSuccess = {
Timber.d("Sort updated")
},
onFailure = {
Timber.e(it, "Error updating sort")
}
)
}
}
fun onFilterChanged(filter: String) {
@ -450,6 +468,12 @@ class AllContentViewModel(
_limitState.value = limit
}
fun onViewBinClicked() {
viewModelScope.launch {
_commands.emit(Command.NavigateToBin(vmParams.spaceId.id))
}
}
fun onItemClicked(item: UiContentItem.Item) {
Timber.d("onItemClicked: ${item.id}")
val layout = item.layout ?: return
@ -506,6 +530,7 @@ class AllContentViewModel(
sealed class Command {
data class NavigateToEditor(val id: Id, val space: Id) : Command()
data class NavigateToSetOrCollection(val id: Id, val space: Id) : Command()
data class NavigateToBin(val space: Id) : Command()
data class SendToast(val message: String) : Command()
}
@ -516,7 +541,7 @@ class AllContentViewModel(
//INITIAL STATE
const val DEFAULT_SEARCH_LIMIT = 50
val DEFAULT_INITIAL_TAB = AllContentTab.PAGES
val DEFAULT_INITIAL_SORT = AllContentSort.ByDateCreated()
val DEFAULT_INITIAL_SORT = AllContentSort.ByName()
val DEFAULT_INITIAL_MODE = AllContentMode.AllContent
val DEFAULT_QUERY = ""
}

View file

@ -3,9 +3,9 @@ package com.anytypeio.anytype.feature_allcontent.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
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.padding
@ -20,10 +20,12 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.ColorFilter.Companion.tint
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_models.DVSortType
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
@ -39,7 +41,8 @@ import com.anytypeio.anytype.feature_allcontent.models.UiMenuState
fun AllContentMenu(
uiMenuState: UiMenuState,
onModeClick: (AllContentMenuMode) -> Unit,
onSortClick: (AllContentSort) -> Unit
onSortClick: (AllContentSort) -> Unit,
onBinClick: () -> Unit
) {
var sortingExpanded by remember { mutableStateOf(false) }
@ -51,15 +54,18 @@ fun AllContentMenu(
onModeClick(item)
}
)
Divider(0.5.dp)
}
Spacer(modifier = Modifier.height(8.dp))
Divider(7.5.dp)
SortingBox(
modifier = Modifier
.clickable {
sortingExpanded = !sortingExpanded
},
subtitle = uiMenuState.container.sort.title()
subtitle = uiMenuState.container.sort.title(),
isExpanded = sortingExpanded
)
Divider(0.5.dp)
if (sortingExpanded) {
uiMenuState.sorts.forEach { item ->
MenuItem(
@ -70,8 +76,10 @@ fun AllContentMenu(
onSortClick(item.sort)
}
)
Divider(0.5.dp)
}
uiMenuState.types.forEach { item ->
Divider(7.5.dp)
uiMenuState.types.forEachIndexed { index, item ->
MenuItem(
title = item.sortType.title(item.sort),
isSelected = item.isSelected,
@ -85,12 +93,34 @@ fun AllContentMenu(
onSortClick(updatedSort)
}
)
if (index < uiMenuState.types.size - 1) {
Divider(0.5.dp)
}
}
}
if (uiMenuState.showBin && !sortingExpanded) {
Divider(7.5.dp)
MenuItem(
title = stringResource(id = R.string.all_content_view_bin),
isSelected = false,
modifier = Modifier.clickable { onBinClick() }
)
}
}
@Composable
private fun SortingBox(modifier: Modifier, subtitle: String) {
private fun Divider(height: Dp) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(height)
.background(colorResource(id = R.color.shape_tertiary))
)
}
@Composable
private fun SortingBox(modifier: Modifier, subtitle: String, isExpanded: Boolean) {
val rotationAngle = if (isExpanded) 90f else 0f
Row(
modifier = modifier
.fillMaxWidth()
@ -98,7 +128,9 @@ private fun SortingBox(modifier: Modifier, subtitle: String) {
verticalAlignment = CenterVertically
) {
Image(
modifier = Modifier.size(32.dp),
modifier = Modifier
.size(32.dp)
.rotate(rotationAngle),
painter = painterResource(R.drawable.ic_menu_arrow_right),
contentDescription = "",
colorFilter = tint(colorResource(id = R.color.glyph_selected))
@ -232,7 +264,8 @@ fun AllContentMenuPreview() {
container = MenuSortsItem.Container(AllContentSort.ByName())
),
onModeClick = {},
onSortClick = {}
onSortClick = {},
onBinClick = {}
)
}
//endregion

View file

@ -20,15 +20,13 @@ import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.FabPosition
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
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.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
@ -42,12 +40,12 @@ import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.anytypeio.anytype.core_models.DVSortType
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
import com.anytypeio.anytype.core_ui.foundation.Divider
import com.anytypeio.anytype.core_ui.views.ButtonSize
@ -90,44 +88,43 @@ fun AllContentWrapperScreen(
onQueryChanged: (String) -> Unit,
onModeClick: (AllContentMenuMode) -> Unit,
onSortClick: (AllContentSort) -> Unit,
onItemClicked: (UiContentItem.Item) -> Unit
onItemClicked: (UiContentItem.Item) -> Unit,
onBinClick: () -> Unit
) {
val objects = remember { mutableStateOf<List<UiContentItem>>(emptyList()) }
if (uiState is UiContentState.Content) {
objects.value = uiState.items
}
AllContentMainScreen(
uiTitleState = uiTitleState,
uiTabsState = uiTabsState,
uiMenuButtonViewState = uiMenuButtonViewState,
onTabClick = onTabClick,
objects = objects,
isLoading = uiState is UiContentState.Loading,
onQueryChanged = onQueryChanged,
uiMenuState = uiMenuState,
onModeClick = onModeClick,
onSortClick = onSortClick,
onItemClicked = onItemClicked
onItemClicked = onItemClicked,
onBinClick = onBinClick,
uiState = uiState
)
}
@Composable
fun AllContentMainScreen(
uiState: UiContentState,
uiTitleState: UiTitleState,
uiTabsState: UiTabsState,
uiMenuButtonViewState: MenuButtonViewState,
uiMenuState: UiMenuState,
objects: MutableState<List<UiContentItem>>,
onTabClick: (AllContentTab) -> Unit,
onQueryChanged: (String) -> Unit,
onModeClick: (AllContentMenuMode) -> Unit,
onSortClick: (AllContentSort) -> Unit,
isLoading: Boolean,
onItemClicked: (UiContentItem.Item) -> Unit
onItemClicked: (UiContentItem.Item) -> Unit,
onBinClick: () -> Unit
) {
val modifier = Modifier
.background(color = colorResource(id = R.color.background_primary))
var isSearchEmpty by remember { mutableStateOf(true) }
Scaffold(
modifier = modifier
.fillMaxSize(),
@ -148,6 +145,7 @@ fun AllContentMainScreen(
uiMenuState = uiMenuState,
onSortClick = onSortClick,
onModeClick = onModeClick,
onBinClick = onBinClick
)
}
@ -157,7 +155,10 @@ fun AllContentMainScreen(
}
}
Spacer(modifier = Modifier.size(10.dp))
AllContentSearchBar(onQueryChanged)
AllContentSearchBar(onQueryChanged = {
isSearchEmpty = it.isEmpty()
onQueryChanged(it)
})
Spacer(modifier = Modifier.size(10.dp))
Divider(paddingStart = 0.dp, paddingEnd = 0.dp)
}
@ -173,16 +174,35 @@ fun AllContentMainScreen(
Modifier
.fillMaxSize()
.padding(paddingValues)
if (isLoading) {
Box(modifier = contentModifier) {
LoadingState()
when (uiState) {
is UiContentState.Content -> {
if (uiState.items.isEmpty()) {
Box(modifier = contentModifier, contentAlignment = Alignment.Center) {
EmptyState(isSearchEmpty = isSearchEmpty)
}
} else {
ContentItems(
modifier = contentModifier,
items = uiState.items,
onItemClicked = onItemClicked
)
}
}
is UiContentState.Error -> {
Box(
modifier = contentModifier,
contentAlignment = Alignment.Center
) {
ErrorState(uiState.message)
}
}
UiContentState.Hidden -> {}
UiContentState.Loading -> {
Box(modifier = contentModifier) {
LoadingState()
}
}
} else {
ContentItems(
modifier = contentModifier,
items = objects.value,
onItemClicked = onItemClicked
)
}
}
)
@ -367,14 +387,54 @@ fun AllContentItemIcon(
@Composable
private fun BoxScope.ErrorState(message: String) {
Text(
modifier = Modifier
.wrapContentSize()
.align(Alignment.Center),
text = "Error : message",
color = colorResource(id = R.color.palette_system_red),
style = UXBody
)
Column {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 32.dp),
text = stringResource(id = R.string.all_content_error_title),
color = colorResource(id = R.color.text_primary),
style = UXBody,
textAlign = TextAlign.Center
)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 32.dp),
text = message,
color = colorResource(id = R.color.palette_system_red),
style = UXBody,
textAlign = TextAlign.Center,
maxLines = 3
)
}
}
@Composable
private fun EmptyState(isSearchEmpty: Boolean) {
val (title, description) = if (!isSearchEmpty) {
stringResource(R.string.all_content_no_results_title) to stringResource(R.string.all_content_no_results_description)
} else {
stringResource(R.string.allContent_empty_state_title) to stringResource(R.string.allContent_empty_state_description)
}
Column {
Text(
modifier = Modifier
.fillMaxWidth(),
text = title,
color = colorResource(id = R.color.text_primary),
style = UXBody,
textAlign = TextAlign.Center
)
Text(
modifier = Modifier
.fillMaxWidth(),
text = description,
color = colorResource(id = R.color.text_secondary),
style = UXBody,
textAlign = TextAlign.Center
)
}
}
@Composable

View file

@ -77,7 +77,8 @@ fun AllContentTopBarContainer(
menuButtonState: MenuButtonViewState,
uiMenuState: UiMenuState,
onModeClick: (AllContentMenuMode) -> Unit,
onSortClick: (AllContentSort) -> Unit
onSortClick: (AllContentSort) -> Unit,
onBinClick: () -> Unit
) {
var isMenuExpanded by remember { mutableStateOf(false) }
@ -96,7 +97,7 @@ fun AllContentTopBarContainer(
onDismissRequest = { isMenuExpanded = false },
shape = RoundedCornerShape(size = 16.dp),
containerColor = colorResource(id = R.color.background_primary),
shadowElevation = 20.dp,
shadowElevation = 5.dp
) {
AllContentMenu(
uiMenuState = uiMenuState,
@ -107,7 +108,8 @@ fun AllContentTopBarContainer(
onSortClick = {
onSortClick(it)
isMenuExpanded = false
}
},
onBinClick = onBinClick
)
}
},
@ -150,7 +152,8 @@ private fun AllContentTopBarContainerPreview() {
)
),
onModeClick = {},
onSortClick = {}
onSortClick = {},
onBinClick = {}
)
}
//endregion
@ -266,7 +269,6 @@ private fun getTabText(tab: AllContentTab): String {
AllContentTab.MEDIA -> stringResource(id = R.string.all_content_title_tab_media)
AllContentTab.BOOKMARKS -> stringResource(id = R.string.all_content_title_tab_bookmarks)
AllContentTab.TYPES -> stringResource(id = R.string.all_content_title_tab_objetc_types)
AllContentTab.RELATIONS -> stringResource(id = R.string.all_content_title_tab_relations)
AllContentTab.LISTS -> stringResource(id = R.string.all_content_title_tab_lists)
}
}
@ -281,8 +283,7 @@ private fun AllContentTabsPreview() {
AllContentTab.FILES,
AllContentTab.MEDIA,
AllContentTab.BOOKMARKS,
AllContentTab.TYPES,
AllContentTab.RELATIONS
AllContentTab.TYPES
),
selectedTab = AllContentTab.MEDIA
),

View file

@ -1760,7 +1760,6 @@ Please provide specific details of your needs here.</string>
<string name="all_content_title_tab_bookmarks">Bookmarks</string>
<string name="all_content_title_tab_files">Files</string>
<string name="all_content_title_tab_objetc_types">Object Types</string>
<string name="all_content_title_tab_relations">Relations</string>
<string name="all_content_sort_by">Sort by</string>
<string name="all_content_sort_name_desc">Z → A</string>
@ -1783,5 +1782,12 @@ Please provide specific details of your needs here.</string>
<string name="introduce_vault_welcome_to_the_vault">Welcome to the Vault</string>
<string name="introduce_vault_simple_flexible"><![CDATA[Simple & Flexible]]></string>
<string name="allContent_empty_state_title">Its empty here.</string>
<string name="allContent_empty_state_description">Create your first objects to get started.</string>
<string name="all_content_no_results_title">No results found.</string>
<string name="all_content_no_results_description">Try searching with different keywords.</string>
<string name="all_content_error_title">Something went wrong.</string>
</resources>

View file

@ -15,6 +15,7 @@ import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_models.primitives.TypeId
import com.anytypeio.anytype.core_models.settings.VaultSettings
import com.anytypeio.anytype.data.auth.repo.UserSettingsCache
import com.anytypeio.anytype.persistence.AllContentSettings
import com.anytypeio.anytype.persistence.GlobalSearchHistoryProto
import com.anytypeio.anytype.persistence.SpacePreference
import com.anytypeio.anytype.persistence.SpacePreferences
@ -434,6 +435,40 @@ class DefaultUserSettingsCache(
}
}
override suspend fun getAllContentSort(space: SpaceId): Id {
return context.spacePrefsStore
.data
.map { preferences ->
preferences
.preferences[space.id]
?.allContent
?.sortKey
.orEmpty()
}
.first()
}
override suspend fun setAllContentSort(space: SpaceId, sort: Id) {
context.spacePrefsStore.updateData { existingPreferences ->
val givenSpacePreference = existingPreferences
.preferences
.getOrDefault(
key = space.id,
defaultValue = SpacePreference()
)
val updated = givenSpacePreference.copy(
allContent = AllContentSettings(
sortKey = sort
)
)
val result = buildMap {
putAll(existingPreferences.preferences)
put(key = space.id, updated)
}
SpacePreferences(preferences = result)
}
}
companion object {
const val CURRENT_SPACE_KEY = "prefs.user_settings.current_space"
const val DEFAULT_OBJECT_TYPE_ID_KEY = "prefs.user_settings.default_object_type.id"

View file

@ -22,9 +22,14 @@ message SpacePreference {
repeated string pinnedObjectTypeIds = 2;
optional string lastOpenedObject = 3;
optional GlobalSearchHistoryProto globalSearchHistory = 5;
optional AllContentSettings allContent = 6;
}
message GlobalSearchHistoryProto {
optional string lastSearchQuery = 1;
optional string lastSearchRelatedObjectId = 2;
}
message AllContentSettings {
optional string sortKey = 1;
}

View file

@ -395,11 +395,7 @@ class HomeScreenViewModel(
spaceWidgetView.map { view -> listOf(view) }
}
}.combine(hasEditAccess) { widgets, hasEditAccess ->
if (hasEditAccess) {
widgets + listOf(WidgetView.Library, bin) + actions
} else {
widgets
}
buildListOfWidgets(hasEditAccess, widgets)
}
.catch {
Timber.e(it, "Error while rendering widgets")
@ -410,6 +406,18 @@ class HomeScreenViewModel(
}
}
private fun buildListOfWidgets(
hasEditAccess: Boolean,
widgets: List<WidgetView>
): List<WidgetView> {
return buildList {
addAll(widgets)
if (hasEditAccess) {
addAll(actions)
}
}
}
private fun proceedWithWidgetContainerPipeline() {
viewModelScope.launch {
widgets.filterNotNull().map { widgets ->

View file

@ -300,7 +300,7 @@ class HomeScreenViewModelTest {
}
@Test
fun `should emit bin, library and actions and space view if there is no block`() = runTest {
fun `should emit actions and space view if there is no block`() = runTest {
// SETUP
@ -314,8 +314,6 @@ class HomeScreenViewModelTest {
blocks = listOf(smartBlock)
)
val binWidget = WidgetView.Bin(id = Subscriptions.SUBSCRIPTION_ARCHIVED)
val events: Flow<List<Event>> = emptyFlow()
stubConfig()
@ -351,8 +349,6 @@ class HomeScreenViewModelTest {
actual = secondTimeState,
expected = buildList {
add(defaultSpaceWidgetView)
add(WidgetView.Library)
add(binWidget)
addAll(HomeScreenViewModel.actions)
}
)
@ -360,7 +356,7 @@ class HomeScreenViewModelTest {
}
@Test
fun `should emit only bin and actions when home screen has no associated widgets except the default ones`() =
fun `should emit only actions when home screen has no associated widgets except the default ones`() =
runTest {
// SETUP
@ -404,8 +400,6 @@ class HomeScreenViewModelTest {
assertEquals(
expected = buildList {
add(defaultSpaceWidgetView)
add(WidgetView.Library)
add(binWidget)
addAll(HomeScreenViewModel.actions)
},
actual = secondTimeItem
@ -421,7 +415,7 @@ class HomeScreenViewModelTest {
}
@Test
fun `should emit tree-widget with empty elements and bin when source has no links`() = runTest {
fun `should emit tree-widget with empty elements when source has no links`() = runTest {
// SETUP
@ -458,8 +452,6 @@ class HomeScreenViewModelTest {
)
)
val binWidget = WidgetView.Bin(id = Subscriptions.SUBSCRIPTION_ARCHIVED)
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenWidgetObject(givenObjectView)
@ -505,8 +497,6 @@ class HomeScreenViewModelTest {
isExpanded = true
)
)
add(WidgetView.Library)
add(binWidget)
addAll(HomeScreenViewModel.actions)
},
actual = secondTimeState
@ -522,7 +512,7 @@ class HomeScreenViewModelTest {
}
@Test
fun `should emit tree-widget with 2 elements, library and bin`() = runTest {
fun `should emit tree-widget with 2 elements`() = runTest {
// SETUP
@ -568,8 +558,6 @@ class HomeScreenViewModelTest {
)
)
val binWidget = WidgetView.Bin(id = Subscriptions.SUBSCRIPTION_ARCHIVED)
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenWidgetObject(givenObjectView)
@ -633,8 +621,6 @@ class HomeScreenViewModelTest {
isExpanded = true
)
)
add(WidgetView.Library)
add(binWidget)
addAll(HomeScreenViewModel.actions)
},
actual = secondTimeState
@ -643,7 +629,7 @@ class HomeScreenViewModelTest {
}
@Test
fun `should emit list without elements, library and bin`() = runTest {
fun `should emit list without elements`() = runTest {
// SETUP
@ -689,8 +675,6 @@ class HomeScreenViewModelTest {
)
)
val binWidget = WidgetView.Bin(id = Subscriptions.SUBSCRIPTION_ARCHIVED)
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenWidgetObject(givenObjectView)
@ -741,8 +725,6 @@ class HomeScreenViewModelTest {
tabs = emptyList()
)
)
add(WidgetView.Library)
add(binWidget)
addAll(HomeScreenViewModel.actions)
},
actual = secondTimeState
@ -751,7 +733,7 @@ class HomeScreenViewModelTest {
}
@Test
fun `should emit compact list without elements, library and bin`() = runTest {
fun `should emit compact list without elements`() = runTest {
// SETUP
@ -797,8 +779,6 @@ class HomeScreenViewModelTest {
)
)
val binWidget = WidgetView.Bin(id = Subscriptions.SUBSCRIPTION_ARCHIVED)
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenWidgetObject(givenObjectView)
@ -849,8 +829,6 @@ class HomeScreenViewModelTest {
tabs = emptyList()
)
)
add(WidgetView.Library)
add(binWidget)
addAll(HomeScreenViewModel.actions)
},
actual = secondTimeState
@ -859,7 +837,7 @@ class HomeScreenViewModelTest {
}
@Test
fun `should emit three bundled widgets with tree layout, each having 2 elements, library and bin`() =
fun `should emit three bundled widgets with tree layout, each having 2 elements`() =
runTest {
// SETUP
@ -924,8 +902,6 @@ class HomeScreenViewModelTest {
)
)
val binWidget = WidgetView.Bin(id = Subscriptions.SUBSCRIPTION_ARCHIVED)
stubConfig()
stubInterceptEvents(events = emptyFlow())
stubOpenWidgetObject(givenObjectView)
@ -1142,8 +1118,6 @@ class HomeScreenViewModelTest {
isExpanded = true
)
)
add(WidgetView.Library)
add(binWidget)
addAll(HomeScreenViewModel.actions)
},
actual = secondTimeState
@ -1152,7 +1126,7 @@ class HomeScreenViewModelTest {
}
@Test
fun `should emit link-widget, library, bin and actions`() = runTest {
fun `should emit link-widget and actions`() = runTest {
// SETUP
@ -1229,8 +1203,6 @@ class HomeScreenViewModelTest {
source = Widget.Source.Default(sourceObject),
)
)
add(WidgetView.Library)
add(binWidget)
addAll(HomeScreenViewModel.actions)
},
actual = secondTimeState
@ -1987,8 +1959,6 @@ class HomeScreenViewModelTest {
actual = secondTimeState,
expected = buildList {
add(defaultSpaceWidgetView)
add(WidgetView.Library)
add(binWidget)
addAll(HomeScreenViewModel.actions)
}
)