1
0
Fork 0
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:
Evgenii Kozlov 2023-02-09 14:20:47 +01:00 committed by GitHub
parent 0573d3461e
commit 3a5c8f75a1
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 460 additions and 75 deletions

View file

@ -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)
}

View file

@ -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()
}

View file

@ -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() {

View file

@ -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
)
}
}

View file

@ -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"

View file

@ -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

View file

@ -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)
}

View file

@ -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

View file

@ -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

View file

@ -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)
}

View file

@ -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

View file

@ -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
)
}

View file

@ -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)
}

View file

@ -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

View file

@ -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
}

View file

@ -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
}
}
}

View file

@ -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
}
}
}

View file

@ -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)
}
}