mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-3438 Widgets | Enhancement | Make "All objects" widget a system widget (#2232)
This commit is contained in:
parent
c8900f3971
commit
359e0d3597
18 changed files with 342 additions and 162 deletions
|
@ -29,6 +29,7 @@ object WidgetAnalytics {
|
|||
const val WIDGET_SOURCE_RECENT_LOCAL = "RecentLocal"
|
||||
const val WIDGET_SOURCE_SETS = "Sets"
|
||||
const val WIDGET_SOURCE_BIN = "Bin"
|
||||
const val WIDGET_SOURCE_ALL_OBJECTS = "AllObjects"
|
||||
const val WIDGET_SOURCE_COLLECTIONS = "Sets"
|
||||
|
||||
const val CUSTOM_OBJECT_TYPE = "custom"
|
||||
|
|
|
@ -416,10 +416,38 @@ private fun WidgetList(
|
|||
)
|
||||
}
|
||||
is WidgetView.AllContent -> {
|
||||
AllContentWidgetCard(
|
||||
mode = mode,
|
||||
onWidgetClicked = { onBundledWidgetHeaderClicked(item.id) }
|
||||
)
|
||||
if (mode is InteractionMode.Edit) {
|
||||
ReorderableItem(
|
||||
lazyListState, key = item.id
|
||||
) { isDragged ->
|
||||
val alpha = animateFloatAsState(if (isDragged) 0.8f else 1.0f)
|
||||
AllContentWidgetCard(
|
||||
index = index,
|
||||
mode = mode,
|
||||
onWidgetClicked = {
|
||||
onWidgetSourceClicked(Widget.Source.Bundled.AllObjects)
|
||||
},
|
||||
onDropDownMenuAction = { action ->
|
||||
onWidgetMenuAction(item.id, action)
|
||||
},
|
||||
alpha = alpha.value,
|
||||
lazyListState = lazyListState,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
AllContentWidgetCard(
|
||||
index = index,
|
||||
mode = mode,
|
||||
onWidgetClicked = {
|
||||
onWidgetSourceClicked(Widget.Source.Bundled.AllObjects)
|
||||
},
|
||||
onDropDownMenuAction = { action ->
|
||||
onWidgetMenuAction(item.id, action)
|
||||
},
|
||||
alpha = 1.0f,
|
||||
lazyListState = lazyListState,
|
||||
)
|
||||
}
|
||||
}
|
||||
is WidgetView.SpaceChat -> {
|
||||
SpaceChatWidgetCard(
|
||||
|
|
|
@ -110,7 +110,7 @@ fun VaultScreen(
|
|||
color = colorResource(id = R.color.background_primary)
|
||||
)
|
||||
.then(
|
||||
if (USE_EDGE_TO_EDGE && SDK_INT >= EDGE_TO_EDGE_MIN_SDK)
|
||||
if (SDK_INT >= EDGE_TO_EDGE_MIN_SDK)
|
||||
Modifier.windowInsetsPadding(WindowInsets.systemBars)
|
||||
else
|
||||
Modifier
|
||||
|
|
|
@ -1,92 +1,164 @@
|
|||
package com.anytypeio.anytype.ui.widgets.types
|
||||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineSubheading
|
||||
import com.anytypeio.anytype.presentation.home.InteractionMode
|
||||
import com.anytypeio.anytype.presentation.widgets.DropDownMenuAction
|
||||
import com.anytypeio.anytype.ui.widgets.menu.WidgetMenu
|
||||
import org.burnoutcrew.reorderable.ReorderableLazyListState
|
||||
import org.burnoutcrew.reorderable.detectReorderAfterLongPress
|
||||
import org.burnoutcrew.reorderable.rememberReorderableLazyListState
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun AllContentWidgetCard(
|
||||
index: Int,
|
||||
mode: InteractionMode,
|
||||
onWidgetClicked: () -> Unit = {}
|
||||
onWidgetClicked: () -> Unit = {},
|
||||
onDropDownMenuAction: (DropDownMenuAction) -> Unit = {},
|
||||
lazyListState: ReorderableLazyListState,
|
||||
alpha: Float,
|
||||
) {
|
||||
val haptic = LocalHapticFeedback.current
|
||||
val isCardMenuExpanded = remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(start = 20.dp, end = 20.dp, top = 6.dp, bottom = 6.dp)
|
||||
.fillMaxWidth()
|
||||
.height(52.dp)
|
||||
.background(
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
color = colorResource(id = R.color.dashboard_card_background)
|
||||
)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.padding(top = if (index == 0) 6.dp else 0.dp)
|
||||
.then(
|
||||
if (mode !is InteractionMode.Edit) {
|
||||
Modifier.clickable {
|
||||
onWidgetClicked()
|
||||
}
|
||||
} else {
|
||||
if (mode is InteractionMode.Edit)
|
||||
Modifier.detectReorderAfterLongPress(lazyListState)
|
||||
else
|
||||
Modifier
|
||||
}
|
||||
)
|
||||
.alpha(alpha)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_widget_all_content),
|
||||
contentDescription = "All content icon",
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
.padding(start = 16.dp)
|
||||
)
|
||||
.padding(start = 20.dp, end = 20.dp, top = 6.dp, bottom = 6.dp)
|
||||
.fillMaxWidth()
|
||||
.height(52.dp)
|
||||
.background(
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
color = colorResource(id = R.color.dashboard_card_background)
|
||||
)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.then(
|
||||
if (mode !is InteractionMode.Edit) {
|
||||
Modifier.combinedClickable(
|
||||
onClick = {
|
||||
onWidgetClicked()
|
||||
},
|
||||
onLongClick = {
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
isCardMenuExpanded.value = true
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Modifier.detectReorderAfterLongPress(lazyListState)
|
||||
}
|
||||
)
|
||||
.alpha(alpha)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_widget_all_content),
|
||||
contentDescription = "All content icon",
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
.padding(start = 16.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.all_content),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
Text(
|
||||
text = stringResource(id = R.string.all_content),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
.padding(start = 44.dp, end = 16.dp),
|
||||
style = HeadlineSubheading,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
)
|
||||
|
||||
WidgetMenu(
|
||||
isExpanded = isCardMenuExpanded,
|
||||
onDropDownMenuAction = onDropDownMenuAction,
|
||||
canEditWidgets = mode !is InteractionMode.Edit
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = mode is InteractionMode.Edit,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
.padding(start = 44.dp, end = 16.dp),
|
||||
style = HeadlineSubheading,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
)
|
||||
|
||||
.align(Alignment.TopStart)
|
||||
.padding(start = 12.dp),
|
||||
enter = fadeIn() + slideInHorizontally { it / 4 },
|
||||
exit = fadeOut() + slideOutHorizontally { it / 4 }
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_remove_widget),
|
||||
modifier = Modifier
|
||||
.height(24.dp)
|
||||
.width(24.dp)
|
||||
.noRippleClickable {
|
||||
onDropDownMenuAction(DropDownMenuAction.RemoveWidget)
|
||||
},
|
||||
contentDescription = "Remove widget icon"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(
|
||||
name = "Dark Mode",
|
||||
showBackground = true,
|
||||
uiMode = UI_MODE_NIGHT_YES
|
||||
)
|
||||
@Preview(
|
||||
name = "Light Mode",
|
||||
showBackground = true,
|
||||
uiMode = UI_MODE_NIGHT_NO
|
||||
)
|
||||
@DefaultPreviews
|
||||
@Composable
|
||||
fun AllContentWidgetPreview() {
|
||||
val lazyListState = rememberReorderableLazyListState(
|
||||
onMove = { from, to ->
|
||||
//
|
||||
},
|
||||
onDragEnd = { from, to ->
|
||||
//
|
||||
}
|
||||
)
|
||||
AllContentWidgetCard(
|
||||
index = 0,
|
||||
onWidgetClicked = {},
|
||||
mode = InteractionMode.Default
|
||||
mode = InteractionMode.Default,
|
||||
alpha = 1.0f,
|
||||
lazyListState = lazyListState
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -601,4 +601,5 @@ fun Widget.Source.Bundled.res(): Int = when (this) {
|
|||
Widget.Source.Bundled.Sets -> R.string.sets
|
||||
Widget.Source.Bundled.Collections -> R.string.collections
|
||||
Widget.Source.Bundled.Bin -> R.string.bin
|
||||
Widget.Source.Bundled.AllObjects -> R.string.all_content
|
||||
}
|
|
@ -238,6 +238,13 @@ class BundledWidgetSourceHolder(
|
|||
ivIcon.setBackgroundResource(R.drawable.ic_widget_system_bin)
|
||||
}
|
||||
}
|
||||
BundledWidgetSourceView.AllObjects -> {
|
||||
with(binding) {
|
||||
tvTitle.setText(R.string.all_content)
|
||||
tvSubtitle.gone()
|
||||
ivIcon.setBackgroundResource(R.drawable.ic_widget_system_all_objects)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -247,8 +254,9 @@ class SuggestWidgetObjectTypeViewHolder(
|
|||
) : DefaultObjectViewAdapter.ObjectViewHolder(binding.root) {
|
||||
|
||||
init {
|
||||
binding.ivIcon.binding.emojiContainer.invisible()
|
||||
binding.tvSubtitle.gone()
|
||||
binding.ivIcon.binding.emojiContainer.background = null
|
||||
|
||||
}
|
||||
|
||||
fun bind(source: SuggestWidgetObjectType) {
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:pathData="M20,20m-20,0a20,20 0,1 1,40 0a20,20 0,1 1,-40 0"
|
||||
android:fillColor="#6D85F2"/>
|
||||
<path
|
||||
android:pathData="M17.313,16.5L17.313,16.5A6.25,6.25 0,0 1,23.563 22.75L23.563,22.75A6.25,6.25 0,0 1,17.313 29L17.313,29A6.25,6.25 0,0 1,11.063 22.75L11.063,22.75A6.25,6.25 0,0 1,17.313 16.5z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M16.13,15.09C16.516,15.031 16.911,15 17.313,15C21.593,15 25.063,18.47 25.063,22.75C25.063,23.152 25.032,23.547 24.973,23.932C25.452,23.879 25.824,23.785 26.152,23.618C26.81,23.283 27.346,22.747 27.681,22.089C28.063,21.34 28.063,20.36 28.063,18.4V17.6C28.063,15.64 28.063,14.66 27.681,13.911C27.346,13.252 26.81,12.717 26.152,12.382C25.403,12 24.423,12 22.462,12H21.663C19.702,12 18.722,12 17.973,12.382C17.315,12.717 16.779,13.252 16.444,13.911C16.277,14.239 16.183,14.611 16.13,15.09Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -96,6 +96,6 @@ class GetSuggestedWidgetTypes @Inject constructor(
|
|||
)
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_LIMIT = 5
|
||||
const val DEFAULT_LIMIT = 10
|
||||
}
|
||||
}
|
|
@ -1559,6 +1559,9 @@ fun CoroutineScope.sendChangeWidgetSourceEvent(
|
|||
BundledWidgetSourceView.Bin -> {
|
||||
put(WidgetAnalytics.TYPE, WidgetAnalytics.WIDGET_SOURCE_BIN)
|
||||
}
|
||||
BundledWidgetSourceView.AllObjects -> {
|
||||
put(WidgetAnalytics.TYPE, WidgetAnalytics.WIDGET_SOURCE_ALL_OBJECTS)
|
||||
}
|
||||
}
|
||||
if (isForNewWidget)
|
||||
put(WidgetAnalytics.ROUTE, WidgetAnalytics.ROUTE_ADD_WIDGET)
|
||||
|
@ -1655,6 +1658,9 @@ fun CoroutineScope.sendDeleteWidgetEvent(
|
|||
Widget.Source.Bundled.Bin -> {
|
||||
put(WidgetAnalytics.TYPE, WidgetAnalytics.WIDGET_SOURCE_BIN)
|
||||
}
|
||||
Widget.Source.Bundled.AllObjects -> {
|
||||
put(WidgetAnalytics.TYPE, WidgetAnalytics.WIDGET_SOURCE_ALL_OBJECTS)
|
||||
}
|
||||
}
|
||||
if (isInEditMode)
|
||||
put(WidgetAnalytics.CONTEXT, WidgetAnalytics.CONTEXT_EDITOR)
|
||||
|
@ -1698,6 +1704,9 @@ fun CoroutineScope.sendSelectHomeTabEvent(
|
|||
Widget.Source.Bundled.Bin -> {
|
||||
put(WidgetAnalytics.TAB, WidgetAnalytics.WIDGET_SOURCE_BIN)
|
||||
}
|
||||
Widget.Source.Bundled.AllObjects -> {
|
||||
put(WidgetAnalytics.TAB, WidgetAnalytics.WIDGET_SOURCE_ALL_OBJECTS)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -1775,6 +1784,9 @@ fun CoroutineScope.sendReorderWidgetEvent(
|
|||
Widget.Source.Bundled.Bin -> {
|
||||
put(WidgetAnalytics.TYPE, WidgetAnalytics.WIDGET_SOURCE_BIN)
|
||||
}
|
||||
Widget.Source.Bundled.AllObjects -> {
|
||||
put(WidgetAnalytics.TYPE, WidgetAnalytics.WIDGET_SOURCE_ALL_OBJECTS)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -135,6 +135,7 @@ import com.anytypeio.anytype.presentation.widgets.collection.Subscription
|
|||
import com.anytypeio.anytype.presentation.widgets.hasValidLayout
|
||||
import com.anytypeio.anytype.presentation.widgets.parseActiveViews
|
||||
import com.anytypeio.anytype.presentation.widgets.parseWidgets
|
||||
import com.anytypeio.anytype.presentation.widgets.source.BundledWidgetSourceView
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -253,8 +254,6 @@ class HomeScreenViewModel(
|
|||
private val containers = MutableStateFlow<Containers>(null)
|
||||
private val treeWidgetBranchStateHolder = TreeWidgetBranchStateHolder()
|
||||
|
||||
private val allContentWidget = AllContentWidgetContainer()
|
||||
|
||||
private val spaceWidgetView = spaceWidgetContainer.view
|
||||
|
||||
private val widgetObjectPipelineJobs = mutableListOf<Job>()
|
||||
|
@ -459,7 +458,6 @@ class HomeScreenViewModel(
|
|||
combine(
|
||||
flows = buildList<Flow<WidgetView>> {
|
||||
add(spaceWidgetView)
|
||||
add(allContentWidget.view)
|
||||
addAll(list.map { m -> m.view })
|
||||
}
|
||||
) { array ->
|
||||
|
@ -584,6 +582,11 @@ class HomeScreenViewModel(
|
|||
storeOfObjectTypes = storeOfObjectTypes
|
||||
)
|
||||
}
|
||||
is Widget.AllObjects -> {
|
||||
AllContentWidgetContainer(
|
||||
widget = widget
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.collect {
|
||||
|
@ -709,15 +712,25 @@ class HomeScreenViewModel(
|
|||
}
|
||||
}
|
||||
is WidgetDispatchEvent.SourcePicked.Bundled -> {
|
||||
commands.emit(
|
||||
Command.SelectWidgetType(
|
||||
if (dispatch.source == BundledWidgetSourceView.AllObjects.id) {
|
||||
// Applying link layout automatically to all-objects widget
|
||||
proceedWithCreatingWidget(
|
||||
ctx = config.widgets,
|
||||
source = dispatch.source,
|
||||
layout = ObjectType.Layout.SET.code,
|
||||
target = dispatch.target,
|
||||
isInEditMode = isInEditMode()
|
||||
type = Command.ChangeWidgetType.TYPE_LINK,
|
||||
target = dispatch.target
|
||||
)
|
||||
)
|
||||
} else {
|
||||
commands.emit(
|
||||
Command.SelectWidgetType(
|
||||
ctx = config.widgets,
|
||||
source = dispatch.source,
|
||||
layout = ObjectType.Layout.SET.code,
|
||||
target = dispatch.target,
|
||||
isInEditMode = isInEditMode()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
is WidgetDispatchEvent.SourceChanged -> {
|
||||
proceedWithUpdatingWidget(
|
||||
|
@ -1065,6 +1078,18 @@ class HomeScreenViewModel(
|
|||
)
|
||||
}
|
||||
}
|
||||
is Widget.Source.Bundled.AllObjects -> {
|
||||
viewModelScope.launch {
|
||||
if (mode.value == InteractionMode.Edit) {
|
||||
return@launch
|
||||
}
|
||||
navigation(
|
||||
Navigation.OpenAllContent(
|
||||
space = spaceManager.get()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1132,16 +1157,6 @@ class HomeScreenViewModel(
|
|||
WidgetView.SpaceChat.id -> {
|
||||
proceedWithSpaceChatWidgetHeaderClick()
|
||||
}
|
||||
WidgetView.AllContent.ALL_CONTENT_WIDGET_ID -> {
|
||||
if (mode.value == InteractionMode.Edit) {
|
||||
return@launch
|
||||
}
|
||||
navigation(
|
||||
Navigation.OpenAllContent(
|
||||
space = space
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
Timber.w("Skipping widget click: $widget")
|
||||
}
|
||||
|
@ -1260,6 +1275,8 @@ class HomeScreenViewModel(
|
|||
else
|
||||
Command.ChangeWidgetType.TYPE_LIST
|
||||
}
|
||||
// All-objects widget has link appearance.
|
||||
is Widget.AllObjects -> Command.ChangeWidgetType.TYPE_LINK
|
||||
}
|
||||
|
||||
// TODO move to a separate reducer inject into this VM's constructor
|
||||
|
|
|
@ -3,8 +3,10 @@ package com.anytypeio.anytype.presentation.widgets
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
class AllContentWidgetContainer : WidgetContainer {
|
||||
class AllContentWidgetContainer(
|
||||
widget: Widget.AllObjects
|
||||
) : WidgetContainer {
|
||||
override val view: Flow<WidgetView> = flowOf(
|
||||
WidgetView.AllContent
|
||||
WidgetView.AllContent(widget.id)
|
||||
)
|
||||
}
|
|
@ -93,7 +93,7 @@ class DataViewListWidgetContainer(
|
|||
)
|
||||
)
|
||||
}
|
||||
is Widget.Link, is Widget.Tree -> {
|
||||
is Widget.Link, is Widget.Tree, is Widget.AllObjects -> {
|
||||
throw IllegalStateException("Incompatible widget type.")
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ class DataViewListWidgetContainer(
|
|||
)
|
||||
)
|
||||
}
|
||||
is Widget.Tree, is Widget.Link -> {
|
||||
is Widget.Tree, is Widget.Link, is Widget.AllObjects -> {
|
||||
throw IllegalStateException("Incompatible widget type.")
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ class DataViewListWidgetContainer(
|
|||
limit = when (widget) {
|
||||
is Widget.List -> widget.limit
|
||||
is Widget.View -> widget.limit
|
||||
is Widget.Tree, is Widget.Link -> {
|
||||
is Widget.Tree, is Widget.Link, is Widget.AllObjects -> {
|
||||
throw IllegalStateException("Incompatible widget type.")
|
||||
}
|
||||
}
|
||||
|
@ -355,7 +355,7 @@ class DataViewListWidgetContainer(
|
|||
prettyPrintName = fieldParser.getObjectPluralName(widget.source.obj)
|
||||
)
|
||||
)
|
||||
is Widget.Link, is Widget.Tree -> {
|
||||
is Widget.Link, is Widget.Tree, is Widget.AllObjects -> {
|
||||
throw IllegalStateException("Incompatible widget type.")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
@ -61,7 +62,7 @@ class SelectWidgetSourceViewModel(
|
|||
storeOfObjectTypes = storeOfObjectTypes
|
||||
) {
|
||||
|
||||
val suggested = MutableStateFlow<List<SuggestWidgetObjectType>>(emptyList())
|
||||
val suggested = MutableStateFlow<List<SuggestWidgetObjectType>?>(null)
|
||||
|
||||
val isDismissed = MutableStateFlow(false)
|
||||
var config : Config = Config.None
|
||||
|
@ -69,7 +70,7 @@ class SelectWidgetSourceViewModel(
|
|||
val viewState = combine(
|
||||
stateData
|
||||
.asFlow(),
|
||||
suggested
|
||||
suggested.filterNotNull()
|
||||
) { state, suggested ->
|
||||
if (suggested.isNotEmpty()) {
|
||||
when(state) {
|
||||
|
@ -83,6 +84,7 @@ class SelectWidgetSourceViewModel(
|
|||
add(BundledWidgetSourceView.Recent)
|
||||
add(BundledWidgetSourceView.RecentLocal)
|
||||
add(BundledWidgetSourceView.Bin)
|
||||
add(BundledWidgetSourceView.AllObjects)
|
||||
|
||||
// Suggested widgets (aka object type widgets)
|
||||
if (suggested.isNotEmpty()) {
|
||||
|
@ -216,6 +218,9 @@ class SelectWidgetSourceViewModel(
|
|||
isInEditMode = curr.isInEditMode
|
||||
)
|
||||
}
|
||||
if (view is BundledWidgetSourceView.AllObjects) {
|
||||
isDismissed.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
is Config.ExistingWidget -> {
|
||||
|
|
|
@ -59,6 +59,12 @@ sealed class Widget {
|
|||
val limit: Int
|
||||
) : Widget()
|
||||
|
||||
data class AllObjects(
|
||||
override val id: Id,
|
||||
override val source: Source.Bundled.AllObjects,
|
||||
override val config: Config,
|
||||
) : Widget()
|
||||
|
||||
sealed class Source {
|
||||
|
||||
abstract val id: Id
|
||||
|
@ -101,6 +107,11 @@ sealed class Widget {
|
|||
override val id: Id = BundledWidgetSourceIds.BIN
|
||||
override val type: Id? = null
|
||||
}
|
||||
|
||||
data object AllObjects : Bundled() {
|
||||
override val id: Id = BundledWidgetSourceIds.ALL_OBJECTS
|
||||
override val type: Id? = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,51 +161,19 @@ fun List<Block>.parseWidgets(
|
|||
is Widget.Source.Default -> source.obj.isValid && source.obj.notDeletedNorArchived
|
||||
}
|
||||
if (hasValidSource && !WidgetConfig.excludedTypes.contains(source.type)) {
|
||||
when (widgetContent.layout) {
|
||||
Block.Content.Widget.Layout.TREE -> {
|
||||
add(
|
||||
Widget.Tree(
|
||||
id = w.id,
|
||||
source = source,
|
||||
limit = widgetContent.limit,
|
||||
config = config
|
||||
)
|
||||
if (source is Widget.Source.Bundled.AllObjects) {
|
||||
add(
|
||||
Widget.AllObjects(
|
||||
id = w.id,
|
||||
source = source,
|
||||
config = config
|
||||
)
|
||||
}
|
||||
Block.Content.Widget.Layout.LINK -> {
|
||||
add(
|
||||
Widget.Link(
|
||||
id = w.id,
|
||||
source = source,
|
||||
config = config
|
||||
)
|
||||
)
|
||||
}
|
||||
Block.Content.Widget.Layout.LIST -> {
|
||||
add(
|
||||
Widget.List(
|
||||
id = w.id,
|
||||
source = source,
|
||||
limit = widgetContent.limit,
|
||||
config = config
|
||||
)
|
||||
)
|
||||
}
|
||||
Block.Content.Widget.Layout.COMPACT_LIST -> {
|
||||
add(
|
||||
Widget.List(
|
||||
id = w.id,
|
||||
source = source,
|
||||
isCompact = true,
|
||||
limit = widgetContent.limit,
|
||||
config = config
|
||||
)
|
||||
)
|
||||
}
|
||||
Block.Content.Widget.Layout.VIEW -> {
|
||||
if (source is Widget.Source.Default) {
|
||||
)
|
||||
} else {
|
||||
when (widgetContent.layout) {
|
||||
Block.Content.Widget.Layout.TREE -> {
|
||||
add(
|
||||
Widget.View(
|
||||
Widget.Tree(
|
||||
id = w.id,
|
||||
source = source,
|
||||
limit = widgetContent.limit,
|
||||
|
@ -202,9 +181,56 @@ fun List<Block>.parseWidgets(
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
Block.Content.Widget.Layout.LINK -> {
|
||||
add(
|
||||
Widget.Link(
|
||||
id = w.id,
|
||||
source = source,
|
||||
config = config
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Block.Content.Widget.Layout.LIST -> {
|
||||
add(
|
||||
Widget.List(
|
||||
id = w.id,
|
||||
source = source,
|
||||
limit = widgetContent.limit,
|
||||
config = config
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Block.Content.Widget.Layout.COMPACT_LIST -> {
|
||||
add(
|
||||
Widget.List(
|
||||
id = w.id,
|
||||
source = source,
|
||||
isCompact = true,
|
||||
limit = widgetContent.limit,
|
||||
config = config
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Block.Content.Widget.Layout.VIEW -> {
|
||||
if (source is Widget.Source.Default) {
|
||||
add(
|
||||
Widget.View(
|
||||
id = w.id,
|
||||
source = source,
|
||||
limit = widgetContent.limit,
|
||||
config = config
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,6 +244,7 @@ fun Id.bundled() : Widget.Source.Bundled = when (this) {
|
|||
BundledWidgetSourceIds.COLLECTIONS -> Widget.Source.Bundled.Collections
|
||||
BundledWidgetSourceIds.FAVORITE -> Widget.Source.Bundled.Favorites
|
||||
BundledWidgetSourceIds.BIN -> Widget.Source.Bundled.Bin
|
||||
BundledWidgetSourceIds.ALL_OBJECTS -> Widget.Source.Bundled.AllObjects
|
||||
else -> throw IllegalStateException("Widget bundled id can't be $this")
|
||||
}
|
||||
|
||||
|
|
|
@ -77,11 +77,12 @@ object BundledWidgetSourceIds {
|
|||
const val RECENT = "recent"
|
||||
const val RECENT_LOCAL = "recentOpen"
|
||||
const val BIN = "bin"
|
||||
const val ALL_OBJECTS = "allObjects"
|
||||
|
||||
@Deprecated("DROID-3438 To be deleted")
|
||||
const val SETS = "set"
|
||||
@Deprecated("DROID-3438 To be deleted")
|
||||
const val COLLECTIONS = "collection"
|
||||
|
||||
val ids = listOf(FAVORITE, RECENT, RECENT_LOCAL, SETS, COLLECTIONS, BIN)
|
||||
val ids = listOf(FAVORITE, RECENT, RECENT_LOCAL, SETS, COLLECTIONS, BIN, ALL_OBJECTS)
|
||||
}
|
|
@ -129,10 +129,10 @@ sealed class WidgetView {
|
|||
val isEmpty: Boolean = false
|
||||
) : WidgetView()
|
||||
|
||||
data object AllContent: WidgetView() {
|
||||
const val ALL_CONTENT_WIDGET_ID = "bundled-widget.all-content"
|
||||
data class AllContent(
|
||||
override val id: Id
|
||||
): WidgetView() {
|
||||
override val isLoading: Boolean = false
|
||||
override val id: Id = ALL_CONTENT_WIDGET_ID
|
||||
}
|
||||
|
||||
data object SpaceChat : WidgetView() {
|
||||
|
|
|
@ -25,6 +25,10 @@ sealed class BundledWidgetSourceView : DefaultSearchItem {
|
|||
data object Bin : BundledWidgetSourceView() {
|
||||
override val id: Id get() = BundledWidgetSourceIds.BIN
|
||||
}
|
||||
|
||||
data object AllObjects : BundledWidgetSourceView() {
|
||||
override val id: Id get() = BundledWidgetSourceIds.ALL_OBJECTS
|
||||
}
|
||||
}
|
||||
|
||||
data class SuggestWidgetObjectType(
|
||||
|
|
|
@ -316,13 +316,8 @@ class HomeScreenViewModelTest {
|
|||
membersCount = 0
|
||||
)
|
||||
|
||||
private val allContentWidgetView = WidgetView.AllContent
|
||||
|
||||
private val secondSpaceWidgetView = WidgetView.SpaceWidget.View(
|
||||
space = StubSpaceView(),
|
||||
icon = SpaceIconView.Placeholder(),
|
||||
type = UNKNOWN_SPACE_TYPE,
|
||||
membersCount = 0
|
||||
private val allContentWidgetView = WidgetView.AllContent(
|
||||
id = MockDataFactory.randomUuid()
|
||||
)
|
||||
|
||||
private lateinit var urlBuilder: UrlBuilder
|
||||
|
@ -394,7 +389,6 @@ class HomeScreenViewModelTest {
|
|||
actual = secondTimeState,
|
||||
expected = buildList {
|
||||
add(defaultSpaceWidgetView)
|
||||
add(allContentWidgetView)
|
||||
addAll(HomeScreenViewModel.actions)
|
||||
}
|
||||
)
|
||||
|
@ -446,7 +440,6 @@ class HomeScreenViewModelTest {
|
|||
assertEquals(
|
||||
expected = buildList {
|
||||
add(defaultSpaceWidgetView)
|
||||
add(allContentWidgetView)
|
||||
addAll(HomeScreenViewModel.actions)
|
||||
},
|
||||
actual = secondTimeItem
|
||||
|
@ -530,14 +523,13 @@ class HomeScreenViewModelTest {
|
|||
)
|
||||
val firstTimeLoadingState = awaitItem()
|
||||
assertTrue {
|
||||
val thirdWidget = firstTimeLoadingState[2]
|
||||
val thirdWidget = firstTimeLoadingState[1]
|
||||
thirdWidget is WidgetView.Tree && thirdWidget.isLoading
|
||||
}
|
||||
val secondTimeState = awaitItem()
|
||||
assertEquals(
|
||||
expected = buildList {
|
||||
add(defaultSpaceWidgetView)
|
||||
add(allContentWidgetView)
|
||||
add(
|
||||
WidgetView.Tree(
|
||||
id = widgetBlock.id,
|
||||
|
@ -644,14 +636,13 @@ class HomeScreenViewModelTest {
|
|||
)
|
||||
val firstTimeLoadingState = awaitItem()
|
||||
assertTrue {
|
||||
val thirdWidget = firstTimeLoadingState[2]
|
||||
val thirdWidget = firstTimeLoadingState[1]
|
||||
thirdWidget is WidgetView.Tree && thirdWidget.isLoading
|
||||
}
|
||||
val secondTimeState = awaitItem()
|
||||
assertEquals(
|
||||
expected = buildList {
|
||||
add(defaultSpaceWidgetView)
|
||||
add(allContentWidgetView)
|
||||
add(
|
||||
WidgetView.Tree(
|
||||
id = widgetBlock.id,
|
||||
|
@ -774,14 +765,13 @@ class HomeScreenViewModelTest {
|
|||
)
|
||||
val firstTimeLoadingState = awaitItem()
|
||||
assertTrue {
|
||||
val thirdWidget = firstTimeLoadingState[2]
|
||||
val thirdWidget = firstTimeLoadingState[1]
|
||||
thirdWidget is WidgetView.SetOfObjects && thirdWidget.isLoading
|
||||
}
|
||||
val secondTimeState = awaitItem()
|
||||
assertEquals(
|
||||
expected = buildList {
|
||||
add(defaultSpaceWidgetView)
|
||||
add(allContentWidgetView)
|
||||
add(
|
||||
WidgetView.SetOfObjects(
|
||||
id = widgetBlock.id,
|
||||
|
@ -883,14 +873,13 @@ class HomeScreenViewModelTest {
|
|||
)
|
||||
val firstTimeLoadingState = awaitItem()
|
||||
assertTrue {
|
||||
val thirdWidget = firstTimeLoadingState[2]
|
||||
val thirdWidget = firstTimeLoadingState[1]
|
||||
thirdWidget is WidgetView.SetOfObjects && thirdWidget.isLoading
|
||||
}
|
||||
val secondTimeState = awaitItem()
|
||||
assertEquals(
|
||||
expected = buildList {
|
||||
add(defaultSpaceWidgetView)
|
||||
add(allContentWidgetView)
|
||||
add(
|
||||
WidgetView.SetOfObjects(
|
||||
id = widgetBlock.id,
|
||||
|
@ -1090,15 +1079,15 @@ class HomeScreenViewModelTest {
|
|||
)
|
||||
val firstTimeLoadingState1 = awaitItem()
|
||||
assertTrue {
|
||||
val firstWidget = firstTimeLoadingState1[2]
|
||||
val firstWidget = firstTimeLoadingState1[1]
|
||||
firstWidget is WidgetView.Tree && firstWidget.isLoading
|
||||
}
|
||||
assertTrue {
|
||||
val secondWidget = firstTimeLoadingState1[3]
|
||||
val secondWidget = firstTimeLoadingState1[2]
|
||||
secondWidget is WidgetView.Tree && secondWidget.isLoading
|
||||
}
|
||||
assertTrue {
|
||||
val thirdWidget = firstTimeLoadingState1[4]
|
||||
val thirdWidget = firstTimeLoadingState1[3]
|
||||
thirdWidget is WidgetView.Tree && thirdWidget.isLoading
|
||||
}
|
||||
|
||||
|
@ -1106,15 +1095,15 @@ class HomeScreenViewModelTest {
|
|||
|
||||
val firstTimeLoadingState2 = awaitItem()
|
||||
assertTrue {
|
||||
val firstWidget = firstTimeLoadingState2[2]
|
||||
val firstWidget = firstTimeLoadingState2[1]
|
||||
firstWidget is WidgetView.Tree && firstWidget.isLoading
|
||||
}
|
||||
assertTrue {
|
||||
val secondWidget = firstTimeLoadingState2[3]
|
||||
val secondWidget = firstTimeLoadingState2[2]
|
||||
secondWidget is WidgetView.Tree && !secondWidget.isLoading
|
||||
}
|
||||
assertTrue {
|
||||
val thirdWidget = firstTimeLoadingState2[4]
|
||||
val thirdWidget = firstTimeLoadingState2[3]
|
||||
thirdWidget is WidgetView.Tree && !thirdWidget.isLoading
|
||||
}
|
||||
|
||||
|
@ -1125,7 +1114,6 @@ class HomeScreenViewModelTest {
|
|||
assertEquals(
|
||||
expected = buildList {
|
||||
add(defaultSpaceWidgetView)
|
||||
add(allContentWidgetView)
|
||||
add(
|
||||
WidgetView.Tree(
|
||||
id = favoriteWidgetBlock.id,
|
||||
|
@ -1308,7 +1296,6 @@ class HomeScreenViewModelTest {
|
|||
assertEquals(
|
||||
expected = buildList {
|
||||
add(defaultSpaceWidgetView)
|
||||
add(allContentWidgetView)
|
||||
add(
|
||||
WidgetView.Link(
|
||||
id = widgetBlock.id,
|
||||
|
@ -2084,7 +2071,6 @@ class HomeScreenViewModelTest {
|
|||
actual = secondTimeState,
|
||||
expected = buildList {
|
||||
add(defaultSpaceWidgetView)
|
||||
add(allContentWidgetView)
|
||||
addAll(HomeScreenViewModel.actions)
|
||||
}
|
||||
)
|
||||
|
@ -2318,20 +2304,20 @@ class HomeScreenViewModelTest {
|
|||
)
|
||||
val firstTimeLoadingState = awaitItem()
|
||||
assertTrue {
|
||||
val thirdWidget = firstTimeLoadingState[2]
|
||||
val thirdWidget = firstTimeLoadingState[1]
|
||||
thirdWidget is WidgetView.Tree && thirdWidget.isLoading
|
||||
}
|
||||
delay(1)
|
||||
val secondTimeItem = awaitItem()
|
||||
assertTrue {
|
||||
val secondWidget = secondTimeItem[2]
|
||||
val secondWidget = secondTimeItem[1]
|
||||
(secondWidget is WidgetView.Tree
|
||||
&& secondWidget.source.id == currentSourceObject.id && !secondWidget.isLoading)
|
||||
}
|
||||
val thirdTimeItem = awaitItem()
|
||||
advanceUntilIdle()
|
||||
assertTrue {
|
||||
val thirdWidget = thirdTimeItem[2]
|
||||
val thirdWidget = thirdTimeItem[1]
|
||||
thirdWidget is WidgetView.Tree
|
||||
&& thirdWidget.source.id == newSourceObject.id
|
||||
&& thirdWidget.isLoading
|
||||
|
@ -2339,7 +2325,7 @@ class HomeScreenViewModelTest {
|
|||
advanceUntilIdle()
|
||||
val fourthTimeItem = awaitItem()
|
||||
assertTrue {
|
||||
val thirdWidget = fourthTimeItem[2]
|
||||
val thirdWidget = fourthTimeItem[1]
|
||||
thirdWidget is WidgetView.Tree
|
||||
&& thirdWidget.source.id == newSourceObject.id
|
||||
&& !thirdWidget.isLoading
|
||||
|
@ -2425,19 +2411,19 @@ class HomeScreenViewModelTest {
|
|||
)
|
||||
val firstTimeLoadingState = awaitItem()
|
||||
assertTrue {
|
||||
val thirdWidget = firstTimeLoadingState[2]
|
||||
val thirdWidget = firstTimeLoadingState[1]
|
||||
thirdWidget is WidgetView.Tree && thirdWidget.isLoading
|
||||
}
|
||||
delay(1)
|
||||
val secondTimeItem = awaitItem()
|
||||
assertTrue {
|
||||
val thirdWidget = secondTimeItem[2]
|
||||
val thirdWidget = secondTimeItem[1]
|
||||
thirdWidget is WidgetView.Tree
|
||||
}
|
||||
val thirdTimeItem = awaitItem()
|
||||
advanceUntilIdle()
|
||||
assertTrue {
|
||||
val thirdWidget = thirdTimeItem[2]
|
||||
val thirdWidget = thirdTimeItem[1]
|
||||
thirdWidget is WidgetView.Link
|
||||
}
|
||||
}
|
||||
|
@ -2582,13 +2568,13 @@ class HomeScreenViewModelTest {
|
|||
)
|
||||
val firstTimeLoadingState = awaitItem()
|
||||
assertTrue {
|
||||
val thirdWidget = firstTimeLoadingState[2]
|
||||
val thirdWidget = firstTimeLoadingState[1]
|
||||
thirdWidget is WidgetView.SetOfObjects && thirdWidget.isLoading
|
||||
}
|
||||
delay(1)
|
||||
val secondTimeItem = awaitItem()
|
||||
assertTrue {
|
||||
val thirdWidget = secondTimeItem[2]
|
||||
val thirdWidget = secondTimeItem[1]
|
||||
thirdWidget is WidgetView.SetOfObjects && thirdWidget.tabs.first().isSelected
|
||||
}
|
||||
verifyBlocking(getObject, times(1)) {
|
||||
|
@ -2614,7 +2600,7 @@ class HomeScreenViewModelTest {
|
|||
val thirdTimeItem = awaitItem()
|
||||
advanceUntilIdle()
|
||||
assertTrue {
|
||||
val thirdWidget = thirdTimeItem[2]
|
||||
val thirdWidget = thirdTimeItem[1]
|
||||
thirdWidget is WidgetView.SetOfObjects && thirdWidget.tabs.last().isSelected
|
||||
}
|
||||
verify(storelessSubscriptionContainer, times(1)).subscribe(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue