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

DROID-1095 Widgets | Enhancement | Add-below actions for widgets (#3030)

This commit is contained in:
Evgenii Kozlov 2023-03-21 19:10:26 +01:00 committed by GitHub
parent 315c4bcd20
commit 56243a870e
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 152 additions and 39 deletions

View file

@ -15,7 +15,6 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
@ -64,9 +63,7 @@ class HomeScreenFragment : BaseComposeFragment() {
widgets = vm.views.collectAsState().value,
mode = vm.mode.collectAsState().value,
onExpand = { path -> vm.onExpand(path) },
onCreateWidget = {
findNavController().navigate(R.id.selectWidgetSourceScreen)
},
onCreateWidget = vm::onCreateWidgetClicked,
onEditWidgets = vm::onEditWidgets,
onExitEditMode = vm::onExitEditMode,
onWidgetMenuAction = { widget: Id, action: DropDownMenuAction ->
@ -136,7 +133,8 @@ class HomeScreenFragment : BaseComposeFragment() {
}
is Command.SelectWidgetSource -> {
findNavController().navigate(
R.id.selectWidgetSourceScreen
R.id.selectWidgetSourceScreen,
args = SelectWidgetSourceFragment.args(command.target)
)
}
is Command.ChangeWidgetType -> {
@ -157,7 +155,8 @@ class HomeScreenFragment : BaseComposeFragment() {
args = SelectWidgetTypeFragment.args(
ctx = command.ctx,
source = command.source,
layout = command.layout
layout = command.layout,
target = command.target
)
)
}

View file

@ -43,6 +43,7 @@ class SelectWidgetSourceFragment : BaseBottomSheetTextInputFragment<FragmentObje
private val ctx: Id get() = arg(CTX_KEY)
private val widget: Id get() = arg(WIDGET_ID_KEY)
private val target: Id? get() = argOrNull(TARGET_KEY)
private val source: Id get() = arg(WIDGET_SOURCE_KEY)
private val type: Int get() = arg(WIDGET_TYPE_KEY)
private val forExistingWidget: Boolean? get() = argOrNull(FLOW_EXISTING_WIDGET)
@ -103,7 +104,7 @@ class SelectWidgetSourceFragment : BaseBottomSheetTextInputFragment<FragmentObje
type = type
)
} else {
vm.onStartWithNewWidget()
vm.onStartWithNewWidget(target = target)
}
with(lifecycleScope) {
jobs += subscribe(vm.isDismissed) { isDismissed ->
@ -241,6 +242,7 @@ class SelectWidgetSourceFragment : BaseBottomSheetTextInputFragment<FragmentObje
private const val WIDGET_ID_KEY = "arg.select-widget-source.widget-id"
private const val WIDGET_TYPE_KEY = "arg.select-widget-source.widget-type"
private const val WIDGET_SOURCE_KEY = "arg.select-widget-source.widget-source"
private const val TARGET_KEY = "arg.select-widget-source.target"
fun args(
ctx: Id,
widget: Id,
@ -251,7 +253,15 @@ class SelectWidgetSourceFragment : BaseBottomSheetTextInputFragment<FragmentObje
WIDGET_ID_KEY to widget,
WIDGET_SOURCE_KEY to source,
WIDGET_TYPE_KEY to type,
TARGET_KEY to null,
FLOW_EXISTING_WIDGET to true
)
/**
* Flow for selecting source for new widget.
*/
fun args(
target: Id?
) = bundleOf(TARGET_KEY to target)
}
}

View file

@ -16,6 +16,7 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_utils.ext.arg
import com.anytypeio.anytype.core_utils.ext.argOrNull
import com.anytypeio.anytype.core_utils.ext.argString
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
@ -31,6 +32,7 @@ class SelectWidgetTypeFragment : BaseBottomSheetComposeFragment() {
private val source: Id get() = argString(WIDGET_SOURCE_KEY)
private val currentType: Int get() = arg(WIDGET_TYPE_KEY)
private val sourceLayout: Int get() = arg(WIDGET_SOURCE_LAYOUT)
private val target: Id? get() = argOrNull(TARGET_KEY)
private val forExistingWidget: Boolean get() = arg(IS_FOR_EXISTING_WIDGET)
private val vm by viewModels<SelectWidgetTypeViewModel> { factory }
@ -58,9 +60,9 @@ class SelectWidgetTypeFragment : BaseBottomSheetComposeFragment() {
)
} else {
vm.onWidgetTypeClicked(
ctx = ctx,
source = source,
view = view
view = view,
target = target
)
}
}
@ -111,6 +113,7 @@ class SelectWidgetTypeFragment : BaseBottomSheetComposeFragment() {
private const val WIDGET_SOURCE_KEY = "arg.select-widget-type.widget-source"
private const val WIDGET_SOURCE_LAYOUT = "arg.select-widget-type.widget-source-layout"
private const val IS_FOR_EXISTING_WIDGET = "arg.select-widget-type.for-existing-widget"
private const val TARGET_KEY = "arg.select-widget-type.target"
fun args(
ctx: Id,
@ -125,17 +128,20 @@ class SelectWidgetTypeFragment : BaseBottomSheetComposeFragment() {
WIDGET_TYPE_KEY to type,
IS_FOR_EXISTING_WIDGET to true,
WIDGET_SOURCE_LAYOUT to layout,
TARGET_KEY to null
)
fun args(
ctx: Id,
source: Id,
layout: Int
layout: Int,
target: Id?
) = bundleOf(
CTX_KEY to ctx,
WIDGET_SOURCE_KEY to source,
WIDGET_SOURCE_LAYOUT to layout,
IS_FOR_EXISTING_WIDGET to false
IS_FOR_EXISTING_WIDGET to false,
TARGET_KEY to target
)
}
}

View file

@ -31,6 +31,7 @@ fun WidgetMenu(
canChangeType: Boolean = true,
canEmptyBin: Boolean = false,
canEditWidgets: Boolean = true,
canAddBelow: Boolean = true,
isExpanded: MutableState<Boolean>,
onDropDownMenuAction: (DropDownMenuAction) -> Unit
) {
@ -43,6 +44,24 @@ fun WidgetMenu(
color = colorResource(id = R.color.text_primary),
fontSize = 17.sp
)
if (canAddBelow) {
DropdownMenuItem(
onClick = {
onDropDownMenuAction(DropDownMenuAction.AddBelow).also {
isExpanded.value = false
}
}
) {
Text(
text = stringResource(R.string.block_action_add_below),
style = defaultTextStyle
)
}
Divider(
thickness = 0.5.dp,
color = colorResource(id = R.color.shape_primary)
)
}
if (canChangeSource) {
DropdownMenuItem(
onClick = {

View file

@ -734,11 +734,15 @@ class BlockDataRepository(
override suspend fun createWidget(
ctx: Id,
source: Id,
layout: WidgetLayout
layout: WidgetLayout,
target: Id?,
position: Position
): Payload = remote.createWidget(
ctx = ctx,
source = source,
layout = layout
layout = layout,
target = target,
position = position
)
override suspend fun updateWidget(

View file

@ -325,7 +325,13 @@ interface BlockDataStore {
command: Command.CreateBlockLinkWithObject
): CreateBlockLinkWithObjectResult
suspend fun createWidget(ctx: Id, source: Id, layout: WidgetLayout): Payload
suspend fun createWidget(
ctx: Id,
source: Id,
layout: WidgetLayout,
target: Id?,
position: Position
): Payload
suspend fun updateWidget(
ctx: Id,

View file

@ -319,7 +319,13 @@ interface BlockRemote {
suspend fun createObject(command: Command.CreateObject): CreateObjectResult
suspend fun createWidget(ctx: Id, source: Id, layout: WidgetLayout): Payload
suspend fun createWidget(
ctx: Id,
source: Id,
layout: WidgetLayout,
target: Id?,
position: Position
): Payload
suspend fun updateWidget(
ctx: Id,

View file

@ -664,11 +664,15 @@ class BlockRemoteDataStore(private val remote: BlockRemote) : BlockDataStore {
override suspend fun createWidget(
ctx: Id,
source: Id,
layout: WidgetLayout
layout: WidgetLayout,
target: Id?,
position: Position
): Payload = remote.createWidget(
ctx = ctx,
source = source,
layout = layout
layout = layout,
target = target,
position = position
)
override suspend fun updateWidget(

View file

@ -371,7 +371,9 @@ interface BlockRepository {
suspend fun createWidget(
ctx: Id,
source: Id,
layout: WidgetLayout
layout: WidgetLayout,
target: Id? = null,
position: Position = Position.NONE
): Payload
suspend fun updateWidget(

View file

@ -2,6 +2,7 @@ package com.anytypeio.anytype.domain.widgets
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_models.Position
import com.anytypeio.anytype.core_models.WidgetLayout
import com.anytypeio.anytype.domain.base.ResultatInteractor
import com.anytypeio.anytype.domain.block.repo.BlockRepository
@ -14,12 +15,16 @@ class CreateWidget @Inject constructor(
override suspend fun execute(params: Params): Payload = repo.createWidget(
ctx = params.ctx,
source = params.source,
layout = params.type
layout = params.type,
target = params.target,
position = params.position
)
data class Params(
val ctx: Id,
val source: Id,
val type: WidgetLayout
val type: WidgetLayout,
val target: Id? = null,
val position: Position = Position.NONE
)
}

View file

@ -701,11 +701,15 @@ class BlockMiddleware(
override suspend fun createWidget(
ctx: Id,
source: Id,
layout: WidgetLayout
layout: WidgetLayout,
target: Id?,
position: Position
): Payload = middleware.createWidgetBlock(
ctx = ctx,
source = source,
layout = layout
layout = layout,
target = target,
position = position
)
override suspend fun updateWidget(

View file

@ -1905,7 +1905,9 @@ class Middleware(
fun createWidgetBlock(
ctx: Id,
source: Id,
layout: WidgetLayout
layout: WidgetLayout,
target: Id?,
position: Position
): Payload {
val request = Rpc.Block.CreateWidget.Request(
contextId = ctx,
@ -1914,7 +1916,9 @@ class Middleware(
link = Block.Content.Link(
targetBlockId = source
)
)
),
targetId = target.orEmpty(),
position = position.toMiddlewareModel()
)
if (BuildConfig.DEBUG) logRequest(request)
val response = service.blockCreateWidget(request)

View file

@ -293,7 +293,8 @@ class HomeScreenViewModel(
Command.SelectWidgetType(
ctx = configStorage.get().widgets,
source = dispatch.source,
layout = dispatch.sourceLayout
layout = dispatch.sourceLayout,
target = dispatch.target
)
)
}
@ -302,7 +303,8 @@ class HomeScreenViewModel(
Command.SelectWidgetType(
ctx = configStorage.get().widgets,
source = dispatch.source,
layout = ObjectType.Layout.SET.code
layout = ObjectType.Layout.SET.code,
target = dispatch.target
)
)
}
@ -316,7 +318,8 @@ class HomeScreenViewModel(
is WidgetDispatchEvent.TypePicked -> {
proceedWithCreatingWidget(
source = dispatch.source,
type = dispatch.widgetType
type = dispatch.widgetType,
target = dispatch.target
)
}
}
@ -324,7 +327,11 @@ class HomeScreenViewModel(
}
}
private fun proceedWithCreatingWidget(source: Id, type: Int) {
private fun proceedWithCreatingWidget(
source: Id,
type: Int,
target: Id?
) {
viewModelScope.launch {
val config = configStorage.get()
createWidget(
@ -336,7 +343,9 @@ class HomeScreenViewModel(
Command.ChangeWidgetType.TYPE_TREE -> WidgetLayout.TREE
Command.ChangeWidgetType.TYPE_LIST -> WidgetLayout.LIST
else -> WidgetLayout.LINK
}
},
target = target,
position = if (!target.isNullOrEmpty()) Position.BOTTOM else Position.NONE
)
).flowOn(appCoroutineDispatchers.io).collect { status ->
Timber.d("Status while creating widget: $status")
@ -452,6 +461,12 @@ class HomeScreenViewModel(
}
}
fun onCreateWidgetClicked() {
viewModelScope.launch {
commands.emit(Command.SelectWidgetSource())
}
}
fun onEditWidgets() {
mode.value = InteractionMode.Edit
}
@ -518,6 +533,9 @@ class HomeScreenViewModel(
DropDownMenuAction.EmptyBin -> {
proceedWithEmptyingBin()
}
DropDownMenuAction.AddBelow -> {
proceedWithAddingWidgetBelow(widget)
}
}
}
@ -538,6 +556,12 @@ class HomeScreenViewModel(
}
}
private fun proceedWithAddingWidgetBelow(widget: Id) {
viewModelScope.launch {
commands.emit(Command.SelectWidgetSource(target = widget))
}
}
private fun proceedWithChangingType(widget: Id) {
Timber.d("onChangeWidgetSourceClicked, widget:[$widget]")
val curr = widgets.value.find { it.id == widget }
@ -804,10 +828,16 @@ sealed class InteractionMode {
}
sealed class Command {
object SelectWidgetSource : Command()
/**
* [target] optional target, below which new widget will be created
*/
data class SelectWidgetSource(val target: Id? = null) : Command()
data class SelectWidgetType(
val ctx: Id,
val source: Id,
val target: Id?,
val layout: Int
) : Command()

View file

@ -90,9 +90,9 @@ class SelectWidgetSourceViewModel(
)
}
fun onStartWithNewWidget() {
fun onStartWithNewWidget(target: Id?) {
Timber.d("onStart with picking source for new widget")
config = Config.NewWidget
config = Config.NewWidget(target)
proceedWithSearchQuery()
}
@ -123,7 +123,10 @@ class SelectWidgetSourceViewModel(
is Config.NewWidget -> {
viewModelScope.launch {
dispatcher.send(
WidgetDispatchEvent.SourcePicked.Bundled(source = view.id)
WidgetDispatchEvent.SourcePicked.Bundled(
source = view.id,
target = curr.target
)
)
}
}
@ -154,7 +157,8 @@ class SelectWidgetSourceViewModel(
dispatcher.send(
WidgetDispatchEvent.SourcePicked.Default(
source = view.id,
sourceLayout = view.layout?.code ?: -1
sourceLayout = view.layout?.code ?: -1,
target = curr.target
)
)
}
@ -202,7 +206,7 @@ class SelectWidgetSourceViewModel(
sealed class Config {
object None: Config()
object NewWidget : Config()
data class NewWidget(val target: Id?) : Config()
data class ExistingWidget(
val ctx: Id,
val widget: Id,

View file

@ -112,8 +112,8 @@ class SelectWidgetTypeViewModel(
}
fun onWidgetTypeClicked(
ctx: Id,
source: Id,
target: Id?,
view: WidgetTypeView
) {
if (!view.isSelected) {
@ -121,6 +121,7 @@ class SelectWidgetTypeViewModel(
widgetDispatcher.send(
WidgetDispatchEvent.TypePicked(
source = source,
target = target,
widgetType = when (view) {
is WidgetTypeView.Link -> Command.ChangeWidgetType.TYPE_LINK
is WidgetTypeView.Tree -> Command.ChangeWidgetType.TYPE_TREE

View file

@ -4,14 +4,22 @@ import com.anytypeio.anytype.core_models.Id
sealed class WidgetDispatchEvent {
sealed class SourcePicked : WidgetDispatchEvent() {
data class Default(val source: Id, val sourceLayout: Int) : SourcePicked()
data class Default(
val source: Id,
val sourceLayout: Int,
val target: Id?
) : SourcePicked()
/**
* [source] bundled source - one of [BundledWidgetSourceIds]
*/
data class Bundled(val source: Id) : SourcePicked()
data class Bundled(val source: Id, val target: Id?) : SourcePicked()
}
data class TypePicked(val source: Id, val widgetType: Int) : WidgetDispatchEvent()
data class TypePicked(
val source: Id,
val target: Id?,
val widgetType: Int
) : WidgetDispatchEvent()
data class SourceChanged(
val ctx: Id,
val widget: Id,

View file

@ -93,6 +93,7 @@ sealed class DropDownMenuAction {
object ChangeWidgetType : DropDownMenuAction()
object ChangeWidgetSource : DropDownMenuAction()
object RemoveWidget : DropDownMenuAction()
object AddBelow: DropDownMenuAction()
object EditWidgets : DropDownMenuAction()
object EmptyBin: DropDownMenuAction()
}