mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-2886 All content | Enhancement | Paging (#1621)
This commit is contained in:
parent
4018a139b5
commit
fa4bebb4ad
9 changed files with 341 additions and 327 deletions
|
@ -18,27 +18,11 @@ import com.anytypeio.anytype.presentation.objects.getProperName
|
|||
import com.anytypeio.anytype.presentation.objects.getProperType
|
||||
|
||||
//region STATE
|
||||
sealed class AllContentState {
|
||||
data object Init : AllContentState()
|
||||
data class Default(
|
||||
val activeTab: AllContentTab,
|
||||
val activeMode: AllContentMode,
|
||||
val activeSort: AllContentSort,
|
||||
val filter: String,
|
||||
val limit: Int
|
||||
) : AllContentState()
|
||||
}
|
||||
|
||||
@Immutable
|
||||
enum class AllContentTab {
|
||||
PAGES, LISTS, MEDIA, BOOKMARKS, FILES, TYPES
|
||||
}
|
||||
|
||||
sealed class AllContentMode {
|
||||
data object AllContent : AllContentMode()
|
||||
data object Unlinked : AllContentMode()
|
||||
}
|
||||
|
||||
sealed class AllContentMenuMode {
|
||||
abstract val isSelected: Boolean
|
||||
|
||||
|
@ -89,12 +73,6 @@ sealed class UiTitleState {
|
|||
data object OnlyUnlinked : UiTitleState()
|
||||
}
|
||||
|
||||
//MENU BUTTON
|
||||
sealed class MenuButtonViewState {
|
||||
data object Hidden : MenuButtonViewState()
|
||||
data object Visible : MenuButtonViewState()
|
||||
}
|
||||
|
||||
// TABS
|
||||
@Immutable
|
||||
sealed class UiTabsState {
|
||||
|
@ -109,19 +87,13 @@ sealed class UiTabsState {
|
|||
|
||||
// CONTENT
|
||||
sealed class UiContentState {
|
||||
|
||||
data object Hidden : UiContentState()
|
||||
|
||||
data object Loading : UiContentState()
|
||||
|
||||
data class Idle(val scrollToTop: Boolean = false) : UiContentState()
|
||||
data object InitLoading : UiContentState()
|
||||
data object Paging : UiContentState()
|
||||
data object Empty : UiContentState()
|
||||
data class Error(
|
||||
val message: String,
|
||||
) : UiContentState()
|
||||
|
||||
@Immutable
|
||||
data class Content(
|
||||
val items: List<UiContentItem>,
|
||||
) : UiContentState()
|
||||
}
|
||||
|
||||
// ITEMS
|
||||
|
@ -160,25 +132,21 @@ sealed class UiContentItem {
|
|||
|
||||
// MENU
|
||||
@Immutable
|
||||
data class UiMenuState(
|
||||
val mode: List<AllContentMenuMode>,
|
||||
val container: MenuSortsItem.Container,
|
||||
val sorts: List<MenuSortsItem.Sort>,
|
||||
val types: List<MenuSortsItem.SortType>,
|
||||
val showBin: Boolean = true
|
||||
) {
|
||||
companion object {
|
||||
fun empty(): UiMenuState {
|
||||
return UiMenuState(
|
||||
mode = emptyList(),
|
||||
container = MenuSortsItem.Container(AllContentSort.ByName()),
|
||||
sorts = emptyList(),
|
||||
types = emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
sealed class UiMenuState{
|
||||
|
||||
data object Hidden : UiMenuState()
|
||||
|
||||
@Immutable
|
||||
data class Visible(
|
||||
val mode: List<AllContentMenuMode>,
|
||||
val container: MenuSortsItem.Container,
|
||||
val sorts: List<MenuSortsItem.Sort>,
|
||||
val types: List<MenuSortsItem.SortType>,
|
||||
val showBin: Boolean = true
|
||||
) : UiMenuState()
|
||||
}
|
||||
|
||||
|
||||
sealed class MenuSortsItem {
|
||||
data class Container(val sort: AllContentSort) : MenuSortsItem()
|
||||
data class Sort(val sort: AllContentSort) : MenuSortsItem()
|
||||
|
@ -192,20 +160,6 @@ sealed class MenuSortsItem {
|
|||
//endregion
|
||||
|
||||
//region MAPPING
|
||||
fun AllContentState.Default.toMenuMode(): AllContentMenuMode {
|
||||
return when (activeMode) {
|
||||
AllContentMode.AllContent -> AllContentMenuMode.AllContent(isSelected = true)
|
||||
AllContentMode.Unlinked -> AllContentMenuMode.Unlinked(isSelected = true)
|
||||
}
|
||||
}
|
||||
|
||||
fun AllContentMode.view(): UiTitleState {
|
||||
return when (this) {
|
||||
AllContentMode.AllContent -> UiTitleState.AllContent
|
||||
AllContentMode.Unlinked -> UiTitleState.OnlyUnlinked
|
||||
}
|
||||
}
|
||||
|
||||
fun Key?.mapRelationKeyToSort(): AllContentSort {
|
||||
return when (this) {
|
||||
Relations.CREATED_DATE -> AllContentSort.ByDateCreated()
|
||||
|
|
|
@ -38,7 +38,7 @@ val allContentTabLayouts = mapOf(
|
|||
// Function to create subscription params
|
||||
fun createSubscriptionParams(
|
||||
spaceId: Id,
|
||||
activeMode: AllContentMode,
|
||||
activeMode: UiTitleState,
|
||||
activeTab: AllContentTab,
|
||||
activeSort: AllContentSort,
|
||||
limitedObjectIds: List<String>,
|
||||
|
@ -90,7 +90,7 @@ fun AllContentTab.filtersForSubscribe(
|
|||
spaces: List<Id>,
|
||||
activeSort: AllContentSort,
|
||||
limitedObjectIds: List<Id>,
|
||||
activeMode: AllContentMode
|
||||
activeMode: UiTitleState
|
||||
): Pair<List<DVFilter>, List<DVSort>> {
|
||||
val tab = this
|
||||
when (this) {
|
||||
|
@ -109,7 +109,7 @@ fun AllContentTab.filtersForSubscribe(
|
|||
if (limitedObjectIds.isNotEmpty()) {
|
||||
add(buildLimitedObjectIdsFilter(limitedObjectIds = limitedObjectIds))
|
||||
}
|
||||
if (activeMode == AllContentMode.Unlinked) {
|
||||
if (activeMode == UiTitleState.OnlyUnlinked) {
|
||||
addAll(buildUnlinkedObjectFilter())
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +133,6 @@ fun AllContentTab.filtersForSearch(
|
|||
AllContentTab.BOOKMARKS -> {
|
||||
val filters = buildList {
|
||||
addAll(buildDeletedFilter())
|
||||
add(buildLayoutFilter(layouts = allContentTabLayouts.getValue(tab)))
|
||||
add(buildSpaceIdFilter(spaces))
|
||||
if (tab == AllContentTab.PAGES) {
|
||||
add(buildTemplateFilter())
|
||||
|
|
|
@ -15,24 +15,20 @@ import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
|||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
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.SearchObjects
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiContentItem
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentMenuMode
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentMode
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentSort
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentTab
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiTitleState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiContentState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.MenuButtonViewState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.MenuSortsItem
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiContentItem
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiContentState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiMenuState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiTabsState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiTitleState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.createSubscriptionParams
|
||||
import com.anytypeio.anytype.feature_allcontent.models.filtersForSearch
|
||||
import com.anytypeio.anytype.feature_allcontent.models.mapRelationKeyToSort
|
||||
import com.anytypeio.anytype.feature_allcontent.models.toUiContentItems
|
||||
import com.anytypeio.anytype.feature_allcontent.models.view
|
||||
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
|
||||
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
|
||||
import com.anytypeio.anytype.presentation.home.navigation
|
||||
|
@ -43,13 +39,9 @@ import java.time.format.TextStyle
|
|||
import java.time.temporal.ChronoUnit
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
|
@ -57,11 +49,11 @@ import kotlinx.coroutines.flow.debounce
|
|||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
@ -75,7 +67,6 @@ import timber.log.Timber
|
|||
class AllContentViewModel(
|
||||
private val vmParams: VmParams,
|
||||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val storeOfRelations: StoreOfRelations,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val analytics: Analytics,
|
||||
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
|
||||
|
@ -86,12 +77,20 @@ class AllContentViewModel(
|
|||
private val localeProvider: LocaleProvider
|
||||
) : ViewModel() {
|
||||
|
||||
private val _limitedObjectIds: MutableStateFlow<List<String>> = MutableStateFlow(emptyList())
|
||||
private val searchResultIds = MutableStateFlow<List<Id>>(emptyList())
|
||||
private val sortState = MutableStateFlow<AllContentSort>(DEFAULT_INITIAL_SORT)
|
||||
|
||||
private val _tabsState = MutableStateFlow<AllContentTab>(DEFAULT_INITIAL_TAB)
|
||||
private val _modeState = MutableStateFlow<AllContentMode>(DEFAULT_INITIAL_MODE)
|
||||
private val _sortState = MutableStateFlow<AllContentSort>(DEFAULT_INITIAL_SORT)
|
||||
private val _limitState = MutableStateFlow(DEFAULT_SEARCH_LIMIT)
|
||||
val uiTitleState = MutableStateFlow<UiTitleState>(UiTitleState.Hidden)
|
||||
val uiTabsState = MutableStateFlow<UiTabsState>(UiTabsState.Hidden)
|
||||
val uiMenuState = MutableStateFlow<UiMenuState>(UiMenuState.Hidden)
|
||||
val uiItemsState = MutableStateFlow<List<UiContentItem>>(emptyList())
|
||||
val uiContentState = MutableStateFlow<UiContentState>(UiContentState.Idle())
|
||||
|
||||
val commands = MutableSharedFlow<Command>()
|
||||
|
||||
/**
|
||||
* Search query
|
||||
*/
|
||||
private val userInput = MutableStateFlow(DEFAULT_QUERY)
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
|
@ -101,58 +100,30 @@ class AllContentViewModel(
|
|||
emitAll(userInput.drop(1).debounce(DEFAULT_DEBOUNCE_DURATION).distinctUntilChanged())
|
||||
}
|
||||
|
||||
private val _uiTitleState = MutableStateFlow<UiTitleState>(UiTitleState.Hidden)
|
||||
val uiTitleState: StateFlow<UiTitleState> = _uiTitleState.asStateFlow()
|
||||
/**
|
||||
* Paging and subscription limit. If true, we can paginate after reaching bottom items.
|
||||
* Could be true only after the first subscription results (if results size == limit)
|
||||
*/
|
||||
val canPaginate = MutableStateFlow(false)
|
||||
private var itemsLimit = DEFAULT_SEARCH_LIMIT
|
||||
private val limitUpdateTrigger = MutableStateFlow(0)
|
||||
|
||||
private val _uiMenuButtonState =
|
||||
MutableStateFlow<MenuButtonViewState>(MenuButtonViewState.Hidden)
|
||||
val uiMenuButtonState: StateFlow<MenuButtonViewState> = _uiMenuButtonState.asStateFlow()
|
||||
|
||||
private val _uiTabsState = MutableStateFlow<UiTabsState>(UiTabsState.Hidden)
|
||||
val uiTabsState: StateFlow<UiTabsState> = _uiTabsState.asStateFlow()
|
||||
|
||||
private val _uiState = MutableStateFlow<UiContentState>(UiContentState.Hidden)
|
||||
val uiState: StateFlow<UiContentState> = _uiState.asStateFlow()
|
||||
|
||||
private val _uiMenu = MutableStateFlow(UiMenuState.empty())
|
||||
val uiMenu: StateFlow<UiMenuState> = _uiMenu.asStateFlow()
|
||||
|
||||
private val _commands = MutableSharedFlow<Command>()
|
||||
val commands: SharedFlow<Command> = _commands
|
||||
private var shouldScrollToTopItems = false
|
||||
|
||||
init {
|
||||
Timber.d("AllContentViewModel init, spaceId:[${vmParams.spaceId.id}]")
|
||||
setupInitialStateParams()
|
||||
proceedWithUiTitleStateSetup()
|
||||
proceedWithUiTabsStateSetup()
|
||||
proceedWithUiStateSetup()
|
||||
proceedWithSearchStateSetup()
|
||||
proceedWithMenuSetup()
|
||||
}
|
||||
|
||||
private fun proceedWithUiTitleStateSetup() {
|
||||
viewModelScope.launch {
|
||||
_modeState.collectLatest { result ->
|
||||
Timber.d("New mode: [$result]")
|
||||
_uiTitleState.value = result.view()
|
||||
_uiMenuButtonState.value = MenuButtonViewState.Visible
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithUiTabsStateSetup() {
|
||||
viewModelScope.launch {
|
||||
_tabsState.collectLatest { result ->
|
||||
Timber.d("New tab: [$result]")
|
||||
_uiTabsState.value = UiTabsState.Default(
|
||||
tabs = AllContentTab.entries,
|
||||
selectedTab = result
|
||||
)
|
||||
}
|
||||
}
|
||||
setupUiStateFlow()
|
||||
setupSearchStateFlow()
|
||||
setupMenuFlow()
|
||||
}
|
||||
|
||||
private fun setupInitialStateParams() {
|
||||
uiTitleState.value = UiTitleState.AllContent
|
||||
uiTabsState.value = UiTabsState.Default(
|
||||
tabs = AllContentTab.entries,
|
||||
selectedTab = DEFAULT_INITIAL_TAB
|
||||
)
|
||||
viewModelScope.launch {
|
||||
if (vmParams.useHistory) {
|
||||
runCatching {
|
||||
|
@ -160,7 +131,7 @@ class AllContentViewModel(
|
|||
RestoreAllContentState.Params(vmParams.spaceId)
|
||||
)
|
||||
if (!initialParams.activeSort.isNullOrEmpty()) {
|
||||
_sortState.value = initialParams.activeSort.mapRelationKeyToSort()
|
||||
sortState.value = initialParams.activeSort.mapRelationKeyToSort()
|
||||
}
|
||||
}.onFailure { e ->
|
||||
Timber.e(e, "Error restoring state")
|
||||
|
@ -169,98 +140,101 @@ class AllContentViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
private fun proceedWithSearchStateSetup() {
|
||||
private fun setupSearchStateFlow() {
|
||||
viewModelScope.launch {
|
||||
searchQuery.collectLatest { query ->
|
||||
Timber.d("New query: [$query]")
|
||||
if (query.isBlank()) {
|
||||
_limitedObjectIds.value = emptyList()
|
||||
searchResultIds.value = emptyList()
|
||||
} else {
|
||||
val searchParams = createSearchParams(
|
||||
activeTab = _tabsState.value,
|
||||
activeQuery = query
|
||||
)
|
||||
searchObjects(searchParams).process(
|
||||
success = { searchResults ->
|
||||
Timber.d("Search objects by query:[$query], size: : ${searchResults.size}")
|
||||
_limitedObjectIds.value = searchResults.map { it.id }
|
||||
},
|
||||
failure = {
|
||||
Timber.e(it, "Error searching objects by query")
|
||||
}
|
||||
)
|
||||
val activeTab = uiTabsState.value
|
||||
if (activeTab is UiTabsState.Default) {
|
||||
resetLimit()
|
||||
val searchParams = createSearchParams(
|
||||
activeTab = activeTab.selectedTab,
|
||||
activeQuery = query
|
||||
)
|
||||
searchObjects(searchParams).process(
|
||||
success = { searchResults ->
|
||||
Timber.d("Search objects by query:[$query], size: : ${searchResults.size}")
|
||||
searchResultIds.value = searchResults.map { it.id }
|
||||
},
|
||||
failure = {
|
||||
Timber.e(it, "Error searching objects by query")
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Timber.w("Unexpected tabs state: $activeTab")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun proceedWithUiStateSetup() {
|
||||
private fun setupUiStateFlow() {
|
||||
viewModelScope.launch {
|
||||
combine(
|
||||
_modeState,
|
||||
_tabsState,
|
||||
_sortState,
|
||||
_limitedObjectIds,
|
||||
_limitState
|
||||
) { mode, tab, sort, limitedObjectIds, limit ->
|
||||
Result(mode, tab, sort, limitedObjectIds, limit)
|
||||
uiTitleState,
|
||||
uiTabsState.filterIsInstance<UiTabsState.Default>(),
|
||||
sortState,
|
||||
searchResultIds,
|
||||
limitUpdateTrigger
|
||||
) { mode, tab, sort, limitedObjectIds, _ ->
|
||||
Result(mode, tab, sort, limitedObjectIds)
|
||||
}
|
||||
.flatMapLatest { currentState ->
|
||||
Timber.d("AllContentNewState:$currentState, restart subscription")
|
||||
Timber.d("New params:$currentState, restart subscription")
|
||||
loadData(currentState)
|
||||
}.collect {
|
||||
_uiState.value = it
|
||||
uiItemsState.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun subscriptionId() = "all_content_subscription_${vmParams.spaceId.id}"
|
||||
private fun subscriptionId() = "all_content_subscription_${vmParams.spaceId.id}"
|
||||
|
||||
private fun loadData(
|
||||
result: Result
|
||||
): Flow<UiContentState> = flow {
|
||||
val loadingStartTime = System.currentTimeMillis()
|
||||
): Flow<List<UiContentItem>> = flow {
|
||||
|
||||
emit(UiContentState.Loading)
|
||||
if (itemsLimit == DEFAULT_SEARCH_LIMIT) {
|
||||
uiContentState.value = UiContentState.InitLoading
|
||||
} else {
|
||||
uiContentState.value = UiContentState.Paging
|
||||
}
|
||||
|
||||
val searchParams = createSubscriptionParams(
|
||||
activeTab = result.tab,
|
||||
activeTab = result.tab.selectedTab,
|
||||
activeSort = result.sort,
|
||||
limitedObjectIds = result.limitedObjectIds,
|
||||
limit = result.limit,
|
||||
limit = itemsLimit,
|
||||
subscriptionId = subscriptionId(),
|
||||
spaceId = vmParams.spaceId.id,
|
||||
activeMode = result.mode
|
||||
)
|
||||
|
||||
val dataFlow = storelessSubscriptionContainer.subscribe(searchParams)
|
||||
.map { objWrappers ->
|
||||
emitAll(
|
||||
dataFlow.map { objWrappers ->
|
||||
canPaginate.value = objWrappers.size == itemsLimit
|
||||
val items = mapToUiContentItems(
|
||||
objectWrappers = objWrappers,
|
||||
activeSort = result.sort
|
||||
)
|
||||
UiContentState.Content(items = items)
|
||||
}
|
||||
.catch { e ->
|
||||
emit(
|
||||
UiContentState.Error(
|
||||
message = e.message ?: "Error loading objects by subscription"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
var isFirstEmission = true
|
||||
|
||||
emitAll(
|
||||
dataFlow.onEach {
|
||||
if (isFirstEmission) {
|
||||
val elapsedTime = System.currentTimeMillis() - loadingStartTime
|
||||
if (elapsedTime < DEFAULT_LOADING_DELAY) {
|
||||
delay(DEFAULT_LOADING_DELAY - elapsedTime)
|
||||
uiContentState.value = if (items.isEmpty()) {
|
||||
UiContentState.Empty
|
||||
} else {
|
||||
UiContentState.Idle(scrollToTop = shouldScrollToTopItems).also {
|
||||
shouldScrollToTopItems = false
|
||||
}
|
||||
isFirstEmission = false
|
||||
}
|
||||
items
|
||||
}.catch { e ->
|
||||
uiContentState.value = UiContentState.Error(
|
||||
message = e.message ?: "An error occurred while loading data."
|
||||
)
|
||||
emit(emptyList())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -274,28 +248,31 @@ class AllContentViewModel(
|
|||
urlBuilder = urlBuilder,
|
||||
objectTypes = storeOfObjectTypes.getAll()
|
||||
)
|
||||
return if (activeSort.canGroupByDate) {
|
||||
groupItemsByDate(
|
||||
items = items,
|
||||
activeSort = activeSort
|
||||
)
|
||||
} else {
|
||||
items
|
||||
return when (activeSort) {
|
||||
is AllContentSort.ByDateCreated -> {
|
||||
groupItemsByDate(items = items, isSortByDateCreated = true)
|
||||
}
|
||||
is AllContentSort.ByDateUpdated -> {
|
||||
groupItemsByDate(items = items, isSortByDateCreated = false)
|
||||
}
|
||||
is AllContentSort.ByName -> {
|
||||
items
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun groupItemsByDate(
|
||||
items: List<UiContentItem.Item>,
|
||||
activeSort: AllContentSort
|
||||
isSortByDateCreated: Boolean
|
||||
): List<UiContentItem> {
|
||||
val groupedItems = mutableListOf<UiContentItem>()
|
||||
var currentGroupKey: String? = null
|
||||
|
||||
for (item in items) {
|
||||
val timestamp = when (activeSort) {
|
||||
is AllContentSort.ByDateCreated -> item.createdDate
|
||||
is AllContentSort.ByDateUpdated -> item.lastModifiedDate
|
||||
is AllContentSort.ByName -> 0L
|
||||
val timestamp = if (isSortByDateCreated) {
|
||||
item.createdDate
|
||||
} else {
|
||||
item.lastModifiedDate
|
||||
}
|
||||
val (groupKey, group) = getDateGroup(timestamp)
|
||||
|
||||
|
@ -347,7 +324,6 @@ class AllContentViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
// Function to create search parameters
|
||||
private fun createSearchParams(
|
||||
activeTab: AllContentTab,
|
||||
activeQuery: String,
|
||||
|
@ -362,25 +338,17 @@ class AllContentViewModel(
|
|||
)
|
||||
}
|
||||
|
||||
// Function to get the menu mode based on the active mode
|
||||
private fun getMenuMode(mode: AllContentMode): AllContentMenuMode {
|
||||
return when (mode) {
|
||||
AllContentMode.AllContent -> AllContentMenuMode.AllContent(isSelected = true)
|
||||
AllContentMode.Unlinked -> AllContentMenuMode.Unlinked(isSelected = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithMenuSetup() {
|
||||
private fun setupMenuFlow() {
|
||||
viewModelScope.launch {
|
||||
combine(
|
||||
_modeState,
|
||||
_sortState
|
||||
uiTitleState,
|
||||
sortState
|
||||
) { mode, sort ->
|
||||
mode to sort
|
||||
}.collectLatest { (mode, sort) ->
|
||||
val uiMode = listOf(
|
||||
AllContentMenuMode.AllContent(isSelected = mode == AllContentMode.AllContent),
|
||||
AllContentMenuMode.Unlinked(isSelected = mode == AllContentMode.Unlinked)
|
||||
AllContentMenuMode.AllContent(isSelected = mode == UiTitleState.AllContent),
|
||||
AllContentMenuMode.Unlinked(isSelected = mode == UiTitleState.OnlyUnlinked)
|
||||
)
|
||||
val container = MenuSortsItem.Container(sort = sort)
|
||||
val uiSorts = listOf(
|
||||
|
@ -406,7 +374,7 @@ class AllContentViewModel(
|
|||
isSelected = sort.sortType == DVSortType.DESC
|
||||
)
|
||||
)
|
||||
_uiMenu.value = UiMenuState(
|
||||
uiMenuState.value = UiMenuState.Visible(
|
||||
mode = uiMode,
|
||||
container = container,
|
||||
sorts = uiSorts,
|
||||
|
@ -420,24 +388,34 @@ class AllContentViewModel(
|
|||
Timber.d("onTabClicked: $tab")
|
||||
if (tab == AllContentTab.TYPES) {
|
||||
viewModelScope.launch {
|
||||
_commands.emit(Command.SendToast("Not implemented yet"))
|
||||
commands.emit(Command.SendToast("Not implemented yet"))
|
||||
}
|
||||
return
|
||||
}
|
||||
_tabsState.value = tab
|
||||
shouldScrollToTopItems = true
|
||||
resetLimit()
|
||||
uiItemsState.value = emptyList()
|
||||
uiTabsState.value = UiTabsState.Default(
|
||||
tabs = AllContentTab.entries,
|
||||
selectedTab = tab
|
||||
)
|
||||
}
|
||||
|
||||
fun onAllContentModeClicked(mode: AllContentMenuMode) {
|
||||
Timber.d("onAllContentModeClicked: $mode")
|
||||
_modeState.value = when (mode) {
|
||||
is AllContentMenuMode.AllContent -> AllContentMode.AllContent
|
||||
is AllContentMenuMode.Unlinked -> AllContentMode.Unlinked
|
||||
shouldScrollToTopItems = true
|
||||
uiItemsState.value = emptyList()
|
||||
uiTitleState.value = when (mode) {
|
||||
is AllContentMenuMode.AllContent -> UiTitleState.AllContent
|
||||
is AllContentMenuMode.Unlinked -> UiTitleState.OnlyUnlinked
|
||||
}
|
||||
}
|
||||
|
||||
fun onSortClicked(sort: AllContentSort) {
|
||||
Timber.d("onSortClicked: $sort")
|
||||
_sortState.value = sort
|
||||
shouldScrollToTopItems = true
|
||||
uiItemsState.value = emptyList()
|
||||
sortState.value = sort
|
||||
proceedWithSortSaving(sort)
|
||||
}
|
||||
|
||||
|
@ -463,14 +441,9 @@ class AllContentViewModel(
|
|||
userInput.value = filter
|
||||
}
|
||||
|
||||
fun onLimitUpdated(limit: Int) {
|
||||
Timber.d("onLimitUpdated: $limit")
|
||||
_limitState.value = limit
|
||||
}
|
||||
|
||||
fun onViewBinClicked() {
|
||||
viewModelScope.launch {
|
||||
_commands.emit(Command.NavigateToBin(vmParams.spaceId.id))
|
||||
commands.emit(Command.NavigateToBin(vmParams.spaceId.id))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -483,7 +456,7 @@ class AllContentViewModel(
|
|||
space = vmParams.spaceId.id
|
||||
)) {
|
||||
is OpenObjectNavigation.OpenDataView -> {
|
||||
_commands.emit(
|
||||
commands.emit(
|
||||
Command.NavigateToSetOrCollection(
|
||||
id = navigation.target,
|
||||
space = navigation.space
|
||||
|
@ -492,7 +465,7 @@ class AllContentViewModel(
|
|||
}
|
||||
|
||||
is OpenObjectNavigation.OpenEditor -> {
|
||||
_commands.emit(
|
||||
commands.emit(
|
||||
Command.NavigateToEditor(
|
||||
id = navigation.target,
|
||||
space = navigation.space
|
||||
|
@ -501,7 +474,7 @@ class AllContentViewModel(
|
|||
}
|
||||
|
||||
is OpenObjectNavigation.UnexpectedLayoutError -> {
|
||||
_commands.emit(Command.SendToast("Unexpected layout: ${navigation.layout}"))
|
||||
commands.emit(Command.SendToast("Unexpected layout: ${navigation.layout}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -514,17 +487,38 @@ class AllContentViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the limit for the number of items fetched and triggers data reload.
|
||||
*/
|
||||
fun updateLimit() {
|
||||
Timber.d("Update limit, canPaginate: ${canPaginate.value} uiContentState: ${uiContentState.value}")
|
||||
if (canPaginate.value && uiContentState.value is UiContentState.Idle) {
|
||||
itemsLimit += DEFAULT_SEARCH_LIMIT
|
||||
limitUpdateTrigger.value++
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
uiItemsState.value = emptyList()
|
||||
resetLimit()
|
||||
}
|
||||
|
||||
private fun resetLimit() {
|
||||
Timber.d("Reset limit")
|
||||
itemsLimit = DEFAULT_SEARCH_LIMIT
|
||||
}
|
||||
|
||||
data class VmParams(
|
||||
val spaceId: SpaceId,
|
||||
val useHistory: Boolean = true
|
||||
)
|
||||
|
||||
internal data class Result(
|
||||
val mode: AllContentMode,
|
||||
val tab: AllContentTab,
|
||||
val mode: UiTitleState,
|
||||
val tab: UiTabsState.Default,
|
||||
val sort: AllContentSort,
|
||||
val limitedObjectIds: List<String>,
|
||||
val limit: Int
|
||||
val limitedObjectIds: List<String>
|
||||
)
|
||||
|
||||
sealed class Command {
|
||||
|
@ -536,13 +530,11 @@ class AllContentViewModel(
|
|||
|
||||
companion object {
|
||||
const val DEFAULT_DEBOUNCE_DURATION = 300L
|
||||
const val DEFAULT_LOADING_DELAY = 250L
|
||||
|
||||
//INITIAL STATE
|
||||
const val DEFAULT_SEARCH_LIMIT = 50
|
||||
const val DEFAULT_SEARCH_LIMIT = 100
|
||||
val DEFAULT_INITIAL_TAB = AllContentTab.PAGES
|
||||
val DEFAULT_INITIAL_SORT = AllContentSort.ByName()
|
||||
val DEFAULT_INITIAL_MODE = AllContentMode.AllContent
|
||||
val DEFAULT_QUERY = ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,17 +9,14 @@ import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
|||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
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.SearchObjects
|
||||
import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModel.VmParams
|
||||
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
class AllContentViewModelFactory @Inject constructor(
|
||||
private val vmParams: VmParams,
|
||||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val storeOfRelations: StoreOfRelations,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val analytics: Analytics,
|
||||
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
|
||||
|
@ -34,7 +31,6 @@ class AllContentViewModelFactory @Inject constructor(
|
|||
AllContentViewModel(
|
||||
vmParams = vmParams,
|
||||
storeOfObjectTypes = storeOfObjectTypes,
|
||||
storeOfRelations = storeOfRelations,
|
||||
urlBuilder = urlBuilder,
|
||||
analytics = analytics,
|
||||
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
|
||||
|
|
|
@ -39,7 +39,7 @@ import com.anytypeio.anytype.feature_allcontent.models.UiMenuState
|
|||
|
||||
@Composable
|
||||
fun AllContentMenu(
|
||||
uiMenuState: UiMenuState,
|
||||
uiMenuState: UiMenuState.Visible,
|
||||
onModeClick: (AllContentMenuMode) -> Unit,
|
||||
onSortClick: (AllContentSort) -> Unit,
|
||||
onBinClick: () -> Unit
|
||||
|
@ -233,7 +233,7 @@ private fun DVSortType.title(sort: AllContentSort): String = when (this) {
|
|||
@Composable
|
||||
fun AllContentMenuPreview() {
|
||||
AllContentMenu(
|
||||
uiMenuState = UiMenuState(
|
||||
uiMenuState = UiMenuState.Visible(
|
||||
mode = listOf(
|
||||
AllContentMenuMode.AllContent(isSelected = true),
|
||||
AllContentMenuMode.Unlinked(isSelected = false)
|
||||
|
|
|
@ -20,12 +20,17 @@ 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.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
|
@ -69,32 +74,54 @@ import com.anytypeio.anytype.feature_allcontent.R
|
|||
import com.anytypeio.anytype.feature_allcontent.models.AllContentMenuMode
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentSort
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentTab
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiContentState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.MenuButtonViewState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiContentItem
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiContentState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiMenuState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiTabsState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiTitleState
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
@Composable
|
||||
fun AllContentWrapperScreen(
|
||||
uiTitleState: UiTitleState,
|
||||
uiTabsState: UiTabsState,
|
||||
uiMenuButtonViewState: MenuButtonViewState,
|
||||
uiMenuState: UiMenuState,
|
||||
uiState: UiContentState,
|
||||
uiItemsState: List<UiContentItem>,
|
||||
onTabClick: (AllContentTab) -> Unit,
|
||||
onQueryChanged: (String) -> Unit,
|
||||
onModeClick: (AllContentMenuMode) -> Unit,
|
||||
onSortClick: (AllContentSort) -> Unit,
|
||||
onItemClicked: (UiContentItem.Item) -> Unit,
|
||||
onBinClick: () -> Unit
|
||||
onBinClick: () -> Unit,
|
||||
canPaginate: Boolean,
|
||||
onUpdateLimitSearch: () -> Unit,
|
||||
uiContentState: UiContentState
|
||||
) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
val canPaginateState = remember { mutableStateOf(false) }
|
||||
LaunchedEffect(key1 = canPaginate) {
|
||||
canPaginateState.value = canPaginate
|
||||
}
|
||||
|
||||
val shouldStartPaging = remember {
|
||||
derivedStateOf {
|
||||
canPaginateState.value && (lazyListState.layoutInfo.visibleItemsInfo.lastOrNull()?.index
|
||||
?: -9) >= (lazyListState.layoutInfo.totalItemsCount - 2)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = shouldStartPaging.value) {
|
||||
if (shouldStartPaging.value && uiContentState is UiContentState.Idle) {
|
||||
onUpdateLimitSearch()
|
||||
}
|
||||
}
|
||||
|
||||
AllContentMainScreen(
|
||||
uiTitleState = uiTitleState,
|
||||
uiTabsState = uiTabsState,
|
||||
uiMenuButtonViewState = uiMenuButtonViewState,
|
||||
onTabClick = onTabClick,
|
||||
onQueryChanged = onQueryChanged,
|
||||
uiMenuState = uiMenuState,
|
||||
|
@ -102,23 +129,27 @@ fun AllContentWrapperScreen(
|
|||
onSortClick = onSortClick,
|
||||
onItemClicked = onItemClicked,
|
||||
onBinClick = onBinClick,
|
||||
uiState = uiState
|
||||
uiItemsState = uiItemsState,
|
||||
lazyListState = lazyListState,
|
||||
uiContentState = uiContentState
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun AllContentMainScreen(
|
||||
uiState: UiContentState,
|
||||
uiItemsState: List<UiContentItem>,
|
||||
uiTitleState: UiTitleState,
|
||||
uiTabsState: UiTabsState,
|
||||
uiMenuButtonViewState: MenuButtonViewState,
|
||||
uiMenuState: UiMenuState,
|
||||
onTabClick: (AllContentTab) -> Unit,
|
||||
onQueryChanged: (String) -> Unit,
|
||||
onModeClick: (AllContentMenuMode) -> Unit,
|
||||
onSortClick: (AllContentSort) -> Unit,
|
||||
onItemClicked: (UiContentItem.Item) -> Unit,
|
||||
onBinClick: () -> Unit
|
||||
onBinClick: () -> Unit,
|
||||
lazyListState: LazyListState,
|
||||
uiContentState: UiContentState
|
||||
) {
|
||||
val modifier = Modifier
|
||||
.background(color = colorResource(id = R.color.background_primary))
|
||||
|
@ -141,7 +172,6 @@ fun AllContentMainScreen(
|
|||
if (uiTitleState !is UiTitleState.Hidden) {
|
||||
AllContentTopBarContainer(
|
||||
titleState = uiTitleState,
|
||||
menuButtonState = uiMenuButtonViewState,
|
||||
uiMenuState = uiMenuState,
|
||||
onSortClick = onSortClick,
|
||||
onModeClick = onModeClick,
|
||||
|
@ -175,34 +205,37 @@ fun AllContentMainScreen(
|
|||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
|
||||
when (uiState) {
|
||||
is UiContentState.Content -> {
|
||||
if (uiState.items.isEmpty()) {
|
||||
Box(modifier = contentModifier, contentAlignment = Alignment.Center) {
|
||||
EmptyState(isSearchEmpty = isSearchEmpty)
|
||||
Box(
|
||||
modifier = contentModifier,
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
when {
|
||||
uiItemsState.isEmpty() -> {
|
||||
when (uiContentState) {
|
||||
is UiContentState.Error -> {
|
||||
ErrorState(uiContentState.message)
|
||||
}
|
||||
is UiContentState.Idle -> {
|
||||
// Do nothing.
|
||||
}
|
||||
UiContentState.InitLoading -> {
|
||||
LoadingState()
|
||||
}
|
||||
UiContentState.Paging -> {}
|
||||
UiContentState.Empty -> {
|
||||
EmptyState(isSearchEmpty = isSearchEmpty)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else -> {
|
||||
ContentItems(
|
||||
modifier = contentModifier,
|
||||
items = uiState.items,
|
||||
onItemClicked = onItemClicked
|
||||
uiItemsState = uiItemsState,
|
||||
onItemClicked = onItemClicked,
|
||||
uiContentState = uiContentState,
|
||||
lazyListState = lazyListState
|
||||
)
|
||||
}
|
||||
}
|
||||
is UiContentState.Error -> {
|
||||
Box(
|
||||
modifier = contentModifier,
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
ErrorState(uiState.message)
|
||||
}
|
||||
}
|
||||
UiContentState.Hidden -> {}
|
||||
UiContentState.Loading -> {
|
||||
Box(modifier = contentModifier) {
|
||||
LoadingState()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -210,27 +243,34 @@ fun AllContentMainScreen(
|
|||
|
||||
@Composable
|
||||
private fun ContentItems(
|
||||
modifier: Modifier,
|
||||
items: List<UiContentItem>,
|
||||
onItemClicked: (UiContentItem.Item) -> Unit
|
||||
uiItemsState: List<UiContentItem>,
|
||||
onItemClicked: (UiContentItem.Item) -> Unit,
|
||||
uiContentState: UiContentState,
|
||||
lazyListState: LazyListState
|
||||
) {
|
||||
LazyColumn(modifier = modifier) {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = lazyListState
|
||||
) {
|
||||
items(
|
||||
count = items.size,
|
||||
key = { index -> items[index].id },
|
||||
count = uiItemsState.size,
|
||||
key = { index -> uiItemsState[index].id },
|
||||
contentType = { index ->
|
||||
when (items[index]) {
|
||||
when (uiItemsState[index]) {
|
||||
is UiContentItem.Group -> "group"
|
||||
is UiContentItem.Item -> "item"
|
||||
}
|
||||
}
|
||||
) { index ->
|
||||
when (val item = items[index]) {
|
||||
when (val item = uiItemsState[index]) {
|
||||
is UiContentItem.Group -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(52.dp),
|
||||
.height(52.dp)
|
||||
.animateItem(),
|
||||
contentAlignment = Alignment.BottomStart
|
||||
) {
|
||||
Text(
|
||||
|
@ -258,6 +298,28 @@ private fun ContentItems(
|
|||
}
|
||||
}
|
||||
}
|
||||
if (uiContentState is UiContentState.Paging) {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillParentMaxWidth()
|
||||
.height(52.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
LoadingState()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = uiContentState) {
|
||||
if (uiContentState is UiContentState.Idle) {
|
||||
if (uiContentState.scrollToTop) {
|
||||
scope.launch {
|
||||
lazyListState.scrollToItem(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,6 +345,25 @@ fun PreviewLoadingState() {
|
|||
}
|
||||
}
|
||||
|
||||
@DefaultPreviews
|
||||
@Composable
|
||||
fun PreviewMainScreen() {
|
||||
AllContentMainScreen(
|
||||
uiItemsState = emptyList(),
|
||||
uiTitleState = UiTitleState.AllContent,
|
||||
uiTabsState = UiTabsState.Default(tabs = listOf(AllContentTab.PAGES, AllContentTab.TYPES, AllContentTab.LISTS), selectedTab = AllContentTab.LISTS),
|
||||
uiMenuState = UiMenuState.Hidden,
|
||||
onTabClick = {},
|
||||
onQueryChanged = {},
|
||||
onModeClick = {},
|
||||
onSortClick = {},
|
||||
onItemClicked = {},
|
||||
onBinClick = {},
|
||||
lazyListState = rememberLazyListState(),
|
||||
uiContentState = UiContentState.Error("Error message")
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Item(
|
||||
modifier: Modifier,
|
||||
|
|
|
@ -63,7 +63,6 @@ import com.anytypeio.anytype.feature_allcontent.R
|
|||
import com.anytypeio.anytype.feature_allcontent.models.AllContentMenuMode
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentSort
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentTab
|
||||
import com.anytypeio.anytype.feature_allcontent.models.MenuButtonViewState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.MenuSortsItem
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiMenuState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiTabsState
|
||||
|
@ -74,7 +73,6 @@ import com.anytypeio.anytype.feature_allcontent.models.UiTitleState
|
|||
@Composable
|
||||
fun AllContentTopBarContainer(
|
||||
titleState: UiTitleState,
|
||||
menuButtonState: MenuButtonViewState,
|
||||
uiMenuState: UiMenuState,
|
||||
onModeClick: (AllContentMenuMode) -> Unit,
|
||||
onSortClick: (AllContentSort) -> Unit,
|
||||
|
@ -88,29 +86,30 @@ fun AllContentTopBarContainer(
|
|||
title = { AllContentTitle(state = titleState) },
|
||||
actions = {
|
||||
AllContentMenuButton(
|
||||
state = menuButtonState,
|
||||
onClick = { isMenuExpanded = true }
|
||||
)
|
||||
DropdownMenu(
|
||||
modifier = Modifier.width(252.dp),
|
||||
expanded = isMenuExpanded,
|
||||
onDismissRequest = { isMenuExpanded = false },
|
||||
shape = RoundedCornerShape(size = 16.dp),
|
||||
containerColor = colorResource(id = R.color.background_primary),
|
||||
shadowElevation = 5.dp
|
||||
) {
|
||||
AllContentMenu(
|
||||
uiMenuState = uiMenuState,
|
||||
onModeClick = {
|
||||
onModeClick(it)
|
||||
isMenuExpanded = false
|
||||
},
|
||||
onSortClick = {
|
||||
onSortClick(it)
|
||||
isMenuExpanded = false
|
||||
},
|
||||
onBinClick = onBinClick
|
||||
)
|
||||
if (uiMenuState is UiMenuState.Visible) {
|
||||
DropdownMenu(
|
||||
modifier = Modifier.width(252.dp),
|
||||
expanded = isMenuExpanded,
|
||||
onDismissRequest = { isMenuExpanded = false },
|
||||
shape = RoundedCornerShape(size = 16.dp),
|
||||
containerColor = colorResource(id = R.color.background_primary),
|
||||
shadowElevation = 5.dp
|
||||
) {
|
||||
AllContentMenu(
|
||||
uiMenuState = uiMenuState,
|
||||
onModeClick = {
|
||||
onModeClick(it)
|
||||
isMenuExpanded = false
|
||||
},
|
||||
onSortClick = {
|
||||
onSortClick(it)
|
||||
isMenuExpanded = false
|
||||
},
|
||||
onBinClick = onBinClick
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
|
||||
|
@ -124,8 +123,7 @@ fun AllContentTopBarContainer(
|
|||
private fun AllContentTopBarContainerPreview() {
|
||||
AllContentTopBarContainer(
|
||||
titleState = UiTitleState.OnlyUnlinked,
|
||||
menuButtonState = MenuButtonViewState.Visible,
|
||||
uiMenuState = UiMenuState(
|
||||
uiMenuState = UiMenuState.Visible(
|
||||
mode = listOf(
|
||||
AllContentMenuMode.AllContent(isSelected = true),
|
||||
AllContentMenuMode.Unlinked()
|
||||
|
@ -188,21 +186,16 @@ fun AllContentTitle(state: UiTitleState) {
|
|||
|
||||
//region AllContentMenuButton
|
||||
@Composable
|
||||
fun AllContentMenuButton(state: MenuButtonViewState, onClick: () -> Unit) {
|
||||
when (state) {
|
||||
MenuButtonViewState.Hidden -> return
|
||||
MenuButtonViewState.Visible -> {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(end = 12.dp)
|
||||
.size(32.dp)
|
||||
.bouncingClickable { onClick() },
|
||||
painter = painterResource(id = R.drawable.ic_space_list_dots),
|
||||
contentDescription = "Menu icon",
|
||||
contentScale = ContentScale.Inside
|
||||
)
|
||||
}
|
||||
}
|
||||
fun AllContentMenuButton(onClick: () -> Unit) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(end = 12.dp)
|
||||
.size(32.dp)
|
||||
.bouncingClickable { onClick() },
|
||||
painter = painterResource(id = R.drawable.ic_space_list_dots),
|
||||
contentDescription = "Menu icon",
|
||||
contentScale = ContentScale.Inside
|
||||
)
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue