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

Editor | Enhancement | Add Callout set icon logic (#2372)

This commit is contained in:
Sergey Boishtyan 2022-06-21 13:21:36 +03:00 committed by GitHub
parent 8d23f59955
commit 62f96685be
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 696 additions and 694 deletions

View file

@ -56,6 +56,7 @@ import com.anytypeio.anytype.di.feature.SetupNewAccountModule
import com.anytypeio.anytype.di.feature.SetupSelectedAccountModule
import com.anytypeio.anytype.di.feature.SplashModule
import com.anytypeio.anytype.di.feature.StartLoginModule
import com.anytypeio.anytype.di.feature.TextBlockIconPickerModule
import com.anytypeio.anytype.di.feature.ViewerCustomizeModule
import com.anytypeio.anytype.di.feature.ViewerFilterModule
import com.anytypeio.anytype.di.feature.ViewerRelationsModule
@ -228,6 +229,14 @@ class ComponentManager(
.build()
}
val textBlockIconPickerComponent = DependentComponentMap { ctx ->
editorComponent
.get(ctx)
.textBlockIconPickerComponent()
.module(TextBlockIconPickerModule)
.build()
}
val objectSetIconPickerComponent = DependentComponentMap { ctx ->
objectSetComponent
.get(ctx)

View file

@ -5,14 +5,19 @@ import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_utils.di.scope.PerModal
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.icon.RemoveDocumentIcon
import com.anytypeio.anytype.domain.icon.RemoveTextBlockIcon
import com.anytypeio.anytype.domain.icon.SetDocumentEmojiIcon
import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon
import com.anytypeio.anytype.domain.icon.SetTextBlockEmoji
import com.anytypeio.anytype.domain.icon.SetTextBlockImage
import com.anytypeio.anytype.emojifier.data.Emoji
import com.anytypeio.anytype.emojifier.suggest.EmojiSuggester
import com.anytypeio.anytype.presentation.editor.picker.ObjectIconPickerViewModelFactory
import com.anytypeio.anytype.presentation.editor.picker.ObjectSetIconPickerViewModelFactory
import com.anytypeio.anytype.presentation.editor.picker.TextBlockIconPickerViewModelFactory
import com.anytypeio.anytype.presentation.util.Dispatcher
import com.anytypeio.anytype.ui.editor.modals.ObjectIconPickerFragment
import com.anytypeio.anytype.ui.editor.modals.TextBlockIconPickerFragment
import com.anytypeio.anytype.ui.sets.ObjectSetIconPickerFragment
import dagger.Module
import dagger.Provides
@ -31,6 +36,19 @@ interface ObjectIconPickerComponent {
fun inject(fragment: ObjectIconPickerFragment)
}
@Subcomponent(modules = [TextBlockIconPickerModule::class])
@PerModal
interface TextBlockIconPickerComponent {
@Subcomponent.Builder
interface Builder {
fun module(module: TextBlockIconPickerModule): Builder
fun build(): TextBlockIconPickerComponent
}
fun inject(fragment: TextBlockIconPickerFragment)
}
@Subcomponent(modules = [ObjectIconPickerBaseModule::class, ObjectSetIconPickerModule::class])
@PerModal
interface ObjectSetIconPickerComponent {
@ -46,6 +64,7 @@ interface ObjectSetIconPickerComponent {
@Module
object ObjectIconPickerBaseModule {
@Provides
@PerModal
fun provideSetDocumentEmojiIconUseCase(
@ -65,6 +84,7 @@ object ObjectIconPickerBaseModule {
@Module
object ObjectIconPickerModule {
@Provides
@PerModal
fun provideViewModelFactory(
@ -85,6 +105,53 @@ object ObjectIconPickerModule {
)
}
@Module
object TextBlockIconPickerModule {
@Provides
@PerModal
fun provideSetEmojiIcon(
repo: BlockRepository
): SetTextBlockEmoji = SetTextBlockEmoji(
repo = repo
)
@Provides
@PerModal
fun provideRemoveIcon(
repo: BlockRepository
): RemoveTextBlockIcon = RemoveTextBlockIcon(
repo = repo
)
@Provides
@PerModal
fun provideSetImage(
repo: BlockRepository
): SetTextBlockImage = SetTextBlockImage(
repo = repo
)
@Provides
@PerModal
fun provideViewModelFactory(
setEmojiIcon: SetTextBlockEmoji,
setImageIcon: SetTextBlockImage,
removeDocumentIcon: RemoveTextBlockIcon,
emojiSuggester: EmojiSuggester,
dispatcher: Dispatcher<Payload>,
analytics: Analytics
): TextBlockIconPickerViewModelFactory = TextBlockIconPickerViewModelFactory(
setEmojiIcon = setEmojiIcon,
setImageIcon = setImageIcon,
removeDocumentIcon = removeDocumentIcon,
emojiSuggester = emojiSuggester,
emojiProvider = Emoji,
dispatcher = dispatcher,
analytics = analytics
)
}
@Module
object ObjectSetIconPickerModule {
@Provides

View file

@ -124,6 +124,7 @@ interface EditorSubComponent {
fun inject(fragment: EditorFragment)
fun objectIconPickerComponent(): ObjectIconPickerComponent.Builder
fun textBlockIconPickerComponent(): TextBlockIconPickerComponent.Builder
// Relations

View file

@ -17,7 +17,6 @@ import com.anytypeio.anytype.data.auth.repo.InfrastructureDataRepository
import com.anytypeio.anytype.data.auth.repo.UserSettingsCache
import com.anytypeio.anytype.data.auth.repo.UserSettingsDataRepository
import com.anytypeio.anytype.data.auth.repo.block.BlockDataRepository
import com.anytypeio.anytype.data.auth.repo.block.BlockDataStoreFactory
import com.anytypeio.anytype.data.auth.repo.block.BlockRemote
import com.anytypeio.anytype.data.auth.repo.block.BlockRemoteDataStore
import com.anytypeio.anytype.data.auth.repo.unsplash.UnsplashDataRepository
@ -190,20 +189,9 @@ object DataModule {
@Provides
@Singleton
fun provideBlockRepository(
factory: BlockDataStoreFactory
blockRemoteDataStore: BlockRemoteDataStore
): BlockRepository {
return BlockDataRepository(
factory = factory
)
}
@JvmStatic
@Provides
@Singleton
fun provideBlockDataStoreFactory(
blockRemoteDataStore: BlockRemoteDataStore
): BlockDataStoreFactory {
return BlockDataStoreFactory(
remote = blockRemoteDataStore
)
}

View file

@ -100,7 +100,6 @@ import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.EditorViewModel
import com.anytypeio.anytype.presentation.editor.EditorViewModelFactory
import com.anytypeio.anytype.presentation.editor.Snack
import com.anytypeio.anytype.presentation.editor.editor.BlockDimensions
import com.anytypeio.anytype.presentation.editor.editor.Command
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.ThemeColor
@ -119,10 +118,11 @@ import com.anytypeio.anytype.ui.editor.cover.SelectCoverObjectFragment
import com.anytypeio.anytype.ui.editor.gallery.FullScreenPictureFragment
import com.anytypeio.anytype.ui.editor.layout.ObjectLayoutFragment
import com.anytypeio.anytype.ui.editor.modals.CreateBookmarkFragment
import com.anytypeio.anytype.ui.editor.modals.ObjectIconPickerBaseFragment
import com.anytypeio.anytype.ui.editor.modals.IconPickerFragmentBase
import com.anytypeio.anytype.ui.editor.modals.SelectProgrammingLanguageFragment
import com.anytypeio.anytype.ui.editor.modals.SelectProgrammingLanguageReceiver
import com.anytypeio.anytype.ui.editor.modals.SetLinkFragment
import com.anytypeio.anytype.ui.editor.modals.TextBlockIconPickerFragment
import com.anytypeio.anytype.ui.editor.sheets.ObjectMenuBaseFragment
import com.anytypeio.anytype.ui.editor.sheets.ObjectMenuBaseFragment.DocumentMenuActionReceiver
import com.anytypeio.anytype.ui.linking.LinkToObjectFragment
@ -862,13 +862,17 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
is Command.OpenDocumentImagePicker -> {
pickerDelegate.openFilePicker(command.mimeType, REQUEST_PROFILE_IMAGE_CODE)
}
is Command.OpenDocumentEmojiIconPicker -> {
is Command.OpenTextBlockIconPicker -> {
TextBlockIconPickerFragment.new(
context = ctx, blockId = command.block
).show(childFragmentManager, null)
}
Command.OpenDocumentEmojiIconPicker -> {
hideSoftInput()
findNavController().navigate(
R.id.action_pageScreen_to_objectIconPickerScreen,
bundleOf(
ObjectIconPickerBaseFragment.ARG_CONTEXT_ID_KEY to ctx,
ObjectIconPickerBaseFragment.ARG_TARGET_ID_KEY to command.target
IconPickerFragmentBase.ARG_CONTEXT_ID_KEY to ctx,
)
)
}
@ -1893,8 +1897,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
findNavController().navigate(
R.id.objectIconPickerScreen,
bundleOf(
ObjectIconPickerBaseFragment.ARG_CONTEXT_ID_KEY to ctx,
ObjectIconPickerBaseFragment.ARG_TARGET_ID_KEY to ctx,
IconPickerFragmentBase.ARG_CONTEXT_ID_KEY to ctx,
)
)
}

View file

@ -10,13 +10,13 @@ import android.view.ViewGroup
import android.widget.EditText
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_utils.ext.GetImageContract
import com.anytypeio.anytype.core_utils.ext.arg
import com.anytypeio.anytype.core_utils.ext.invisible
import com.anytypeio.anytype.core_utils.ext.parseImagePath
import com.anytypeio.anytype.core_utils.ext.showSnackbar
@ -25,31 +25,27 @@ import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ext.visible
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetTextInputFragment
import com.anytypeio.anytype.databinding.FragmentPageIconPickerBinding
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.library_page_icon_picker_widget.ui.DocumentEmojiIconPickerAdapter
import com.anytypeio.anytype.presentation.editor.picker.EmojiPickerView.Companion.HOLDER_EMOJI_CATEGORY_HEADER
import com.anytypeio.anytype.presentation.editor.picker.EmojiPickerView.Companion.HOLDER_EMOJI_ITEM
import com.anytypeio.anytype.presentation.editor.picker.ObjectIconPickerBaseViewModel
import com.anytypeio.anytype.presentation.editor.picker.ObjectIconPickerBaseViewModel.ViewState
import com.anytypeio.anytype.presentation.editor.picker.ObjectIconPickerViewModel
import com.anytypeio.anytype.presentation.editor.picker.ObjectIconPickerViewModelFactory
import com.anytypeio.anytype.presentation.editor.picker.IconPickerViewModel
import com.anytypeio.anytype.presentation.editor.picker.IconPickerViewModel.ViewState
import com.google.android.material.snackbar.Snackbar
import timber.log.Timber
import javax.inject.Inject
abstract class ObjectIconPickerBaseFragment : BaseBottomSheetTextInputFragment<FragmentPageIconPickerBinding>() {
abstract class IconPickerFragmentBase<T> :
BaseBottomSheetTextInputFragment<FragmentPageIconPickerBinding>() {
protected val target: String
get() = requireArguments()
.getString(ARG_TARGET_ID_KEY)
?: throw IllegalStateException(MISSING_TARGET_ERROR)
protected val context: Id
get() = arg(ARG_CONTEXT_ID_KEY)
protected val context: String
get() = requireArguments()
.getString(ARG_CONTEXT_ID_KEY)
?: throw IllegalStateException(MISSING_CONTEXT_ERROR)
/**
* The target for which we choose icon
* i.e. Object, callout text block
*/
protected abstract val target: T
abstract val vm : ObjectIconPickerBaseViewModel
abstract val vm: IconPickerViewModel<T>
private val emojiPickerAdapter by lazy {
DocumentEmojiIconPickerAdapter(
@ -57,8 +53,7 @@ abstract class ObjectIconPickerBaseFragment : BaseBottomSheetTextInputFragment<F
onEmojiClicked = { unicode ->
vm.onEmojiClicked(
unicode = unicode,
target = target,
context = context
iconable = target
)
}
)
@ -75,8 +70,8 @@ abstract class ObjectIconPickerBaseFragment : BaseBottomSheetTextInputFragment<F
clearSearchText.invisible()
}
filterInputField.doAfterTextChanged { vm.onQueryChanged(it.toString()) }
btnRemoveIcon.setOnClickListener { vm.onRemoveClicked(context) }
tvTabRandom.setOnClickListener { vm.onRandomEmoji(ctx = context, target = target) }
btnRemoveIcon.setOnClickListener { vm.onRemoveClicked(target) }
tvTabRandom.setOnClickListener { vm.onRandomEmoji(target) }
tvTabUpload.setOnClickListener { proceedWithImagePick() }
}
skipCollapsed()
@ -160,12 +155,12 @@ abstract class ObjectIconPickerBaseFragment : BaseBottomSheetTextInputFragment<F
}
}
val getContent = registerForActivityResult(GetImageContract()) { uri: Uri? ->
private val getContent = registerForActivityResult(GetImageContract()) { uri: Uri? ->
if (uri != null) {
try {
val path = uri.parseImagePath(requireContext())
vm.onPickedFromDevice(
ctx = context,
iconable = target,
path = path
)
} catch (e: Exception) {
@ -193,39 +188,12 @@ abstract class ObjectIconPickerBaseFragment : BaseBottomSheetTextInputFragment<F
private const val EMPTY_FILTER_TEXT = ""
private const val PAGE_ICON_PICKER_DEFAULT_SPAN_COUNT = 6
private const val EMOJI_RECYCLER_ITEM_VIEW_CACHE_SIZE = 2000
private const val MISSING_TARGET_ERROR = "Missing target id"
private const val MISSING_CONTEXT_ERROR = "Missing context id"
private const val UNEXPECTED_VIEW_TYPE_MESSAGE = "Unexpected view type"
const val ARG_CONTEXT_ID_KEY = "arg.picker.context.id"
const val ARG_TARGET_ID_KEY = "arg.picker.target.id"
private const val SELECT_IMAGE_CODE = 1
private const val COULD_NOT_PARSE_PATH_ERROR = "Could not parse path to your image"
private const val REQUEST_PERMISSION_CODE = 2
}
}
open class ObjectIconPickerFragment : ObjectIconPickerBaseFragment() {
@Inject
lateinit var factory: ObjectIconPickerViewModelFactory
override val vm by viewModels<ObjectIconPickerViewModel> { factory }
override fun injectDependencies() {
componentManager().objectIconPickerComponent.get(context).inject(this)
}
override fun releaseDependencies() {
componentManager().objectIconPickerComponent.release(context)
}
companion object {
fun new(context: String, target: String) = ObjectIconPickerFragment().apply {
arguments = bundleOf(
ARG_CONTEXT_ID_KEY to context,
ARG_TARGET_ID_KEY to target
)
}
}
}

View file

@ -0,0 +1,35 @@
package com.anytypeio.anytype.ui.editor.modals
import androidx.core.os.bundleOf
import androidx.fragment.app.viewModels
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.editor.picker.IconPickerViewModel
import com.anytypeio.anytype.presentation.editor.picker.ObjectIconPickerViewModelFactory
import javax.inject.Inject
open class ObjectIconPickerFragment : IconPickerFragmentBase<Id>() {
@Inject
lateinit var factory: ObjectIconPickerViewModelFactory
override val vm by viewModels<IconPickerViewModel<Id>> { factory }
override val target: Id
get() = context
override fun injectDependencies() {
componentManager().objectIconPickerComponent.get(context).inject(this)
}
override fun releaseDependencies() {
componentManager().objectIconPickerComponent.release(context)
}
companion object {
fun new(context: String) = ObjectIconPickerFragment().apply {
arguments = bundleOf(
ARG_CONTEXT_ID_KEY to context,
)
}
}
}

View file

@ -0,0 +1,46 @@
package com.anytypeio.anytype.ui.editor.modals
import androidx.core.os.bundleOf
import androidx.fragment.app.viewModels
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_utils.ext.arg
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.domain.icon.TextBlockTarget
import com.anytypeio.anytype.presentation.editor.picker.IconPickerViewModel
import com.anytypeio.anytype.presentation.editor.picker.TextBlockIconPickerViewModelFactory
import javax.inject.Inject
class TextBlockIconPickerFragment : IconPickerFragmentBase<TextBlockTarget>() {
@Inject
lateinit var factory: TextBlockIconPickerViewModelFactory
override val vm by viewModels<IconPickerViewModel<TextBlockTarget>> { factory }
private val blockId: Id
get() = arg(ARG_BLOCK_ID_KEY)
override val target: TextBlockTarget by lazy {
TextBlockTarget(context, blockId)
}
override fun injectDependencies() {
componentManager().textBlockIconPickerComponent.get(context).inject(this)
}
override fun releaseDependencies() {
componentManager().textBlockIconPickerComponent.release(context)
}
companion object {
const val ARG_BLOCK_ID_KEY = "arg.picker.block.id"
fun new(
context: Id,
blockId: Id
) = TextBlockIconPickerFragment().apply {
arguments = bundleOf(
ARG_CONTEXT_ID_KEY to context,
ARG_BLOCK_ID_KEY to blockId
)
}
}
}

View file

@ -22,7 +22,7 @@ import com.anytypeio.anytype.presentation.objects.menu.ObjectMenuViewModelBase
import com.anytypeio.anytype.ui.editor.cover.SelectCoverObjectFragment
import com.anytypeio.anytype.ui.editor.cover.SelectCoverObjectSetFragment
import com.anytypeio.anytype.ui.editor.layout.ObjectLayoutFragment
import com.anytypeio.anytype.ui.editor.modals.ObjectIconPickerBaseFragment
import com.anytypeio.anytype.ui.editor.modals.IconPickerFragmentBase
import com.anytypeio.anytype.ui.relations.RelationListFragment
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@ -133,8 +133,7 @@ abstract class ObjectMenuBaseFragment : BaseBottomSheetFragment<FragmentObjectMe
findNavController().navigate(
R.id.objectIconPickerScreen,
bundleOf(
ObjectIconPickerBaseFragment.ARG_CONTEXT_ID_KEY to ctx,
ObjectIconPickerBaseFragment.ARG_TARGET_ID_KEY to ctx,
IconPickerFragmentBase.ARG_CONTEXT_ID_KEY to ctx,
)
)
}
@ -163,8 +162,7 @@ abstract class ObjectMenuBaseFragment : BaseBottomSheetFragment<FragmentObjectMe
findNavController().navigate(
R.id.objectSetIconPickerScreen,
bundleOf(
ObjectIconPickerBaseFragment.ARG_CONTEXT_ID_KEY to ctx,
ObjectIconPickerBaseFragment.ARG_TARGET_ID_KEY to ctx,
IconPickerFragmentBase.ARG_CONTEXT_ID_KEY to ctx,
)
)
}

View file

@ -43,7 +43,16 @@ import com.anytypeio.anytype.core_ui.widgets.FeaturedRelationGroupWidget
import com.anytypeio.anytype.core_ui.widgets.StatusBadgeWidget
import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
import com.anytypeio.anytype.core_utils.OnSwipeListener
import com.anytypeio.anytype.core_utils.ext.*
import com.anytypeio.anytype.core_utils.ext.argString
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_utils.ext.drawable
import com.anytypeio.anytype.core_utils.ext.gone
import com.anytypeio.anytype.core_utils.ext.hideKeyboard
import com.anytypeio.anytype.core_utils.ext.subscribe
import com.anytypeio.anytype.core_utils.ext.syncFocusWithImeVisibility
import com.anytypeio.anytype.core_utils.ext.syncTranslationWithImeVisibility
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ext.visible
import com.anytypeio.anytype.databinding.FragmentObjectSetBinding
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.editor.cover.CoverColor
@ -57,14 +66,18 @@ import com.anytypeio.anytype.presentation.sets.model.SortingExpression
import com.anytypeio.anytype.presentation.sets.model.Viewer
import com.anytypeio.anytype.ui.base.NavigationFragment
import com.anytypeio.anytype.ui.editor.cover.SelectCoverObjectSetFragment
import com.anytypeio.anytype.ui.editor.modals.ObjectIconPickerBaseFragment
import com.anytypeio.anytype.ui.editor.modals.IconPickerFragmentBase
import com.anytypeio.anytype.ui.editor.sheets.ObjectMenuBaseFragment
import com.anytypeio.anytype.ui.relations.RelationDateValueFragment
import com.anytypeio.anytype.ui.relations.RelationDateValueFragment.DateValueEditReceiver
import com.anytypeio.anytype.ui.relations.RelationTextValueFragment
import com.anytypeio.anytype.ui.relations.RelationTextValueFragment.TextValueEditReceiver
import com.anytypeio.anytype.ui.relations.RelationValueBaseFragment
import com.anytypeio.anytype.ui.sets.modals.*
import com.anytypeio.anytype.ui.sets.modals.CreateDataViewViewerFragment
import com.anytypeio.anytype.ui.sets.modals.EditDataViewViewerFragment
import com.anytypeio.anytype.ui.sets.modals.ManageViewerFragment
import com.anytypeio.anytype.ui.sets.modals.ViewerBottomSheetRootFragment
import com.anytypeio.anytype.ui.sets.modals.ViewerRelationsFragment
import com.anytypeio.anytype.ui.sets.modals.sort.ViewerSortFragment
import com.bumptech.glide.Glide
import kotlinx.coroutines.flow.filterNotNull
@ -615,8 +628,7 @@ open class ObjectSetFragment :
findNavController().navigate(
R.id.action_objectSetScreen_to_objectSetIconPickerScreen,
bundleOf(
ObjectIconPickerBaseFragment.ARG_CONTEXT_ID_KEY to ctx,
ObjectIconPickerBaseFragment.ARG_TARGET_ID_KEY to command.target
IconPickerFragmentBase.ARG_CONTEXT_ID_KEY to ctx,
)
)
}

View file

@ -1,17 +1,21 @@
package com.anytypeio.anytype.ui.sets
import androidx.fragment.app.viewModels
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.editor.picker.ObjectSetIconPickerViewModel
import com.anytypeio.anytype.presentation.editor.picker.IconPickerViewModel
import com.anytypeio.anytype.presentation.editor.picker.ObjectSetIconPickerViewModelFactory
import com.anytypeio.anytype.ui.editor.modals.ObjectIconPickerBaseFragment
import com.anytypeio.anytype.ui.editor.modals.IconPickerFragmentBase
import javax.inject.Inject
class ObjectSetIconPickerFragment : ObjectIconPickerBaseFragment() {
class ObjectSetIconPickerFragment : IconPickerFragmentBase<Id>() {
@Inject
lateinit var factory: ObjectSetIconPickerViewModelFactory
override val vm by viewModels<ObjectSetIconPickerViewModel> { factory }
override val vm by viewModels<IconPickerViewModel<Id>> { factory }
override val target: Id
get() = context
override fun injectDependencies() {
componentManager().objectSetIconPickerComponent.get(context).inject(this)

View file

@ -5,7 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.anytypeio.anytype.ui.editor.modals.ObjectIconPickerBaseFragment">
tools:context="com.anytypeio.anytype.ui.editor.modals.IconPickerFragmentBase">
<FrameLayout
android:layout_width="match_parent"

View file

@ -117,6 +117,24 @@ sealed class Command {
val style: Block.Content.Text.Style
)
/**
* Command for set icon to text block
* @property context context id
* @property blockId text block id
* @property icon
*/
data class SetTextIcon(
val context: Id,
val blockId: Id,
val icon: Icon,
) {
sealed interface Icon {
object None : Icon
data class Emoji(val unicode: String) : Icon
data class Image(val hash: Hash) : Icon
}
}
/**
* Command for creating a block
* @property context id of the context of the block (i.e. page, dashboard or something else)
@ -250,12 +268,10 @@ sealed class Command {
/**
* Command for setting document's emoji icon
* @property emoji emoji's unicode
* @property target id of the target block (icon)
* @property context id of the context for this operation
*/
data class SetDocumentEmojiIcon(
val context: Id,
val target: Id,
val emoji: String
)

View file

@ -69,7 +69,9 @@ sealed class Event {
val backgroundColor: String? = null,
val marks: List<Text.Mark>? = null,
val alignment: Block.Align? = null,
val checked: Boolean? = null
val checked: Boolean? = null,
val emojiIcon: String? = null,
val imageIcon: String? = null,
) : Command()
/**

View file

@ -237,7 +237,10 @@ class BlockAdapter(
val holder = when (viewType) {
HOLDER_PARAGRAPH -> {
Paragraph(ItemBlockTextBinding.inflate(inflater, parent, false))
Paragraph(
ItemBlockTextBinding.inflate(inflater, parent, false),
clicked = onClickListener
)
}
HOLDER_TITLE -> {
Title.Document(
@ -369,17 +372,20 @@ class BlockAdapter(
}
HOLDER_HEADER_ONE -> {
HeaderOne(
binding = ItemBlockHeaderOneBinding.inflate(inflater, parent, false)
binding = ItemBlockHeaderOneBinding.inflate(inflater, parent, false),
clicked = onClickListener,
)
}
HOLDER_HEADER_TWO -> {
HeaderTwo(
binding = ItemBlockHeaderTwoBinding.inflate(inflater, parent, false)
binding = ItemBlockHeaderTwoBinding.inflate(inflater, parent, false),
clicked = onClickListener,
)
}
HOLDER_HEADER_THREE -> {
HeaderThree(
binding = ItemBlockHeaderThreeBinding.inflate(inflater, parent, false)
binding = ItemBlockHeaderThreeBinding.inflate(inflater, parent, false),
clicked = onClickListener,
)
}
HOLDER_CODE_SNIPPET -> {
@ -391,28 +397,28 @@ class BlockAdapter(
Checkbox(
binding = ItemBlockCheckboxBinding.inflate(
inflater, parent, false
)
), clicked = onClickListener
)
}
HOLDER_BULLET -> {
Bulleted(
binding = ItemBlockBulletedBinding.inflate(
inflater, parent, false
)
), clicked = onClickListener
)
}
HOLDER_NUMBERED -> {
Numbered(
binding = ItemBlockNumberedBinding.inflate(
inflater, parent, false
)
), clicked = onClickListener
)
}
HOLDER_TOGGLE -> {
Toggle(
binding = ItemBlockToggleBinding.inflate(
inflater, parent, false
)
), clicked = onClickListener
)
}
HOLDER_DESCRIPTION -> {
@ -624,11 +630,14 @@ class BlockAdapter(
Highlight(
binding = ItemBlockHighlightBinding.inflate(
inflater, parent, false
)
), clicked = onClickListener
)
}
HOLDER_CALLOUT -> {
Callout(ItemBlockCalloutBinding.inflate(inflater, parent, false))
Callout(
binding = ItemBlockCalloutBinding.inflate(inflater, parent, false),
clicked = onClickListener
)
}
HOLDER_RELATION_DEFAULT -> {
RelationViewHolder.Default(
@ -724,9 +733,6 @@ class BlockAdapter(
holder.content.setOnClickListener { view ->
val pos = holder.bindingAdapterPosition
if (pos != RecyclerView.NO_POSITION) {
// if (view.hasFocus()) {
// view.context.imm().showSoftInput(view, InputMethodManager.SHOW_FORCED)
// }
onTextInputClicked(blocks[pos].id)
}
}
@ -738,11 +744,6 @@ class BlockAdapter(
item.isFocused = hasFocus
}
onFocusChanged(item.id, hasFocus)
// if (hasFocus) {
// holder.content.context
// .imm()
// .showSoftInput(holder.content, InputMethodManager.SHOW_FORCED)
// }
}
}
holder.content.selectionWatcher = { selection ->
@ -1141,14 +1142,11 @@ class BlockAdapter(
onTextBlockTextChanged(item)
}
},
onSelectionChanged = onSelectionChanged,
clicked = onClickListener,
onMentionEvent = onMentionEvent,
onSlashEvent = onSlashEvent,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
onSplitLineEnterClicked = onSplitLineEnterClicked,
onNonEmptyBlockBackspaceClicked = onNonEmptyBlockBackspaceClicked,
onTextInputClicked = onTextInputClicked,
onBackPressedCallback = onBackPressedCallback
)
}
@ -1156,7 +1154,6 @@ class BlockAdapter(
holder.bind(
block = blocks[position] as BlockView.Text.Header.One,
onTextBlockTextChanged = onTextBlockTextChanged,
clicked = onClickListener,
onMentionEvent = onMentionEvent,
onSlashEvent = onSlashEvent,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
@ -1169,7 +1166,6 @@ class BlockAdapter(
holder.bind(
block = blocks[position] as BlockView.Text.Header.Two,
onTextBlockTextChanged = onTextBlockTextChanged,
clicked = onClickListener,
onMentionEvent = onMentionEvent,
onSlashEvent = onSlashEvent,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
@ -1182,7 +1178,6 @@ class BlockAdapter(
holder.bind(
block = blocks[position] as BlockView.Text.Header.Three,
onTextBlockTextChanged = onTextBlockTextChanged,
clicked = onClickListener,
onMentionEvent = onMentionEvent,
onSlashEvent = onSlashEvent,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
@ -1196,14 +1191,11 @@ class BlockAdapter(
item = blocks[position] as BlockView.Text.Checkbox,
onTextBlockTextChanged = onTextBlockTextChanged,
onCheckboxClicked = onCheckboxClicked,
onSelectionChanged = onSelectionChanged,
clicked = onClickListener,
onMentionEvent = onMentionEvent,
onSlashEvent = onSlashEvent,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
onSplitLineEnterClicked = onSplitLineEnterClicked,
onNonEmptyBlockBackspaceClicked = onNonEmptyBlockBackspaceClicked,
onTextInputClicked = onTextInputClicked,
onBackPressedCallback = onBackPressedCallback
)
}
@ -1211,7 +1203,6 @@ class BlockAdapter(
holder.bind(
item = blocks[position] as BlockView.Text.Bulleted,
onTextBlockTextChanged = onTextBlockTextChanged,
clicked = onClickListener,
onMentionEvent = onMentionEvent,
onSlashEvent = onSlashEvent,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
@ -1224,7 +1215,6 @@ class BlockAdapter(
holder.bind(
item = blocks[position] as BlockView.Text.Numbered,
onTextBlockTextChanged = onTextBlockTextChanged,
clicked = onClickListener,
onMentionEvent = onMentionEvent,
onSlashEvent = onSlashEvent,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
@ -1239,7 +1229,6 @@ class BlockAdapter(
onTextBlockTextChanged = onTextBlockTextChanged,
onTogglePlaceholderClicked = onTogglePlaceholderClicked,
onToggleClicked = onToggleClicked,
clicked = onClickListener,
onMentionEvent = onMentionEvent,
onSlashEvent = onSlashEvent,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
@ -1255,11 +1244,9 @@ class BlockAdapter(
clicked = onClickListener,
onMentionEvent = onMentionEvent,
onSlashEvent = onSlashEvent,
onSelectionChanged = onSelectionChanged,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
onSplitLineEnterClicked = onSplitLineEnterClicked,
onNonEmptyBlockBackspaceClicked = onNonEmptyBlockBackspaceClicked,
onTextInputClicked = onTextInputClicked,
onBackPressedCallback = onBackPressedCallback
)
}
@ -1267,7 +1254,6 @@ class BlockAdapter(
holder.bind(
item = blocks[position] as BlockView.Text.Callout,
onTextBlockTextChanged = onTextBlockTextChanged,
clicked = onClickListener,
onMentionEvent = onMentionEvent,
onSlashEvent = onSlashEvent,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,

View file

@ -20,8 +20,9 @@ import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashEvent
class Bulleted(
val binding: ItemBlockBulletedBinding
) : Text(binding.root), SupportNesting {
val binding: ItemBlockBulletedBinding,
clicked: (ListenerType) -> Unit,
) : Text(binding.root, clicked), SupportNesting {
val indent: View = binding.bulletIndent
val bullet = binding.bullet
@ -51,7 +52,6 @@ class Bulleted(
fun bind(
item: BlockView.Text.Bulleted,
onTextBlockTextChanged: (BlockView.Text) -> Unit,
clicked: (ListenerType) -> Unit,
onMentionEvent: (MentionEvent) -> Unit,
onSlashEvent: (SlashEvent) -> Unit,
onSplitLineEnterClicked: (String, Editable, IntRange) -> Unit,
@ -67,7 +67,6 @@ class Bulleted(
}
onTextBlockTextChanged(item)
},
clicked = clicked,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
onSplitLineEnterClicked = onSplitLineEnterClicked,
onNonEmptyBlockBackspaceClicked = onNonEmptyBlockBackspaceClicked,

View file

@ -17,8 +17,12 @@ import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashEvent
class Callout(
val binding: ItemBlockCalloutBinding
) : Text(binding.root), BlockViewHolder.IndentableHolder {
val binding: ItemBlockCalloutBinding,
clicked: (ListenerType) -> Unit,
) : Text(
view = binding.root,
clicked = clicked
), BlockViewHolder.IndentableHolder {
override val root: View = itemView
override val content: TextInputWidget = binding.calloutText
@ -48,7 +52,6 @@ class Callout(
fun bind(
item: BlockView.Text.Callout,
onTextBlockTextChanged: (BlockView.Text) -> Unit,
clicked: (ListenerType) -> Unit,
onMentionEvent: (MentionEvent) -> Unit,
onSlashEvent: (SlashEvent) -> Unit,
onSplitLineEnterClicked: (String, Editable, IntRange) -> Unit,
@ -64,13 +67,15 @@ class Callout(
}
onTextBlockTextChanged(item)
},
clicked = clicked,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
onSplitLineEnterClicked = onSplitLineEnterClicked,
onNonEmptyBlockBackspaceClicked = onNonEmptyBlockBackspaceClicked,
onBackPressedCallback = onBackPressedCallback
).also {
icon.setIcon(item.icon)
icon.setOnClickListener {
clicked(ListenerType.Callout.Icon(item.id))
}
setupMentionWatcher(onMentionEvent)
setupSlashWatcher(onSlashEvent, item.getViewType())
}

View file

@ -18,8 +18,9 @@ import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashEvent
class Checkbox(
val binding: ItemBlockCheckboxBinding
) : Text(binding.root), SupportNesting {
val binding: ItemBlockCheckboxBinding,
clicked: (ListenerType) -> Unit,
) : Text(binding.root, clicked), SupportNesting {
var mode = BlockView.Mode.EDIT
@ -51,14 +52,11 @@ class Checkbox(
item: BlockView.Text.Checkbox,
onTextBlockTextChanged: (BlockView.Text) -> Unit,
onCheckboxClicked: (BlockView.Text.Checkbox) -> Unit,
onSelectionChanged: (String, IntRange) -> Unit,
clicked: (ListenerType) -> Unit,
onMentionEvent: (MentionEvent) -> Unit,
onSlashEvent: (SlashEvent) -> Unit,
onSplitLineEnterClicked: (String, Editable, IntRange) -> Unit,
onEmptyBlockBackspaceClicked: (String) -> Unit,
onNonEmptyBlockBackspaceClicked: (String, Editable) -> Unit,
onTextInputClicked: (String) -> Unit,
onBackPressedCallback: () -> Boolean
) = super.bind(
item = item,
@ -69,7 +67,6 @@ class Checkbox(
}
onTextBlockTextChanged(item)
},
clicked = clicked,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
onSplitLineEnterClicked = onSplitLineEnterClicked,
onNonEmptyBlockBackspaceClicked = onNonEmptyBlockBackspaceClicked,

View file

@ -15,15 +15,15 @@ import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashEvent
abstract class Header(
view: View
) : Text(view), TextBlockHolder, BlockViewHolder.IndentableHolder {
view: View,
clicked: (ListenerType) -> Unit,
) : Text(view, clicked), TextBlockHolder, BlockViewHolder.IndentableHolder {
abstract val header: TextInputWidget
fun bind(
block: BlockView.Text.Header,
onTextBlockTextChanged: (BlockView.Text) -> Unit,
clicked: (ListenerType) -> Unit,
onMentionEvent: (MentionEvent) -> Unit,
onSlashEvent: (SlashEvent) -> Unit,
onSplitLineEnterClicked: (String, Editable, IntRange) -> Unit,
@ -39,7 +39,6 @@ abstract class Header(
}
onTextBlockTextChanged(block)
},
clicked = clicked,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
onSplitLineEnterClicked = onSplitLineEnterClicked,
onNonEmptyBlockBackspaceClicked = onNonEmptyBlockBackspaceClicked,

View file

@ -6,10 +6,12 @@ import androidx.core.content.ContextCompat
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockHeaderOneBinding
import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
class HeaderOne(
val binding: ItemBlockHeaderOneBinding
) : Header(binding.root) {
val binding: ItemBlockHeaderOneBinding,
clicked: (ListenerType) -> Unit,
) : Header(binding.root, clicked) {
override val header: TextInputWidget = binding.headerOne
override val content: TextInputWidget get() = header

View file

@ -6,10 +6,12 @@ import androidx.core.content.ContextCompat
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockHeaderThreeBinding
import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
class HeaderThree(
val binding: ItemBlockHeaderThreeBinding
) : Header(binding.root) {
val binding: ItemBlockHeaderThreeBinding,
clicked: (ListenerType) -> Unit,
) : Header(binding.root, clicked) {
override val header: TextInputWidget = binding.headerThree
override val content: TextInputWidget get() = header

View file

@ -6,10 +6,12 @@ import androidx.core.content.ContextCompat
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockHeaderTwoBinding
import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
class HeaderTwo(
val binding: ItemBlockHeaderTwoBinding
) : Header(binding.root) {
val binding: ItemBlockHeaderTwoBinding,
clicked: (ListenerType) -> Unit,
) : Header(binding.root, clicked) {
override val header: TextInputWidget = binding.headerTwo
override val content: TextInputWidget get() = header

View file

@ -14,8 +14,6 @@ import com.anytypeio.anytype.core_ui.features.editor.BlockViewHolder
import com.anytypeio.anytype.core_ui.features.editor.SupportNesting
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableViewHolder
import com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
import com.anytypeio.anytype.core_ui.features.editor.decoration.OffsetBottom
import com.anytypeio.anytype.core_ui.features.editor.decoration.OffsetLeft
import com.anytypeio.anytype.core_ui.features.editor.marks
import com.anytypeio.anytype.core_ui.tools.DefaultSpannableFactory
import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
@ -26,8 +24,9 @@ import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashEvent
class Highlight(
val binding: ItemBlockHighlightBinding
) : Text(binding.root), BlockViewHolder.IndentableHolder, SupportNesting, DecoratableViewHolder {
val binding: ItemBlockHighlightBinding,
clicked: (ListenerType) -> Unit,
) : Text(binding.root, clicked), BlockViewHolder.IndentableHolder, SupportNesting, DecoratableViewHolder {
override val content: TextInputWidget = binding.highlightContent
override val root: View = itemView
@ -78,14 +77,12 @@ class Highlight(
fun bind(
item: BlockView.Text.Highlight,
onTextBlockTextChanged: (BlockView.Text) -> Unit,
onSelectionChanged: (String, IntRange) -> Unit,
clicked: (ListenerType) -> Unit,
onMentionEvent: (MentionEvent) -> Unit,
onSlashEvent: (SlashEvent) -> Unit,
onSplitLineEnterClicked: (String, Editable, IntRange) -> Unit,
onEmptyBlockBackspaceClicked: (String) -> Unit,
onNonEmptyBlockBackspaceClicked: (String, Editable) -> Unit,
onTextInputClicked: (String) -> Unit,
onBackPressedCallback: () -> Boolean
) = super.bind(
item = item,
@ -96,7 +93,6 @@ class Highlight(
}
onTextBlockTextChanged(item)
},
clicked = clicked,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
onSplitLineEnterClicked = onSplitLineEnterClicked,
onNonEmptyBlockBackspaceClicked = onNonEmptyBlockBackspaceClicked,

View file

@ -16,15 +16,15 @@ import com.anytypeio.anytype.core_ui.features.editor.marks
import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
import com.anytypeio.anytype.core_utils.ext.addDot
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.presentation.editor.editor.ThemeColor
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.mention.MentionEvent
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashEvent
class Numbered(
val binding: ItemBlockNumberedBinding
) : Text(binding.root), SupportNesting {
val binding: ItemBlockNumberedBinding,
clicked: (ListenerType) -> Unit,
) : Text(binding.root, clicked), SupportNesting {
private val container = binding.numberedBlockContentContainer
val number = binding.number
@ -53,7 +53,6 @@ class Numbered(
fun bind(
item: BlockView.Text.Numbered,
onTextBlockTextChanged: (BlockView.Text) -> Unit,
clicked: (ListenerType) -> Unit,
onMentionEvent: (MentionEvent) -> Unit,
onSlashEvent: (SlashEvent) -> Unit,
onSplitLineEnterClicked: (String, Editable, IntRange) -> Unit,
@ -69,7 +68,6 @@ class Numbered(
}
onTextBlockTextChanged(item)
},
clicked = clicked,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
onSplitLineEnterClicked = onSplitLineEnterClicked,
onNonEmptyBlockBackspaceClicked = onNonEmptyBlockBackspaceClicked,

View file

@ -22,8 +22,9 @@ import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashEvent
class Paragraph(
val binding: ItemBlockTextBinding
) : Text(binding.root), SupportNesting, DecoratableViewHolder {
val binding: ItemBlockTextBinding,
clicked: (ListenerType) -> Unit,
) : Text(binding.root, clicked), SupportNesting, DecoratableViewHolder {
override val root: View = binding.root
override val content: TextInputWidget = binding.textContent
@ -65,19 +66,15 @@ class Paragraph(
fun bind(
item: BlockView.Text.Paragraph,
onTextChanged: (String, Editable) -> Unit,
onSelectionChanged: (String, IntRange) -> Unit,
clicked: (ListenerType) -> Unit,
onMentionEvent: (MentionEvent) -> Unit,
onSlashEvent: (SlashEvent) -> Unit,
onSplitLineEnterClicked: (String, Editable, IntRange) -> Unit,
onEmptyBlockBackspaceClicked: (String) -> Unit,
onNonEmptyBlockBackspaceClicked: (String, Editable) -> Unit,
onTextInputClicked: (String) -> Unit,
onBackPressedCallback: () -> Boolean
) = super.bind(
item = item,
onTextChanged = onTextChanged,
clicked = clicked,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
onSplitLineEnterClicked = onSplitLineEnterClicked,
onNonEmptyBlockBackspaceClicked = onNonEmptyBlockBackspaceClicked,

View file

@ -13,7 +13,8 @@ import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.Checkable
abstract class Text(
view: View
view: View,
protected val clicked: (ListenerType) -> Unit,
) : BlockViewHolder(view), TextBlockHolder, BlockViewHolder.IndentableHolder,
BlockViewHolder.DragAndDropHolder {
@ -21,7 +22,6 @@ abstract class Text(
fun bind(
item: BlockView.TextBlockProps,
clicked: (ListenerType) -> Unit,
onTextChanged: (String, Editable) -> Unit,
onSplitLineEnterClicked: (String, Editable, IntRange) -> Unit,
onEmptyBlockBackspaceClicked: (String) -> Unit,

View file

@ -18,8 +18,9 @@ import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashEvent
class Toggle(
val binding: ItemBlockToggleBinding
) : Text(binding.root), SupportNesting {
val binding: ItemBlockToggleBinding,
clicked: (ListenerType) -> Unit,
) : Text(binding.root, clicked), SupportNesting {
private var mode = BlockView.Mode.EDIT
@ -54,7 +55,6 @@ class Toggle(
onTextBlockTextChanged: (BlockView.Text) -> Unit,
onToggleClicked: (String) -> Unit,
onTogglePlaceholderClicked: (String) -> Unit,
clicked: (ListenerType) -> Unit,
onMentionEvent: (MentionEvent) -> Unit,
onSlashEvent: (SlashEvent) -> Unit,
onSplitLineEnterClicked: (String, Editable, IntRange) -> Unit,
@ -70,7 +70,6 @@ class Toggle(
}
onTextBlockTextChanged(item)
},
clicked = clicked,
onEmptyBlockBackspaceClicked = onEmptyBlockBackspaceClicked,
onSplitLineEnterClicked = onSplitLineEnterClicked,
onNonEmptyBlockBackspaceClicked = onNonEmptyBlockBackspaceClicked,

View file

@ -11,12 +11,12 @@
<com.anytypeio.anytype.core_ui.widgets.ObjectIconWidget
android:id="@+id/callout_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
app:emojiSize="20dp"
app:imageSize="20dp"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:layout_marginTop="18dp"
android:padding="2dp"
tools:background="@color/black" />

View file

@ -9,7 +9,9 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
inline fun <reified T> Fragment.arg(key: String): T {
return requireArguments().get(key) as T
return checkNotNull(requireArguments().get(key)) {
"Fragment args missing value for $key"
} as T
}
inline fun <reified T> Fragment.argOrNull(key: String): T? {

View file

@ -28,16 +28,16 @@ import com.anytypeio.anytype.domain.page.Redo
import com.anytypeio.anytype.domain.page.Undo
class BlockDataRepository(
private val factory: BlockDataStoreFactory
private val remote: BlockDataStore
) : BlockRepository {
override suspend fun openDashboard(
contextId: String,
id: String
) = factory.remote.openDashboard(id = id, contextId = contextId)
) = remote.openDashboard(id = id, contextId = contextId)
override suspend fun openObjectPreview(id: Id): Result<Payload> = try {
Result.Success(factory.remote.openObjectPreview(id))
Result.Success(remote.openObjectPreview(id))
} catch (e: BackwardCompatilityNotSupportedException) {
Result.Failure(Error.BackwardCompatibility)
} catch (e: NotFoundObjectException) {
@ -45,7 +45,7 @@ class BlockDataRepository(
}
override suspend fun openPage(id: String): Result<Payload> = try {
Result.Success(factory.remote.openPage(id))
Result.Success(remote.openPage(id))
} catch (e: BackwardCompatilityNotSupportedException) {
Result.Failure(Error.BackwardCompatibility)
} catch (e: NotFoundObjectException) {
@ -53,10 +53,10 @@ class BlockDataRepository(
}
override suspend fun openProfile(id: String): Payload =
factory.remote.openProfile(id)
remote.openProfile(id)
override suspend fun openObjectSet(id: String): Result<Payload> = try {
Result.Success(factory.remote.openObjectSet(id))
Result.Success(remote.openObjectSet(id))
} catch (e: BackwardCompatilityNotSupportedException) {
Result.Failure(Error.BackwardCompatibility)
} catch (e: NotFoundObjectException) {
@ -64,12 +64,12 @@ class BlockDataRepository(
}
override suspend fun closeDashboard(id: String) {
factory.remote.closeDashboard(id)
remote.closeDashboard(id)
}
override suspend fun updateAlignment(
command: Command.UpdateAlignment
): Payload = factory.remote.updateAlignment(command)
): Payload = remote.updateAlignment(command)
override suspend fun createPage(
ctx: Id?,
@ -77,7 +77,7 @@ class BlockDataRepository(
isDraft: Boolean?,
type: String?,
template: Id?
) = factory.remote.createPage(
) = remote.createPage(
ctx = ctx,
emoji = emoji,
isDraft = isDraft,
@ -86,59 +86,62 @@ class BlockDataRepository(
)
override suspend fun closePage(id: String) {
factory.remote.closePage(id)
remote.closePage(id)
}
override suspend fun updateDocumentTitle(
command: Command.UpdateTitle
) = factory.remote.updateDocumentTitle(command)
) = remote.updateDocumentTitle(command)
override suspend fun updateText(command: Command.UpdateText) {
factory.remote.updateText(command)
remote.updateText(command)
}
override suspend fun updateTextStyle(
command: Command.UpdateStyle
): Payload = factory.remote.updateTextStyle(command)
): Payload = remote.updateTextStyle(command)
override suspend fun setTextIcon(command: Command.SetTextIcon): Payload =
remote.setTextIcon(command)
override suspend fun updateTextColor(
command: Command.UpdateTextColor
): Payload = factory.remote.updateTextColor(command)
): Payload = remote.updateTextColor(command)
override suspend fun setLinkAppearance(command: Command.SetLinkAppearance): Payload {
return factory.remote.setLinkAppearance(command)
return remote.setLinkAppearance(command)
}
override suspend fun updateBackgroundColor(
command: Command.UpdateBackgroundColor
): Payload = factory.remote.updateBackroundColor(command)
): Payload = remote.updateBackroundColor(command)
override suspend fun updateCheckbox(
command: Command.UpdateCheckbox
): Payload = factory.remote.updateCheckbox(command)
): Payload = remote.updateCheckbox(command)
override suspend fun create(command: Command.Create): Pair<Id, Payload> {
return factory.remote.create(command).let { (id, payload) ->
return remote.create(command).let { (id, payload) ->
Pair(id, payload)
}
}
override suspend fun replace(
command: Command.Replace
): Pair<Id, Payload> = factory.remote.replace(command).let { (id, payload) ->
): Pair<Id, Payload> = remote.replace(command).let { (id, payload) ->
Pair(id, payload)
}
override suspend fun duplicate(
command: Command.Duplicate
): Pair<List<Id>, Payload> = factory.remote.duplicate(command).let { (ids, payload) ->
): Pair<List<Id>, Payload> = remote.duplicate(command).let { (ids, payload) ->
Pair(ids, payload)
}
override suspend fun createDocument(
command: Command.CreateDocument
): Triple<String, String, Payload> {
return factory.remote.createDocument(
return remote.createDocument(
command
).let { (id, target, payload) ->
Triple(id, target, payload)
@ -148,73 +151,73 @@ class BlockDataRepository(
override suspend fun createNewDocument(
command: Command.CreateNewDocument
): Id {
return factory.remote.createNewDocument(command)
return remote.createNewDocument(command)
}
override suspend fun move(command: Command.Move): Payload {
return factory.remote.move(command)
return remote.move(command)
}
override suspend fun unlink(
command: Command.Unlink
): Payload = factory.remote.unlink(command)
): Payload = remote.unlink(command)
override suspend fun merge(
command: Command.Merge
): Payload = factory.remote.merge(command)
): Payload = remote.merge(command)
override suspend fun split(
command: Command.Split
): Pair<Id, Payload> = factory.remote.split(command).let { (id, payload) ->
): Pair<Id, Payload> = remote.split(command).let { (id, payload) ->
Pair(id, payload)
}
override suspend fun setDocumentEmojiIcon(
command: Command.SetDocumentEmojiIcon
): Payload = factory.remote.setDocumentEmojiIcon(command)
): Payload = remote.setDocumentEmojiIcon(command)
override suspend fun setDocumentImageIcon(
command: Command.SetDocumentImageIcon
): Payload = factory.remote.setDocumentImageIcon(command)
): Payload = remote.setDocumentImageIcon(command)
override suspend fun setDocumentCoverColor(
ctx: String,
color: String
): Payload = factory.remote.setDocumentCoverColor(ctx = ctx, color = color)
): Payload = remote.setDocumentCoverColor(ctx = ctx, color = color)
override suspend fun setDocumentCoverGradient(
ctx: String,
gradient: String
): Payload = factory.remote.setDocumentCoverGradient(ctx = ctx, gradient = gradient)
): Payload = remote.setDocumentCoverGradient(ctx = ctx, gradient = gradient)
override suspend fun setDocumentCoverImage(
ctx: String,
hash: String
): Payload = factory.remote.setDocumentCoverImage(ctx = ctx, hash = hash)
): Payload = remote.setDocumentCoverImage(ctx = ctx, hash = hash)
override suspend fun removeDocumentCover(
ctx: String
): Payload = factory.remote.removeDocumentCover(ctx)
): Payload = remote.removeDocumentCover(ctx)
override suspend fun removeDocumentIcon(
ctx: Id
): Payload = factory.remote.removeDocumentIcon(ctx)
): Payload = remote.removeDocumentIcon(ctx)
override suspend fun setupBookmark(
command: Command.SetupBookmark
): Payload = factory.remote.setupBookmark(command)
): Payload = remote.setupBookmark(command)
override suspend fun createBookmark(
command: Command.CreateBookmark
): Payload = factory.remote.createBookmark(command)
): Payload = remote.createBookmark(command)
override suspend fun uploadBlock(command: Command.UploadBlock): Payload =
factory.remote.uploadBlock(command)
remote.uploadBlock(command)
override suspend fun undo(
command: Command.Undo
): Undo.Result = try {
Undo.Result.Success(factory.remote.undo(command))
Undo.Result.Success(remote.undo(command))
} catch (e: UndoRedoExhaustedException) {
Undo.Result.Exhausted
}
@ -222,43 +225,43 @@ class BlockDataRepository(
override suspend fun redo(
command: Command.Redo
): Redo.Result = try {
Redo.Result.Success(factory.remote.redo(command))
Redo.Result.Success(remote.redo(command))
} catch (e: UndoRedoExhaustedException) {
Redo.Result.Exhausted
}
override suspend fun turnIntoDocument(
command: Command.TurnIntoDocument
): List<Id> = factory.remote.turnIntoDocument(command)
): List<Id> = remote.turnIntoDocument(command)
override suspend fun paste(
command: Command.Paste
): Response.Clipboard.Paste = factory.remote.paste(command)
): Response.Clipboard.Paste = remote.paste(command)
override suspend fun copy(
command: Command.Copy
): Response.Clipboard.Copy = factory.remote.copy(command)
): Response.Clipboard.Copy = remote.copy(command)
override suspend fun uploadFile(
command: Command.UploadFile
): Hash = factory.remote.uploadFile(command)
): Hash = remote.uploadFile(command)
override suspend fun getObjectInfoWithLinks(
pageId: String
): ObjectInfoWithLinks = factory.remote.getObjectInfoWithLinks(pageId)
): ObjectInfoWithLinks = remote.getObjectInfoWithLinks(pageId)
override suspend fun getListPages(): List<DocumentInfo> = factory.remote.getListPages()
override suspend fun getListPages(): List<DocumentInfo> = remote.getListPages()
override suspend fun setRelationKey(command: Command.SetRelationKey): Payload =
factory.remote.setRelationKey(command)
remote.setRelationKey(command)
override suspend fun updateDivider(
command: Command.UpdateDivider
): Payload = factory.remote.updateDivider(command = command)
): Payload = remote.updateDivider(command = command)
override suspend fun setFields(
command: Command.SetFields
): Payload = factory.remote.setFields(
): Payload = remote.setFields(
command = Command.SetFields(
context = command.context,
fields = command.fields.map { (id, fields) ->
@ -271,19 +274,19 @@ class BlockDataRepository(
context: Id,
targets: List<Id>,
style: Block.Content.Text.Style
): Payload = factory.remote.turnInto(
): Payload = remote.turnInto(
context = context,
targets = targets,
style = style
)
override suspend fun getObjectTypes(): List<ObjectType> {
return factory.remote.getObjectTypes()
return remote.getObjectTypes()
}
override suspend fun createObjectType(
prototype: ObjectType.Prototype
): ObjectType = factory.remote.createObjectType(
): ObjectType = remote.createObjectType(
ObjectType.Prototype(
name = prototype.name,
emoji = prototype.emoji,
@ -297,7 +300,7 @@ class BlockDataRepository(
position: Position?,
objectType: String?
): CreateObjectSet.Response {
val result = factory.remote.createSet(
val result = remote.createSet(
contextId = context,
targetId = target,
objectType = objectType,
@ -316,7 +319,7 @@ class BlockDataRepository(
view: Id,
offset: Int,
limit: Int
): Payload = factory.remote.setActiveDataViewViewer(
): Payload = remote.setActiveDataViewViewer(
context = context,
block = block,
view = view,
@ -330,7 +333,7 @@ class BlockDataRepository(
name: String,
format: Relation.Format,
limitObjectTypes: List<Id>
): Pair<Id, Payload> = factory.remote.addNewRelationToDataView(
): Pair<Id, Payload> = remote.addNewRelationToDataView(
context = context,
target = target,
name = name,
@ -342,7 +345,7 @@ class BlockDataRepository(
ctx: Id,
dv: Id,
relation: Id
): Payload = factory.remote.addRelationToDataView(
): Payload = remote.addRelationToDataView(
ctx = ctx,
dv = dv,
relation = relation
@ -352,7 +355,7 @@ class BlockDataRepository(
ctx: Id,
dv: Id,
relation: Id
): Payload = factory.remote.deleteRelationFromDataView(
): Payload = remote.deleteRelationFromDataView(
ctx = ctx,
dv = dv,
relation = relation
@ -362,7 +365,7 @@ class BlockDataRepository(
context: Id,
target: Id,
viewer: DVViewer
): Payload = factory.remote.updateDataViewViewer(
): Payload = remote.updateDataViewViewer(
context = context,
target = target,
viewer = viewer
@ -372,7 +375,7 @@ class BlockDataRepository(
context: Id,
target: Id,
viewer: DVViewer
): Payload = factory.remote.duplicateDataViewViewer(
): Payload = remote.duplicateDataViewViewer(
context = context,
target = target,
viewer = viewer
@ -383,7 +386,7 @@ class BlockDataRepository(
target: String,
name: String,
type: DVViewerType
): Payload = factory.remote.addDataViewViewer(
): Payload = remote.addDataViewViewer(
ctx = ctx,
target = target,
name = name,
@ -394,7 +397,7 @@ class BlockDataRepository(
ctx: Id,
dataview: Id,
viewer: Id
): Payload = factory.remote.removeDataViewViewer(
): Payload = remote.removeDataViewViewer(
ctx = ctx,
dataview = dataview,
viewer = viewer
@ -405,7 +408,7 @@ class BlockDataRepository(
target: Id,
record: Id,
values: Map<String, Any?>
) = factory.remote.updateDataViewRecord(
) = remote.updateDataViewRecord(
context = context,
target = target,
record = record,
@ -416,7 +419,7 @@ class BlockDataRepository(
context: Id,
target: Id,
template: Id?
): Map<String, Any?> = factory.remote.createDataViewRecord(
): Map<String, Any?> = remote.createDataViewRecord(
context = context,
target = target,
template = template
@ -429,7 +432,7 @@ class BlockDataRepository(
record: Id,
name: Id,
color: String
): Pair<Payload, Id?> = factory.remote.addDataViewRelationOption(
): Pair<Payload, Id?> = remote.addDataViewRelationOption(
ctx = ctx,
dataview = dataview,
relation = relation,
@ -443,7 +446,7 @@ class BlockDataRepository(
relation: Id,
name: String,
color: String
): Pair<Payload, Id?> = factory.remote.addObjectRelationOption(
): Pair<Payload, Id?> = remote.addObjectRelationOption(
ctx = ctx,
relation = relation,
name = name,
@ -457,7 +460,7 @@ class BlockDataRepository(
offset: Int,
limit: Int,
keys: List<Id>
): List<Map<String, Any?>> = factory.remote.searchObjects(
): List<Map<String, Any?>> = remote.searchObjects(
sorts = sorts,
filters = filters,
fulltext = fulltext,
@ -475,7 +478,7 @@ class BlockDataRepository(
limit: Long,
beforeId: Id?,
afterId: Id?
): SearchResult = factory.remote.searchObjectsWithSubscription(
): SearchResult = remote.searchObjectsWithSubscription(
subscription = subscription,
sorts = sorts,
filters = filters,
@ -490,7 +493,7 @@ class BlockDataRepository(
subscription: Id,
ids: List<Id>,
keys: List<String>
): SearchResult = factory.remote.searchObjectsByIdWithSubscription(
): SearchResult = remote.searchObjectsByIdWithSubscription(
subscription = subscription,
ids = ids,
keys = keys
@ -498,16 +501,16 @@ class BlockDataRepository(
override suspend fun cancelObjectSearchSubscription(
subscriptions: List<Id>
) = factory.remote.cancelObjectSearchSubscription(subscriptions)
) = remote.cancelObjectSearchSubscription(subscriptions)
override suspend fun relationListAvailable(ctx: Id) = factory.remote.relationListAvailable(ctx)
override suspend fun relationListAvailable(ctx: Id) = remote.relationListAvailable(ctx)
override suspend fun addRelationToObject(
ctx: Id, relation: Id
): Payload = factory.remote.addRelationToObject(ctx, relation)
): Payload = remote.addRelationToObject(ctx, relation)
override suspend fun deleteRelationFromObject(ctx: Id, relation: Id): Payload {
return factory.remote.deleteRelationFromObject(ctx = ctx, relation = relation)
return remote.deleteRelationFromObject(ctx = ctx, relation = relation)
}
override suspend fun addNewRelationToObject(
@ -515,78 +518,78 @@ class BlockDataRepository(
name: String,
format: RelationFormat,
limitObjectTypes: List<Id>
): Pair<Id, Payload> = factory.remote.addNewRelationToObject(
): Pair<Id, Payload> = remote.addNewRelationToObject(
ctx = ctx,
format = format,
name = name,
limitObjectTypes = limitObjectTypes
)
override suspend fun debugSync(): String = factory.remote.debugSync()
override suspend fun debugSync(): String = remote.debugSync()
override suspend fun debugLocalStore(path: String): String =
factory.remote.debugLocalStore(path)
remote.debugLocalStore(path)
override suspend fun updateDetail(
ctx: Id,
key: String,
value: Any?
): Payload = factory.remote.updateDetail(
): Payload = remote.updateDetail(
ctx = ctx,
key = key,
value = value
)
override suspend fun updateBlocksMark(command: Command.UpdateBlocksMark): Payload =
factory.remote.updateBlocksMark(command)
remote.updateBlocksMark(command)
override suspend fun addRelationToBlock(command: Command.AddRelationToBlock): Payload =
factory.remote.addRelationToBlock(command)
remote.addRelationToBlock(command)
override suspend fun setObjectTypeToObject(ctx: Id, typeId: Id): Payload =
factory.remote.setObjectTypeToObject(ctx = ctx, typeId = typeId)
remote.setObjectTypeToObject(ctx = ctx, typeId = typeId)
override suspend fun addToFeaturedRelations(
ctx: Id,
relations: List<Id>
): Payload = factory.remote.addToFeaturedRelations(ctx, relations)
): Payload = remote.addToFeaturedRelations(ctx, relations)
override suspend fun removeFromFeaturedRelations(
ctx: Id,
relations: List<Id>
): Payload = factory.remote.removeFromFeaturedRelations(ctx, relations)
): Payload = remote.removeFromFeaturedRelations(ctx, relations)
override suspend fun setObjectIsFavorite(
ctx: Id,
isFavorite: Boolean
): Payload = factory.remote.setObjectIsFavorite(ctx = ctx, isFavorite = isFavorite)
): Payload = remote.setObjectIsFavorite(ctx = ctx, isFavorite = isFavorite)
override suspend fun setObjectIsArchived(
ctx: Id,
isArchived: Boolean
): Payload = factory.remote.setObjectIsArchived(ctx = ctx, isArchived = isArchived)
): Payload = remote.setObjectIsArchived(ctx = ctx, isArchived = isArchived)
override suspend fun setObjectListIsArchived(
targets: List<Id>,
isArchived: Boolean
) = factory.remote.setObjectListIsArchived(
) = remote.setObjectListIsArchived(
targets = targets,
isArchived = isArchived
)
override suspend fun deleteObjects(targets: List<Id>) =
factory.remote.deleteObjects(targets = targets)
remote.deleteObjects(targets = targets)
override suspend fun setObjectLayout(ctx: Id, layout: ObjectType.Layout): Payload =
factory.remote.setObjectLayout(ctx, layout)
remote.setObjectLayout(ctx, layout)
override suspend fun clearFileCache() = factory.remote.clearFileCache()
override suspend fun clearFileCache() = remote.clearFileCache()
override suspend fun applyTemplate(ctx: Id, template: Id) = factory.remote.applyTemplate(
override suspend fun applyTemplate(ctx: Id, template: Id) = remote.applyTemplate(
ctx = ctx,
template = template
)
override suspend fun duplicateObject(id: Id): Id {
return factory.remote.duplicateObject(id)
return remote.duplicateObject(id)
}
}

View file

@ -35,6 +35,7 @@ interface BlockDataStore {
suspend fun updateDocumentTitle(command: Command.UpdateTitle)
suspend fun updateText(command: Command.UpdateText)
suspend fun updateTextStyle(command: Command.UpdateStyle): Payload
suspend fun setTextIcon(command: Command.SetTextIcon): Payload
suspend fun setLinkAppearance(command: Command.SetLinkAppearance): Payload

View file

@ -1,3 +0,0 @@
package com.anytypeio.anytype.data.auth.repo.block
class BlockDataStoreFactory(val remote: BlockRemoteDataStore)

View file

@ -34,6 +34,7 @@ interface BlockRemote {
suspend fun updateDocumentTitle(command: Command.UpdateTitle)
suspend fun updateText(command: Command.UpdateText)
suspend fun updateTextStyle(command: Command.UpdateStyle) : Payload
suspend fun setTextIcon(command: Command.SetTextIcon): Payload
suspend fun setLinkAppearance(command: Command.SetLinkAppearance): Payload

View file

@ -67,6 +67,10 @@ class BlockRemoteDataStore(private val remote: BlockRemote) : BlockDataStore {
command: Command.UpdateStyle
): Payload = remote.updateTextStyle(command)
override suspend fun setTextIcon(command: Command.SetTextIcon): Payload {
return remote.setTextIcon(command)
}
override suspend fun updateTextColor(
command: Command.UpdateTextColor
): Payload = remote.updateTextColor(command)

View file

@ -75,6 +75,7 @@ interface BlockRepository {
suspend fun updateDocumentTitle(command: Command.UpdateTitle)
suspend fun updateText(command: Command.UpdateText)
suspend fun updateTextStyle(command: Command.UpdateStyle): Payload
suspend fun setTextIcon(command: Command.SetTextIcon): Payload
suspend fun setLinkAppearance(command: Command.SetLinkAppearance): Payload
suspend fun updateTextColor(command: Command.UpdateTextColor): Payload

View file

@ -1,8 +1,6 @@
package com.anytypeio.anytype.domain.icon
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.block.repo.BlockRepository
/**
@ -10,17 +8,9 @@ import com.anytypeio.anytype.domain.block.repo.BlockRepository
*/
class RemoveDocumentIcon(
private val repo: BlockRepository
) : BaseUseCase<Payload, RemoveDocumentIcon.Params>() {
) : RemoveIcon<Id>() {
override suspend fun run(params: Params) = safe {
repo.removeDocumentIcon(params.ctx)
override suspend fun run(params: Id) = safe {
repo.removeDocumentIcon(params)
}
/**
* Params for for setting document's emoji icon
* @property [ctx] id of the object, whose icon we need to remove.
*/
data class Params(
val ctx: Id
)
}

View file

@ -0,0 +1,6 @@
package com.anytypeio.anytype.domain.icon
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.base.BaseUseCase
abstract class RemoveIcon<T> : BaseUseCase<Payload, T>()

View file

@ -0,0 +1,22 @@
package com.anytypeio.anytype.domain.icon
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.domain.block.repo.BlockRepository
/**
* Remove text block icon i.e. callout block
*/
class RemoveTextBlockIcon(
private val repo: BlockRepository
) : RemoveIcon<TextBlockTarget>() {
override suspend fun run(params: TextBlockTarget) = safe {
repo.setTextIcon(
Command.SetTextIcon(
context = params.context,
blockId = params.blockId,
icon = Command.SetTextIcon.Icon.None
)
)
}
}

View file

@ -1,37 +1,22 @@
package com.anytypeio.anytype.domain.icon
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.block.repo.BlockRepository
/**
* Use-case for setting emoji icon.
*/
class SetDocumentEmojiIcon(
private val repo: BlockRepository
) : BaseUseCase<Payload, SetDocumentEmojiIcon.Params>() {
) : SetEmojiIcon<Id>() {
override suspend fun run(params: Params) = safe {
override suspend fun run(params: SetEmojiIcon.Params<Id>) = safe {
repo.setDocumentEmojiIcon(
command = Command.SetDocumentEmojiIcon(
context = params.context,
target = params.target,
context = params.target,
emoji = params.emoji
)
)
}
/**
* Params for for setting document's emoji icon
* @property emoji emoji's unicode
* @property target id of the target block (icon)
* @property context id of the context for this operation
*/
data class Params(
val emoji: String,
val target: Id,
val context: Id
)
}

View file

@ -2,16 +2,14 @@ package com.anytypeio.anytype.domain.icon
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.Hash
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.domain.block.repo.BlockRepository
class SetDocumentImageIcon(
private val repo: BlockRepository
) : BaseUseCase<Pair<Payload, Hash>, SetDocumentImageIcon.Params>() {
) : SetImageIcon<Id>() {
override suspend fun run(params: Params) = safe {
override suspend fun run(params: Params<Id>) = safe {
val hash = repo.uploadFile(
command = Command.UploadFile(
path = params.path,
@ -21,19 +19,9 @@ class SetDocumentImageIcon(
val payload = repo.setDocumentImageIcon(
command = Command.SetDocumentImageIcon(
hash = hash,
context = params.context
context = params.target
)
)
Pair(payload, hash)
}
/**
* Params for for setting document's image icon
* @property path image path in file system
* @property context id of the context for this operation
*/
class Params(
val context: String,
val path: String
)
}

View file

@ -0,0 +1,17 @@
package com.anytypeio.anytype.domain.icon
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.base.BaseUseCase
abstract class SetEmojiIcon<T> :
BaseUseCase<Payload, SetEmojiIcon.Params<T>>() {
/**
* @property emoji emoji's unicode
* @property target i.e. object or text block
*/
data class Params<T>(
val target: T,
val emoji: String
)
}

View file

@ -0,0 +1,17 @@
package com.anytypeio.anytype.domain.icon
import com.anytypeio.anytype.core_models.Hash
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.base.BaseUseCase
abstract class SetImageIcon<T> : BaseUseCase<Pair<Payload, Hash>, SetImageIcon.Params<T>>() {
/**
* Params for for setting document's image icon
* @property path image path in file system
* @property target i.e. id of the context or text blockId
*/
data class Params<T>(
val target: T,
val path: String
)
}

View file

@ -0,0 +1,21 @@
package com.anytypeio.anytype.domain.icon
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.domain.block.repo.BlockRepository
class SetTextBlockEmoji(
private val repo: BlockRepository
) : SetEmojiIcon<TextBlockTarget>() {
override suspend fun run(
params: Params<TextBlockTarget>
) = safe {
repo.setTextIcon(
command = Command.SetTextIcon(
context = params.target.context,
blockId = params.target.blockId,
icon = Command.SetTextIcon.Icon.Emoji(params.emoji)
)
)
}
}

View file

@ -0,0 +1,29 @@
package com.anytypeio.anytype.domain.icon
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.domain.block.repo.BlockRepository
class SetTextBlockImage(
private val repo: BlockRepository
) : SetImageIcon<TextBlockTarget>() {
override suspend fun run(
params: Params<TextBlockTarget>
) = safe {
val hash = repo.uploadFile(
command = Command.UploadFile(
path = params.path,
type = Block.Content.File.Type.IMAGE
)
)
val payload = repo.setTextIcon(
command = Command.SetTextIcon(
icon = Command.SetTextIcon.Icon.Image(hash),
context = params.target.context,
blockId = params.target.blockId
)
)
Pair(payload, hash)
}
}

View file

@ -0,0 +1,8 @@
package com.anytypeio.anytype.domain.icon
import com.anytypeio.anytype.core_models.Id
data class TextBlockTarget(
val context: Id,
val blockId: Id,
)

View file

@ -1,6 +1,5 @@
package com.anytypeio.anytype.domain.auth
import com.anytypeio.anytype.core_models.Account
import com.anytypeio.anytype.core_models.CoroutineTestRule
import com.anytypeio.anytype.core_models.StubAccountSetup
import com.anytypeio.anytype.domain.auth.interactor.CreateAccount

View file

@ -86,6 +86,10 @@ class BlockMiddleware(
command: Command.UpdateStyle
): Payload = middleware.blockTextListSetStyle(command)
override suspend fun setTextIcon(
command: Command.SetTextIcon
): Payload = middleware.blockTextSetIcon(command)
override suspend fun updateTextColor(
command: Command.UpdateTextColor
): Payload = middleware.blockTextListSetColor(command)
@ -467,7 +471,8 @@ class BlockMiddleware(
)
override suspend fun debugSync(): String = middleware.debugSync()
override suspend fun debugLocalStore(path: String): String = middleware.debugExportLocalStore(path)
override suspend fun debugLocalStore(path: String): String =
middleware.debugExportLocalStore(path)
override suspend fun turnInto(
context: String,

View file

@ -758,6 +758,25 @@ class Middleware(
return response.event.toPayload()
}
@Throws(Exception::class)
fun blockTextSetIcon(command: Command.SetTextIcon): Payload {
val (image, emoji) = when (val icon = command.icon) {
is Command.SetTextIcon.Icon.Emoji -> "" to icon.unicode
is Command.SetTextIcon.Icon.Image -> icon.hash to ""
Command.SetTextIcon.Icon.None -> "" to ""
}
val request = Rpc.BlockText.SetIcon.Request(
contextId = command.context,
blockId = command.blockId,
iconImage = image,
iconEmoji = emoji,
)
if (BuildConfig.DEBUG) logRequest(request)
val response = service.blockTextSetIcon(request)
if (BuildConfig.DEBUG) logResponse(response)
return response.event.toPayload()
}
@Throws(Exception::class)
fun blockTextSetChecked(
context: String,

View file

@ -50,7 +50,9 @@ fun anytype.Event.Message.toCoreModels(
style = event.style?.value_?.toCoreModels(),
color = event.color?.value_,
marks = event.marks?.value_?.marks?.map { it.toCoreModels() },
checked = event.checked?.value_
checked = event.checked?.value_,
emojiIcon = event.iconEmoji?.value_,
imageIcon = event.iconImage?.value_,
)
}
blockSetBackgroundColor != null -> {

View file

@ -284,6 +284,9 @@ interface MiddlewareService {
@Throws(Exception::class)
fun blockTextListSetStyle(request: Rpc.BlockText.ListSetStyle.Request): Rpc.BlockText.ListSetStyle.Response
@Throws(Exception::class)
fun blockTextSetIcon(request: Rpc.BlockText.SetIcon.Request): Rpc.BlockText.SetIcon.Response
//endregion
//region LINK BLOCK commands

View file

@ -487,6 +487,18 @@ class MiddlewareServiceImplementation : MiddlewareService {
}
}
override fun blockTextSetIcon(request: Rpc.BlockText.SetIcon.Request): Rpc.BlockText.SetIcon.Response {
val encoded =
Service.blockTextSetIcon(Rpc.BlockText.SetIcon.Request.ADAPTER.encode(request))
val response = Rpc.BlockText.SetIcon.Response.ADAPTER.decode(encoded)
val error = response.error
if (error != null && error.code != Rpc.BlockText.SetIcon.Response.Error.Code.NULL) {
throw Exception(error.description)
} else {
return response
}
}
override fun blockTextSetChecked(request: Rpc.BlockText.SetChecked.Request): Rpc.BlockText.SetChecked.Response {
val encoded = Service.blockTextSetChecked(
Rpc.BlockText.SetChecked.Request.ADAPTER.encode(request)

View file

@ -15,7 +15,12 @@ import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.*
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.stub
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@ -213,7 +218,6 @@ class MiddlewareTest {
val command = Command.SetDocumentEmojiIcon(
context = MockDataFactory.randomUuid(),
target = MockDataFactory.randomUuid(),
emoji = MockDataFactory.randomString()
)

View file

@ -49,7 +49,9 @@ class DocumentExternalEventReducer : StateReducer<List<Block>, Event> {
text = event.text ?: content.text,
marks = event.marks ?: content.marks,
isChecked = event.checked ?: content.isChecked,
align = event.alignment ?: content.align
align = event.alignment ?: content.align,
iconEmoji = event.emojiIcon ?: content.iconEmoji,
iconImage = event.imageIcon ?: content.iconImage
),
backgroundColor = event.backgroundColor ?: block.backgroundColor,
)

View file

@ -61,6 +61,7 @@ import com.anytypeio.anytype.domain.editor.Editor
import com.anytypeio.anytype.domain.error.Error
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon
import com.anytypeio.anytype.domain.icon.SetImageIcon
import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.SetObjectIsArchived
@ -79,7 +80,6 @@ import com.anytypeio.anytype.presentation.common.StateReducer
import com.anytypeio.anytype.presentation.common.SupportCommand
import com.anytypeio.anytype.presentation.editor.ControlPanelMachine.Interactor
import com.anytypeio.anytype.presentation.editor.Editor.Restore
import com.anytypeio.anytype.presentation.editor.editor.BlockDimensions
import com.anytypeio.anytype.presentation.editor.editor.Command
import com.anytypeio.anytype.presentation.editor.editor.DetailModificationManager
import com.anytypeio.anytype.presentation.editor.editor.Intent
@ -380,8 +380,8 @@ class EditorViewModel(
override fun onPickedDocImageFromDevice(ctx: Id, path: String) {
viewModelScope.launch {
setDocImageIcon(
SetDocumentImageIcon.Params(
context = ctx,
SetImageIcon.Params(
target = ctx,
path = path
)
).process(
@ -3657,6 +3657,9 @@ class EditorViewModel(
else -> Unit
}
}
is ListenerType.Callout.Icon -> {
dispatch(Command.OpenTextBlockIconPicker(clicked.blockId))
}
}
}
@ -3722,7 +3725,7 @@ class EditorViewModel(
val isDetailsAllowed = restrictions.none { it == ObjectRestriction.DETAILS }
if (isDetailsAllowed) {
controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnDocumentIconClicked)
dispatch(Command.OpenDocumentEmojiIconPicker(target = context))
dispatch(Command.OpenDocumentEmojiIconPicker)
} else {
sendToast(NOT_ALLOWED_FOR_OBJECT)
}

View file

@ -9,9 +9,9 @@ sealed class Command {
data class OpenDocumentImagePicker(val mimeType: Mimetype) : Command()
data class OpenDocumentEmojiIconPicker(
val target: String
) : Command()
object OpenDocumentEmojiIconPicker : Command()
data class OpenTextBlockIconPicker(val block: Id) : Command()
data class OpenGallery(
val mimeType: Mimetype

View file

@ -5,57 +5,61 @@ import com.anytypeio.anytype.presentation.editor.editor.BlockDimensions
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.relations.DocumentRelationView
sealed class ListenerType {
sealed interface ListenerType {
sealed class Bookmark : ListenerType() {
sealed class Bookmark : ListenerType {
data class View(val item: BlockView.Media.Bookmark) : Bookmark()
data class Placeholder(val target: String) : Bookmark()
data class Error(val item: BlockView.Error.Bookmark) : Bookmark()
}
sealed class File : ListenerType() {
sealed class File : ListenerType {
data class View(val target: String) : File()
data class Placeholder(val target: String) : File()
data class Upload(val target: String) : File()
data class Error(val target: String) : File()
}
sealed class Picture: ListenerType() {
sealed class Picture: ListenerType {
data class View(val target: String) : Picture()
data class Placeholder(val target: String) : Picture()
data class Upload(val target: String) : Picture()
data class Error(val target: String) : Picture()
}
sealed class Video : ListenerType() {
sealed class Video : ListenerType {
data class View(val target: String) : Video()
data class Placeholder(val target: String) : Video()
data class Upload(val target: String) : Video()
data class Error(val target: String) : Video()
}
sealed class Code : ListenerType() {
sealed class Code : ListenerType {
data class SelectLanguage(val target: String) : Code()
}
data class LongClick(val target: String, val dimensions: BlockDimensions = BlockDimensions()) : ListenerType()
sealed interface Callout : ListenerType {
data class Icon(val blockId: String): Callout
}
data class EditableBlock(val target: String) : ListenerType()
data class LongClick(val target: String, val dimensions: BlockDimensions = BlockDimensions()) : ListenerType
object TitleBlock : ListenerType()
object ProfileImageIcon : ListenerType()
data class EditableBlock(val target: String) : ListenerType
data class LinkToObject(val target: String) : ListenerType()
data class LinkToObjectArchived(val target: String) : ListenerType()
data class LinkToObjectDeleted(val target: String) : ListenerType()
object TitleBlock : ListenerType
object ProfileImageIcon : ListenerType
data class Mention(val target: String) : ListenerType()
data class LinkToObject(val target: String) : ListenerType
data class LinkToObjectArchived(val target: String) : ListenerType
data class LinkToObjectDeleted(val target: String) : ListenerType
data class DividerClick(val target: String) : ListenerType()
data class Mention(val target: String) : ListenerType
data class Latex(val id: Id) : ListenerType()
data class DividerClick(val target: String) : ListenerType
sealed class Relation : ListenerType() {
data class Latex(val id: Id) : ListenerType
sealed class Relation : ListenerType {
data class Placeholder(val target: Id) : Relation()
data class Related(val value: BlockView.Relation) : Relation()
data class ObjectType(val type: String) : Relation()
@ -63,5 +67,5 @@ sealed class ListenerType {
data class Featured(val relation: DocumentRelationView) : Relation()
}
data class TableOfContentsItem(val target: Id, val item: Id) : ListenerType()
data class TableOfContentsItem(val target: Id, val item: Id) : ListenerType
}

View file

@ -1,247 +0,0 @@
package com.anytypeio.anytype.presentation.editor.picker
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_utils.ui.ViewStateViewModel
import com.anytypeio.anytype.domain.icon.SetDocumentEmojiIcon
import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon
import com.anytypeio.anytype.emojifier.data.Emoji
import com.anytypeio.anytype.presentation.common.StateReducer
import com.anytypeio.anytype.presentation.editor.editor.DetailModificationManager
import com.anytypeio.anytype.presentation.editor.picker.DocumentIconActionMenuViewModel.Contract.*
import com.anytypeio.anytype.presentation.editor.picker.DocumentIconActionMenuViewModel.ViewState
import com.anytypeio.anytype.presentation.util.Dispatcher
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
@Deprecated("To be deleted")
class DocumentIconActionMenuViewModel(
private val setEmojiIcon: SetDocumentEmojiIcon,
private val setImageIcon: SetDocumentImageIcon,
private val dispatcher: Dispatcher<Payload>,
private val details: DetailModificationManager
) : ViewStateViewModel<ViewState>(), StateReducer<State, Event> {
private val events = ConflatedBroadcastChannel<Event>()
private val actions = Channel<Action>()
private val flow: Flow<State> = events.asFlow().scan(State.init(), function)
override val function: suspend (State, Event) -> State
get() = { state, event -> reduce(state, event) }
init {
flow
.map { state ->
when {
state.error != null -> ViewState.Error(state.error)
state.isCompleted -> ViewState.Exit
state.isUploading -> ViewState.Uploading
else -> ViewState.Idle
}
}
.onEach { stateData.postValue(it) }
.launchIn(viewModelScope)
actions
.consumeAsFlow()
.onEach { action ->
when (action) {
is Action.SetEmojiIcon -> setEmojiIcon(
params = SetDocumentEmojiIcon.Params(
target = action.target,
emoji = action.unicode,
context = action.context
)
).proceed(
success = {
dispatcher.send(it)
details.setEmojiIcon(target = action.target, unicode = action.unicode)
events.send(Event.OnCompleted)
},
failure = { events.send(Event.Failure(it)) }
)
is Action.ClearEmoji -> setEmojiIcon(
params = SetDocumentEmojiIcon.Params(
target = action.target,
emoji = "",
context = action.context
)
).proceed(
success = { payload ->
dispatcher.send(payload)
details.removeIcon(action.target)
events.send(Event.OnCompleted)
},
failure = { events.send(Event.Failure(it)) }
)
is Action.SetImageIcon -> setImageIcon(
SetDocumentImageIcon.Params(
context = action.context,
path = action.path
)
).proceed(
failure = { events.send(Event.Failure(it)) },
success = { (payload, hash) ->
dispatcher.send(payload)
details.setImageIcon(target = action.context, hash = hash)
events.send(Event.OnCompleted)
}
)
is Action.PickRandomEmoji -> {
val random = Emoji.DATA.random().random()
events.send(
Event.OnRandomEmojiSelected(
target = action.target,
context = action.context,
unicode = random
)
)
}
}
}
.launchIn(viewModelScope)
}
fun onEvent(event: Event) {
viewModelScope.launch { events.send(event) }
}
sealed class ViewState {
object Loading : ViewState()
object Uploading : ViewState()
object Exit : ViewState()
object Idle : ViewState()
data class Error(val message: String) : ViewState()
}
sealed class Contract {
sealed class Action {
class PickRandomEmoji(
val context: String,
val target: String
) : Action()
class ClearEmoji(
val target: String,
val context: String
) : Action()
class SetEmojiIcon(
val unicode: String,
val target: String,
val context: String
) : Action()
class SetImageIcon(
val context: String,
val path: String
) : Action()
}
data class State(
val isLoading: Boolean,
val isUploading: Boolean = false,
val isCompleted: Boolean = false,
val error: String? = null
) : Contract() {
companion object {
fun init() = State(isLoading = false)
}
}
sealed class Event : Contract() {
class OnImagePickedFromGallery(
val context: String,
val path: String
) : Event()
class OnSetRandomEmojiClicked(
val target: String,
val context: String
) : Event()
class OnRandomEmojiSelected(
val unicode: String,
val context: String,
val target: String
) : Event()
class OnRemoveEmojiSelected(
val context: String,
val target: String
) : Event()
object OnCompleted : Event()
class Failure(val error: Throwable) : Event()
}
}
override suspend fun reduce(state: State, event: Event): State {
return when (event) {
is Event.OnRandomEmojiSelected -> state.copy(
isLoading = true
).also {
actions.send(
Action.SetEmojiIcon(
target = event.target,
context = event.context,
unicode = event.unicode
)
)
}
is Event.OnSetRandomEmojiClicked -> {
state.copy(
isLoading = true
).also {
actions.send(
Action.PickRandomEmoji(
target = event.target,
context = event.context
)
)
}
}
is Event.OnRemoveEmojiSelected -> {
state.copy(
isLoading = true
).also {
actions.send(
Action.ClearEmoji(
context = event.context,
target = event.target
)
)
}
}
is Event.OnImagePickedFromGallery -> {
state.copy(
isUploading = true,
isLoading = false,
).also {
actions.send(
Action.SetImageIcon(
context = event.context,
path = event.path
)
)
}
}
is Event.OnCompleted -> state.copy(
isLoading = false,
isCompleted = true,
error = null
)
is Event.Failure -> state.copy(
isLoading = false,
isCompleted = false,
error = event.error.toString()
)
}
}
}

View file

@ -1,25 +0,0 @@
package com.anytypeio.anytype.presentation.editor.picker
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.icon.SetDocumentEmojiIcon
import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon
import com.anytypeio.anytype.presentation.editor.editor.DetailModificationManager
import com.anytypeio.anytype.presentation.util.Dispatcher
class DocumentIconActionMenuViewModelFactory(
private val setEmojiIcon: SetDocumentEmojiIcon,
private val setImageIcon: SetDocumentImageIcon,
private val dispatcher: Dispatcher<Payload>,
private val details: DetailModificationManager
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T = DocumentIconActionMenuViewModel(
setEmojiIcon = setEmojiIcon,
setImageIcon = setImageIcon,
dispatcher = dispatcher,
details = details
) as T
}

View file

@ -5,11 +5,10 @@ import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.analytics.base.EventsDictionary
import com.anytypeio.anytype.analytics.base.sendEvent
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.icon.RemoveDocumentIcon
import com.anytypeio.anytype.domain.icon.SetDocumentEmojiIcon
import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon
import com.anytypeio.anytype.domain.icon.RemoveIcon
import com.anytypeio.anytype.domain.icon.SetEmojiIcon
import com.anytypeio.anytype.domain.icon.SetImageIcon
import com.anytypeio.anytype.emojifier.data.Emoji
import com.anytypeio.anytype.emojifier.data.EmojiProvider
import com.anytypeio.anytype.emojifier.suggest.EmojiSuggester
@ -17,15 +16,21 @@ import com.anytypeio.anytype.emojifier.suggest.model.EmojiSuggest
import com.anytypeio.anytype.presentation.editor.editor.Proxy
import com.anytypeio.anytype.presentation.util.Dispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
abstract class ObjectIconPickerBaseViewModel(
private val setEmojiIcon: SetDocumentEmojiIcon,
private val setImageIcon: SetDocumentImageIcon,
private val removeDocumentIcon: RemoveDocumentIcon,
class IconPickerViewModel<Iconable>(
private val setEmojiIcon: SetEmojiIcon<Iconable>,
private val setImageIcon: SetImageIcon<Iconable>,
private val removeDocumentIcon: RemoveIcon<Iconable>,
private val provider: EmojiProvider,
private val suggester: EmojiSuggester,
private val dispatcher: Dispatcher<Payload>,
@ -126,13 +131,22 @@ abstract class ObjectIconPickerBaseViewModel(
fun state(): StateFlow<ViewState> = state
fun onEmojiClicked(unicode: String, target: Id, context: Id) {
fun onEmojiClicked(unicode: String, iconable: Iconable) {
setEmoji(iconable, unicode)
}
fun onRandomEmoji(iconable: Iconable) {
setEmoji(iconable, provider.emojis.random().random())
}
private fun setEmoji(
iconable: Iconable, emojiUnicode: String
) {
viewModelScope.launch {
setEmojiIcon(
params = SetDocumentEmojiIcon.Params(
emoji = unicode,
target = target,
context = context
params = SetEmojiIcon.Params(
emoji = emojiUnicode,
target = iconable
)
).process(
failure = { Timber.e(it, "Error while setting emoji") },
@ -148,28 +162,10 @@ abstract class ObjectIconPickerBaseViewModel(
}
}
fun onRandomEmoji(ctx: Id, target: Id) {
fun onRemoveClicked(iconable: Iconable) {
viewModelScope.launch {
setEmojiIcon(
params = SetDocumentEmojiIcon.Params(
emoji = provider.emojis.random().random(),
target = target,
context = ctx
)
).process(
failure = { Timber.e(it, "Error while setting emoji") },
success = { payload ->
if (payload.events.isNotEmpty()) dispatcher.send(payload)
state.value = ViewState.Exit
}
)
}
}
fun onRemoveClicked(ctx: Id) {
viewModelScope.launch {
removeDocumentIcon(RemoveDocumentIcon.Params(ctx = ctx)).process(
failure = { Timber.e(it, "Error while setting emoji") },
removeDocumentIcon(iconable).process(
failure = { Timber.e(it, "Error while removing icon") },
success = { payload ->
sendEvent(
analytics = analytics,
@ -182,20 +178,22 @@ abstract class ObjectIconPickerBaseViewModel(
}
}
fun onPickedFromDevice(ctx: Id, path: String) {
fun onPickedFromDevice(iconable: Iconable, path: String) {
viewModelScope.launch {
state.value = ViewState.Loading
setImageIcon(
SetDocumentImageIcon.Params(
context = ctx,
SetImageIcon.Params(
target = iconable,
path = path
)
).process(
failure = {
Timber.e("Error while setting image icon").also { state.value = ViewState.Init }
Timber.e("Error while setting image icon")
state.value = ViewState.Init
},
success = { (payload, _) ->
dispatcher.send(payload).also { state.value = ViewState.Exit }
dispatcher.send(payload)
state.value = ViewState.Exit
}
)
}
@ -215,40 +213,4 @@ abstract class ObjectIconPickerBaseViewModel(
companion object {
const val DEBOUNCE_DURATION = 300L
}
}
class ObjectIconPickerViewModel(
setEmojiIcon: SetDocumentEmojiIcon,
setImageIcon: SetDocumentImageIcon,
removeDocumentIcon: RemoveDocumentIcon,
provider: EmojiProvider,
suggester: EmojiSuggester,
dispatcher: Dispatcher<Payload>,
analytics: Analytics
) : ObjectIconPickerBaseViewModel(
setEmojiIcon = setEmojiIcon,
setImageIcon = setImageIcon,
removeDocumentIcon = removeDocumentIcon,
provider = provider,
suggester = suggester,
dispatcher = dispatcher,
analytics = analytics
)
class ObjectSetIconPickerViewModel(
setEmojiIcon: SetDocumentEmojiIcon,
setImageIcon: SetDocumentImageIcon,
removeDocumentIcon: RemoveDocumentIcon,
provider: EmojiProvider,
suggester: EmojiSuggester,
dispatcher: Dispatcher<Payload>,
analytics: Analytics
) : ObjectIconPickerBaseViewModel(
setEmojiIcon = setEmojiIcon,
setImageIcon = setImageIcon,
removeDocumentIcon = removeDocumentIcon,
provider = provider,
suggester = suggester,
dispatcher = dispatcher,
analytics = analytics
)
}

View file

@ -23,7 +23,7 @@ class ObjectIconPickerViewModelFactory(
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ObjectIconPickerViewModel(
return IconPickerViewModel(
setEmojiIcon = setEmojiIcon,
setImageIcon = setImageIcon,
removeDocumentIcon = removeDocumentIcon,
@ -47,7 +47,7 @@ class ObjectSetIconPickerViewModelFactory(
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ObjectSetIconPickerViewModel(
return IconPickerViewModel(
setEmojiIcon = setEmojiIcon,
setImageIcon = setImageIcon,
removeDocumentIcon = removeDocumentIcon,

View file

@ -0,0 +1,36 @@
package com.anytypeio.anytype.presentation.editor.picker
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.icon.RemoveTextBlockIcon
import com.anytypeio.anytype.domain.icon.SetTextBlockEmoji
import com.anytypeio.anytype.domain.icon.SetTextBlockImage
import com.anytypeio.anytype.emojifier.data.EmojiProvider
import com.anytypeio.anytype.emojifier.suggest.EmojiSuggester
import com.anytypeio.anytype.presentation.util.Dispatcher
class TextBlockIconPickerViewModelFactory(
private val setEmojiIcon: SetTextBlockEmoji,
private val setImageIcon: SetTextBlockImage,
private val removeDocumentIcon: RemoveTextBlockIcon,
private val emojiSuggester: EmojiSuggester,
private val emojiProvider: EmojiProvider,
private val dispatcher: Dispatcher<Payload>,
private val analytics: Analytics
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return IconPickerViewModel(
setEmojiIcon = setEmojiIcon,
setImageIcon = setImageIcon,
removeDocumentIcon = removeDocumentIcon,
suggester = emojiSuggester,
provider = emojiProvider,
dispatcher = dispatcher,
analytics = analytics
) as T
}
}