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

DROID-2492 Sets | Enhancement | Support empty values sorting position (#1343)

This commit is contained in:
Konstantin Ivanov 2024-07-01 14:06:56 +02:00 committed by GitHub
parent 522363e7d0
commit c5e2731fbf
Signed by: github
GPG key ID: B5690EEEBB952194
11 changed files with 288 additions and 76 deletions

View file

@ -8,6 +8,7 @@ import androidx.core.os.bundleOf
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.DVSortEmptyType
import com.anytypeio.anytype.core_models.DVSortType
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
@ -42,12 +43,18 @@ class ModifyViewerSortFragment : BaseBottomSheetFragment<FragmentModifyViewerSor
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
with(lifecycleScope) {
subscribe(binding.tvSortAsc.clicks()) {
subscribe(binding.flSortAsc.clicks()) {
vm.onSortAscSelected(ctx = ctx, viewerId = viewer, sortId = sortId)
}
subscribe(binding.tvSortDesc.clicks()) {
subscribe(binding.flSortDesc.clicks()) {
vm.onSortDescSelected(ctx = ctx, viewerId = viewer, sortId = sortId)
}
subscribe(binding.flSortEmptyBottom.clicks()) {
vm.onEmptyBottomSelected(ctx = ctx, viewerId = viewer, sortId = sortId)
}
subscribe(binding.flSortEmptyUp.clicks()) {
vm.onEmptyUpSelected(ctx = ctx, viewerId = viewer, sortId = sortId)
}
}
}
@ -70,6 +77,20 @@ class ModifyViewerSortFragment : BaseBottomSheetFragment<FragmentModifyViewerSor
}
else -> {}
}
when (state.emptyType) {
DVSortEmptyType.START -> {
ivEmptyUpSelected.visible()
ivEmptyBottomSelected.invisible()
}
DVSortEmptyType.END -> {
ivEmptyUpSelected.invisible()
ivEmptyBottomSelected.visible()
}
else -> {
ivEmptyUpSelected.invisible()
ivEmptyBottomSelected.invisible()
}
}
}
}
}

View file

@ -1,99 +1,188 @@
<?xml version="1.0" encoding="utf-8"?>
<!--typography, buttons 05.04-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<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="wrap_content">
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="28dp">
<androidx.constraintlayout.widget.ConstraintLayout
<ImageView
android:id="@+id/dragger"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="6dp"
android:contentDescription="@string/content_description_modal_icon"
android:src="@drawable/sheet_top"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/txtName"
style="@style/TextView.UXStyle.Titles.1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="18dp">
android:layout_marginTop="18dp"
android:gravity="center"
tools:text="Object" />
<ImageView
android:id="@+id/dragger"
android:layout_width="wrap_content"
<FrameLayout
android:id="@+id/flSortAsc"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvSortAsc"
style="@style/TextView.UXStyle.Body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="6dp"
android:contentDescription="@string/content_description_modal_icon"
android:src="@drawable/sheet_top"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:layout_marginStart="20dp"
android:layout_marginEnd="44dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
tools:text="@string/sort_from_a_to_z" />
<ImageView
android:id="@+id/ivAscSelected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_gravity="center_vertical|end"
android:layout_marginEnd="20dp"
android:contentDescription="@string/content_description_check_icon"
android:src="@drawable/ic_viewer_chosen"
app:layout_constraintBottom_toBottomOf="@+id/tvSortAsc"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/tvSortAsc" />
</FrameLayout>
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@drawable/divider" />
<FrameLayout
android:id="@+id/flSortDesc"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvSortAsc"
android:id="@+id/tvSortDesc"
style="@style/TextView.UXStyle.Body"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toStartOf="@+id/ivAscSelected"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/txtName"
android:layout_marginEnd="44dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
tools:text="@string/sort_from_a_to_z" />
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginStart="20dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="12dp"
android:background="@drawable/divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvSortAsc" />
<ImageView
android:id="@+id/ivDescSelected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_gravity="center_vertical|end"
android:layout_marginEnd="20dp"
android:contentDescription="@string/content_description_check_icon"
android:src="@drawable/ic_viewer_chosen"
app:layout_constraintBottom_toBottomOf="@+id/tvSortDesc"
app:layout_constraintBottom_toBottomOf="@+id/tvSortAsc"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/tvSortDesc" />
app:layout_constraintTop_toTopOf="@+id/tvSortAsc" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@drawable/divider" />
<TextView
android:id="@+id/tvSectionEmptyValues"
style="@style/TextView.UXStyle.Captions.1.Regular"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="26dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="8dp"
android:text="@string/sort_empty_values_desc"
android:textColor="@color/text_secondary" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@drawable/divider" />
<FrameLayout
android:id="@+id/flSortEmptyBottom"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvSortDesc"
android:id="@+id/tvSectionEmptyBottom"
style="@style/TextView.UXStyle.Body"
android:layout_width="0dp"
android:layout_height="19dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toStartOf="@+id/ivAscSelected"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view"
tools:text="@string/sort_from_z_to_a" />
android:layout_marginEnd="44dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:text="@string/sort_empty_values_bottom" />
<TextView
android:id="@+id/txtName"
style="@style/TextView.UXStyle.Titles.1"
<ImageView
android:id="@+id/ivEmptyBottomSelected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
tools:text="Object"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/dragger" />
android:layout_gravity="center_vertical|end"
android:layout_marginEnd="20dp"
android:contentDescription="@string/content_desc_empty_values_bottom_check_icon"
android:src="@drawable/ic_viewer_chosen" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@drawable/divider" />
<FrameLayout
android:id="@+id/flSortEmptyUp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvSectionEmptyUp"
style="@style/TextView.UXStyle.Body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="44dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:text="@string/sort_empty_values_top" />
<ImageView
android:id="@+id/ivEmptyUpSelected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:layout_marginEnd="20dp"
android:contentDescription="@string/content_desc_empty_values_top_check_icon"
android:src="@drawable/ic_viewer_chosen" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@drawable/divider" />
</LinearLayout>

View file

@ -25,6 +25,7 @@ typealias DVFilterQuickOption = Block.Content.DataView.Filter.QuickOption
typealias DVFilterOperator = Block.Content.DataView.Filter.Operator
typealias DVSort = Block.Content.DataView.Sort
typealias DVSortType = Block.Content.DataView.Sort.Type
typealias DVSortEmptyType = Block.Content.DataView.Sort.EmptyType
typealias DVDateFormat = Block.Content.DataView.DateFormat
typealias DVTimeFormat = Block.Content.DataView.TimeFormat
typealias DVViewerRelation = Block.Content.DataView.Viewer.ViewerRelation

View file

@ -330,13 +330,18 @@ data class Block(
val type: Type,
val includeTime: Boolean = false,
val customOrder: List<Id> = emptyList(),
val relationFormat: RelationFormat
val relationFormat: RelationFormat,
val emptyType: EmptyType? =null
) {
enum class Type(val formattedName: String) {
ASC("ascending"),
DESC("descending"),
CUSTOM("custom")
}
enum class EmptyType(val formattedName: String) {
NOT_SPECIFIC("None"), START("Start"), END("End")
}
}
/**

View file

@ -131,8 +131,8 @@ class ViewerGridAdapter(
when (row.layout) {
Layout.TODO -> binding.objectIcon.setTask(row.isChecked)
Layout.BASIC, Layout.BOOKMARK, Layout.SET, Layout.COLLECTION -> {
if ((row.image != null || row.emoji != null)) {
if (row.image != null) {
if (!row.image.isNullOrEmpty() || !row.emoji.isNullOrEmpty()) {
if (!row.image.isNullOrEmpty()) {
binding.objectIcon.setRectangularImage(row.image)
} else {
binding.objectIcon.setEmoji(row.emoji)
@ -142,7 +142,7 @@ class ViewerGridAdapter(
}
}
Layout.PROFILE -> {
if (row.image != null) {
if (!row.image.isNullOrEmpty()) {
binding.objectIcon.setCircularImage(row.image)
} else {
binding.objectIcon.setProfileInitials(row.name.orEmpty())

View file

@ -737,6 +737,8 @@
<string name="reset">Reset</string>
<string name="search_a_relation">Search a relation</string>
<string name="content_description_check_icon">Check icon</string>
<string name="content_desc_empty_values_bottom_check_icon">Sort empty values at the bottom</string>
<string name="content_desc_empty_values_top_check_icon">Sort empty values at the top</string>
<string name="filter_condition_is">Is</string>
<string name="today">Today</string>
<string name="yesterday">Yesterday</string>
@ -1678,4 +1680,8 @@ Please provide specific details of your needs here.</string>
<string name="global_search_related_to">"Related to: "</string>
<string name="global_search_no_related_objects_found">No related objects found</string>
<string name="sort_empty_values_desc">Show empty values</string>
<string name="sort_empty_values_bottom">On bottom</string>
<string name="sort_empty_values_top">On top</string>
</resources>

View file

@ -38,6 +38,7 @@ typealias MDVViewType = anytype.model.Block.Content.Dataview.View.Type
typealias MDVViewCardSize = anytype.model.Block.Content.Dataview.View.Size
typealias MDVSort = anytype.model.Block.Content.Dataview.Sort
typealias MDVSortType = anytype.model.Block.Content.Dataview.Sort.Type
typealias MDVSortEmptyType = anytype.model.Block.Content.Dataview.Sort.EmptyType
typealias MDVFilter = anytype.model.Block.Content.Dataview.Filter
typealias MDVFilterCondition = anytype.model.Block.Content.Dataview.Filter.Condition
typealias MDVFilterQuickOption = anytype.model.Block.Content.Dataview.Filter.QuickOption

View file

@ -17,6 +17,7 @@ import com.anytypeio.anytype.core_models.DVFilterCondition
import com.anytypeio.anytype.core_models.DVFilterOperator
import com.anytypeio.anytype.core_models.DVFilterQuickOption
import com.anytypeio.anytype.core_models.DVSort
import com.anytypeio.anytype.core_models.DVSortEmptyType
import com.anytypeio.anytype.core_models.DVSortType
import com.anytypeio.anytype.core_models.DVTimeFormat
import com.anytypeio.anytype.core_models.DVViewer
@ -632,9 +633,16 @@ fun MDVSort.toCoreModels(): Block.Content.DataView.Sort = DVSort(
customOrder = customOrder.mapNotNull { value ->
if (value is Id) value else null
},
relationFormat = format.format()
relationFormat = format.format(),
emptyType = emptyPlacement.toCoreModels()
)
fun MDVSortEmptyType.toCoreModels(): DVSortEmptyType = when (this) {
MDVSortEmptyType.NotSpecified -> DVSortEmptyType.NOT_SPECIFIC
MDVSortEmptyType.Start -> DVSortEmptyType.START
MDVSortEmptyType.End -> DVSortEmptyType.END
}
fun MDVSortType.toCoreModels(): DVSortType = when (this) {
MDVSortType.Asc -> DVSortType.ASC
MDVSortType.Desc -> DVSortType.DESC

View file

@ -7,6 +7,7 @@ import anytype.model.RelationFormat
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.BlockSplitMode
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.DVSortEmptyType
import com.anytypeio.anytype.core_models.InternalFlags
import com.anytypeio.anytype.core_models.NetworkMode
import com.anytypeio.anytype.core_models.ObjectType
@ -350,15 +351,35 @@ fun Block.Content.DataView.Viewer.Type.toMiddlewareModel(): MDVViewType = when (
Block.Content.DataView.Viewer.Type.GRAPH -> MDVViewType.Graph
}
fun Block.Content.DataView.Sort.toMiddlewareModel(): MDVSort =
MDVSort(
id = id,
RelationKey = relationKey,
type = type.toMiddlewareModel(),
includeTime = includeTime,
customOrder = customOrder,
format = relationFormat.toMiddlewareModel()
)
fun Block.Content.DataView.Sort.toMiddlewareModel(): MDVSort {
val emptyType = this.emptyType
return if (emptyType != null) {
MDVSort(
id = id,
RelationKey = relationKey,
type = type.toMiddlewareModel(),
includeTime = includeTime,
customOrder = customOrder,
format = relationFormat.toMiddlewareModel(),
emptyPlacement = emptyType.toMiddlewareModel()
)
} else {
MDVSort(
id = id,
RelationKey = relationKey,
type = type.toMiddlewareModel(),
includeTime = includeTime,
customOrder = customOrder,
format = relationFormat.toMiddlewareModel(),
)
}
}
fun DVSortEmptyType.toMiddlewareModel(): MDVSortEmptyType = when (this) {
DVSortEmptyType.NOT_SPECIFIC -> MDVSortEmptyType.NotSpecified
DVSortEmptyType.START -> MDVSortEmptyType.Start
DVSortEmptyType.END -> MDVSortEmptyType.End
}
fun Block.Content.DataView.Sort.Type.toMiddlewareModel(): MDVSortType = when (this) {
Block.Content.DataView.Sort.Type.ASC -> MDVSortType.Asc

View file

@ -17,7 +17,6 @@ import com.anytypeio.anytype.presentation.editor.editor.mention.createMentionMar
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.UiBlock
import com.anytypeio.anytype.presentation.extension.getUrlForFileBlock
import com.anytypeio.anytype.presentation.extension.getUrlForFileContent
import com.anytypeio.anytype.presentation.objects.ObjectLayoutView
import com.anytypeio.anytype.presentation.objects.ObjectTypeView

View file

@ -45,7 +45,8 @@ class ModifyViewerSortViewModel(
viewState.value = ViewState(
format = relation.format,
type = sort.type,
name = relation.name.orEmpty()
name = relation.name.orEmpty(),
emptyType = sort.emptyType
)
} else {
Timber.e("Couldn't find relation in StoreOfRelations by relationKey:[$relationKey]")
@ -82,6 +83,26 @@ class ModifyViewerSortViewModel(
)
}
fun onEmptyBottomSelected(ctx: Id, viewerId: Id, sortId: Id) {
Timber.d("onEmptyBottomSelected, ctx:[$ctx], sortId:[$sortId]")
proceedWithUpdatingEmptySortType(
ctx = ctx,
sortId = sortId,
type = DVSortEmptyType.END,
viewerId = viewerId
)
}
fun onEmptyUpSelected(ctx: Id, viewerId: Id, sortId: Id) {
Timber.d("onEmptyUpSelected, ctx:[$ctx], sortId:[$sortId]")
proceedWithUpdatingEmptySortType(
ctx = ctx,
sortId = sortId,
type = DVSortEmptyType.START,
viewerId = viewerId
)
}
private fun proceedWithUpdatingSortType(
ctx: Id,
viewerId: Id,
@ -121,9 +142,49 @@ class ModifyViewerSortViewModel(
}
}
private fun proceedWithUpdatingEmptySortType(
ctx: Id,
viewerId: Id,
sortId: Id,
type: DVSortEmptyType
) {
val state = objectState.value.dataViewState() ?: return
val viewer = state.viewerById(viewerId) ?: return
val sort = viewer.sorts.find { it.id == sortId }
if (sort == null) {
Timber.e("Couldn't find sort in view:[$viewer] by sortId:[$sortId]")
return
}
val startTime = System.currentTimeMillis()
viewModelScope.launch {
val params = UpdateDataViewViewer.Params.Sort.Replace(
ctx = ctx,
dv = state.dataViewBlock.id,
view = viewer.id,
sort = sort.copy(emptyType = type)
)
updateDataViewViewer.async(params).fold(
onSuccess = {
dispatcher.send(it).also {
logEvent(
state = objectState.value,
analytics = analytics,
event = ObjectStateAnalyticsEvent.CHANGE_SORT_VALUE,
startTime = startTime,
type = type.formattedName
)
isDismissed.emit(true)
}
},
onFailure = { Timber.e(it, "Error while updating empty sort type") }
)
}
}
class ViewState(
val format: Relation.Format,
val type: DVSortType,
val emptyType: DVSortEmptyType?,
val name: String
)