mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-10 18:10:44 +09:00
Feature/move to (#840)
This commit is contained in:
parent
479d12c09b
commit
080a310540
16 changed files with 713 additions and 10 deletions
|
@ -5,6 +5,7 @@
|
|||
### New features 🚀
|
||||
|
||||
* Link to existing object (#770)
|
||||
* Move-to from one document to other document (#770)
|
||||
|
||||
### Design & UX 🔳
|
||||
|
||||
|
|
|
@ -156,6 +156,13 @@ class ComponentManager(private val main: MainComponent) {
|
|||
.build()
|
||||
}
|
||||
|
||||
val moveToComponent = Component {
|
||||
main
|
||||
.moveToBuilder()
|
||||
.module(MoveToModule)
|
||||
.build()
|
||||
}
|
||||
|
||||
val pageSearchComponent = Component {
|
||||
main.pageSearchComponentBuilder()
|
||||
.pageSearchModule(PageSearchModule)
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package com.agileburo.anytype.di.feature
|
||||
|
||||
import com.agileburo.anytype.core_utils.di.scope.PerScreen
|
||||
import com.agileburo.anytype.domain.block.interactor.Move
|
||||
import com.agileburo.anytype.domain.block.repo.BlockRepository
|
||||
import com.agileburo.anytype.domain.config.GetConfig
|
||||
import com.agileburo.anytype.domain.misc.UrlBuilder
|
||||
import com.agileburo.anytype.domain.page.navigation.GetPageInfoWithLinks
|
||||
import com.agileburo.anytype.presentation.moving.MoveToViewModelFactory
|
||||
import com.agileburo.anytype.ui.moving.MoveToFragment
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.Subcomponent
|
||||
|
||||
@Subcomponent(
|
||||
modules = [MoveToModule::class]
|
||||
)
|
||||
@PerScreen
|
||||
interface MoveToSubComponent {
|
||||
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
fun module(module: MoveToModule): Builder
|
||||
fun build(): MoveToSubComponent
|
||||
}
|
||||
|
||||
fun inject(fragment: MoveToFragment)
|
||||
}
|
||||
|
||||
@Module
|
||||
object MoveToModule {
|
||||
|
||||
@JvmStatic
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideGetPageInfoWithLinks(
|
||||
repo: BlockRepository
|
||||
): GetPageInfoWithLinks = GetPageInfoWithLinks(repo = repo)
|
||||
|
||||
@JvmStatic
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideGetConfigUseCase(
|
||||
repo: BlockRepository
|
||||
): GetConfig = GetConfig(repo)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideMoveUseCase(
|
||||
repo: BlockRepository
|
||||
): Move = Move(
|
||||
repo = repo
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideMoveToViewModelFactory(
|
||||
urlBuilder: UrlBuilder,
|
||||
getPageInfoWithLinks: GetPageInfoWithLinks,
|
||||
getConfig: GetConfig,
|
||||
move: Move
|
||||
): MoveToViewModelFactory = MoveToViewModelFactory(
|
||||
urlBuilder = urlBuilder,
|
||||
getPageInfoWithLinks = getPageInfoWithLinks,
|
||||
getConfig = getConfig,
|
||||
move = move
|
||||
)
|
||||
}
|
|
@ -43,5 +43,6 @@ interface MainComponent {
|
|||
fun debugSettingsBuilder(): DebugSettingsSubComponent.Builder
|
||||
fun navigationComponentBuilder(): PageNavigationSubComponent.Builder
|
||||
fun linkToObjectBuilder(): LinkToObjectSubComponent.Builder
|
||||
fun moveToBuilder(): MoveToSubComponent.Builder
|
||||
fun pageSearchComponentBuilder(): PageSearchSubComponent.Builder
|
||||
}
|
|
@ -10,6 +10,7 @@ import com.agileburo.anytype.presentation.settings.EditorSettings
|
|||
import com.agileburo.anytype.ui.auth.Keys
|
||||
import com.agileburo.anytype.ui.auth.account.CreateAccountFragment.Companion.ARGS_CODE
|
||||
import com.agileburo.anytype.ui.linking.LinkToObjectFragment
|
||||
import com.agileburo.anytype.ui.moving.MoveToFragment
|
||||
import com.agileburo.anytype.ui.navigation.PageNavigationFragment
|
||||
import com.agileburo.anytype.ui.page.PageFragment
|
||||
|
||||
|
@ -158,6 +159,14 @@ class Navigator : AppNavigation {
|
|||
navController?.navigate(R.id.linkToFragment, bundle)
|
||||
}
|
||||
|
||||
override fun openMoveTo(targets: List<String>, context: String) {
|
||||
val bundle = bundleOf(
|
||||
MoveToFragment.CONTEXT_ID_KEY to context,
|
||||
MoveToFragment.TARGETS_ID_KEY to ArrayList(targets)
|
||||
)
|
||||
navController?.navigate(R.id.moveToFragment, bundle)
|
||||
}
|
||||
|
||||
override fun openPageSearch() {
|
||||
navController?.navigate(R.id.pageSearchFragment)
|
||||
}
|
||||
|
|
|
@ -55,6 +55,10 @@ abstract class NavigationFragment(
|
|||
command.context,
|
||||
command.replace
|
||||
)
|
||||
is Command.OpenMoveToScreen -> navigation.openMoveTo(
|
||||
targets = command.targets,
|
||||
context = command.context
|
||||
)
|
||||
is Command.ExitToDesktopAndOpenPage -> navigation.exitToDesktopAndOpenPage(command.pageId)
|
||||
is Command.OpenPageSearch -> navigation.openPageSearch()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
package com.agileburo.anytype.ui.moving
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.view.minusAssign
|
||||
import androidx.core.view.plusAssign
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.agileburo.anytype.R
|
||||
import com.agileburo.anytype.core_ui.features.navigation.FilterView
|
||||
import com.agileburo.anytype.core_ui.features.navigation.PageNavigationAdapter
|
||||
import com.agileburo.anytype.core_ui.layout.AppBarLayoutStateChangeListener
|
||||
import com.agileburo.anytype.core_ui.layout.State
|
||||
import com.agileburo.anytype.core_utils.ext.*
|
||||
import com.agileburo.anytype.core_utils.ui.ViewState
|
||||
import com.agileburo.anytype.di.common.componentManager
|
||||
import com.agileburo.anytype.emojifier.Emojifier
|
||||
import com.agileburo.anytype.presentation.moving.MoveToViewModel
|
||||
import com.agileburo.anytype.presentation.moving.MoveToViewModelFactory
|
||||
import com.agileburo.anytype.presentation.navigation.PageNavigationView
|
||||
import com.agileburo.anytype.ui.base.ViewStateFragment
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import kotlinx.android.synthetic.main.fragment_move_to.*
|
||||
import kotlinx.android.synthetic.main.view_move_to_bottom.*
|
||||
import kotlinx.android.synthetic.main.view_move_to_preview.*
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class MoveToFragment : ViewStateFragment<ViewState<PageNavigationView>>(R.layout.fragment_move_to) {
|
||||
|
||||
private val targets: List<String>
|
||||
get() = arguments?.getStringArrayList(TARGETS_ID_KEY)
|
||||
?: throw IllegalStateException("Missing target")
|
||||
|
||||
private val targetContext: String
|
||||
get() = arguments?.getString(CONTEXT_ID_KEY)
|
||||
?: throw IllegalStateException("Missing target")
|
||||
|
||||
private val vm by viewModels<MoveToViewModel> { factory }
|
||||
|
||||
@Inject
|
||||
lateinit var factory: MoveToViewModelFactory
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
vm.state.observe(viewLifecycleOwner, this)
|
||||
vm.navigation.observe(viewLifecycleOwner, navObserver)
|
||||
vm.onViewCreated()
|
||||
observeLinkingButtonState()
|
||||
}
|
||||
|
||||
private fun observeLinkingButtonState() {
|
||||
lifecycleScope.launch {
|
||||
vm.isMovingDisabled.collect { isDisabled ->
|
||||
if (isDisabled)
|
||||
disableMoving()
|
||||
else
|
||||
enableMoving()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun enableMoving() {
|
||||
btnMoveToSmall.isEnabled = true
|
||||
btnMoveTo.isEnabled = true
|
||||
btnMoveTo.alpha = 1f
|
||||
btnMoveToSmall.alpha = 1f
|
||||
}
|
||||
|
||||
private fun disableMoving() {
|
||||
btnMoveToSmall.isEnabled = false
|
||||
btnMoveTo.isEnabled = false
|
||||
btnMoveTo.alpha = 0.2f
|
||||
btnMoveToSmall.alpha = 0.2f
|
||||
}
|
||||
|
||||
override fun render(state: ViewState<PageNavigationView>) {
|
||||
when (state) {
|
||||
ViewState.Init -> {
|
||||
appBarLayout.addOnOffsetChangedListener(
|
||||
object : AppBarLayoutStateChangeListener() {
|
||||
override fun onStateChanged(state: State) {
|
||||
if (state == State.COLLAPSED) {
|
||||
pagePreviewSmall.visible()
|
||||
} else {
|
||||
pagePreviewSmall.gone()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
viewPager.adapter = PageNavigationAdapter(
|
||||
onClick = { vm.onLinkClicked(it, targetContext) }
|
||||
) { links ->
|
||||
filterContainer.plusAssign(
|
||||
FilterView(requireContext()).apply {
|
||||
cancelClicked = { closeFilterView() }
|
||||
pageClicked = {
|
||||
closeFilterView()
|
||||
vm.onLinkClicked(it, targetContext)
|
||||
}
|
||||
bind(links)
|
||||
}
|
||||
)
|
||||
}
|
||||
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
|
||||
when (position) {
|
||||
POSITION_FROM -> tab.text = getString(R.string.page_nav_link_from)
|
||||
POSITION_TO -> tab.text = getString(R.string.page_nav_links_to)
|
||||
}
|
||||
}.attach()
|
||||
btnMoveTo.setOnClickListener {
|
||||
vm.onMoveToClicked(
|
||||
context = targetContext,
|
||||
targets = targets
|
||||
)
|
||||
}
|
||||
btnMoveToSmall.setOnClickListener {
|
||||
vm.onMoveToClicked(
|
||||
context = targetContext,
|
||||
targets = targets
|
||||
)
|
||||
}
|
||||
vm.onStart(targetContext)
|
||||
}
|
||||
ViewState.Loading -> {
|
||||
progressBar.visible()
|
||||
}
|
||||
is ViewState.Success -> {
|
||||
progressBar.invisible()
|
||||
state.data.title.let { title ->
|
||||
pageTitleSmall.text =
|
||||
if (title.isEmpty()) getString(R.string.untitled) else title
|
||||
tvPageTitle.text =
|
||||
if (title.isEmpty()) getString(R.string.untitled) else title
|
||||
}
|
||||
if (state.data.subtitle.isNotEmpty()) {
|
||||
tvPageSubtitle.visible()
|
||||
tvPageSubtitle.text = state.data.subtitle
|
||||
} else {
|
||||
tvPageSubtitle.text = null
|
||||
tvPageSubtitle.gone()
|
||||
}
|
||||
|
||||
imageIcon.setImageDrawable(null)
|
||||
avatarSmall.setImageDrawable(null)
|
||||
emojiIcon.setImageDrawable(null)
|
||||
|
||||
state.data.image?.let { url ->
|
||||
Glide
|
||||
.with(imageIcon)
|
||||
.load(url)
|
||||
.centerInside()
|
||||
.circleCrop()
|
||||
.into(imageIcon)
|
||||
Glide
|
||||
.with(avatarSmall)
|
||||
.load(url)
|
||||
.centerInside()
|
||||
.circleCrop()
|
||||
.into(avatarSmall)
|
||||
}
|
||||
|
||||
state.data.emoji?.let { emoji ->
|
||||
try {
|
||||
Glide
|
||||
.with(emojiIcon)
|
||||
.load(Emojifier.uri(emoji))
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.into(emojiIcon)
|
||||
Glide
|
||||
.with(avatarSmall)
|
||||
.load(Emojifier.uri(emoji))
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.into(avatarSmall)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error while setting emoji icon for: $emoji")
|
||||
}
|
||||
}
|
||||
|
||||
(viewPager.adapter as? PageNavigationAdapter)?.setPageLinks(
|
||||
inbound = state.data.inbound,
|
||||
outbound = state.data.outbound
|
||||
)
|
||||
}
|
||||
is ViewState.Error -> {
|
||||
progressBar.invisible()
|
||||
coordinatorLayout.showSnackbar(state.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun closeFilterView() {
|
||||
filterContainer.getChildAt(0)?.let { filterContainer.minusAssign(it) }
|
||||
requireActivity().hideSoftInput()
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
componentManager().moveToComponent.get().inject(this)
|
||||
}
|
||||
|
||||
override fun releaseDependencies() {
|
||||
componentManager().moveToComponent.release()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TARGETS_ID_KEY = "arg.link_to.targets"
|
||||
const val CONTEXT_ID_KEY = "arg.link_to.context"
|
||||
const val POSITION_FROM = 0
|
||||
const val POSITION_TO = 1
|
||||
}
|
||||
}
|
80
app/src/main/res/layout/fragment_move_to.xml
Normal file
80
app/src/main/res/layout/fragment_move_to.xml
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/coordinatorLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#9BB0B7">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#9BB0B7"
|
||||
android:elevation="0dp"
|
||||
app:elevation="0dp">
|
||||
|
||||
<include
|
||||
android:id="@+id/pagePreviewContainer"
|
||||
layout="@layout/view_move_to_preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_8"
|
||||
android:layout_marginTop="@dimen/dp_8"
|
||||
android:layout_marginEnd="@dimen/dp_8"
|
||||
app:layout_scrollFlags="scroll|enterAlways" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/bg_tab_layout">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabLayout"
|
||||
style="@style/PageNavigationTab"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@color/white"
|
||||
app:tabBackground="@drawable/custom_tab_indicator"
|
||||
app:tabTextAppearance="@style/TabTextStyle" />
|
||||
</FrameLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/viewPager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@color/white"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
<include
|
||||
android:id="@+id/pagePreviewSmall"
|
||||
layout="@layout/view_move_to_bottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginStart="@dimen/dp_16"
|
||||
android:layout_marginEnd="@dimen/dp_16"
|
||||
android:layout_marginBottom="16dp" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/filterContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminateTint="#FFB522" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
61
app/src/main/res/layout/view_move_to_bottom.xml
Normal file
61
app/src/main/res/layout/view_move_to_bottom.xml
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true"
|
||||
android:background="@drawable/bg_page_nav_filter"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatarSmall"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="18dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/pageTitleSmall"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/pageTitleSmall"
|
||||
tools:srcCompat="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pageTitleSmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/inter_regular"
|
||||
android:maxLines="1"
|
||||
android:textColor="#2C2B27"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/btnMoveToSmall"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btnMoveToSmall"
|
||||
app:layout_constraintStart_toEndOf="@+id/avatarSmall"
|
||||
app:layout_constraintTop_toTopOf="@+id/btnMoveToSmall"
|
||||
tools:text="The Iraqi-British Zaha Hadid became famous for her intensely futuristic architecture " />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnMoveToSmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@drawable/rounded_button_nav_open"
|
||||
android:drawablePadding="0dp"
|
||||
android:fontFamily="@font/inter_medium"
|
||||
android:gravity="center"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:text="@string/move_into"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
100
app/src/main/res/layout/view_move_to_preview.xml
Normal file
100
app/src/main/res/layout/view_move_to_preview.xml
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_page_preview_layout">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnMoveTo"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="@drawable/rounded_button_nav_open"
|
||||
android:drawablePadding="0dp"
|
||||
android:fontFamily="@font/inter_medium"
|
||||
android:gravity="center"
|
||||
android:paddingTop="7dp"
|
||||
android:paddingBottom="7dp"
|
||||
android:text="@string/move_into_this_page"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tvPageSubtitle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPageSubtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/inter_regular"
|
||||
android:lineSpacingMultiplier="1.21"
|
||||
android:maxLines="3"
|
||||
android:textColor="#6C6A5F"
|
||||
android:textSize="15sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tvPageTitle"
|
||||
tools:text="The Iraqi-British Zaha Hadid became famous for her intensely futuristic architecture characterized by curving faсt"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPageTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="11dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:fontFamily="@font/inter_medium"
|
||||
android:textColor="#2C2B27"
|
||||
android:textSize="17sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/documentIconContainer"
|
||||
tools:text="Zaha Hadid" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/documentIconContainer"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="18dp"
|
||||
android:background="@drawable/rectangle_default_page_logo_background"
|
||||
android:transitionName="@string/logo_transition"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/emojiIcon"
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageIcon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/btnMoveTo"
|
||||
app:layout_constraintStart_toStartOf="@+id/btnMoveTo"
|
||||
app:layout_constraintTop_toTopOf="@+id/btnMoveTo"
|
||||
app:srcCompat="@drawable/ic_open_as_page" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -371,12 +371,12 @@
|
|||
android:id="@+id/linkToFragment"
|
||||
android:name="com.agileburo.anytype.ui.linking.LinkToObjectFragment"
|
||||
android:label="LinkToFragment"
|
||||
tools:layout="@layout/fragment_link_to_object">
|
||||
<argument
|
||||
android:name="ID_KEY"
|
||||
app:argType="string"
|
||||
android:defaultValue='""' />
|
||||
</fragment>
|
||||
tools:layout="@layout/fragment_link_to_object" />
|
||||
<fragment
|
||||
android:id="@+id/moveToFragment"
|
||||
android:name="com.agileburo.anytype.ui.moving.MoveToFragment"
|
||||
android:label="MoveToFragment"
|
||||
tools:layout="@layout/fragment_move_to" />
|
||||
<fragment
|
||||
android:id="@+id/pageSearchFragment"
|
||||
android:name="com.agileburo.anytype.ui.search.PageSearchFragment"
|
||||
|
|
|
@ -132,5 +132,7 @@ Do the computation of an expensive paragraph of text on a background thread:
|
|||
<string name="search_empty_pages">You have no pages to search for.</string>
|
||||
<string name="link_to_this_page">Link to this page</string>
|
||||
<string name="link">Link</string>
|
||||
<string name="move_into_this_page">Move into this page</string>
|
||||
<string name="move_into">Move into</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
package com.agileburo.anytype.presentation.moving
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.agileburo.anytype.core_utils.common.EventWrapper
|
||||
import com.agileburo.anytype.core_utils.ext.timber
|
||||
import com.agileburo.anytype.core_utils.ui.ViewState
|
||||
import com.agileburo.anytype.core_utils.ui.ViewStateViewModel
|
||||
import com.agileburo.anytype.domain.block.interactor.Move
|
||||
import com.agileburo.anytype.domain.block.model.Position
|
||||
import com.agileburo.anytype.domain.common.Id
|
||||
import com.agileburo.anytype.domain.config.GetConfig
|
||||
import com.agileburo.anytype.domain.misc.UrlBuilder
|
||||
import com.agileburo.anytype.domain.page.navigation.GetPageInfoWithLinks
|
||||
import com.agileburo.anytype.presentation.mapper.toEmojiView
|
||||
import com.agileburo.anytype.presentation.mapper.toImageView
|
||||
import com.agileburo.anytype.presentation.mapper.toView
|
||||
import com.agileburo.anytype.presentation.navigation.AppNavigation
|
||||
import com.agileburo.anytype.presentation.navigation.PageNavigationView
|
||||
import com.agileburo.anytype.presentation.navigation.SupportNavigation
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
class MoveToViewModel(
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val getPageInfoWithLinks: GetPageInfoWithLinks,
|
||||
private val getConfig: GetConfig,
|
||||
private val move: Move
|
||||
) : ViewStateViewModel<ViewState<PageNavigationView>>(),
|
||||
SupportNavigation<EventWrapper<AppNavigation.Command>> {
|
||||
|
||||
private var pageId: String = ""
|
||||
private var home: String = ""
|
||||
|
||||
val isMovingDisabled: MutableStateFlow<Boolean> = MutableStateFlow(true)
|
||||
|
||||
override val navigation: MutableLiveData<EventWrapper<AppNavigation.Command>> =
|
||||
MutableLiveData()
|
||||
|
||||
fun onViewCreated() {
|
||||
stateData.postValue(ViewState.Init)
|
||||
}
|
||||
|
||||
fun onStart(initialTarget: Id) {
|
||||
viewModelScope.launch {
|
||||
getConfig(Unit).proceed(
|
||||
failure = { Timber.e(it, "Error while getting config") },
|
||||
success = { config ->
|
||||
home = config.home
|
||||
proceedWithGettingDocumentLinks(initialTarget)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun proceedWithGettingDocumentLinks(target: String) {
|
||||
stateData.postValue(ViewState.Loading)
|
||||
viewModelScope.launch {
|
||||
getPageInfoWithLinks.invoke(GetPageInfoWithLinks.Params(pageId = target)).proceed(
|
||||
failure = { error ->
|
||||
error.timber()
|
||||
stateData.postValue(ViewState.Error(error.message ?: "Unknown error"))
|
||||
},
|
||||
success = { response ->
|
||||
with(response.pageInfoWithLinks) {
|
||||
pageId = this.id
|
||||
stateData.postValue(
|
||||
ViewState.Success(
|
||||
PageNavigationView(
|
||||
title = pageInfo.fields.name.orEmpty(),
|
||||
subtitle = pageInfo.snippet.orEmpty(),
|
||||
image = pageInfo.fields.toImageView(urlBuilder),
|
||||
emoji = pageInfo.fields.toEmojiView(),
|
||||
inbound = links.inbound.map { it.toView(urlBuilder) },
|
||||
outbound = links.outbound.map { it.toView(urlBuilder) }
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onLinkClicked(
|
||||
target: Id,
|
||||
context: Id
|
||||
) {
|
||||
isMovingDisabled.value = (target == context || target == home)
|
||||
proceedWithGettingDocumentLinks(target)
|
||||
}
|
||||
|
||||
fun onMoveToClicked(
|
||||
context: Id,
|
||||
targets: List<Id>
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
move(
|
||||
Move.Params(
|
||||
context = context,
|
||||
blockIds = targets,
|
||||
position = Position.INNER,
|
||||
targetId = pageId,
|
||||
targetContext = pageId
|
||||
)
|
||||
).proceed(
|
||||
failure = { Timber.e(it, "Error while moving blocks") },
|
||||
success = { navigate(EventWrapper(AppNavigation.Command.Exit)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.agileburo.anytype.presentation.moving
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.agileburo.anytype.domain.block.interactor.Move
|
||||
import com.agileburo.anytype.domain.config.GetConfig
|
||||
import com.agileburo.anytype.domain.misc.UrlBuilder
|
||||
import com.agileburo.anytype.domain.page.navigation.GetPageInfoWithLinks
|
||||
|
||||
class MoveToViewModelFactory(
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val getPageInfoWithLinks: GetPageInfoWithLinks,
|
||||
private val getConfig: GetConfig,
|
||||
private val move: Move
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return MoveToViewModel(
|
||||
urlBuilder = urlBuilder,
|
||||
getPageInfoWithLinks = getPageInfoWithLinks,
|
||||
getConfig = getConfig,
|
||||
move = move
|
||||
) as T
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ interface AppNavigation {
|
|||
fun openDebugSettings()
|
||||
fun openPageNavigation(target: String)
|
||||
fun openLinkTo(target: String, context: String, replace: Boolean)
|
||||
fun openMoveTo(targets: List<String>, context: String)
|
||||
fun openPageSearch()
|
||||
fun exitToDesktopAndOpenPage(pageId: String)
|
||||
fun exitToInvitationCodeScreen()
|
||||
|
@ -71,12 +72,18 @@ interface AppNavigation {
|
|||
object OpenDebugSettingsScreen: Command()
|
||||
|
||||
data class OpenPageNavigationScreen(val target: String) : Command()
|
||||
|
||||
data class OpenLinkToScreen(
|
||||
val context: String,
|
||||
val target: String,
|
||||
val replace: Boolean
|
||||
) : Command()
|
||||
|
||||
data class OpenMoveToScreen(
|
||||
val context: String,
|
||||
val targets: List<String>
|
||||
) : Command()
|
||||
|
||||
data class ExitToDesktopAndOpenPage(val pageId: String) : Command()
|
||||
object OpenPageSearch: Command()
|
||||
}
|
||||
|
|
|
@ -1057,7 +1057,16 @@ class PageViewModel(
|
|||
_error.value = "Rename not implemented"
|
||||
}
|
||||
ActionItemType.MoveTo -> {
|
||||
_error.value = "Move To not implemented"
|
||||
onExitActionMode()
|
||||
dispatch(Command.PopBackStack)
|
||||
navigate(
|
||||
EventWrapper(
|
||||
AppNavigation.Command.OpenMoveToScreen(
|
||||
context = context,
|
||||
targets = listOf(id)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
ActionItemType.Style -> {
|
||||
val textSelection = orchestrator.stores.textSelection.current()
|
||||
|
@ -1414,9 +1423,7 @@ class PageViewModel(
|
|||
private fun onExitActionMode() {
|
||||
mode = EditorMode.EDITING
|
||||
controlPanelInteractor.onEvent(ControlPanelMachine.Event.ReadMode.OnExit)
|
||||
viewModelScope.launch {
|
||||
refresh()
|
||||
}
|
||||
viewModelScope.launch { refresh() }
|
||||
}
|
||||
|
||||
fun onMultiSelectModeDeleteClicked() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue