DROID-763 Editor | Design | Set Status relation design update (#2814)

DROID-763 | Implemented set status relation design update
This commit is contained in:
Allan Quatermain 2023-01-16 14:22:29 +03:00 committed by GitHub
parent 3765807181
commit db15e9e8d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 788 additions and 315 deletions

View File

@ -1,6 +1,16 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value />
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="false" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />

View File

@ -265,7 +265,7 @@ class DisplayRelationObjectValueTest {
// Checking that the name is set
onView(withId(R.id.tvTagOrStatusRelationHeader)).apply {
onView(withId(R.id.tvRelationHeader)).apply {
check(matches(withText(name)))
}
}

View File

@ -257,7 +257,7 @@ class DisplayRelationStatusValueTest {
// Checking that the name is set
onView(withId(R.id.tvTagOrStatusRelationHeader)).apply {
onView(withId(R.id.tvRelationHeader)).apply {
check(matches(withText(name)))
}
}

View File

@ -255,7 +255,7 @@ class DisplayRelationTagValueTest {
// Checking that the name is set
onView(withId(R.id.tvTagOrStatusRelationHeader)).apply {
onView(withId(R.id.tvRelationHeader)).apply {
check(matches(withText(name)))
}
}

View File

@ -16,6 +16,7 @@ import com.anytypeio.anytype.presentation.sets.RelationValueViewModel
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
import com.anytypeio.anytype.presentation.util.DefaultCopyFileToCacheDirectory
import com.anytypeio.anytype.presentation.util.Dispatcher
import com.anytypeio.anytype.ui.relations.RelationStatusValueFragment
import com.anytypeio.anytype.ui.relations.RelationValueDVFragment
import com.anytypeio.anytype.ui.relations.RelationValueFragment
import dagger.Module
@ -48,6 +49,7 @@ interface ObjectObjectRelationValueSubComponent {
}
fun inject(fragment: RelationValueFragment)
fun inject(fragment: RelationStatusValueFragment)
fun addObjectRelationValueComponent(): AddObjectRelationValueSubComponent.Builder
fun addObjectRelationObjectValueComponent(): AddObjectRelationSubComponent.Builder

View File

@ -137,6 +137,16 @@ open class RelationListFragment : BaseBottomSheetFragment<FragmentRelationListBi
}
dismiss()
}
is Command.EditStatusRelationValue -> {
val fr = RelationStatusValueFragment.new(
ctx = ctx,
target = command.target,
relationKey = command.relationKey,
targetObjectTypes = command.targetObjectTypes,
isLocked = command.isLocked
)
fr.showChildFragment()
}
}
}

View File

@ -0,0 +1,166 @@
package com.anytypeio.anytype.ui.relations
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.os.bundleOf
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_ui.reactive.clicks
import com.anytypeio.anytype.core_utils.ext.drawable
import com.anytypeio.anytype.core_utils.ext.gone
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ext.visible
import com.anytypeio.anytype.core_utils.ui.proceed
import com.anytypeio.anytype.databinding.FragmentRelationStatusValueBinding
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.relations.RelationValueView
import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel
import com.anytypeio.anytype.presentation.sets.RelationValueViewModel
import com.anytypeio.anytype.ui.relations.add.AddOptionsRelationFragment
import javax.inject.Inject
class RelationStatusValueFragment :
RelationValueBaseFragment<FragmentRelationStatusValueBinding>() {
@Inject
lateinit var factory: RelationValueViewModel.Factory
override val vm: RelationValueViewModel by viewModels { factory }
override val root: View
get() = binding.root
override val recycler: RecyclerView
get() = binding.recycler
override val btnAddValue: ImageView
get() = binding.btnAddValue
override val btnEditOrDone: TextView
get() = binding.btnEditOrDone
override val refresh: ProgressBar
get() = binding.refresh
override val tvRelationHeader: TextView
get() = binding.tvRelationHeader
private val btnClear
get() = binding.btnClear
private val tvEmptyPlaceholder
get() = binding.tvEmptyPlaceholder
override val onStatusClickedCallback: (RelationValueView.Option.Status) -> Unit = {
vm.onAddValueClicked(isLocked)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recycler.apply {
layoutManager = LinearLayoutManager(context)
adapter = relationValueAdapter
}
dividerItem = DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply {
setDrawable(drawable(R.drawable.divider_relations))
}
dividerItemEdit = DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply {
setDrawable(drawable(R.drawable.divider_relations_edit))
}
proceed(btnAddValue.clicks()) { vm.onAddValueClicked(isLocked) }
proceed(btnClear.clicks()) {
vm.onRemoveStatusFromObjectClicked(
target = target,
relationKey = relationKey,
)
}
}
override fun observeViews(values: List<RelationValueView>) {
if (values.isNotEmpty() && values.first() is RelationValueView.Empty) {
recycler.gone()
btnClear.gone()
tvEmptyPlaceholder.visible()
btnAddValue.visible()
} else {
recycler.visible()
btnClear.visible()
tvEmptyPlaceholder.gone()
btnAddValue.gone()
}
relationValueAdapter.update(values)
}
override fun onObjectValueChanged(ctx: Id, objectId: Id, relationKey: Key, ids: List<Id>) {
vm.onAddObjectsOrFilesValueToObject(
ctx = ctx,
target = objectId,
relationKey = relationKey,
ids = ids
)
}
override fun PickiTonMultipleCompleteListener(
paths: ArrayList<String>?,
wasSuccessful: Boolean,
Reason: String?
) {
toast("Not implemented yet")
}
override fun observeCommands(command: RelationValueBaseViewModel.ObjectRelationValueCommand) {
when (command) {
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddStatusOrTagScreen -> {
showAddStatusOrTagScreen()
}
else -> {}
}
}
private fun showAddStatusOrTagScreen() {
val fr = AddOptionsRelationFragment.new(
ctx = ctx,
objectId = target,
relationKey = relationKey
)
fr.showChildFragment()
}
override fun inflateBinding(
inflater: LayoutInflater,
container: ViewGroup?
): FragmentRelationStatusValueBinding {
return FragmentRelationStatusValueBinding.inflate(
inflater, container, false
)
}
override fun injectDependencies() {
componentManager().objectObjectRelationValueComponent.get(ctx).inject(this)
}
override fun releaseDependencies() {
componentManager().objectObjectRelationValueComponent.release(ctx)
}
companion object {
fun new(
ctx: Id,
target: Id,
relationKey: Key,
targetObjectTypes: List<Id>,
isLocked: Boolean = false
) = RelationStatusValueFragment().apply {
arguments = bundleOf(
CTX_KEY to ctx,
TARGET_KEY to target,
RELATION_KEY to relationKey,
TARGET_TYPES_KEY to targetObjectTypes,
IS_LOCKED_KEY to isLocked
)
}
}
}

View File

@ -8,32 +8,28 @@ import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.core.os.bundleOf
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import com.anytypeio.anytype.BuildConfig
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_ui.features.sets.RelationValueAdapter
import com.anytypeio.anytype.core_ui.reactive.clicks
import com.anytypeio.anytype.core_ui.tools.DefaultDragAndDropBehavior
import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_FILE_SAF_CODE
import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_MEDIA_CODE
import com.anytypeio.anytype.core_utils.ext.Mimetype
import com.anytypeio.anytype.core_utils.ext.arg
import com.anytypeio.anytype.core_utils.ext.argString
import com.anytypeio.anytype.core_utils.ext.drawable
import com.anytypeio.anytype.core_utils.ext.gone
import com.anytypeio.anytype.core_utils.ext.invisible
import com.anytypeio.anytype.core_utils.ext.isPermissionGranted
@ -46,31 +42,21 @@ import com.anytypeio.anytype.core_utils.ext.visible
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetFragment
import com.anytypeio.anytype.core_utils.ui.DragAndDropViewHolder
import com.anytypeio.anytype.core_utils.ui.OnStartDragListener
import com.anytypeio.anytype.databinding.FragmentRelationValueBinding
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.navigation.AppNavigation
import com.anytypeio.anytype.presentation.relations.RelationValueView
import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel
import com.anytypeio.anytype.presentation.sets.RelationValueDVViewModel
import com.anytypeio.anytype.presentation.sets.RelationValueViewModel
import com.anytypeio.anytype.presentation.util.CopyFileStatus
import com.anytypeio.anytype.ui.editor.EditorFragment
import com.anytypeio.anytype.ui.relations.add.AddFileRelationFragment
import com.anytypeio.anytype.ui.relations.add.AddObjectRelationFragment
import com.anytypeio.anytype.ui.relations.add.AddOptionsRelationDVFragment
import com.anytypeio.anytype.ui.relations.add.AddOptionsRelationFragment
import com.anytypeio.anytype.ui.sets.ObjectSetFragment
import com.google.android.material.snackbar.Snackbar
import com.hbisoft.pickit.PickiT
import com.hbisoft.pickit.PickiTCallbacks
import timber.log.Timber
import javax.inject.Inject
abstract class RelationValueBaseFragment : BaseBottomSheetFragment<FragmentRelationValueBinding>(),
abstract class RelationValueBaseFragment<T: ViewBinding> : BaseBottomSheetFragment<T>(),
OnStartDragListener,
AddObjectRelationFragment.ObjectValueAddReceiver,
FileActionsFragment.FileActionReceiver,
AddFileRelationFragment.FileValueAddReceiver,
PickiTCallbacks {
protected val ctx get() = argString(CTX_KEY)
@ -79,13 +65,23 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment<FragmentRelat
protected val dataview get() = argString(DATAVIEW_KEY)
protected val viewer get() = argString(VIEWER_KEY)
protected val types get() = arg<List<String>>(TARGET_TYPES_KEY)
private val isLocked get() = arg<Boolean>(IS_LOCKED_KEY)
protected val isLocked get() = arg<Boolean>(IS_LOCKED_KEY)
abstract val vm: RelationValueBaseViewModel
private val dndItemTouchHelper: ItemTouchHelper by lazy { ItemTouchHelper(dndBehavior) }
private lateinit var dividerItem: DividerItemDecoration
private lateinit var dividerItemEdit: DividerItemDecoration
protected lateinit var dividerItem: DividerItemDecoration
protected lateinit var dividerItemEdit: DividerItemDecoration
protected abstract val root: View
protected abstract val recycler: RecyclerView
protected abstract val btnAddValue: ImageView
protected abstract val btnEditOrDone: TextView
protected abstract val refresh: ProgressBar
protected abstract val tvRelationHeader: TextView
protected abstract val onStatusClickedCallback: (RelationValueView.Option.Status) -> Unit
protected abstract fun observeViews(values: List<RelationValueView>)
private val dndBehavior by lazy {
object : DefaultDragAndDropBehavior(
@ -115,7 +111,7 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment<FragmentRelat
RelationValueAdapter(
onCreateOptionClicked = {},
onTagClicked = {},
onStatusClicked = {},
onStatusClicked = onStatusClickedCallback,
onRemoveStatusClicked = { status ->
vm.onRemoveStatusFromObjectClicked(
target = target,
@ -162,31 +158,13 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment<FragmentRelat
)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recycler.apply {
layoutManager = LinearLayoutManager(context)
adapter = relationValueAdapter
}
dividerItem = DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply {
setDrawable(drawable(R.drawable.divider_relations))
}
dividerItemEdit = DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply {
setDrawable(drawable(R.drawable.divider_relations_edit))
}
with(lifecycleScope) {
subscribe(binding.btnEditOrDone.clicks()) { vm.onEditOrDoneClicked(isLocked) }
subscribe(binding.btnAddValue.clicks()) { vm.onAddValueClicked(isLocked) }
}
}
override fun onStart() {
jobs += lifecycleScope.subscribe(vm.toasts) { toast(it) }
jobs += lifecycleScope.subscribe(vm.commands) { observeCommands(it) }
jobs += lifecycleScope.subscribe(vm.isDismissed) { observeDismiss(it) }
jobs += lifecycleScope.subscribe(vm.isEditing) { observeEditing(it) }
jobs += lifecycleScope.subscribe(vm.views) { relationValueAdapter.update(it) }
jobs += lifecycleScope.subscribe(vm.name) { binding.tvTagOrStatusRelationHeader.text = it }
jobs += lifecycleScope.subscribe(vm.views) { observeViews(it) }
jobs += lifecycleScope.subscribe(vm.name) { tvRelationHeader.text = it }
jobs += lifecycleScope.subscribe(vm.navigation) { command -> navigate(command) }
jobs += lifecycleScope.subscribe(vm.isLoading) { isLoading -> observeLoading(isLoading) }
jobs += lifecycleScope.subscribe(vm.copyFileStatus) { command -> onCopyFileCommand(command) }
@ -199,11 +177,11 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment<FragmentRelat
private fun observeLoading(isLoading: Boolean) {
if (isLoading) {
binding.refresh.visible()
binding.btnAddValue.invisible()
refresh.visible()
btnAddValue.invisible()
} else {
binding.refresh.gone()
binding.btnAddValue.visible()
refresh.gone()
btnAddValue.visible()
}
}
@ -236,20 +214,20 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment<FragmentRelat
private fun observeEditing(isEditing: Boolean) {
if (isEditing) {
binding.recycler.apply {
recycler.apply {
removeItemDecoration(dividerItem)
addItemDecoration(dividerItemEdit)
}
binding.btnAddValue.invisible()
binding.btnEditOrDone.setText(R.string.done)
dndItemTouchHelper.attachToRecyclerView(binding.recycler)
btnAddValue.invisible()
btnEditOrDone.setText(R.string.done)
dndItemTouchHelper.attachToRecyclerView(recycler)
} else {
binding.recycler.apply {
recycler.apply {
removeItemDecoration(dividerItemEdit)
addItemDecoration(dividerItem)
}
binding.btnAddValue.visible()
binding.btnEditOrDone.setText(R.string.edit)
btnAddValue.visible()
btnEditOrDone.setText(R.string.edit)
dndItemTouchHelper.attachToRecyclerView(null)
}
}
@ -262,7 +240,21 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment<FragmentRelat
//region PICK IT
abstract fun onFilePathReady(filePath: String?)
/**
* Called when a file was picked from file picker.
*/
private fun onFilePathReady(filePath: String?) {
if (filePath != null) {
vm.onAddFileToObject(
ctx = ctx,
target = target,
relationKey = relationKey,
filePath = filePath
)
} else {
Timber.e("Couldn't get file path")
}
}
private lateinit var pickiT: PickiT
@ -352,7 +344,7 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment<FragmentRelat
//region READ PERMISSION
private fun takeReadStoragePermission() {
if (requireActivity().shouldShowRequestPermissionRationaleCompat(READ_EXTERNAL_STORAGE)) {
binding.root.showSnackbar(
root.showSnackbar(
R.string.permission_read_rationale,
Snackbar.LENGTH_INDEFINITE,
R.string.button_ok
@ -370,7 +362,7 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment<FragmentRelat
if (readResult == true) {
startFilePicker(Mimetype.MIME_FILE_ALL)
} else {
binding.root.showSnackbar(R.string.permission_read_denied, Snackbar.LENGTH_SHORT)
root.showSnackbar(R.string.permission_read_denied, Snackbar.LENGTH_SHORT)
}
}
//endregion
@ -419,7 +411,7 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment<FragmentRelat
onFilePathReady(command.result)
}
CopyFileStatus.Started -> {
mSnackbar = binding.root.showSnackbar(
mSnackbar = root.showSnackbar(
R.string.loading_file,
Snackbar.LENGTH_INDEFINITE,
R.string.cancel
@ -445,13 +437,6 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment<FragmentRelat
abstract fun observeCommands(command: RelationValueBaseViewModel.ObjectRelationValueCommand)
override fun inflateBinding(
inflater: LayoutInflater,
container: ViewGroup?
): FragmentRelationValueBinding = FragmentRelationValueBinding.inflate(
inflater, container, false
)
companion object {
const val CTX_KEY = "arg.edit-cell-tag.ctx"
const val IS_LOCKED_KEY = "arg.edit-cell-tag.locked"
@ -461,248 +446,4 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment<FragmentRelat
const val VIEWER_KEY = "arg.edit-cell-tag.viewer"
const val TARGET_TYPES_KEY = "arg.relation-value.target-types"
}
}
open class RelationValueDVFragment : RelationValueBaseFragment() {
@Inject
lateinit var factory: RelationValueDVViewModel.Factory
override val vm: RelationValueDVViewModel by viewModels { factory }
override fun onObjectValueChanged(
ctx: Id,
objectId: Id,
relationKey: Key,
ids: List<Id>
) {
vm.onAddObjectsOrFilesValueToRecord(
record = objectId,
relationKey = relationKey,
ids = ids
)
}
override fun onFileValueChanged(ctx: Id, objectId: Id, relationKey: Key, ids: List<Id>) {
vm.onAddObjectsOrFilesValueToRecord(
record = objectId,
relationKey = relationKey,
ids = ids
)
}
override fun observeCommands(command: RelationValueBaseViewModel.ObjectRelationValueCommand) {
when (command) {
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddObjectScreen -> {
val fragmentFlow = AddObjectRelationFragment.FLOW_DATAVIEW
val fr = AddObjectRelationFragment.new(
ctx = ctx,
relationKey = relationKey,
objectId = target,
types = types,
flow = fragmentFlow
)
fr.showChildFragment()
}
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddStatusOrTagScreen -> {
val fr = AddOptionsRelationDVFragment.new(
ctx = ctx,
target = target,
relationKey = relationKey,
dataview = dataview,
viewer = viewer
)
fr.showChildFragment()
}
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddFileScreen -> {
val fr = AddFileRelationFragment.new(
ctx = ctx,
objectId = target,
flow = AddFileRelationFragment.FLOW_DATAVIEW,
relationKey = relationKey
)
fr.showChildFragment()
}
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowFileValueActionScreen -> {
//turn off for now https://app.clickup.com/t/h59z1j
//FileActionsFragment().showChildFragment()
openFilePicker()
}
}
}
override fun onFileValueActionAdd() {
vm.onFileValueActionAddClicked()
}
override fun onFileValueActionUploadFromGallery() {
toast("Not implemented")
vm.onFileValueActionUploadFromGalleryClicked()
}
/**
* Called when a file was picked from file picker.
*/
override fun onFilePathReady(filePath: String?) {
if (filePath != null) {
vm.onAddFileToObject(
ctx = ctx,
target = target,
relationKey = relationKey,
filePath = filePath
)
} else {
Timber.e("Couldn't get file path")
}
}
override fun onFileValueActionUploadFromStorage() {
toast("Not implemented")
vm.onFileValueActionUploadFromStorageClicked()
}
override fun PickiTonMultipleCompleteListener(
paths: ArrayList<String>?,
wasSuccessful: Boolean,
Reason: String?
) {
toast("Not implemented yet")
}
override fun injectDependencies() {
componentManager().objectSetObjectRelationValueComponent.get(ctx).inject(this)
}
override fun releaseDependencies() {
componentManager().objectSetObjectRelationValueComponent.release(ctx)
}
}
class RelationValueFragment : RelationValueBaseFragment() {
@Inject
lateinit var factory: RelationValueViewModel.Factory
override val vm: RelationValueViewModel by viewModels { factory }
override fun onObjectValueChanged(
ctx: Id,
objectId: Id,
relationKey: Key,
ids: List<Id>
) {
vm.onAddObjectsOrFilesValueToObject(
ctx = ctx,
target = objectId,
relationKey = relationKey,
ids = ids
)
}
override fun onFileValueChanged(ctx: Id, objectId: Id, relationKey: Key, ids: List<Id>) {
vm.onAddObjectsOrFilesValueToObject(
ctx = ctx,
target = objectId,
relationKey = relationKey,
ids = ids
)
}
override fun PickiTonMultipleCompleteListener(
paths: ArrayList<String>?,
wasSuccessful: Boolean,
Reason: String?
) {
toast("Not implemented yet")
}
override fun observeCommands(command: RelationValueBaseViewModel.ObjectRelationValueCommand) {
when (command) {
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddObjectScreen -> {
val fr = AddObjectRelationFragment.new(
ctx = ctx,
relationKey = relationKey,
objectId = target,
types = types
)
fr.showChildFragment()
}
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddStatusOrTagScreen -> {
val fr = AddOptionsRelationFragment.new(
ctx = ctx,
objectId = target,
relationKey = relationKey
)
fr.showChildFragment()
}
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddFileScreen -> {
val fr = AddFileRelationFragment.new(
ctx = ctx,
relationKey = relationKey,
objectId = target,
flow = AddFileRelationFragment.FLOW_DEFAULT
)
fr.showChildFragment()
}
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowFileValueActionScreen -> {
//turn off for now https://app.clickup.com/t/h59z1j
//FileActionsFragment().showChildFragment()
openFilePicker()
}
}
}
override fun onFileValueActionAdd() {
vm.onFileValueActionAddClicked()
}
override fun onFileValueActionUploadFromGallery() {
toast("Not implemented")
vm.onFileValueActionUploadFromGalleryClicked()
}
override fun onFileValueActionUploadFromStorage() {
toast("Not implemented")
vm.onFileValueActionUploadFromStorageClicked()
}
/**
* Called when a file was picked from file picker.
*/
override fun onFilePathReady(filePath: String?) {
if (filePath != null) {
vm.onAddFileToObject(
ctx = ctx,
target = target,
relationKey = relationKey,
filePath = filePath
)
} else {
Timber.e("Couldn't get file path")
}
}
override fun injectDependencies() {
componentManager().objectObjectRelationValueComponent.get(ctx).inject(this)
}
override fun releaseDependencies() {
componentManager().objectObjectRelationValueComponent.release(ctx)
}
companion object {
fun new(
ctx: Id,
target: Id,
relationKey: Key,
targetObjectTypes: List<Id>,
isLocked: Boolean = false
) = RelationValueFragment().apply {
arguments = bundleOf(
CTX_KEY to ctx,
TARGET_KEY to target,
RELATION_KEY to relationKey,
TARGET_TYPES_KEY to targetObjectTypes,
IS_LOCKED_KEY to isLocked
)
}
}
}

View File

@ -0,0 +1,186 @@
package com.anytypeio.anytype.ui.relations
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_ui.reactive.clicks
import com.anytypeio.anytype.core_utils.ext.drawable
import com.anytypeio.anytype.core_utils.ext.subscribe
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.databinding.FragmentRelationValueBinding
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.relations.RelationValueView
import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel
import com.anytypeio.anytype.presentation.sets.RelationValueDVViewModel
import com.anytypeio.anytype.ui.relations.add.AddFileRelationFragment
import com.anytypeio.anytype.ui.relations.add.AddObjectRelationFragment
import com.anytypeio.anytype.ui.relations.add.AddOptionsRelationDVFragment
import javax.inject.Inject
import timber.log.Timber
open class RelationValueDVFragment : RelationValueBaseFragment<FragmentRelationValueBinding>(),
FileActionsFragment.FileActionReceiver,
AddFileRelationFragment.FileValueAddReceiver {
@Inject
lateinit var factory: RelationValueDVViewModel.Factory
override val vm: RelationValueDVViewModel by viewModels { factory }
override val root: View
get() = binding.root
override val recycler: RecyclerView
get() = binding.recycler
override val btnAddValue: ImageView
get() = binding.btnAddValue
override val btnEditOrDone: TextView
get() = binding.btnEditOrDone
override val refresh: ProgressBar
get() = binding.refresh
override val tvRelationHeader: TextView
get() = binding.tvRelationHeader
override val onStatusClickedCallback: (RelationValueView.Option.Status) -> Unit = {}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recycler.apply {
layoutManager = LinearLayoutManager(context)
adapter = relationValueAdapter
}
dividerItem = DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply {
setDrawable(drawable(R.drawable.divider_relations))
}
dividerItemEdit = DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply {
setDrawable(drawable(R.drawable.divider_relations_edit))
}
with(lifecycleScope) {
subscribe(btnEditOrDone.clicks()) { vm.onEditOrDoneClicked(isLocked) }
subscribe(btnAddValue.clicks()) { vm.onAddValueClicked(isLocked) }
}
}
override fun observeViews(values: List<RelationValueView>) {
relationValueAdapter.update(values)
}
override fun onObjectValueChanged(
ctx: Id,
objectId: Id,
relationKey: Key,
ids: List<Id>
) {
vm.onAddObjectsOrFilesValueToRecord(
record = objectId,
relationKey = relationKey,
ids = ids
)
}
override fun onFileValueChanged(ctx: Id, objectId: Id, relationKey: Key, ids: List<Id>) {
vm.onAddObjectsOrFilesValueToRecord(
record = objectId,
relationKey = relationKey,
ids = ids
)
}
override fun observeCommands(command: RelationValueBaseViewModel.ObjectRelationValueCommand) {
when (command) {
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddObjectScreen -> {
showAddObjectScreen()
}
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddStatusOrTagScreen -> {
showAddStatusOrTagScreen()
}
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddFileScreen -> {
showAddFileScreen()
}
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowFileValueActionScreen -> {
//turn off for now https://app.clickup.com/t/h59z1j
//FileActionsFragment().showChildFragment()
openFilePicker()
}
}
}
private fun showAddObjectScreen() {
val fr = AddObjectRelationFragment.new(
ctx = ctx,
relationKey = relationKey,
objectId = target,
types = types,
flow = AddObjectRelationFragment.FLOW_DATAVIEW
)
fr.showChildFragment()
}
private fun showAddStatusOrTagScreen() {
val fr = AddOptionsRelationDVFragment.new(
ctx = ctx,
target = target,
relationKey = relationKey,
dataview = dataview,
viewer = viewer
)
fr.showChildFragment()
}
private fun showAddFileScreen() {
val fr = AddFileRelationFragment.new(
ctx = ctx,
objectId = target,
flow = AddFileRelationFragment.FLOW_DATAVIEW,
relationKey = relationKey
)
fr.showChildFragment()
}
override fun onFileValueActionAdd() {
vm.onFileValueActionAddClicked()
}
override fun onFileValueActionUploadFromGallery() {
toast("Not implemented")
vm.onFileValueActionUploadFromGalleryClicked()
}
override fun onFileValueActionUploadFromStorage() {
toast("Not implemented")
vm.onFileValueActionUploadFromStorageClicked()
}
override fun PickiTonMultipleCompleteListener(
paths: ArrayList<String>?,
wasSuccessful: Boolean,
Reason: String?
) {
toast("Not implemented yet")
}
override fun inflateBinding(
inflater: LayoutInflater,
container: ViewGroup?
): FragmentRelationValueBinding = FragmentRelationValueBinding.inflate(
inflater, container, false
)
override fun injectDependencies() {
componentManager().objectSetObjectRelationValueComponent.get(ctx).inject(this)
}
override fun releaseDependencies() {
componentManager().objectSetObjectRelationValueComponent.release(ctx)
}
}

View File

@ -0,0 +1,204 @@
package com.anytypeio.anytype.ui.relations
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.os.bundleOf
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_ui.reactive.clicks
import com.anytypeio.anytype.core_utils.ext.drawable
import com.anytypeio.anytype.core_utils.ext.subscribe
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.databinding.FragmentRelationValueBinding
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.relations.RelationValueView
import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel
import com.anytypeio.anytype.presentation.sets.RelationValueViewModel
import com.anytypeio.anytype.ui.relations.add.AddFileRelationFragment
import com.anytypeio.anytype.ui.relations.add.AddObjectRelationFragment
import com.anytypeio.anytype.ui.relations.add.AddOptionsRelationFragment
import javax.inject.Inject
import timber.log.Timber
class RelationValueFragment : RelationValueBaseFragment<FragmentRelationValueBinding>(),
FileActionsFragment.FileActionReceiver,
AddFileRelationFragment.FileValueAddReceiver {
@Inject
lateinit var factory: RelationValueViewModel.Factory
override val vm: RelationValueViewModel by viewModels { factory }
override val root: View
get() = binding.root
override val recycler: RecyclerView
get() = binding.recycler
override val btnAddValue: ImageView
get() = binding.btnAddValue
override val btnEditOrDone: TextView
get() = binding.btnEditOrDone
override val refresh: ProgressBar
get() = binding.refresh
override val tvRelationHeader: TextView
get() = binding.tvRelationHeader
override val onStatusClickedCallback: (RelationValueView.Option.Status) -> Unit = {}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recycler.apply {
layoutManager = LinearLayoutManager(context)
adapter = relationValueAdapter
}
dividerItem = DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply {
setDrawable(drawable(R.drawable.divider_relations))
}
dividerItemEdit = DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply {
setDrawable(drawable(R.drawable.divider_relations_edit))
}
with(lifecycleScope) {
subscribe(btnEditOrDone.clicks()) { vm.onEditOrDoneClicked(isLocked) }
subscribe(btnAddValue.clicks()) { vm.onAddValueClicked(isLocked) }
}
}
override fun observeViews(values: List<RelationValueView>) {
relationValueAdapter.update(values)
}
override fun onObjectValueChanged(
ctx: Id,
objectId: Id,
relationKey: Key,
ids: List<Id>
) {
vm.onAddObjectsOrFilesValueToObject(
ctx = ctx,
target = objectId,
relationKey = relationKey,
ids = ids
)
}
override fun onFileValueChanged(ctx: Id, objectId: Id, relationKey: Key, ids: List<Id>) {
vm.onAddObjectsOrFilesValueToObject(
ctx = ctx,
target = objectId,
relationKey = relationKey,
ids = ids
)
}
override fun PickiTonMultipleCompleteListener(
paths: ArrayList<String>?,
wasSuccessful: Boolean,
Reason: String?
) {
toast("Not implemented yet")
}
override fun observeCommands(command: RelationValueBaseViewModel.ObjectRelationValueCommand) {
when (command) {
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddObjectScreen -> {
showAddObjectScreen()
}
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddStatusOrTagScreen -> {
showAddStatusOrTagScreen()
}
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddFileScreen -> {
showAddFileScreen()
}
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowFileValueActionScreen -> {
//turn off for now https://app.clickup.com/t/h59z1j
//FileActionsFragment().showChildFragment()
openFilePicker()
}
}
}
private fun showAddFileScreen() {
val fr = AddFileRelationFragment.new(
ctx = ctx,
relationKey = relationKey,
objectId = target,
flow = AddFileRelationFragment.FLOW_DEFAULT
)
fr.showChildFragment()
}
private fun showAddStatusOrTagScreen() {
val fr = AddOptionsRelationFragment.new(
ctx = ctx,
objectId = target,
relationKey = relationKey
)
fr.showChildFragment()
}
private fun showAddObjectScreen() {
val fr = AddObjectRelationFragment.new(
ctx = ctx,
relationKey = relationKey,
objectId = target,
types = types
)
fr.showChildFragment()
}
override fun onFileValueActionAdd() {
vm.onFileValueActionAddClicked()
}
override fun onFileValueActionUploadFromGallery() {
toast("Not implemented")
vm.onFileValueActionUploadFromGalleryClicked()
}
override fun onFileValueActionUploadFromStorage() {
toast("Not implemented")
vm.onFileValueActionUploadFromStorageClicked()
}
override fun inflateBinding(
inflater: LayoutInflater,
container: ViewGroup?
) = FragmentRelationValueBinding.inflate(
inflater, container, false
)
override fun injectDependencies() {
componentManager().objectObjectRelationValueComponent.get(ctx).inject(this)
}
override fun releaseDependencies() {
componentManager().objectObjectRelationValueComponent.release(ctx)
}
companion object {
fun new(
ctx: Id,
target: Id,
relationKey: Key,
targetObjectTypes: List<Id>,
isLocked: Boolean = false
) = RelationValueFragment().apply {
arguments = bundleOf(
CTX_KEY to ctx,
TARGET_KEY to target,
RELATION_KEY to relationKey,
TARGET_TYPES_KEY to targetObjectTypes,
IS_LOCKED_KEY to isLocked
)
}
}
}

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="match_parent"
android:id="@+id/root"
android:orientation="vertical"
tools:context=".ui.relations.RelationValueBaseFragment">
<View
android:id="@+id/dragger"
android:layout_width="48dp"
android:layout_height="4dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="6dp"
android:background="@drawable/dragger" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp">
<TextView
android:id="@+id/btnEditOrDone"
style="@style/DefaultCellInteractionTextButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/dp_16"
android:paddingTop="@dimen/dp_12"
android:paddingBottom="@dimen/dp_12"
android:text="@string/edit"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone" />
<TextView
android:id="@+id/tvRelationHeader"
style="@style/DefaultCellRelationHeaderStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="52dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="52dp"
android:layout_marginBottom="12dp"
android:layout_weight="1"
android:gravity="center"
android:maxLines="1"
android:singleLine="true"
android:ellipsize="end"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Tags" />
<ImageView
android:id="@+id/btnAddValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingEnd="@dimen/dp_16"
android:paddingTop="@dimen/dp_12"
android:paddingBottom="@dimen/dp_12"
android:contentDescription="@string/content_description_plus_button"
android:src="@drawable/ic_dv_modal_plus"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/btnClear"
style="@style/DefaultCellInteractionTextButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/dp_12"
android:paddingBottom="@dimen/dp_12"
android:text="@string/clear"
android:layout_marginEnd="@dimen/dp_16"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<ProgressBar
android:id="@+id/refresh"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:layout_marginEnd="16dp"
android:theme="@style/GreyProgressBar"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="invisible" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/dp_20"
android:layout_marginEnd="@dimen/dp_20"
android:layout_marginBottom="@dimen/dp_20"
tools:listitem="@layout/item_edit_cell_tag" />
<TextView
android:id="@+id/tvEmptyPlaceholder"
style="@style/DefaultListPlaceholderTextStyle"
android:layout_marginTop="@dimen/dp_12"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/relation_status_value_empty_state"
android:layout_marginStart="@dimen/dp_20"
android:layout_marginEnd="@dimen/dp_20"
android:layout_marginBottom="@dimen/dp_32" />
</LinearLayout>

View File

@ -36,7 +36,7 @@
tools:visibility="visible" />
<TextView
android:id="@+id/tvTagOrStatusRelationHeader"
android:id="@+id/tvRelationHeader"
style="@style/DefaultCellRelationHeaderStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"

View File

@ -171,6 +171,7 @@ Do the computation of an expensive paragraph of text on a background thread:
<string name="delete">Delete</string>
<string name="view_relations">View relations</string>
<string name="edit">Edit</string>
<string name="clear">Clear</string>
<string name="views">Views</string>
<string name="create">Create</string>
<string name="new_view">New view</string>

View File

@ -121,6 +121,7 @@
<string name="sort">Sort</string>
<string name="sort_empty_state">No sorts here. You can add some</string>
<string name="filter_empty_state">No filters here. You can add some</string>
<string name="relation_status_value_empty_state">No related options here. You can add some</string>
<string name="filter_by">Filter by</string>
<string name="filter">Filter</string>
<string name="modal_view">View</string>

View File

@ -428,6 +428,13 @@
<item name="android:fontFamily">@font/inter_regular</item>
</style>
<style name="DefaultListPlaceholderTextStyle">
<item name="android:gravity">center</item>
<item name="android:textColor">@color/editor_default_hint_text_color</item>
<item name="android:textSize">15sp</item>
<item name="android:fontFamily">@font/inter_regular</item>
</style>
<style name="ViewerModalStateButtonsStyle">
<item name="android:textColor">@color/text_secondary</item>
<item name="android:textSize">17sp</item>

View File

@ -271,7 +271,18 @@ class RelationListViewModel(
)
)
}
RelationFormat.STATUS,
RelationFormat.STATUS -> {
commands.emit(
Command.EditStatusRelationValue(
ctx = ctx,
relationId = relation.id,
relationKey = relation.key,
target = ctx,
targetObjectTypes = relation.relationFormatObjectTypes,
isLocked = resolveIsLockedState(ctx)
)
)
}
RelationFormat.TAG,
RelationFormat.FILE,
RelationFormat.OBJECT -> {
@ -408,6 +419,15 @@ class RelationListViewModel(
val isLocked: Boolean = false
) : Command()
data class EditStatusRelationValue(
val ctx: Id,
val relationId: Id,
val relationKey: Key,
val target: Id,
val targetObjectTypes: List<Id>,
val isLocked: Boolean = false
) : Command()
data class SetRelationKey(
val blockId: Id,
val key: Id

View File

@ -320,11 +320,14 @@ abstract class RelationValueBaseViewModel(
fun onRemoveStatusFromObjectClicked(
target: Id,
relationKey: Key,
status: Id
status: Id? = null
) {
viewModelScope.launch {
val statusId = status ?: ((views.value.first {
it is RelationValueView.Option.Status
}) as? RelationValueView.Option.Status)?.id ?: return@launch
val obj = values.get(target)
val remaining = obj[relationKey].filterIdsById(status)
val remaining = obj[relationKey].filterIdsById(statusId)
setObjectDetails(
UpdateDetail.Params(
ctx = target,