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:
parent
97fa5232b9
commit
8878d0760b
18 changed files with 287 additions and 110 deletions
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = ""
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
),
|
||||
|
|
|
@ -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">It’s 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>
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 ->
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue