mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-935 Widgets | Enhancement | Allow changing existing widget's layout type (#2907)
This commit is contained in:
parent
0573d3461e
commit
3a5c8f75a1
18 changed files with 460 additions and 75 deletions
|
@ -1,6 +1,11 @@
|
|||
package com.anytypeio.anytype.di.feature.widgets
|
||||
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerModal
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.widgets.UpdateWidget
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.presentation.widgets.SelectWidgetTypeViewModel
|
||||
import com.anytypeio.anytype.ui.widgets.SelectWidgetTypeFragment
|
||||
import dagger.Module
|
||||
|
@ -29,7 +34,17 @@ object SelectWidgetTypeModule {
|
|||
@PerModal
|
||||
@Provides
|
||||
fun factory(
|
||||
dispatcher: Dispatcher<Payload>,
|
||||
updateWidget: UpdateWidget,
|
||||
appCoroutineDispatchers: AppCoroutineDispatchers
|
||||
): SelectWidgetTypeViewModel.Factory = SelectWidgetTypeViewModel.Factory(
|
||||
// TODO
|
||||
dispatcher = dispatcher,
|
||||
updateWidget = updateWidget,
|
||||
appCoroutineDispatchers = appCoroutineDispatchers
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@PerModal
|
||||
@Provides
|
||||
fun updateWidget(repo: BlockRepository) = UpdateWidget(repo = repo)
|
||||
}
|
|
@ -24,6 +24,7 @@ import androidx.compose.material.DropdownMenuItem
|
|||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
|
@ -37,7 +38,6 @@ import com.anytypeio.anytype.core_models.Id
|
|||
import com.anytypeio.anytype.presentation.widgets.TreePath
|
||||
import com.anytypeio.anytype.presentation.widgets.WidgetView
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun HomeScreen(
|
||||
widgets: List<WidgetView>,
|
||||
|
@ -45,9 +45,7 @@ fun HomeScreen(
|
|||
onCreateWidget: () -> Unit,
|
||||
onEditWidgets: () -> Unit,
|
||||
onRefresh: () -> Unit,
|
||||
onDeleteWidget: (Id) -> Unit,
|
||||
onChangeWidgetSource: (Id) -> Unit,
|
||||
onChangeWidgetType: (Id) -> Unit
|
||||
onWidgetMenuAction: (Id, DropDownMenuAction) -> Unit
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
|
@ -62,14 +60,17 @@ fun HomeScreen(
|
|||
TreeWidgetCard(
|
||||
item = item,
|
||||
onExpand = onExpand,
|
||||
onChangeWidgetSource = onChangeWidgetSource,
|
||||
onChangeWidgetType = onChangeWidgetType,
|
||||
onDeleteWidget = onDeleteWidget
|
||||
onDropDownMenuAction = { action ->
|
||||
onWidgetMenuAction(item.id, action)
|
||||
}
|
||||
)
|
||||
}
|
||||
is WidgetView.Link -> {
|
||||
LinkWidgetCard(
|
||||
item = item
|
||||
item = item,
|
||||
onDropDownMenuAction = { action ->
|
||||
onWidgetMenuAction(item.id, action)
|
||||
}
|
||||
)
|
||||
}
|
||||
is WidgetView.Action.CreateWidget -> {
|
||||
|
@ -105,8 +106,15 @@ fun HomeScreen(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun LinkWidgetCard(item: WidgetView.Link) {
|
||||
private fun LinkWidgetCard(
|
||||
item: WidgetView.Link,
|
||||
onDropDownMenuAction: (DropDownMenuAction) -> Unit
|
||||
) {
|
||||
val isDropDownMenuExpanded = remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
@ -114,7 +122,16 @@ private fun LinkWidgetCard(item: WidgetView.Link) {
|
|||
.padding(start = 20.dp, end = 20.dp, top = 6.dp, bottom = 6.dp),
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
) {
|
||||
Box(Modifier.fillMaxHeight()) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.combinedClickable(
|
||||
onClick = {},
|
||||
onLongClick = {
|
||||
isDropDownMenuExpanded.value = !isDropDownMenuExpanded.value
|
||||
}
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = item.obj.name.orEmpty().trim(),
|
||||
style = MaterialTheme.typography.h6,
|
||||
|
@ -123,6 +140,10 @@ private fun LinkWidgetCard(item: WidgetView.Link) {
|
|||
.padding(start = 16.dp)
|
||||
)
|
||||
}
|
||||
WidgetMenu(
|
||||
isExpanded = isDropDownMenuExpanded,
|
||||
onDropDownMenuAction = onDropDownMenuAction
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,9 +152,7 @@ private fun LinkWidgetCard(item: WidgetView.Link) {
|
|||
private fun TreeWidgetCard(
|
||||
item: WidgetView.Tree,
|
||||
onExpand: (TreePath) -> Unit,
|
||||
onChangeWidgetSource: (Id) -> Unit,
|
||||
onChangeWidgetType: (Id) -> Unit,
|
||||
onDeleteWidget: (Id) -> Unit
|
||||
onDropDownMenuAction: (DropDownMenuAction) -> Unit
|
||||
) {
|
||||
val isDropDownMenuExpanded = remember {
|
||||
mutableStateOf(false)
|
||||
|
@ -150,8 +169,7 @@ private fun TreeWidgetCard(
|
|||
.combinedClickable(
|
||||
onClick = {},
|
||||
onLongClick = {
|
||||
isDropDownMenuExpanded.value =
|
||||
!isDropDownMenuExpanded.value
|
||||
isDropDownMenuExpanded.value = !isDropDownMenuExpanded.value
|
||||
}
|
||||
)
|
||||
) {
|
||||
|
@ -209,52 +227,47 @@ private fun TreeWidgetCard(
|
|||
}
|
||||
}
|
||||
WidgetMenu(
|
||||
expanded = isDropDownMenuExpanded.value,
|
||||
onDismiss = {
|
||||
isDropDownMenuExpanded.value = false
|
||||
},
|
||||
onChangeSourceClicked = {
|
||||
isDropDownMenuExpanded.value = false
|
||||
onChangeWidgetSource(item.id)
|
||||
},
|
||||
onChangeTypeClicked = {
|
||||
isDropDownMenuExpanded.value = false
|
||||
onChangeWidgetType(item.id)
|
||||
},
|
||||
onRemoveWidgetClicked = {
|
||||
isDropDownMenuExpanded.value = false
|
||||
onDeleteWidget(item.id)
|
||||
}
|
||||
isExpanded = isDropDownMenuExpanded,
|
||||
onDropDownMenuAction = onDropDownMenuAction
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WidgetMenu(
|
||||
expanded: Boolean,
|
||||
onDismiss: () -> Unit,
|
||||
onChangeTypeClicked: () -> Unit,
|
||||
onRemoveWidgetClicked: () -> Unit,
|
||||
onChangeSourceClicked: () -> Unit
|
||||
isExpanded: MutableState<Boolean>,
|
||||
onDropDownMenuAction: (DropDownMenuAction) -> Unit
|
||||
) {
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = onDismiss
|
||||
expanded = isExpanded.value,
|
||||
onDismissRequest = { isExpanded.value = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
onClick = onChangeSourceClicked
|
||||
onClick = {
|
||||
onDropDownMenuAction(DropDownMenuAction.ChangeWidgetSource).also {
|
||||
isExpanded.value = false
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.widget_change_source))
|
||||
}
|
||||
Divider(thickness = 0.5.dp)
|
||||
DropdownMenuItem(
|
||||
onClick = onChangeTypeClicked
|
||||
onClick = {
|
||||
onDropDownMenuAction(DropDownMenuAction.ChangeWidgetType).also {
|
||||
isExpanded.value = false
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.widget_change_type))
|
||||
}
|
||||
Divider(thickness = 0.5.dp)
|
||||
DropdownMenuItem(
|
||||
onClick = onRemoveWidgetClicked
|
||||
onClick = {
|
||||
onDropDownMenuAction(DropDownMenuAction.RemoveWidget).also {
|
||||
isExpanded.value = false
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(stringResource(id = R.string.remove_widget))
|
||||
}
|
||||
|
@ -298,3 +311,10 @@ fun WidgetActionButton(
|
|||
Text(text = label)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DropDownMenuAction {
|
||||
object ChangeWidgetType : DropDownMenuAction()
|
||||
object ChangeWidgetSource : DropDownMenuAction()
|
||||
object RemoveWidget : DropDownMenuAction()
|
||||
object EditWidgets : DropDownMenuAction()
|
||||
}
|
|
@ -4,17 +4,27 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
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
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.presentation.home.Command
|
||||
import com.anytypeio.anytype.presentation.home.HomeScreenViewModel
|
||||
import com.anytypeio.anytype.ui.settings.typography
|
||||
import com.anytypeio.anytype.ui.widgets.SelectWidgetTypeFragment
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class HomeScreenFragment : BaseComposeFragment() {
|
||||
|
||||
|
@ -30,28 +40,62 @@ class HomeScreenFragment : BaseComposeFragment() {
|
|||
): View = ComposeView(requireContext()).apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
HomeScreen(
|
||||
widgets = vm.views.collectAsState().value,
|
||||
onExpand = { path -> vm.onExpand(path) },
|
||||
onCreateWidget = {
|
||||
findNavController().navigate(R.id.selectWidgetSourceScreen)
|
||||
},
|
||||
onDeleteWidget = vm::onDeleteWidgetClicked,
|
||||
onEditWidgets = { context.toast("Coming soon") },
|
||||
onRefresh = vm::onRefresh,
|
||||
onChangeWidgetSource = {
|
||||
toast("TODO")
|
||||
},
|
||||
onChangeWidgetType = {
|
||||
toast("TODO")
|
||||
}
|
||||
)
|
||||
MaterialTheme(typography = typography) {
|
||||
HomeScreen(
|
||||
widgets = vm.views.collectAsState().value,
|
||||
onExpand = { path -> vm.onExpand(path) },
|
||||
onCreateWidget = {
|
||||
findNavController().navigate(R.id.selectWidgetSourceScreen)
|
||||
},
|
||||
onEditWidgets = { context.toast("Coming soon") },
|
||||
onRefresh = vm::onRefresh,
|
||||
onWidgetMenuAction = { widget: Id, action: DropDownMenuAction ->
|
||||
when(action) {
|
||||
DropDownMenuAction.ChangeWidgetSource -> {
|
||||
toast("TODO")
|
||||
}
|
||||
DropDownMenuAction.ChangeWidgetType -> {
|
||||
vm.onChangeWidgetTypeClicked(widget)
|
||||
}
|
||||
DropDownMenuAction.EditWidgets -> {
|
||||
toast("TODO")
|
||||
}
|
||||
DropDownMenuAction.RemoveWidget -> {
|
||||
vm.onDeleteWidgetClicked(widget)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
vm.onStart()
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
vm.commands.collect { command -> proceed(command) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceed(command: Command) {
|
||||
when(command) {
|
||||
is Command.SelectWidgetSource -> {
|
||||
findNavController().navigate(R.id.selectWidgetSourceScreen)
|
||||
}
|
||||
is Command.SelectWidgetType -> {
|
||||
findNavController().navigate(
|
||||
R.id.selectWidgetTypeScreen,
|
||||
args = SelectWidgetTypeFragment.args(
|
||||
ctx = command.ctx,
|
||||
widget = command.widget,
|
||||
source = command.source,
|
||||
type = command.type
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
|
|
|
@ -4,16 +4,32 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ext.argString
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.presentation.widgets.SelectWidgetTypeViewModel
|
||||
import com.anytypeio.anytype.ui.settings.typography
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SelectWidgetTypeFragment : BaseBottomSheetComposeFragment() {
|
||||
|
||||
private val ctx: Id get() = argString(CTX_KEY)
|
||||
private val widget: Id get() = argString(WIDGET_ID_KEY)
|
||||
private val source: Id get() = argString(WIDGET_SOURCE_KEY)
|
||||
private val currentType: Int get() = arg(WIDGET_TYPE_KEY)
|
||||
|
||||
private val vm by viewModels<SelectWidgetTypeViewModel> { factory }
|
||||
|
||||
@Inject
|
||||
|
@ -26,13 +42,38 @@ class SelectWidgetTypeFragment : BaseBottomSheetComposeFragment() {
|
|||
): View = ComposeView(requireContext()).apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
SelectWidgetTypeScreen(
|
||||
views = emptyList(),
|
||||
onViewClicked = {}
|
||||
)
|
||||
MaterialTheme(typography = typography) {
|
||||
SelectWidgetTypeScreen(
|
||||
views = vm.views.collectAsState().value,
|
||||
onViewClicked = { view ->
|
||||
vm.onWidgetTypeClicked(
|
||||
ctx = ctx,
|
||||
view = view,
|
||||
widget = widget,
|
||||
source = source
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
vm.isDismissed.collect { isDismissed ->
|
||||
if (isDismissed) dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
vm.onStart(currentType = currentType)
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
componentManager().selectWidgetTypeSubcomponent.get().inject(this)
|
||||
}
|
||||
|
@ -40,4 +81,23 @@ class SelectWidgetTypeFragment : BaseBottomSheetComposeFragment() {
|
|||
override fun releaseDependencies() {
|
||||
componentManager().selectWidgetTypeSubcomponent.release()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CTX_KEY = "arg.select-widget-type.ctx"
|
||||
private const val WIDGET_ID_KEY = "arg.select-widget-type.widget-id"
|
||||
private const val WIDGET_TYPE_KEY = "arg.select-widget-type.widget-type"
|
||||
private const val WIDGET_SOURCE_KEY = "arg.select-widget-type.widget-source"
|
||||
|
||||
fun args(
|
||||
ctx: Id,
|
||||
widget: Id,
|
||||
source: Id,
|
||||
type: Int
|
||||
) = bundleOf(
|
||||
CTX_KEY to ctx,
|
||||
WIDGET_ID_KEY to widget,
|
||||
WIDGET_SOURCE_KEY to source,
|
||||
WIDGET_TYPE_KEY to type
|
||||
)
|
||||
}
|
||||
}
|
|
@ -156,6 +156,11 @@
|
|||
android:name="com.anytypeio.anytype.ui.widgets.SelectWidgetSourceFragment">
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/selectWidgetTypeScreen"
|
||||
android:name="com.anytypeio.anytype.ui.widgets.SelectWidgetTypeFragment">
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/settingsScreen"
|
||||
android:name="com.anytypeio.anytype.ui.settings.MainSettingFragment"
|
||||
|
|
|
@ -29,4 +29,7 @@ typealias DVViewerRelation = Block.Content.DataView.Viewer.ViewerRelation
|
|||
|
||||
typealias RelationFormat = Relation.Format
|
||||
|
||||
typealias DVRecord = Map<String, Any?>
|
||||
typealias DVRecord = Map<String, Any?>
|
||||
|
||||
typealias Widget = Block.Content.Widget
|
||||
typealias WidgetLayout = Block.Content.Widget.Layout
|
|
@ -711,6 +711,18 @@ class BlockDataRepository(
|
|||
source = source
|
||||
)
|
||||
|
||||
override suspend fun updateWidget(
|
||||
ctx: Id,
|
||||
target: Id,
|
||||
source: Id,
|
||||
type: Block.Content.Widget.Layout
|
||||
): Payload = remote.updateWidget(
|
||||
ctx = ctx,
|
||||
target = target,
|
||||
source = source,
|
||||
type = type
|
||||
)
|
||||
|
||||
override suspend fun addDataViewFilter(command: Command.AddFilter): Payload {
|
||||
return remote.addDataViewFilter(command = command)
|
||||
}
|
||||
|
|
|
@ -308,6 +308,13 @@ interface BlockDataStore {
|
|||
|
||||
suspend fun createWidget(ctx: Id, source: Id): Payload
|
||||
|
||||
suspend fun updateWidget(
|
||||
ctx: Id,
|
||||
target: Id,
|
||||
source: Id,
|
||||
type: Block.Content.Widget.Layout
|
||||
): Payload
|
||||
|
||||
suspend fun addDataViewFilter(command: Command.AddFilter): Payload
|
||||
suspend fun removeDataViewFilter(command: Command.RemoveFilter): Payload
|
||||
suspend fun replaceDataViewFilter(command: Command.ReplaceFilter): Payload
|
||||
|
|
|
@ -308,6 +308,13 @@ interface BlockRemote {
|
|||
|
||||
suspend fun createWidget(ctx: Id, source: Id): Payload
|
||||
|
||||
suspend fun updateWidget(
|
||||
ctx: Id,
|
||||
target: Id,
|
||||
source: Id,
|
||||
type: Block.Content.Widget.Layout
|
||||
): Payload
|
||||
|
||||
suspend fun addDataViewFilter(command: Command.AddFilter): Payload
|
||||
suspend fun removeDataViewFilter(command: Command.RemoveFilter): Payload
|
||||
suspend fun replaceDataViewFilter(command: Command.ReplaceFilter): Payload
|
||||
|
|
|
@ -640,6 +640,18 @@ class BlockRemoteDataStore(private val remote: BlockRemote) : BlockDataStore {
|
|||
source = source
|
||||
)
|
||||
|
||||
override suspend fun updateWidget(
|
||||
ctx: Id,
|
||||
target: Id,
|
||||
source: Id,
|
||||
type: Block.Content.Widget.Layout
|
||||
): Payload = remote.updateWidget(
|
||||
ctx = ctx,
|
||||
target = target,
|
||||
source = source,
|
||||
type = type
|
||||
)
|
||||
|
||||
override suspend fun addDataViewFilter(command: Command.AddFilter): Payload {
|
||||
return remote.addDataViewFilter(command)
|
||||
}
|
||||
|
|
|
@ -361,6 +361,13 @@ interface BlockRepository {
|
|||
source: Id
|
||||
): Payload
|
||||
|
||||
suspend fun updateWidget(
|
||||
ctx: Id,
|
||||
target: Id,
|
||||
source: Id,
|
||||
type: Block.Content.Widget.Layout
|
||||
): Payload
|
||||
|
||||
suspend fun addDataViewFilter(command: Command.AddFilter): Payload
|
||||
suspend fun removeDataViewFilter(command: Command.RemoveFilter): Payload
|
||||
suspend fun replaceDataViewFilter(command: Command.ReplaceFilter): Payload
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package com.anytypeio.anytype.domain.widgets
|
||||
|
||||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.domain.base.ResultatInteractor
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
|
||||
/**
|
||||
* Use-case for updating widget block contained in widgets.
|
||||
*/
|
||||
class UpdateWidget(
|
||||
private val repo: BlockRepository
|
||||
) : ResultatInteractor<UpdateWidget.Params, Payload>() {
|
||||
|
||||
override suspend fun execute(params: Params): Payload = repo.updateWidget(
|
||||
ctx = params.ctx,
|
||||
source = params.source,
|
||||
type = params.type,
|
||||
target = params.target
|
||||
)
|
||||
|
||||
/**
|
||||
* [ctx] context of widgets — id of object containing widgets
|
||||
* [target] widget block to update
|
||||
* [source] source for the given widget block
|
||||
* [type] layout type to update
|
||||
*/
|
||||
data class Params(
|
||||
val ctx: Id,
|
||||
val target: Id,
|
||||
val source: Id,
|
||||
val type: Block.Content.Widget.Layout
|
||||
)
|
||||
}
|
|
@ -679,6 +679,18 @@ class BlockMiddleware(
|
|||
source = source
|
||||
)
|
||||
|
||||
override suspend fun updateWidget(
|
||||
ctx: Id,
|
||||
target: Id,
|
||||
source: Id,
|
||||
type: Block.Content.Widget.Layout
|
||||
): Payload = middleware.updateWidget(
|
||||
ctx = ctx,
|
||||
target = target,
|
||||
source = source,
|
||||
type = type
|
||||
)
|
||||
|
||||
override suspend fun addDataViewFilter(command: Command.AddFilter): Payload {
|
||||
return middleware.addDataViewFilter(command)
|
||||
}
|
||||
|
|
|
@ -29,12 +29,14 @@ import com.anytypeio.anytype.core_models.Response
|
|||
import com.anytypeio.anytype.core_models.SearchResult
|
||||
import com.anytypeio.anytype.core_models.Struct
|
||||
import com.anytypeio.anytype.core_models.Url
|
||||
import com.anytypeio.anytype.core_models.WidgetLayout
|
||||
import com.anytypeio.anytype.middleware.BuildConfig
|
||||
import com.anytypeio.anytype.middleware.auth.toAccountSetup
|
||||
import com.anytypeio.anytype.middleware.const.Constants
|
||||
import com.anytypeio.anytype.middleware.mappers.MDVFilter
|
||||
import com.anytypeio.anytype.middleware.mappers.MRelationFormat
|
||||
import com.anytypeio.anytype.middleware.mappers.core
|
||||
import com.anytypeio.anytype.middleware.mappers.mw
|
||||
import com.anytypeio.anytype.middleware.mappers.parse
|
||||
import com.anytypeio.anytype.middleware.mappers.toCore
|
||||
import com.anytypeio.anytype.middleware.mappers.toCoreModel
|
||||
|
@ -1860,6 +1862,30 @@ class Middleware(
|
|||
return response.event.toPayload()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun updateWidget(
|
||||
ctx: Id,
|
||||
target: Id,
|
||||
source: Id,
|
||||
type: WidgetLayout
|
||||
): Payload {
|
||||
val request = Rpc.Block.CreateWidget.Request(
|
||||
contextId = ctx,
|
||||
targetId = target,
|
||||
widgetLayout = type.mw(),
|
||||
position = Block.Position.Replace,
|
||||
block = Block(
|
||||
link = Block.Content.Link(
|
||||
targetBlockId = source
|
||||
)
|
||||
)
|
||||
)
|
||||
if (BuildConfig.DEBUG) logRequest(request)
|
||||
val response = service.blockCreateWidget(request)
|
||||
if (BuildConfig.DEBUG) logResponse(response)
|
||||
return response.event.toPayload()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun addDataViewFilter(
|
||||
command: Command.AddFilter
|
||||
|
|
|
@ -470,4 +470,9 @@ fun List<InternalFlags>.toMiddlewareModel(): List<InternalFlag> = map {
|
|||
InternalFlags.ShouldSelectTemplate -> InternalFlag(InternalFlag.Value.editorSelectTemplate)
|
||||
InternalFlags.ShouldSelectType -> InternalFlag(InternalFlag.Value.editorSelectType)
|
||||
}
|
||||
}
|
||||
|
||||
fun Block.Content.Widget.Layout.mw() : MWidgetLayout = when(this) {
|
||||
Block.Content.Widget.Layout.TREE -> MWidgetLayout.Tree
|
||||
Block.Content.Widget.Layout.LINK -> MWidgetLayout.Link
|
||||
}
|
|
@ -30,6 +30,7 @@ import com.anytypeio.anytype.presentation.widgets.WidgetDispatchEvent
|
|||
import com.anytypeio.anytype.presentation.widgets.WidgetView
|
||||
import com.anytypeio.anytype.presentation.widgets.parseWidgets
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
|
@ -54,6 +55,7 @@ class HomeScreenViewModel(
|
|||
) : BaseViewModel(), Reducer<ObjectView, Payload> {
|
||||
|
||||
val views = MutableStateFlow<List<WidgetView>>(actions)
|
||||
val commands = MutableSharedFlow<Command>()
|
||||
|
||||
private val objectViewState = MutableStateFlow<ObjectViewState>(ObjectViewState.Idle)
|
||||
private val widgets = MutableStateFlow<List<Widget>>(emptyList())
|
||||
|
@ -242,10 +244,6 @@ class HomeScreenViewModel(
|
|||
proceedWithOpeningWidgetObject(widgetObject = configStorage.get().widgets)
|
||||
}
|
||||
|
||||
fun onStart() {
|
||||
Timber.d("onStart")
|
||||
}
|
||||
|
||||
fun onExpand(path: TreePath) {
|
||||
expanded.onExpand(linkPath = path)
|
||||
}
|
||||
|
@ -254,6 +252,27 @@ class HomeScreenViewModel(
|
|||
proceedWithDeletingWidget(widget)
|
||||
}
|
||||
|
||||
fun onChangeWidgetTypeClicked(widget: Id) {
|
||||
val curr = widgets.value.find { it.id == widget }
|
||||
if (curr != null) {
|
||||
viewModelScope.launch {
|
||||
commands.emit(
|
||||
Command.SelectWidgetType(
|
||||
ctx = configStorage.get().widgets,
|
||||
widget = widget,
|
||||
source = curr.source.id,
|
||||
type = when(curr) {
|
||||
is Widget.Link -> Command.SelectWidgetType.TYPE_LINK
|
||||
is Widget.Tree -> Command.SelectWidgetType.TYPE_TREE
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
sendToast("Widget missing. Please try again later")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO move to a separate reducer inject into this VM's constructor
|
||||
override fun reduce(state: ObjectView, event: Payload): ObjectView {
|
||||
var curr = state
|
||||
|
@ -330,4 +349,21 @@ sealed class ObjectViewState {
|
|||
object Loading : ObjectViewState()
|
||||
data class Success(val obj: ObjectView) : ObjectViewState()
|
||||
data class Failure(val e: Throwable) : ObjectViewState()
|
||||
}
|
||||
|
||||
sealed class Command {
|
||||
object SelectWidgetSource : Command()
|
||||
data class SelectWidgetType(
|
||||
val ctx: Id,
|
||||
val widget: Id,
|
||||
val source: Id,
|
||||
val type: Int
|
||||
) : Command() {
|
||||
companion object {
|
||||
const val UNKNOWN_TYPE = -1
|
||||
const val TYPE_TREE = 0
|
||||
const val TYPE_LINK = 1
|
||||
const val TYPE_LIST = 2
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,19 +2,87 @@ package com.anytypeio.anytype.presentation.widgets
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_models.WidgetLayout
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
import com.anytypeio.anytype.domain.widgets.UpdateWidget
|
||||
import com.anytypeio.anytype.presentation.common.BaseViewModel
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
class SelectWidgetTypeViewModel : BaseViewModel() {
|
||||
class SelectWidgetTypeViewModel(
|
||||
private val appCoroutineDispatchers: AppCoroutineDispatchers,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val updateWidget: UpdateWidget
|
||||
) : BaseViewModel() {
|
||||
|
||||
val views = MutableStateFlow(
|
||||
listOf(
|
||||
WidgetTypeView.Tree(isSelected = false),
|
||||
WidgetTypeView.Link(isSelected = false)
|
||||
)
|
||||
)
|
||||
|
||||
val isDismissed = MutableStateFlow(false)
|
||||
|
||||
fun onStart(currentType: Int) {
|
||||
views.value = views.value.map { view -> view.setIsSelected(currentType) }
|
||||
}
|
||||
|
||||
fun onWidgetTypeClicked(
|
||||
ctx: Id,
|
||||
widget: Id,
|
||||
source: Id,
|
||||
view: WidgetTypeView
|
||||
) {
|
||||
if (!view.isSelected) {
|
||||
viewModelScope.launch {
|
||||
updateWidget(
|
||||
UpdateWidget.Params(
|
||||
ctx = ctx,
|
||||
target = widget,
|
||||
source = source,
|
||||
type = when (view) {
|
||||
is WidgetTypeView.Link -> WidgetLayout.LINK
|
||||
is WidgetTypeView.Tree -> WidgetLayout.TREE
|
||||
is WidgetTypeView.List -> TODO()
|
||||
}
|
||||
)
|
||||
).flowOn(appCoroutineDispatchers.io).collect { result ->
|
||||
result.fold(
|
||||
onFailure = {
|
||||
Timber.e(it, "Error while updating widget type")
|
||||
},
|
||||
onSuccess = {
|
||||
dispatcher.send(it).also {
|
||||
isDismissed.value = true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val appCoroutineDispatchers: AppCoroutineDispatchers,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val updateWidget: UpdateWidget
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return SelectWidgetTypeViewModel(
|
||||
|
||||
appCoroutineDispatchers = appCoroutineDispatchers,
|
||||
dispatcher = dispatcher,
|
||||
updateWidget = updateWidget
|
||||
) as T
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +1,19 @@
|
|||
package com.anytypeio.anytype.presentation.widgets
|
||||
|
||||
import com.anytypeio.anytype.presentation.home.Command.SelectWidgetType.Companion.TYPE_LINK
|
||||
import com.anytypeio.anytype.presentation.home.Command.SelectWidgetType.Companion.TYPE_LIST
|
||||
import com.anytypeio.anytype.presentation.home.Command.SelectWidgetType.Companion.TYPE_TREE
|
||||
|
||||
sealed class WidgetTypeView {
|
||||
abstract val isSelected: Boolean
|
||||
|
||||
data class List(override val isSelected: Boolean) : WidgetTypeView()
|
||||
data class Tree(override val isSelected: Boolean): WidgetTypeView()
|
||||
data class Link(override val isSelected: Boolean): WidgetTypeView()
|
||||
data class Tree(override val isSelected: Boolean) : WidgetTypeView()
|
||||
data class Link(override val isSelected: Boolean) : WidgetTypeView()
|
||||
|
||||
fun setIsSelected(type: Int): WidgetTypeView = when (this) {
|
||||
is Link -> copy(isSelected = type == TYPE_LINK)
|
||||
is List -> copy(isSelected = type == TYPE_LIST)
|
||||
is Tree -> copy(isSelected = type == TYPE_TREE)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue