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

DROID-3538 Widgets | Enhancement | Add plus button to system and object-type widgets (#2260)

This commit is contained in:
Evgenii Kozlov 2025-04-08 20:15:59 +02:00 committed by GitHub
parent d635e34465
commit c51a302051
Signed by: github
GPG key ID: B5690EEEBB952194
10 changed files with 146 additions and 22 deletions

View file

@ -99,7 +99,8 @@ fun HomeScreen(
onSpaceWidgetShareIconClicked: (ObjectWrapper.SpaceView) -> Unit,
onSeeAllObjectsClicked: (WidgetView.Gallery) -> Unit,
onCreateObjectInsideWidget: (Id) -> Unit,
onCreateDataViewObject: (WidgetId, ViewId?) -> Unit
onCreateDataViewObject: (WidgetId, ViewId?) -> Unit,
onCreateElement: (WidgetView) -> Unit = {}
) {
Box(modifier = modifier.fillMaxSize()) {
@ -121,7 +122,8 @@ fun HomeScreen(
onSeeAllObjectsClicked = onSeeAllObjectsClicked,
onCreateWidget = onCreateWidget,
onCreateObjectInsideWidget = onCreateObjectInsideWidget,
onCreateDataViewObject = onCreateDataViewObject
onCreateDataViewObject = onCreateDataViewObject,
onCreateElement = onCreateElement
)
AnimatedVisibility(
visible = mode is InteractionMode.Edit,
@ -192,7 +194,8 @@ private fun WidgetList(
onSeeAllObjectsClicked: (WidgetView.Gallery) -> Unit,
onCreateWidget: () -> Unit,
onCreateObjectInsideWidget: (Id) -> Unit,
onCreateDataViewObject: (WidgetId, ViewId?) -> Unit
onCreateDataViewObject: (WidgetId, ViewId?) -> Unit,
onCreateElement: (WidgetView) -> Unit = {}
) {
val views = remember { mutableStateOf(widgets) }
views.value = widgets
@ -315,7 +318,8 @@ private fun WidgetList(
onChangeWidgetView = onChangeWidgetView,
onToggleExpandedWidgetState = onToggleExpandedWidgetState,
onObjectCheckboxClicked = onObjectCheckboxClicked,
onCreateDataViewObject = onCreateDataViewObject
onCreateDataViewObject = onCreateDataViewObject,
onCreateElement = onCreateElement
)
}
} else {
@ -331,7 +335,8 @@ private fun WidgetList(
onChangeWidgetView = onChangeWidgetView,
onToggleExpandedWidgetState = onToggleExpandedWidgetState,
onObjectCheckboxClicked = onObjectCheckboxClicked,
onCreateDataViewObject = onCreateDataViewObject
onCreateDataViewObject = onCreateDataViewObject,
onCreateElement = onCreateElement
)
}
}
@ -389,7 +394,8 @@ private fun WidgetList(
onWidgetSourceClicked = onWidgetSourceClicked,
onWidgetMenuAction = onWidgetMenuAction,
onToggleExpandedWidgetState = onToggleExpandedWidgetState,
onObjectCheckboxClicked = onObjectCheckboxClicked
onObjectCheckboxClicked = onObjectCheckboxClicked,
onCreateElement = onCreateElement
)
}
} else {
@ -403,7 +409,8 @@ private fun WidgetList(
onWidgetSourceClicked = onWidgetSourceClicked,
onWidgetMenuAction = onWidgetMenuAction,
onToggleExpandedWidgetState = onToggleExpandedWidgetState,
onObjectCheckboxClicked = onObjectCheckboxClicked
onObjectCheckboxClicked = onObjectCheckboxClicked,
onCreateElement = onCreateElement
)
}
}
@ -521,7 +528,8 @@ private fun ListOfObjectsItem(
onWidgetSourceClicked: (Widget.Source) -> Unit,
onWidgetMenuAction: (WidgetId, DropDownMenuAction) -> Unit,
onToggleExpandedWidgetState: (WidgetId) -> Unit,
onObjectCheckboxClicked: (Id, Boolean) -> Unit
onObjectCheckboxClicked: (Id, Boolean) -> Unit,
onCreateElement: (WidgetView) -> Unit = {}
) {
Box(
modifier = Modifier
@ -545,7 +553,8 @@ private fun ListOfObjectsItem(
onWidgetMenuAction(item.id, action)
},
onToggleExpandedWidgetState = onToggleExpandedWidgetState,
onObjectCheckboxClicked = onObjectCheckboxClicked
onObjectCheckboxClicked = onObjectCheckboxClicked,
onCreateElement = onCreateElement
)
AnimatedVisibility(
visible = mode is InteractionMode.Edit,
@ -584,7 +593,8 @@ private fun SetOfObjectsItem(
onChangeWidgetView: (WidgetId, ViewId) -> Unit,
onToggleExpandedWidgetState: (WidgetId) -> Unit,
onObjectCheckboxClicked: (Id, Boolean) -> Unit,
onCreateDataViewObject: (WidgetId, ViewId?) -> Unit
onCreateDataViewObject: (WidgetId, ViewId?) -> Unit,
onCreateElement: (WidgetView) -> Unit = {}
) {
Box(
modifier = Modifier
@ -610,7 +620,8 @@ private fun SetOfObjectsItem(
onToggleExpandedWidgetState = onToggleExpandedWidgetState,
mode = mode,
onObjectCheckboxClicked = onObjectCheckboxClicked,
onCreateDataViewObject = onCreateDataViewObject
onCreateDataViewObject = onCreateDataViewObject,
onCreateElement = onCreateElement
)
AnimatedVisibility(
visible = mode is InteractionMode.Edit,

View file

@ -210,6 +210,7 @@ class HomeScreenFragment : BaseComposeFragment(),
onNavBarShareButtonClicked = vm::onNavBarShareIconClicked,
navPanelState = vm.navPanelState.collectAsStateWithLifecycle().value,
onHomeButtonClicked = vm::onHomeButtonClicked,
onCreateElement = vm::onCreateWidgetElementClicked
)
}

View file

@ -71,7 +71,8 @@ fun DataViewListWidgetCard(
onChangeWidgetView: (WidgetId, ViewId) -> Unit,
onToggleExpandedWidgetState: (WidgetId) -> Unit,
onObjectCheckboxClicked: (Id, Boolean) -> Unit,
onCreateDataViewObject: (WidgetId, ViewId?) -> Unit
onCreateDataViewObject: (WidgetId, ViewId?) -> Unit,
onCreateElement: (WidgetView) -> Unit = {}
) {
val isCardMenuExpanded = remember {
mutableStateOf(false)
@ -115,7 +116,9 @@ fun DataViewListWidgetCard(
isExpanded = item.isExpanded,
isInEditMode = mode is InteractionMode.Edit,
hasReadOnlyAccess = mode is InteractionMode.ReadOnly,
onDropDownMenuAction = onDropDownMenuAction
onDropDownMenuAction = onDropDownMenuAction,
canCreate = mode is InteractionMode.Default,
onCreateElement = { onCreateElement(item) }
)
if (item.tabs.size > 1 && item.isExpanded) {
DataViewTabs(

View file

@ -44,7 +44,8 @@ fun ListWidgetCard(
onWidgetSourceClicked: (Widget.Source) -> Unit,
onDropDownMenuAction: (DropDownMenuAction) -> Unit,
onToggleExpandedWidgetState: (WidgetId) -> Unit,
onObjectCheckboxClicked: (Id, Boolean) -> Unit
onObjectCheckboxClicked: (Id, Boolean) -> Unit,
onCreateElement: (WidgetView) -> Unit = {}
) {
val isCardMenuExpanded = remember {
mutableStateOf(false)
@ -89,7 +90,9 @@ fun ListWidgetCard(
isExpanded = item.isExpanded,
isInEditMode = mode is InteractionMode.Edit,
hasReadOnlyAccess = mode is InteractionMode.ReadOnly,
onDropDownMenuAction = onDropDownMenuAction
onDropDownMenuAction = onDropDownMenuAction,
canCreate = (item.type is Type.Favorites && mode is InteractionMode.Default),
onCreateElement = { onCreateElement(item) }
)
if (item.elements.isNotEmpty()) {
if (item.isCompact) {

View file

@ -10,16 +10,19 @@ import androidx.compose.animation.fadeOut
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.interaction.MutableInteractionSource
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.fillMaxHeight
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.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Divider
import androidx.compose.material.Text
@ -32,6 +35,7 @@ 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.draw.rotate
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
@ -254,9 +258,11 @@ fun WidgetHeader(
onWidgetHeaderClicked: () -> Unit,
onDropDownMenuAction: (DropDownMenuAction) -> Unit,
onExpandElement: () -> Unit = {},
onCreateElement: () -> Unit = {},
isExpanded: Boolean = false,
isInEditMode: Boolean = true,
hasReadOnlyAccess: Boolean = false
hasReadOnlyAccess: Boolean = false,
canCreate: Boolean = false
) {
val haptic = LocalHapticFeedback.current
Box(
@ -297,6 +303,25 @@ fun WidgetHeader(
)
)
if (canCreate) {
Box(
Modifier
.align(Alignment.CenterEnd)
.padding(end = 42.dp)
.fillMaxHeight()
.width(34.dp)
.noRippleClickable {
onCreateElement()
}
) {
Image(
painter = painterResource(R.drawable.ic_widget_system_plus_18),
contentDescription = stringResource(R.string.content_description_plus_button),
modifier = Modifier.align(Alignment.Center)
)
}
}
WidgetArrow(
modifier = Modifier.align(Alignment.CenterEnd),
isInEditMode = isInEditMode,

View file

@ -7,6 +7,6 @@
android:pathData="M7.5,15L12.5,10L7.5,5"
android:strokeWidth="1.5"
android:fillColor="@color/transparent"
android:strokeColor="@color/text_primary"
android:strokeColor="@color/glyph_active"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="18"
android:viewportHeight="18">
<path
android:pathData="M8.25,15.25C8.25,15.664 8.586,16 9,16C9.414,16 9.75,15.664 9.75,15.25V9.75H15.25C15.664,9.75 16,9.414 16,9C16,8.586 15.664,8.25 15.25,8.25H9.75V2.75C9.75,2.336 9.414,2 9,2C8.586,2 8.25,2.336 8.25,2.75V8.25H2.75C2.336,8.25 2,8.586 2,9C2,9.414 2.336,9.75 2.75,9.75H8.25V15.25Z"
android:fillColor="@color/glyph_active"
android:fillType="evenOdd"/>
</vector>

View file

@ -4,8 +4,9 @@ import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import javax.inject.Inject
class SetObjectListIsFavorite(
class SetObjectListIsFavorite @Inject constructor(
private val repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
) : ResultInteractor<SetObjectListIsFavorite.Params, Unit>(dispatchers.io) {

View file

@ -16,6 +16,7 @@ import com.anytypeio.anytype.core_models.DVFilterCondition
import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.ObjectTypeIds
import com.anytypeio.anytype.core_models.ObjectTypeUniqueKeys
import com.anytypeio.anytype.core_models.ObjectView
import com.anytypeio.anytype.core_models.ObjectWrapper
@ -47,6 +48,7 @@ import com.anytypeio.anytype.domain.bin.EmptyBin
import com.anytypeio.anytype.domain.block.interactor.CreateBlock
import com.anytypeio.anytype.domain.block.interactor.Move
import com.anytypeio.anytype.domain.collections.AddObjectToCollection
import com.anytypeio.anytype.domain.dashboard.interactor.SetObjectListIsFavorite
import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.launch.GetDefaultObjectType
@ -226,7 +228,8 @@ class HomeScreenViewModel(
private val spaceViewSubscriptionContainer: SpaceViewSubscriptionContainer,
private val getSpaceInviteLink: GetSpaceInviteLink,
private val deleteSpace: DeleteSpace,
private val spaceMembers: ActiveSpaceMemberSubscriptionContainer
private val spaceMembers: ActiveSpaceMemberSubscriptionContainer,
private val setAsFavourite: SetObjectListIsFavorite
) : NavigationViewModel<HomeScreenViewModel.Navigation>(),
Reducer<ObjectView, Payload>,
WidgetActiveViewStateHolder by widgetActiveViewStateHolder,
@ -2347,6 +2350,66 @@ class HomeScreenViewModel(
}
}
fun onCreateWidgetElementClicked(view: WidgetView) {
viewModelScope.launch {
}
when(view) {
is WidgetView.ListOfObjects -> {
if (view.type == WidgetView.ListOfObjects.Type.Favorites) {
viewModelScope.launch {
val space = SpaceId(spaceManager.get())
val type = getDefaultObjectType.async(space)
.getOrNull()
?.type ?: TypeKey(ObjectTypeIds.PAGE)
createObject.async(
params = CreateObject.Param(
space = SpaceId(spaceManager.get()),
type = type,
prefilled = mapOf(Relations.IS_FAVORITE to true)
)
).onSuccess { result ->
proceedWithNavigation(result.obj.navigation())
setAsFavourite.async(
params = SetObjectListIsFavorite.Params(
objectIds = listOf(result.obj.id),
isFavorite = true
)
)
}.onFailure {
Timber.e(it, "Error while creating object")
}
}
}
}
is WidgetView.SetOfObjects -> {
viewModelScope.launch {
val source = view.source
if (source is Widget.Source.Default) {
if (source.obj.layout == ObjectType.Layout.OBJECT_TYPE) {
val wrapper = ObjectWrapper.Type(source.obj.map)
createObject.async(
params = CreateObject.Param(
space = SpaceId(spaceManager.get()),
type = TypeKey(wrapper.uniqueKey),
prefilled = mapOf(Relations.IS_FAVORITE to true)
)
).onSuccess { result ->
proceedWithNavigation(result.obj.navigation())
}
} else {
Timber.w("Unexpected source layout: ${source.obj.layout}")
}
}
}
}
else -> {
Timber.w("Unexpected widget type: ${view::class.java.simpleName}")
}
}
}
sealed class Navigation {
data class OpenObject(val ctx: Id, val space: Id) : Navigation()
data class OpenChat(val ctx: Id, val space: Id) : Navigation()
@ -2426,7 +2489,8 @@ class HomeScreenViewModel(
private val spaceViewSubscriptionContainer: SpaceViewSubscriptionContainer,
private val getSpaceInviteLink: GetSpaceInviteLink,
private val deleteSpace: DeleteSpace,
private val activeSpaceMemberSubscriptionContainer: ActiveSpaceMemberSubscriptionContainer
private val activeSpaceMemberSubscriptionContainer: ActiveSpaceMemberSubscriptionContainer,
private val setObjectListIsFavorite: SetObjectListIsFavorite
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T = HomeScreenViewModel(
@ -2483,7 +2547,8 @@ class HomeScreenViewModel(
spaceViewSubscriptionContainer = spaceViewSubscriptionContainer,
getSpaceInviteLink = getSpaceInviteLink,
deleteSpace = this@Factory.deleteSpace,
spaceMembers = activeSpaceMemberSubscriptionContainer
spaceMembers = activeSpaceMemberSubscriptionContainer,
setAsFavourite = setObjectListIsFavorite
) as T
}

View file

@ -40,6 +40,7 @@ import com.anytypeio.anytype.domain.block.interactor.Move
import com.anytypeio.anytype.domain.collections.AddObjectToCollection
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.dashboard.interactor.SetObjectListIsFavorite
import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject
import com.anytypeio.anytype.domain.debugging.Logger
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
@ -280,6 +281,9 @@ class HomeScreenViewModelTest {
@Mock
lateinit var deleteSpace: DeleteSpace
@Mock
lateinit var setObjectListIsFavorite: SetObjectListIsFavorite
lateinit var userPermissionProvider: UserPermissionProvider
private val objectPayloadDispatcher = Dispatcher.Default<Payload>()
@ -2925,7 +2929,8 @@ class HomeScreenViewModelTest {
getSpaceInviteLink = getSpaceInviteLink,
spaceMembers = activeSpaceMemberSubscriptionContainer,
spaceViewSubscriptionContainer = spaceViewSubscriptionContainer,
deleteSpace = deleteSpace
deleteSpace = deleteSpace,
setAsFavourite = setObjectListIsFavorite
)
companion object {