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

Dashboard | Feature | Bulk delete & bulk put-back in Bin tab (#1858)

This commit is contained in:
Evgenii Kozlov 2021-10-21 14:22:58 +03:00 committed by GitHub
parent 80795f5ff7
commit d75d6e9c06
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1052 additions and 73 deletions

View file

@ -16,6 +16,8 @@ import com.anytypeio.anytype.domain.event.interactor.EventChannel
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.icon.DocumentEmojiIconProvider
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.DeleteObjects
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.page.CreatePage
import com.anytypeio.anytype.presentation.dashboard.HomeDashboardEventConverter
import com.anytypeio.anytype.presentation.dashboard.HomeDashboardViewModelFactory
@ -59,7 +61,9 @@ object HomeDashboardModule {
getDebugSettings: GetDebugSettings,
analytics: Analytics,
searchObjects: SearchObjects,
urlBuilder: UrlBuilder
urlBuilder: UrlBuilder,
setObjectListIsArchived: SetObjectListIsArchived,
deleteObjects: DeleteObjects
): HomeDashboardViewModelFactory = HomeDashboardViewModelFactory(
getProfile = getProfile,
openDashboard = openDashboard,
@ -72,7 +76,9 @@ object HomeDashboardModule {
getDebugSettings = getDebugSettings,
searchObjects = searchObjects,
analytics = analytics,
urlBuilder = urlBuilder
urlBuilder = urlBuilder,
setObjectListIsArchived = setObjectListIsArchived,
deleteObjects = deleteObjects
)
@JvmStatic
@ -171,4 +177,22 @@ object HomeDashboardModule {
) : SearchObjects = SearchObjects(
repo = repo
)
@JvmStatic
@Provides
@PerScreen
fun deleteObjects(
repo: BlockRepository
) : DeleteObjects = DeleteObjects(
repo = repo
)
@JvmStatic
@Provides
@PerScreen
fun setObjectListIsArchived(
repo: BlockRepository
) : SetObjectListIsArchived = SetObjectListIsArchived(
repo = repo
)
}

View file

@ -195,6 +195,8 @@ class DashboardAdapter(
}
}
}
holder.bindSelection(data[position].isSelected)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
@ -250,6 +252,9 @@ class DashboardAdapter(
if (payload.isLoadingChanged) {
bindLoading(item.isLoading)
}
if (payload.isSelectionChanged) {
bindSelection(item.isSelected)
}
}
}
@ -266,18 +271,29 @@ class DashboardAdapter(
if (payload.isLoadingChanged) {
bindLoading(item.isLoading)
}
if (payload.isSelectionChanged) {
bindSelection(item.isSelected)
}
}
}
sealed class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun bindSelection(isSelected: Boolean)
class ArchiveHolder(itemView: View) : ViewHolder(itemView) {
private val selection = itemView.findViewById<ImageView>(R.id.ivSelection)
fun bindTitle(title: String) {
if (title.isNotEmpty()) {
itemView.archiveTitle.text = title
}
}
override fun bindSelection(isSelected: Boolean) {
if (isSelected) selection.visible() else selection.invisible()
}
}
class DocumentHolder(itemView: View) : ViewHolder(itemView) {
@ -285,6 +301,7 @@ class DashboardAdapter(
private val tvTitle = itemView.title
private val tvSubtitle = itemView.typeTitle
private val shimmer = itemView.shimmer
private val selection = itemView.findViewById<ImageView>(R.id.ivSelection)
fun bindTitle(title: String?) {
if (title.isNullOrEmpty())
@ -312,6 +329,10 @@ class DashboardAdapter(
tvTitle.visible()
}
}
override fun bindSelection(isSelected: Boolean) {
if (isSelected) selection.visible() else selection.invisible()
}
}
class DocumentWithoutIconViewHolder(parent: ViewGroup) : ViewHolder(
@ -325,6 +346,7 @@ class DashboardAdapter(
private val tvTitle = itemView.findViewById<TextView>(R.id.tvDocTitle)
private val tvSubtitle = itemView.findViewById<TextView>(R.id.tvDocTypeName)
private val shimmer = itemView.findViewById<ShimmerFrameLayout>(R.id.shimmer)
private val selection = itemView.findViewById<ImageView>(R.id.ivSelection)
fun bindTitle(title: String?) {
tvTitle.text = title
@ -347,6 +369,10 @@ class DashboardAdapter(
tvSubtitle.visible()
}
}
override fun bindSelection(isSelected: Boolean) {
if (isSelected) selection.visible() else selection.invisible()
}
}
class DocumentTaskViewHolder(parent: ViewGroup) : ViewHolder(
@ -361,6 +387,7 @@ class DashboardAdapter(
private val tvSubtitle = itemView.findViewById<TextView>(R.id.tvDocTypeName)
private val checkbox = itemView.findViewById<ImageView>(R.id.ivCheckbox)
private val shimmer = itemView.findViewById<ShimmerFrameLayout>(R.id.shimmer)
private val selection = itemView.findViewById<ImageView>(R.id.ivSelection)
fun bindTitle(title: String?) {
tvTitle.text = title
@ -391,12 +418,17 @@ class DashboardAdapter(
checkbox.setImageResource(R.drawable.ic_dashboard_task_checkbox_not_checked)
}
}
override fun bindSelection(isSelected: Boolean) {
if (isSelected) selection.visible() else selection.invisible()
}
}
class ObjectSetHolder(itemView: View) : ViewHolder(itemView) {
private val tvTitle = itemView.title
private val shimmer = itemView.shimmer
private val selection = itemView.findViewById<ImageView>(R.id.ivSelection)
fun bindLoading(isLoading: Boolean) {
if (isLoading) {
@ -420,12 +452,17 @@ class DashboardAdapter(
fun bindIcon(icon: ObjectIcon) {
itemView.iconWidget.bind(icon)
}
override fun bindSelection(isSelected: Boolean) {
if (isSelected) selection.visible() else selection.invisible()
}
}
class ObjectSetWithoutIconHolder(itemView: View) : ViewHolder(itemView) {
private val tvTitle = itemView.findViewById<TextView>(R.id.tvSetTitle)
private val shimmer = itemView.findViewById<ShimmerFrameLayout>(R.id.shimmer)
private val selection = itemView.findViewById<ImageView>(R.id.ivSelection)
fun bindLoading(isLoading: Boolean) {
if (isLoading) {
@ -445,6 +482,10 @@ class DashboardAdapter(
else
tvTitle.text = title
}
override fun bindSelection(isSelected: Boolean) {
if (isSelected) selection.visible() else selection.invisible()
}
}
}

View file

@ -2,13 +2,17 @@ package com.anytypeio.anytype.ui.dashboard
import android.os.Bundle
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.LinearInterpolator
import androidx.constraintlayout.widget.ConstraintSet
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.transition.ChangeBounds
import androidx.transition.TransitionManager
import androidx.transition.TransitionSet
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.reactive.clicks
import com.anytypeio.anytype.core_utils.ext.subscribe
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ext.visible
import com.anytypeio.anytype.core_utils.ext.*
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.presentation.dashboard.DashboardView
@ -133,6 +137,51 @@ class DashboardFragment : ViewStateFragment<State>(R.layout.fragment_dashboard)
lifecycleScope.subscribe(vm.recent) { dashboardRecentAdapter.update(it) }
lifecycleScope.subscribe(vm.sets) { dashboardSetsAdapter.update(it) }
lifecycleScope.subscribe(vm.archived) { dashboardArchiveAdapter.update(it) }
lifecycleScope.subscribe(vm.count) { tvSelectedCount.text = "$it object selected" }
lifecycleScope.subscribe(vm.mode) { mode ->
when(mode) {
HomeDashboardViewModel.Mode.DEFAULT -> {
selectionTopToolbar.invisible()
tabsLayout.visible()
val set = ConstraintSet().apply {
clone(dashboardRoot)
clear(R.id.selectionBottomToolbar, ConstraintSet.BOTTOM)
connect(
R.id.selectionBottomToolbar,
ConstraintSet.TOP,
R.id.dashboardRoot,
ConstraintSet.BOTTOM
)
}
val transitionSet = TransitionSet().apply {
addTransition(ChangeBounds())
duration = 100
}
TransitionManager.beginDelayedTransition(dashboardRoot, transitionSet)
set.applyTo(dashboardRoot)
}
HomeDashboardViewModel.Mode.SELECTION -> {
tabsLayout.invisible()
selectionTopToolbar.visible()
val set = ConstraintSet().apply {
clone(dashboardRoot)
clear(R.id.selectionBottomToolbar, ConstraintSet.TOP)
connect(
R.id.selectionBottomToolbar,
ConstraintSet.BOTTOM,
R.id.dashboardRoot,
ConstraintSet.BOTTOM
)
}
val transitionSet = TransitionSet().apply {
addTransition(ChangeBounds())
duration = 100
}
TransitionManager.beginDelayedTransition(dashboardRoot, transitionSet)
set.applyTo(dashboardRoot)
}
}
}
}
override fun onPause() {
@ -228,6 +277,26 @@ class DashboardFragment : ViewStateFragment<State>(R.layout.fragment_dashboard)
.clicks()
.onEach { vm.onAvatarClicked() }
.launchIn(lifecycleScope)
tvCancel
.clicks()
.onEach { vm.onCancelSelectionClicked() }
.launchIn(lifecycleScope)
tvSelectAll
.clicks()
.onEach { vm.onSelectAllClicked() }
.launchIn(lifecycleScope)
tvPutBack
.clicks()
.onEach { vm.onPutBackClicked() }
.launchIn(lifecycleScope)
tvDelete
.clicks()
.onEach { vm.onDeleteObjectsClicked() }
.launchIn(lifecycleScope)
}
override fun injectDependencies() {

View file

@ -64,6 +64,10 @@ class DesktopDiffUtil(
}
}
if (oldDoc.isSelected != newDoc.isSelected) {
changes.add(SELECTION_CHANGED)
}
return if (changes.isNotEmpty()) {
Payload(changes).also { Timber.d("Returning payload: $it") }
} else {
@ -76,6 +80,7 @@ class DesktopDiffUtil(
) {
val isLoadingChanged: Boolean = changes.contains(LOADING_STATE_CHANGED)
val isSelectionChanged: Boolean = changes.contains(SELECTION_CHANGED)
fun targetChanged() = changes.contains(TARGET_CHANGED)
fun titleChanged() = changes.contains(TITLE_CHANGED)
@ -89,5 +94,6 @@ class DesktopDiffUtil(
const val EMOJI_CHANGED = 4
const val IMAGE_CHANGED = 5
const val LOADING_STATE_CHANGED = 6
const val SELECTION_CHANGED = 7
}
}

View file

@ -107,22 +107,77 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnMarketplace">
<com.google.android.material.tabs.TabLayout
app:tabRippleColor="@null"
android:id="@+id/tabsLayout"
<FrameLayout
android:id="@+id/topToolbarContainer"
android:layout_width="match_parent"
android:layout_height="72dp"
android:paddingStart="10dp"
android:background="@null"
app:tabIndicator="@null"
app:tabMinWidth="10dp"
app:tabPaddingBottom="@dimen/dp_12"
app:tabPaddingEnd="10dp"
app:tabPaddingStart="10dp"
app:tabPaddingTop="@dimen/dp_12"
app:tabSelectedTextColor="@color/black"
app:tabTextAppearance="@style/DashboardTabTextStyle"
app:tabTextColor="#CC0066C3" />
android:layout_height="72dp">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabsLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
android:paddingStart="10dp"
app:tabIndicator="@null"
app:tabMinWidth="10dp"
app:tabPaddingBottom="@dimen/dp_12"
app:tabPaddingEnd="10dp"
app:tabPaddingStart="10dp"
app:tabPaddingTop="@dimen/dp_12"
app:tabRippleColor="@null"
app:tabSelectedTextColor="@color/black"
app:tabTextAppearance="@style/DashboardTabTextStyle"
app:tabTextColor="#CC0066C3" />
<LinearLayout
android:visibility="gone"
android:id="@+id/selectionTopToolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/tvSelectAll"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:fontFamily="@font/inter_regular"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/select_all"
android:textColor="@color/black"
android:background="?attr/selectableItemBackgroundBorderless"
android:textSize="17sp" />
<TextView
android:id="@+id/tvSelectedCount"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:fontFamily="@font/inter_semibold"
android:gravity="center"
android:maxLines="1"
android:singleLine="true"
android:textColor="@color/black"
android:textSize="17sp" />
<TextView
android:id="@+id/tvCancel"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:fontFamily="@font/inter_regular"
android:gravity="end|center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/cancel"
android:textColor="@color/black"
android:background="?attr/selectableItemBackgroundBorderless"
android:textSize="17sp" />
</LinearLayout>
</FrameLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/dashboardPager"
@ -132,4 +187,43 @@
</LinearLayout>
<LinearLayout
android:id="@+id/selectionBottomToolbar"
android:layout_width="0dp"
android:layout_height="@dimen/default_toolbar_height"
android:orientation="horizontal"
android:background="#99F5F5F5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:id="@+id/tvDelete"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:fontFamily="@font/inter_regular"
android:gravity="center"
android:paddingStart="20dp"
android:background="@drawable/default_ripple"
android:paddingEnd="20dp"
android:text="@string/delete"
android:textColor="@color/black"
android:textSize="17sp" />
<TextView
android:id="@+id/tvPutBack"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:fontFamily="@font/inter_regular"
android:gravity="center"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/put_back"
android:background="@drawable/default_ripple"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
</androidx.constraintlayout.motion.widget.MotionLayout>

View file

@ -71,4 +71,14 @@
</com.facebook.shimmer.ShimmerFrameLayout>
<ImageView
android:visibility="invisible"
android:paddingEnd="10dp"
android:paddingBottom="10dp"
android:layout_gravity="end|bottom"
android:id="@+id/ivSelection"
android:src="@drawable/ic_bin_selection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</androidx.cardview.widget.CardView>

View file

@ -43,4 +43,14 @@
</LinearLayout>
<ImageView
android:visibility="invisible"
android:paddingEnd="10dp"
android:paddingBottom="10dp"
android:layout_gravity="end|bottom"
android:id="@+id/ivSelection"
android:src="@drawable/ic_bin_selection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</androidx.cardview.widget.CardView>

View file

@ -70,4 +70,14 @@
</com.facebook.shimmer.ShimmerFrameLayout>
<ImageView
android:visibility="invisible"
android:paddingEnd="10dp"
android:paddingBottom="10dp"
android:layout_gravity="end|bottom"
android:id="@+id/ivSelection"
android:src="@drawable/ic_bin_selection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</androidx.cardview.widget.CardView>

View file

@ -64,4 +64,14 @@
</com.facebook.shimmer.ShimmerFrameLayout>
<ImageView
android:visibility="invisible"
android:paddingEnd="10dp"
android:paddingBottom="10dp"
android:layout_gravity="end|bottom"
android:id="@+id/ivSelection"
android:src="@drawable/ic_bin_selection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</androidx.cardview.widget.CardView>

View file

@ -64,4 +64,14 @@
</com.facebook.shimmer.ShimmerFrameLayout>
<ImageView
android:visibility="invisible"
android:paddingEnd="10dp"
android:paddingBottom="10dp"
android:layout_gravity="end|bottom"
android:id="@+id/ivSelection"
android:src="@drawable/ic_bin_selection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</androidx.cardview.widget.CardView>

View file

@ -235,5 +235,6 @@ Do the computation of an expensive paragraph of text on a background thread:
<string name="number_selected_blocks">%1$d selected blocks</string>
<string name="select_all">Select all</string>
<string name="unselect_all">Unselect all (%1$d)</string>
<string name="put_back">Put back</string>
</resources>

View file

@ -8,66 +8,66 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="58dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toTopOf="@+id/selectionBottomToolbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnMarketplace" />
<Constraint
android:id="@+id/tvGreeting"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="70dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="70dp">
app:layout_constraintTop_toTopOf="parent">
<CustomAttribute
app:attributeName="textColor"
app:customColorValue="@color/white" />
</Constraint>
<Constraint
android:id="@+id/avatarContainer"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="80dp"
android:layout_height="80dp"
app:layout_constraintTop_toBottomOf="@+id/tvGreeting"
android:layout_marginTop="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="12dp">
app:layout_constraintTop_toBottomOf="@+id/tvGreeting">
<CustomAttribute
app:attributeName="alpha"
app:customFloatValue="1.0" />
</Constraint>
<Constraint
android:id="@+id/btnSearch"
app:layout_constraintEnd_toStartOf="@+id/btnMarketplace"
android:layout_width="52dp"
android:layout_height="52dp"
android:layout_marginTop="50dp"
android:layout_marginEnd="20dp"
app:layout_constraintTop_toBottomOf="@+id/avatarContainer"
android:layout_marginTop="50dp">
app:layout_constraintEnd_toStartOf="@+id/btnMarketplace"
app:layout_constraintTop_toBottomOf="@+id/avatarContainer">
<CustomAttribute
app:attributeName="alpha"
app:customFloatValue="1.0" />
</Constraint>
<Constraint
android:id="@+id/btnMarketplace"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="52dp"
android:layout_height="52dp"
app:layout_constraintTop_toBottomOf="@+id/avatarContainer"
android:layout_marginTop="50dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="50dp">
app:layout_constraintTop_toBottomOf="@+id/avatarContainer">
<CustomAttribute
app:attributeName="alpha"
app:customFloatValue="1.0" />
</Constraint>
<Constraint
android:id="@+id/btnAddDoc"
app:layout_constraintStart_toEndOf="@+id/btnMarketplace"
android:layout_width="52dp"
android:layout_height="52dp"
app:layout_constraintTop_toBottomOf="@+id/avatarContainer"
android:layout_marginStart="20dp"
android:layout_marginTop="50dp">
android:layout_marginTop="50dp"
app:layout_constraintStart_toEndOf="@+id/btnMarketplace"
app:layout_constraintTop_toBottomOf="@+id/avatarContainer">
<CustomAttribute
app:attributeName="alpha"
app:customFloatValue="1.0" />
@ -79,67 +79,67 @@
android:id="@+id/bottomSheet"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="16dp"
app:layout_constraintBottom_toTopOf="@+id/selectionBottomToolbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ivSettings"
android:layout_marginTop="16dp" />
app:layout_constraintTop_toBottomOf="@+id/ivSettings" />
<Constraint
android:id="@+id/tvGreeting"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="70dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="70dp">
app:layout_constraintTop_toTopOf="parent">
<CustomAttribute
app:attributeName="textColor"
app:customColorValue="#0066C3" />
</Constraint>
<Constraint
android:id="@+id/avatarContainer"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="80dp"
android:layout_height="80dp"
app:layout_constraintTop_toBottomOf="@+id/tvGreeting"
android:layout_marginTop="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="12dp">
app:layout_constraintTop_toBottomOf="@+id/tvGreeting">
<CustomAttribute
app:attributeName="alpha"
app:customFloatValue="0.0" />
</Constraint>
<Constraint
android:id="@+id/btnSearch"
app:layout_constraintEnd_toStartOf="@+id/btnMarketplace"
android:layout_width="52dp"
android:layout_height="52dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="20dp"
app:layout_constraintTop_toBottomOf="@+id/avatarContainer"
android:layout_marginTop="16dp">
app:layout_constraintEnd_toStartOf="@+id/btnMarketplace"
app:layout_constraintTop_toBottomOf="@+id/avatarContainer">
<CustomAttribute
app:attributeName="alpha"
app:customFloatValue="0.0" />
</Constraint>
<Constraint
android:id="@+id/btnMarketplace"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="52dp"
android:layout_height="52dp"
app:layout_constraintTop_toBottomOf="@+id/avatarContainer"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="16dp">
app:layout_constraintTop_toBottomOf="@+id/avatarContainer">
<CustomAttribute
app:attributeName="alpha"
app:customFloatValue="0.0" />
</Constraint>
<Constraint
android:id="@+id/btnAddDoc"
app:layout_constraintStart_toEndOf="@+id/btnMarketplace"
android:layout_width="52dp"
android:layout_height="52dp"
app:layout_constraintTop_toBottomOf="@+id/avatarContainer"
android:layout_marginStart="20dp"
android:layout_marginTop="16dp">
android:layout_marginTop="16dp"
app:layout_constraintStart_toEndOf="@+id/btnMarketplace"
app:layout_constraintTop_toBottomOf="@+id/avatarContainer">
<CustomAttribute
app:attributeName="alpha"
app:customFloatValue="0.0" />

View file

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M0,10C0,4.4771 4.4771,0 10,0C15.5228,0 20,4.4771 20,10C20,15.5228 15.5228,20 10,20C4.4771,20 0,15.5228 0,10Z"
android:fillColor="#000000"/>
<path
android:pathData="M6,9.5L9.5,13.5L14.5,5.5"
android:strokeWidth="1.35"
android:fillColor="#00000000"
android:strokeColor="#ffffff"/>
</vector>

View file

@ -462,4 +462,14 @@ class BlockDataRepository(
ctx: Id,
isArchived: Boolean
): Payload = factory.remote.setObjectIsArchived(ctx = ctx, isArchived = isArchived)
override fun setObjectListIsArchived(
targets: List<Id>,
isArchived: Boolean
) = factory.remote.setObjectListIsArchived(
targets = targets,
isArchived = isArchived
)
override fun deleteObjects(targets: List<Id>) = factory.remote.deleteObjects(targets = targets)
}

View file

@ -172,4 +172,7 @@ interface BlockDataStore {
fun setObjectIsFavorite(ctx: Id, isFavorite: Boolean) : Payload
fun setObjectIsArchived(ctx: Id, isArchived: Boolean) : Payload
fun setObjectListIsArchived(targets: List<Id>, isArchived: Boolean)
fun deleteObjects(targets: List<Id>)
}

View file

@ -172,4 +172,7 @@ interface BlockRemote {
fun setObjectIsFavorite(ctx: Id, isFavorite: Boolean) : Payload
fun setObjectIsArchived(ctx: Id, isArchived: Boolean) : Payload
fun setObjectListIsArchived(targets: List<Id>, isArchived: Boolean)
fun deleteObjects(targets: List<Id>)
}

View file

@ -396,4 +396,14 @@ class BlockRemoteDataStore(private val remote: BlockRemote) : BlockDataStore {
ctx: Id,
isArchived: Boolean
): Payload = remote.setObjectIsArchived(ctx = ctx, isArchived = isArchived)
override fun setObjectListIsArchived(
targets: List<Id>,
isArchived: Boolean
) = remote.setObjectListIsArchived(
targets = targets,
isArchived = isArchived
)
override fun deleteObjects(targets: List<Id>) = remote.deleteObjects(targets = targets)
}

View file

@ -226,4 +226,7 @@ interface BlockRepository {
fun setObjectIsFavorite(ctx: Id, isFavorite: Boolean) : Payload
fun setObjectIsArchived(ctx: Id, isArchived: Boolean) : Payload
fun setObjectListIsArchived(targets: List<Id>, isArchived: Boolean)
fun deleteObjects(targets: List<Id>)
}

View file

@ -0,0 +1,24 @@
package com.anytypeio.anytype.domain.objects
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
/**
* Use-case for deleting objects.
* @see SetObjectIsArchived
*/
class DeleteObjects(
private val repo: BlockRepository
) : BaseUseCase<Unit, DeleteObjects.Params>() {
override suspend fun run(params: Params) = safe {
repo.deleteObjects(params.targets)
}
/**
* @property [targets] id of the objects to delete.
*/
class Params(val targets: List<Id>)
}

View file

@ -0,0 +1,30 @@
package com.anytypeio.anytype.domain.objects
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.block.repo.BlockRepository
/**
* Use-case for archiving (or restoring from archive) a list of objects.
*/
class SetObjectListIsArchived(
private val repo: BlockRepository
) : BaseUseCase<Unit, SetObjectListIsArchived.Params>() {
override suspend fun run(params: Params) = safe {
repo.setObjectListIsArchived(
isArchived = params.isArchived,
targets = params.targets
)
}
/**
* Params for archiving a list of objects.
* @property [targets] id of the objects to archive/restore.
*/
data class Params(
val targets: List<Id>,
val isArchived: Boolean
)
}

View file

@ -423,4 +423,16 @@ class BlockMiddleware(
ctx: Id,
isArchived: Boolean
): Payload = middleware.setObjectIsArchived(ctx = ctx, isArchived = isArchived)
override fun deleteObjects(targets: List<Id>) = middleware.deleteObjects(
targets = targets
)
override fun setObjectListIsArchived(
targets: List<Id>,
isArchived: Boolean
) = middleware.setObjectListIsArchived(
targets = targets,
isArchived = isArchived
)
}

View file

@ -1442,4 +1442,24 @@ class Middleware(
if (BuildConfig.DEBUG) logResponse(response)
return response.event.toPayload()
}
fun setObjectListIsArchived(
targets: List<Id>,
isArchived: Boolean
) {
val request = Rpc.ObjectList.Set.IsArchived.Request(
objectIds = targets,
isArchived = isArchived,
)
if (BuildConfig.DEBUG) logRequest(request)
val response = service.objectListSetIsArchived(request)
if (BuildConfig.DEBUG) logResponse(response)
}
fun deleteObjects(targets: List<Id>) {
val request = Rpc.ObjectList.Delete.Request(objectIds = targets)
if (BuildConfig.DEBUG) logRequest(request)
val response = service.objectListDelete(request)
if (BuildConfig.DEBUG) logResponse(response)
}
}

View file

@ -197,4 +197,10 @@ interface MiddlewareService {
@Throws(Exception::class)
fun objectSetIsArchived(request: Object.SetIsArchived.Request): Object.SetIsArchived.Response
@Throws(Exception::class)
fun objectListSetIsArchived(request: ObjectList.Set.IsArchived.Request): ObjectList.Set.IsArchived.Response
@Throws(Exception::class)
fun objectListDelete(request: ObjectList.Delete.Request): ObjectList.Delete.Response
}

View file

@ -782,4 +782,30 @@ class MiddlewareServiceImplementation : MiddlewareService {
return response
}
}
override fun objectListSetIsArchived(request: ObjectList.Set.IsArchived.Request): ObjectList.Set.IsArchived.Response {
val encoded = Service.objectListSetIsArchived(
ObjectList.Set.IsArchived.Request.ADAPTER.encode(request)
)
val response = ObjectList.Set.IsArchived.Response.ADAPTER.decode(encoded)
val error = response.error
if (error != null && error.code != ObjectList.Set.IsArchived.Response.Error.Code.NULL) {
throw Exception(error.description)
} else {
return response
}
}
override fun objectListDelete(request: ObjectList.Delete.Request): ObjectList.Delete.Response {
val encoded = Service.objectListDelete(
ObjectList.Delete.Request.ADAPTER.encode(request)
)
val response = ObjectList.Delete.Response.ADAPTER.decode(encoded)
val error = response.error
if (error != null && error.code != ObjectList.Delete.Response.Error.Code.NULL) {
throw Exception(error.description)
} else {
return response
}
}
}

View file

@ -9,6 +9,7 @@ sealed class DashboardView {
abstract val id: Id
abstract val isArchived: Boolean
abstract val isSelected: Boolean
abstract val isLoading: Boolean
data class Profile(
@ -16,6 +17,7 @@ sealed class DashboardView {
val name: String,
val avatar: Url? = null,
override val isArchived: Boolean = false,
override val isSelected: Boolean = false,
override val isLoading: Boolean = false
) : DashboardView()
@ -30,6 +32,7 @@ sealed class DashboardView {
val type: String? = null,
val done: Boolean? = null,
override val isArchived: Boolean,
override val isSelected: Boolean = false,
override val isLoading: Boolean = false,
val icon: ObjectIcon = ObjectIcon.None
) : DashboardView() {
@ -41,6 +44,7 @@ sealed class DashboardView {
val target: Id,
val title: String,
override val isArchived: Boolean = false,
override val isSelected: Boolean = false,
override val isLoading: Boolean = false
) : DashboardView()
@ -49,6 +53,7 @@ sealed class DashboardView {
val target: Id,
val title: String? = null,
override val isArchived: Boolean,
override val isSelected: Boolean = false,
override val isLoading: Boolean = false,
val icon: ObjectIcon = ObjectIcon.None
) : DashboardView()

View file

@ -23,10 +23,13 @@ import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.block.interactor.Move
import com.anytypeio.anytype.domain.config.GetConfig
import com.anytypeio.anytype.domain.config.GetDebugSettings
import com.anytypeio.anytype.domain.dashboard.interactor.*
import com.anytypeio.anytype.domain.dashboard.interactor.CloseDashboard
import com.anytypeio.anytype.domain.dashboard.interactor.OpenDashboard
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.DeleteObjects
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.page.CreatePage
import com.anytypeio.anytype.presentation.BuildConfig
import com.anytypeio.anytype.presentation.dashboard.HomeDashboardStateMachine.Interactor
@ -55,7 +58,9 @@ class HomeDashboardViewModel(
private val getDebugSettings: GetDebugSettings,
private val analytics: Analytics,
private val searchObjects: SearchObjects,
private val urlBuilder: UrlBuilder
private val urlBuilder: UrlBuilder,
private val setObjectListIsArchived: SetObjectListIsArchived,
private val deleteObjects: DeleteObjects
) : ViewStateViewModel<State>(),
HomeDashboardEventConverter by eventConverter,
SupportNavigation<EventWrapper<AppNavigation.Command>> {
@ -80,6 +85,9 @@ class HomeDashboardViewModel(
val inbox = MutableStateFlow(emptyList<DashboardView>())
val sets = MutableStateFlow(emptyList<DashboardView>())
val mode = MutableStateFlow(Mode.DEFAULT)
val count = MutableStateFlow(0)
private val views: List<DashboardView>
get() = stateData.value?.blocks ?: emptyList()
@ -303,24 +311,27 @@ class HomeDashboardViewModel(
fun onTabObjectClicked(target: Id, isLoading: Boolean, tab: TAB = TAB.FAVOURITE) {
if (!isLoading) {
val view = when (tab) {
TAB.FAVOURITE -> views.find { it is DashboardView.Document && it.target == target }
TAB.RECENT -> recent.value.find { it is DashboardView.Document && it.target == target }
TAB.ARCHIVE -> archived.value.find { it.target == target }
else -> null
}
if (view is DashboardView.Document && supportedLayouts.contains(view.layout)) {
if (view.type != ObjectType.TEMPLATE_URL) {
if (view.layout == ObjectType.Layout.SET) {
proceedWithOpeningObjectSet(target)
if (tab == TAB.ARCHIVE) {
proceedWithClickInArchiveTab(target)
} else {
val view = when (tab) {
TAB.FAVOURITE -> views.find { it is DashboardView.Document && it.target == target }
TAB.RECENT -> recent.value.find { it is DashboardView.Document && it.target == target }
else -> null
}
if (view is DashboardView.Document && supportedLayouts.contains(view.layout)) {
if (view.type != ObjectType.TEMPLATE_URL) {
if (view.layout == ObjectType.Layout.SET) {
proceedWithOpeningObjectSet(target)
} else {
proceedWithOpeningDocument(target)
}
} else {
proceedWithOpeningDocument(target)
toast("Can't open a template on Android. Coming soon")
}
} else {
toast("Can't open a template on Android. Coming soon")
toast("Currently unsupported layout on Android")
}
} else {
toast("Currently unsupported layout on Android")
}
} else {
toast("This object is still syncing.")
@ -528,6 +539,83 @@ class HomeDashboardViewModel(
}
}
//region BIN SELECTION
private fun proceedWithClickInArchiveTab(target: Id) {
if (mode.value == Mode.DEFAULT) mode.value = Mode.SELECTION
proceedWithTogglingSelectionForTarget(target)
}
private fun proceedWithTogglingSelectionForTarget(target: Id) {
val updatedViews = archived.value.map { obj ->
if (obj.id == target) {
obj.copy(isSelected = !obj.isSelected)
} else {
obj
}
}
val updatedCount = updatedViews.count { it.isSelected }
archived.value = updatedViews
count.value = updatedCount
if (updatedCount == 0) {
mode.value = Mode.DEFAULT
}
}
fun onSelectAllClicked() {
archived.value = archived.value.map { obj -> obj.copy(isSelected = true) }
count.value = archived.value.size
}
fun onCancelSelectionClicked() {
mode.value = Mode.DEFAULT
archived.value = archived.value.map { obj -> obj.copy(isSelected = false) }
count.value = 0
}
fun onPutBackClicked() {
viewModelScope.launch {
mode.value = Mode.DEFAULT
setObjectListIsArchived(
SetObjectListIsArchived.Params(
targets = archived.value.filter { it.isSelected }.map { it.id },
isArchived = false
)
).process(
failure = { e ->
Timber.e(e, "Error while restoring objects from archive")
proceedWithArchivedObjectSearch()
},
success = {
proceedWithArchivedObjectSearch()
}
)
}
}
fun onDeleteObjectsClicked() {
viewModelScope.launch {
mode.value = Mode.DEFAULT
deleteObjects(
DeleteObjects.Params(
targets = archived.value.filter { it.isSelected }.map { it.id }
)
).process(
failure = { e ->
Timber.e(e, "Error while deleting objects")
proceedWithArchivedObjectSearch()
},
success = {
proceedWithArchivedObjectSearch()
}
)
}
}
//endregion
/**
* Represents movements of blocks during block dragging action.
* @param subject id of the block being dragged
@ -553,4 +641,6 @@ class HomeDashboardViewModel(
}
enum class TAB { FAVOURITE, RECENT, INBOX, SETS, ARCHIVE }
enum class Mode { DEFAULT, SELECTION }
}

View file

@ -11,6 +11,8 @@ import com.anytypeio.anytype.domain.dashboard.interactor.*
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.DeleteObjects
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.page.CreatePage
class HomeDashboardViewModelFactory(
@ -25,7 +27,9 @@ class HomeDashboardViewModelFactory(
private val getDebugSettings: GetDebugSettings,
private val analytics: Analytics,
private val searchObjects: SearchObjects,
private val urlBuilder: UrlBuilder
private val urlBuilder: UrlBuilder,
private val setObjectListIsArchived: SetObjectListIsArchived,
private val deleteObjects: DeleteObjects
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@ -42,7 +46,9 @@ class HomeDashboardViewModelFactory(
getDebugSettings = getDebugSettings,
analytics = analytics,
searchObjects = searchObjects,
urlBuilder = urlBuilder
urlBuilder = urlBuilder,
deleteObjects = deleteObjects,
setObjectListIsArchived = setObjectListIsArchived
) as T
}
}

View file

@ -0,0 +1,403 @@
package com.anytypeio.anytype.presentation.dashboard
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
import org.junit.Before
import org.junit.Test
import org.mockito.MockitoAnnotations
import kotlin.test.assertEquals
class DashboardBinTest : DashboardTestSetup() {
private val objectIds = listOf(
MockDataFactory.randomUuid(),
MockDataFactory.randomUuid(),
MockDataFactory.randomUuid(),
MockDataFactory.randomUuid(),
)
private val objects : List<Map<String, Any?>> = listOf(
mapOf(
Relations.ID to objectIds[0]
),
mapOf(
Relations.ID to objectIds[1]
),
mapOf(
Relations.ID to objectIds[2]
),
mapOf(
Relations.ID to objectIds[3]
)
)
@Before
fun setup() {
MockitoAnnotations.openMocks(this)
}
@Test
fun `should enter select mode when block is clicked in bin tab`() {
// SETUP
val dashboard = Block(
id = config.home,
content = Block.Content.Smart(SmartBlockType.HOME),
children = emptyList(),
fields = Block.Fields.empty()
)
stubGetConfig(Either.Right(config))
stubObserveEvents(params = InterceptEvents.Params(context = config.home))
stubOpenDashboard(
payload = Payload(
context = config.home,
events = listOf(
Event.Command.ShowObject(
root = config.home,
context = config.home,
blocks = listOf(dashboard),
type = SmartBlockType.HOME
)
)
)
)
stubSearchObjects(
params = SearchObjects.Params(
filters = ObjectSearchConstants.filterTabArchive,
sorts = ObjectSearchConstants.sortTabArchive
),
objects = objects
)
vm = buildViewModel()
vm.onViewCreated()
// TESTING
val expectedBeforeSelection = listOf(
DashboardView.Document(
id = objectIds[0],
isArchived = true,
target = objectIds[0]
),
DashboardView.Document(
id = objectIds[1],
isArchived = true,
target = objectIds[1]
),
DashboardView.Document(
id = objectIds[2],
isArchived = true,
target = objectIds[2]
),
DashboardView.Document(
id = objectIds[3],
isArchived = true,
target = objectIds[3]
)
)
assertEquals(
expected = expectedBeforeSelection,
actual = vm.archived.value
)
assertEquals(
expected = HomeDashboardViewModel.Mode.DEFAULT,
actual = vm.mode.value
)
vm.onTabObjectClicked(
target = objectIds[0],
tab = HomeDashboardViewModel.TAB.ARCHIVE,
isLoading = false
)
val expectedAfterSelection = expectedBeforeSelection.map { obj ->
if (obj.id == objectIds[0])
obj.copy(isSelected = true)
else
obj
}
assertEquals(
expected = expectedAfterSelection,
actual = vm.archived.value
)
assertEquals(
expected = HomeDashboardViewModel.Mode.SELECTION,
actual = vm.mode.value
)
}
@Test
fun `should toggle selection on object click in bin tab`() {
// SETUP
val dashboard = Block(
id = config.home,
content = Block.Content.Smart(SmartBlockType.HOME),
children = emptyList(),
fields = Block.Fields.empty()
)
stubGetConfig(Either.Right(config))
stubObserveEvents(params = InterceptEvents.Params(context = config.home))
stubOpenDashboard(
payload = Payload(
context = config.home,
events = listOf(
Event.Command.ShowObject(
root = config.home,
context = config.home,
blocks = listOf(dashboard),
type = SmartBlockType.HOME
)
)
)
)
stubSearchObjects(
params = SearchObjects.Params(
filters = ObjectSearchConstants.filterTabArchive,
sorts = ObjectSearchConstants.sortTabArchive
),
objects = objects
)
vm = buildViewModel()
vm.onViewCreated()
// TESTING
val expectedBeforeSelection = listOf(
DashboardView.Document(
id = objectIds[0],
isArchived = true,
target = objectIds[0]
),
DashboardView.Document(
id = objectIds[1],
isArchived = true,
target = objectIds[1]
),
DashboardView.Document(
id = objectIds[2],
isArchived = true,
target = objectIds[2]
),
DashboardView.Document(
id = objectIds[3],
isArchived = true,
target = objectIds[3]
)
)
assertEquals(
expected = expectedBeforeSelection,
actual = vm.archived.value
)
assertEquals(
expected = HomeDashboardViewModel.Mode.DEFAULT,
actual = vm.mode.value
)
// Clicking on the first object, in order to enter select mode
vm.onTabObjectClicked(
target = objectIds[0],
tab = HomeDashboardViewModel.TAB.ARCHIVE,
isLoading = false
)
// Clicking on the second object, in order select it
vm.onTabObjectClicked(
target = objectIds[1],
tab = HomeDashboardViewModel.TAB.ARCHIVE,
isLoading = false
)
// Checking that two object are selected
val expectedAfterSelection = expectedBeforeSelection.map { obj ->
if (obj.id == objectIds[0] || obj.id == objectIds[1])
obj.copy(isSelected = true)
else
obj
}
assertEquals(
expected = expectedAfterSelection,
actual = vm.archived.value
)
// Clicking on the second object, in order unselect it
vm.onTabObjectClicked(
target = objectIds[1],
tab = HomeDashboardViewModel.TAB.ARCHIVE,
isLoading = false
)
// Checking that only the first object is selected now
val expectedAfterUnselect = expectedBeforeSelection.map { obj ->
if (obj.id == objectIds[0])
obj.copy(isSelected = true)
else
obj
}
assertEquals(
expected = expectedAfterUnselect,
actual = vm.archived.value
)
}
@Test
fun `should exit select mode if no object is selected in bin tab`() {
// SETUP
val dashboard = Block(
id = config.home,
content = Block.Content.Smart(SmartBlockType.HOME),
children = emptyList(),
fields = Block.Fields.empty()
)
stubGetConfig(Either.Right(config))
stubObserveEvents(params = InterceptEvents.Params(context = config.home))
stubOpenDashboard(
payload = Payload(
context = config.home,
events = listOf(
Event.Command.ShowObject(
root = config.home,
context = config.home,
blocks = listOf(dashboard),
type = SmartBlockType.HOME
)
)
)
)
stubSearchObjects(
params = SearchObjects.Params(
filters = ObjectSearchConstants.filterTabArchive,
sorts = ObjectSearchConstants.sortTabArchive
),
objects = objects
)
vm = buildViewModel()
vm.onViewCreated()
// TESTING
val expectedBeforeSelection = listOf(
DashboardView.Document(
id = objectIds[0],
isArchived = true,
target = objectIds[0]
),
DashboardView.Document(
id = objectIds[1],
isArchived = true,
target = objectIds[1]
),
DashboardView.Document(
id = objectIds[2],
isArchived = true,
target = objectIds[2]
),
DashboardView.Document(
id = objectIds[3],
isArchived = true,
target = objectIds[3]
)
)
assertEquals(
expected = expectedBeforeSelection,
actual = vm.archived.value
)
assertEquals(
expected = HomeDashboardViewModel.Mode.DEFAULT,
actual = vm.mode.value
)
// Clicking on the first object, in order to enter select mode
vm.onTabObjectClicked(
target = objectIds[0],
tab = HomeDashboardViewModel.TAB.ARCHIVE,
isLoading = false
)
// Checking that this first object is selected
val expectedAfterSelection = expectedBeforeSelection.map { obj ->
if (obj.id == objectIds[0])
obj.copy(isSelected = true)
else
obj
}
assertEquals(
expected = expectedAfterSelection,
actual = vm.archived.value
)
// Checking that bin tab is now in select mode
assertEquals(
expected = HomeDashboardViewModel.Mode.SELECTION,
actual = vm.mode.value
)
// Clicking on the first object again to unselect it
vm.onTabObjectClicked(
target = objectIds[0],
tab = HomeDashboardViewModel.TAB.ARCHIVE,
isLoading = false
)
// Checking that no object is selected now
val expectedAfterUnselect = expectedBeforeSelection
assertEquals(
expected = expectedAfterUnselect,
actual = vm.archived.value
)
// Checking that bin tab is not select mode
assertEquals(
expected = HomeDashboardViewModel.Mode.DEFAULT,
actual = vm.mode.value
)
}
}

View file

@ -19,6 +19,8 @@ import com.anytypeio.anytype.domain.dashboard.interactor.*
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.DeleteObjects
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.page.CreatePage
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import kotlinx.coroutines.flow.Flow
@ -50,6 +52,12 @@ open class DashboardTestSetup {
@Mock
lateinit var closeDashboard: CloseDashboard
@Mock
lateinit var deleteObjects: DeleteObjects
@Mock
lateinit var setObjectListIsArchived: SetObjectListIsArchived
@Mock
lateinit var createPage: CreatePage
@ -99,7 +107,9 @@ open class DashboardTestSetup {
getDebugSettings = getDebugSettings,
analytics = analytics,
searchObjects = searchObjects,
urlBuilder = builder
urlBuilder = builder,
setObjectListIsArchived = setObjectListIsArchived,
deleteObjects = deleteObjects
)
fun stubGetConfig(response: Either.Right<Config>) {
@ -159,4 +169,13 @@ open class DashboardTestSetup {
}
}
}
fun stubSearchObjects(
params: SearchObjects.Params,
objects: List<Map<String, Any?>> = emptyList()
) {
searchObjects.stub {
onBlocking { invoke(params) } doReturn Either.Right(objects)
}
}
}

View file

@ -17,6 +17,8 @@ import com.anytypeio.anytype.domain.dashboard.interactor.*
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.DeleteObjects
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.page.CreatePage
import com.anytypeio.anytype.presentation.navigation.AppNavigation
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
@ -57,6 +59,12 @@ class HomeDashboardViewModelTest {
@Mock
lateinit var searchObjects: SearchObjects
@Mock
lateinit var setObjectListIsArchived: SetObjectListIsArchived
@Mock
lateinit var deleteObjects: DeleteObjects
@Mock
lateinit var interceptEvents: InterceptEvents
@ -106,6 +114,8 @@ class HomeDashboardViewModelTest {
getDebugSettings = getDebugSettings,
analytics = analytics,
searchObjects = searchObjects,
deleteObjects = deleteObjects,
setObjectListIsArchived = setObjectListIsArchived,
urlBuilder = builder
)
}