DROID-824 Library | Enhancement | Added reactive changes on library support (#2883)
DROID-824 Library | Enhancement | Added reactive changes on library support
This commit is contained in:
parent
b89099c915
commit
7b373339f0
|
@ -3,10 +3,12 @@ package com.anytypeio.anytype.di.feature.library
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
|
||||
import com.anytypeio.anytype.domain.workspace.WorkspaceManager
|
||||
import com.anytypeio.anytype.presentation.library.LibraryInteractor
|
||||
import com.anytypeio.anytype.presentation.library.LibraryListDelegate
|
||||
import com.anytypeio.anytype.presentation.library.LibraryViewModel
|
||||
import com.anytypeio.anytype.presentation.library.delegates.LibraryRelationsDelegate
|
||||
|
@ -18,6 +20,7 @@ import dagger.Binds
|
|||
import dagger.Component
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
@Component(
|
||||
dependencies = [LibraryDependencies::class],
|
||||
|
@ -43,33 +46,49 @@ object LibraryModule {
|
|||
@PerScreen
|
||||
@Provides
|
||||
fun provideMyTypesDelegate(
|
||||
interactor: LibraryInteractor,
|
||||
workspaceManager: WorkspaceManager
|
||||
container: StorelessSubscriptionContainer,
|
||||
workspaceManager: WorkspaceManager,
|
||||
urlBuilder: UrlBuilder
|
||||
): LibraryListDelegate {
|
||||
return MyTypesDelegate(interactor, workspaceManager)
|
||||
return MyTypesDelegate(container, workspaceManager, urlBuilder)
|
||||
}
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideLibTypesDelegate(interactor: LibraryInteractor): LibraryListDelegate {
|
||||
return LibraryTypesDelegate(interactor)
|
||||
fun provideLibTypesDelegate(
|
||||
container: StorelessSubscriptionContainer,
|
||||
urlBuilder: UrlBuilder
|
||||
): LibraryListDelegate {
|
||||
return LibraryTypesDelegate(container, urlBuilder)
|
||||
}
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideMyRelationsDelegate(
|
||||
interactor: LibraryInteractor,
|
||||
workspaceManager: WorkspaceManager
|
||||
container: StorelessSubscriptionContainer,
|
||||
workspaceManager: WorkspaceManager,
|
||||
urlBuilder: UrlBuilder
|
||||
): LibraryListDelegate {
|
||||
return MyRelationsDelegate(interactor, workspaceManager)
|
||||
return MyRelationsDelegate(container, workspaceManager, urlBuilder)
|
||||
}
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideLibRelationsDelegate(interactor: LibraryInteractor): LibraryListDelegate {
|
||||
return LibraryRelationsDelegate(interactor)
|
||||
fun provideLibRelationsDelegate(
|
||||
container: StorelessSubscriptionContainer,
|
||||
urlBuilder: UrlBuilder
|
||||
): LibraryListDelegate {
|
||||
return LibraryRelationsDelegate(container, urlBuilder)
|
||||
}
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideAppCoroutineDispatchers() : AppCoroutineDispatchers = AppCoroutineDispatchers(
|
||||
io = Dispatchers.IO,
|
||||
computation = Dispatchers.Default,
|
||||
main = Dispatchers.Main
|
||||
)
|
||||
|
||||
@Module
|
||||
interface Declarations {
|
||||
|
||||
|
@ -79,7 +98,7 @@ object LibraryModule {
|
|||
|
||||
@PerScreen
|
||||
@Binds
|
||||
fun bindInteractor(interactor: LibraryInteractor.Impl): LibraryInteractor
|
||||
fun bindContainer(container: StorelessSubscriptionContainer.Impl): StorelessSubscriptionContainer
|
||||
|
||||
}
|
||||
|
||||
|
@ -89,4 +108,5 @@ interface LibraryDependencies : ComponentDependencies {
|
|||
fun blockRepository(): BlockRepository
|
||||
fun workspaceManager(): WorkspaceManager
|
||||
fun urlBuilder(): UrlBuilder
|
||||
fun channel(): SubscriptionEventChannel
|
||||
}
|
|
@ -76,7 +76,7 @@ fun TabContentScreen(
|
|||
verticalArrangement = Arrangement.Top,
|
||||
) {
|
||||
Text(
|
||||
color = colorResource(id = R.color.black),
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
text = stringResource(config.description),
|
||||
style = MaterialTheme.typography.h1,
|
||||
textAlign = TextAlign.Center,
|
||||
|
@ -85,7 +85,9 @@ fun TabContentScreen(
|
|||
Box(Modifier.height(18.dp))
|
||||
Button(
|
||||
onClick = { /*TODO*/ },
|
||||
colors = ButtonDefaults.buttonColors(backgroundColor = colorResource(id = R.color.black)),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
backgroundColor = colorResource(id = R.color.glyph_selected)
|
||||
),
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
contentPadding = PaddingValues(
|
||||
28.dp, 10.dp, 28.dp, 10.dp
|
||||
|
@ -93,7 +95,7 @@ fun TabContentScreen(
|
|||
content = {
|
||||
Text(
|
||||
text = stringResource(config.mainBtnTitle),
|
||||
color = colorResource(id = R.color.library_action_btn_text_color),
|
||||
color = colorResource(id = R.color.text_white),
|
||||
fontSize = 17.sp,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
|
|
|
@ -52,13 +52,13 @@ fun LibraryListSearchWidget(
|
|||
},
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
textColor = colorResource(id = R.color.text_primary),
|
||||
backgroundColor = colorResource(id = R.color.light_grayish),
|
||||
backgroundColor = colorResource(id = R.color.shape_transparent),
|
||||
disabledBorderColor = Color.Transparent,
|
||||
errorBorderColor = Color.Transparent,
|
||||
focusedBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent,
|
||||
placeholderColor = colorResource(id = R.color.text_tertiary),
|
||||
cursorColor = colorResource(id = R.color.black)
|
||||
placeholderColor = colorResource(id = R.color.glyph_active),
|
||||
cursorColor = colorResource(id = R.color.text_primary)
|
||||
),
|
||||
singleLine = true,
|
||||
maxLines = 1,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.anytypeio.anytype.ui.library.views.list
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
|
@ -10,17 +9,15 @@ import androidx.compose.foundation.layout.height
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.OBJECT_TYPE as MY_TYPE
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds.RELATION as MY_RELATION
|
||||
import com.anytypeio.anytype.core_models.MarketplaceObjectTypeIds.OBJECT_TYPE as LIB_TYPE
|
||||
import com.anytypeio.anytype.core_models.MarketplaceObjectTypeIds.RELATION as LIB_RELATION
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.presentation.library.LibraryEvent
|
||||
import com.anytypeio.anytype.presentation.library.LibraryScreenState
|
||||
import com.anytypeio.anytype.presentation.navigation.LibraryView
|
||||
import com.anytypeio.anytype.ui.library.LibraryListConfig
|
||||
import com.anytypeio.anytype.ui.library.views.list.items.ItemDefaults
|
||||
import com.anytypeio.anytype.ui.library.views.list.items.LibRelationItem
|
||||
|
@ -64,29 +61,59 @@ fun LibraryListTabsContent(
|
|||
.fillMaxHeight(),
|
||||
contentPadding = PaddingValues(start = 16.dp, end = 16.dp)
|
||||
) {
|
||||
items(data.items.size) { index ->
|
||||
val item = data.items[index]
|
||||
when (item.type) {
|
||||
LIB_TYPE -> {
|
||||
LibTypeItem(modifier = itemModifier, item = item)
|
||||
|
||||
items(
|
||||
count = data.items.size,
|
||||
key = { index ->
|
||||
data.items[index].id
|
||||
},
|
||||
itemContent = { ix ->
|
||||
when (val item = data.items[ix]) {
|
||||
is LibraryView.LibraryTypeView -> {
|
||||
LibTypeItem(
|
||||
name = item.name,
|
||||
icon = item.icon,
|
||||
installed = item.installed,
|
||||
modifier = itemModifier
|
||||
)
|
||||
}
|
||||
is LibraryView.MyTypeView -> {
|
||||
MyTypeItem(
|
||||
name = item.name,
|
||||
icon = item.icon,
|
||||
readOnly = item.readOnly,
|
||||
modifier = itemModifier
|
||||
)
|
||||
}
|
||||
is LibraryView.LibraryRelationView -> {
|
||||
LibRelationItem(
|
||||
modifier = itemModifier,
|
||||
name = item.name,
|
||||
format = item.format,
|
||||
installed = item.installed
|
||||
)
|
||||
}
|
||||
is LibraryView.MyRelationView -> {
|
||||
MyRelationItem(
|
||||
modifier = itemModifier,
|
||||
name = item.name,
|
||||
readOnly = item.readOnly,
|
||||
format = item.format
|
||||
)
|
||||
}
|
||||
is LibraryView.UnknownView -> {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
MY_TYPE -> {
|
||||
MyTypeItem(modifier = itemModifier, item = item)
|
||||
}
|
||||
LIB_RELATION -> {
|
||||
LibRelationItem(modifier = itemModifier, item = item)
|
||||
}
|
||||
MY_RELATION -> {
|
||||
MyRelationItem(modifier = itemModifier, item = item)
|
||||
}
|
||||
else -> {
|
||||
Timber.d("Unknown item type: ${item.type}")
|
||||
if (ix < data.items.lastIndex) {
|
||||
Divider(
|
||||
thickness = 1.dp,
|
||||
modifier = Modifier.padding(start = 4.dp, end = 4.dp),
|
||||
color = colorResource(id = R.color.shape_primary)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (index < data.items.size.minus(1)) {
|
||||
Divider(thickness = 1.dp, modifier = Modifier.padding(start = 4.dp, end = 4.dp))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,92 +1,161 @@
|
|||
package com.anytypeio.anytype.ui.library.views.list.items
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
|
||||
import com.anytypeio.anytype.core_ui.widgets.ObjectIconWidget
|
||||
import com.anytypeio.anytype.core_ui.widgets.RelationFormatIconWidget
|
||||
import com.anytypeio.anytype.presentation.navigation.LibraryView
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.ui.library.views.list.items.ItemDefaults.TEXT_PADDING_START
|
||||
|
||||
@Composable
|
||||
fun MyTypeItem(item: LibraryView, modifier: Modifier) {
|
||||
fun MyTypeItem(
|
||||
name: String,
|
||||
icon: ObjectIcon?,
|
||||
readOnly: Boolean = false,
|
||||
modifier: Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier,
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(icon = item.icon)
|
||||
Icon(icon = icon)
|
||||
Text(
|
||||
text = item.name,
|
||||
text = name,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier
|
||||
.padding(start = TEXT_PADDING_START)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LibTypeItem(item: LibraryView, modifier: Modifier) {
|
||||
Row(
|
||||
modifier,
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(icon = item.icon)
|
||||
Text(
|
||||
text = item.name,
|
||||
modifier = Modifier
|
||||
.padding(start = TEXT_PADDING_START)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyRelationItem(item: LibraryView, modifier: Modifier) {
|
||||
Row(
|
||||
modifier,
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = item.name,
|
||||
modifier = Modifier
|
||||
.padding(start = TEXT_PADDING_START)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LibRelationItem(item: LibraryView, modifier: Modifier) {
|
||||
Row(
|
||||
modifier,
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = item.name,
|
||||
modifier = Modifier
|
||||
.padding(start = TEXT_PADDING_START)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Icon(icon: ObjectIcon) {
|
||||
AndroidView(factory = { ctx ->
|
||||
ObjectIconWidget(ctx).apply {
|
||||
setIcon(icon)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
if (readOnly) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_object_locked),
|
||||
contentDescription = "",
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LibTypeItem(
|
||||
name: String,
|
||||
icon: ObjectIcon?,
|
||||
installed: Boolean = false,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier,
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(icon = icon)
|
||||
Text(
|
||||
text = name,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier
|
||||
.padding(start = TEXT_PADDING_START)
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
val installedImageRes = if (installed) {
|
||||
R.drawable.ic_type_installed
|
||||
} else {
|
||||
R.drawable.ic_type_not_installed
|
||||
}
|
||||
Image(
|
||||
painter = painterResource(id = installedImageRes),
|
||||
contentDescription = installedImageRes.toString(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyRelationItem(
|
||||
modifier: Modifier,
|
||||
name: String,
|
||||
format: RelationFormat,
|
||||
readOnly: Boolean = false,
|
||||
) {
|
||||
Row(
|
||||
modifier,
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
format.simpleIcon()?.let {
|
||||
Image(painter = painterResource(id = it), contentDescription = "")
|
||||
}
|
||||
Text(
|
||||
text = name,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier
|
||||
.padding(start = TEXT_PADDING_START)
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
if (readOnly) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_object_locked),
|
||||
contentDescription = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LibRelationItem(
|
||||
modifier: Modifier,
|
||||
name: String,
|
||||
format: RelationFormat,
|
||||
installed: Boolean
|
||||
) {
|
||||
Row(
|
||||
modifier,
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
format.simpleIcon()?.let {
|
||||
Image(painter = painterResource(id = it), contentDescription = "")
|
||||
}
|
||||
Text(
|
||||
text = name,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier
|
||||
.padding(start = TEXT_PADDING_START)
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
val installedImageRes = if (installed) {
|
||||
R.drawable.ic_type_installed
|
||||
} else {
|
||||
R.drawable.ic_type_not_installed
|
||||
}
|
||||
Image(
|
||||
painter = painterResource(id = installedImageRes),
|
||||
contentDescription = installedImageRes.toString(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Icon(icon: ObjectIcon?) {
|
||||
icon?.let {
|
||||
AndroidView(factory = { ctx ->
|
||||
ObjectIconWidget(ctx).apply {
|
||||
setIcon(it)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
14
app/src/main/res/drawable/ic_type_installed.xml
Normal file
14
app/src/main/res/drawable/ic_type_installed.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="28dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="28"
|
||||
android:viewportHeight="28">
|
||||
<path
|
||||
android:pathData="M14,0L14,0A14,14 0,0 1,28 14L28,14A14,14 0,0 1,14 28L14,28A14,14 0,0 1,0 14L0,14A14,14 0,0 1,14 0z"
|
||||
android:fillColor="@color/shape_tertiary"/>
|
||||
<path
|
||||
android:pathData="M9,14L13.031,18.5L18.5,9"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/text_primary"/>
|
||||
</vector>
|
17
app/src/main/res/drawable/ic_type_not_installed.xml
Normal file
17
app/src/main/res/drawable/ic_type_not_installed.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="28dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="28"
|
||||
android:viewportHeight="28">
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M14,0.5L14,0.5A13.5,13.5 0,0 1,27.5 14L27.5,14A13.5,13.5 0,0 1,14 27.5L14,27.5A13.5,13.5 0,0 1,0.5 14L0.5,14A13.5,13.5 0,0 1,14 0.5z"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/shape_primary"/>
|
||||
<path
|
||||
android:pathData="M8.75,14.75L19.25,14.75A0.75,0.75 0,0 0,20 14L20,14A0.75,0.75 0,0 0,19.25 13.25L8.75,13.25A0.75,0.75 0,0 0,8 14L8,14A0.75,0.75 0,0 0,8.75 14.75z"
|
||||
android:fillColor="@color/text_primary"/>
|
||||
<path
|
||||
android:pathData="M13.25,8.75L13.25,19.25A0.75,0.75 0,0 0,14 20L14,20A0.75,0.75 0,0 0,14.75 19.25L14.75,8.75A0.75,0.75 0,0 0,14 8L14,8A0.75,0.75 0,0 0,13.25 8.75z"
|
||||
android:fillColor="@color/text_primary"/>
|
||||
</vector>
|
|
@ -221,6 +221,22 @@ fun RelationFormat.icon(isMedium: Boolean = false): Int = when (this) {
|
|||
}
|
||||
}
|
||||
|
||||
fun RelationFormat.simpleIcon(): Int? = when (this) {
|
||||
RelationFormat.SHORT_TEXT -> R.drawable.ic_relation_format_text_small
|
||||
RelationFormat.LONG_TEXT -> R.drawable.ic_relation_format_text_small
|
||||
RelationFormat.NUMBER -> R.drawable.ic_relation_format_number_small
|
||||
RelationFormat.STATUS -> R.drawable.ic_relation_format_status_small
|
||||
RelationFormat.TAG -> R.drawable.ic_relation_format_tag_small
|
||||
RelationFormat.DATE -> R.drawable.ic_relation_format_date_small
|
||||
RelationFormat.FILE -> R.drawable.ic_relation_format_attachment_small
|
||||
RelationFormat.CHECKBOX -> R.drawable.ic_relation_format_checkbox_small
|
||||
RelationFormat.URL -> R.drawable.ic_relation_format_url_small
|
||||
RelationFormat.EMAIL -> R.drawable.ic_relation_format_email_small
|
||||
RelationFormat.PHONE -> R.drawable.ic_relation_format_phone_number_small
|
||||
RelationFormat.OBJECT -> R.drawable.ic_relation_format_object_small
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun DVSortType.text(format: RelationFormat): Int = when (format) {
|
||||
RelationFormat.TAG, RelationFormat.STATUS -> {
|
||||
if (this == DVSortType.ASC)
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.util.AttributeSet
|
|||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
|
||||
import com.anytypeio.anytype.presentation.sets.model.ColumnView
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -13,21 +14,10 @@ class RelationFormatIconWidget @JvmOverloads constructor(
|
|||
attrs: AttributeSet? = null
|
||||
) : AppCompatImageView(context, attrs) {
|
||||
fun bind(format: RelationFormat) {
|
||||
when (format) {
|
||||
RelationFormat.SHORT_TEXT -> setImageResource(R.drawable.ic_relation_format_text_small)
|
||||
RelationFormat.LONG_TEXT -> setImageResource(R.drawable.ic_relation_format_text_small)
|
||||
RelationFormat.NUMBER -> setImageResource(R.drawable.ic_relation_format_number_small)
|
||||
RelationFormat.STATUS -> setImageResource(R.drawable.ic_relation_format_status_small)
|
||||
RelationFormat.TAG -> setImageResource(R.drawable.ic_relation_format_tag_small)
|
||||
RelationFormat.DATE -> setImageResource(R.drawable.ic_relation_format_date_small)
|
||||
RelationFormat.FILE -> setImageResource(R.drawable.ic_relation_format_attachment_small)
|
||||
RelationFormat.CHECKBOX -> setImageResource(R.drawable.ic_relation_format_checkbox_small)
|
||||
RelationFormat.URL -> setImageResource(R.drawable.ic_relation_format_url_small)
|
||||
RelationFormat.EMAIL -> setImageResource(R.drawable.ic_relation_format_email_small)
|
||||
RelationFormat.PHONE -> setImageResource(R.drawable.ic_relation_format_phone_number_small)
|
||||
RelationFormat.OBJECT -> setImageResource(R.drawable.ic_relation_format_object_small)
|
||||
else -> Timber.d("Unexpected format: $format")
|
||||
}
|
||||
format.simpleIcon()?.let {
|
||||
setImageResource(it)
|
||||
return
|
||||
} ?: Timber.e("Unexpected format: $format")
|
||||
}
|
||||
fun bind(format: ColumnView.Format) {
|
||||
when (format) {
|
||||
|
|
|
@ -10,6 +10,8 @@ dependencies {
|
|||
implementation libs.kotlin
|
||||
implementation libs.coroutines
|
||||
|
||||
compileOnly libs.javaxInject
|
||||
|
||||
testImplementation project(":test:utils")
|
||||
testImplementation libs.kotlinTest
|
||||
testImplementation libs.turbine
|
||||
|
|
|
@ -4,7 +4,7 @@ import com.anytypeio.anytype.core_models.DVFilter
|
|||
import com.anytypeio.anytype.core_models.DVSort
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
|
||||
class LibrarySearchParams(
|
||||
class StoreSearchParams(
|
||||
val subscription: Id,
|
||||
val sorts: List<DVSort> = emptyList(),
|
||||
val filters: List<DVFilter> = emptyList(),
|
|
@ -0,0 +1,105 @@
|
|||
package com.anytypeio.anytype.domain.library
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.SubscriptionEvent
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.library.processors.EventAddProcessor
|
||||
import com.anytypeio.anytype.domain.library.processors.EventAmendProcessor
|
||||
import com.anytypeio.anytype.domain.library.processors.EventPositionProcessor
|
||||
import com.anytypeio.anytype.domain.library.processors.EventRemoveProcessor
|
||||
import com.anytypeio.anytype.domain.library.processors.EventSetProcessor
|
||||
import com.anytypeio.anytype.domain.library.processors.EventUnsetProcessor
|
||||
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.scan
|
||||
|
||||
interface StorelessSubscriptionContainer {
|
||||
|
||||
fun subscribe(searchParams: StoreSearchParams): Flow<List<ObjectWrapper.Basic>>
|
||||
|
||||
class Impl @Inject constructor(
|
||||
private val repo: BlockRepository,
|
||||
private val channel: SubscriptionEventChannel,
|
||||
private val dispatchers: AppCoroutineDispatchers
|
||||
) : StorelessSubscriptionContainer {
|
||||
|
||||
private val addEventProcessor by lazy { EventAddProcessor() }
|
||||
private val unsetEventProcessor by lazy { EventUnsetProcessor() }
|
||||
private val removeEventProcessor by lazy { EventRemoveProcessor() }
|
||||
private val setEventProcessor by lazy { EventSetProcessor() }
|
||||
private val amendEventProcessor by lazy { EventAmendProcessor() }
|
||||
private val positionEventProcessor by lazy { EventPositionProcessor() }
|
||||
|
||||
private fun subscribe(subscriptions: List<Id>) = channel.subscribe(subscriptions)
|
||||
|
||||
override fun subscribe(searchParams: StoreSearchParams): Flow<List<ObjectWrapper.Basic>> =
|
||||
flow {
|
||||
with(searchParams) {
|
||||
|
||||
val initial = repo.searchObjectsWithSubscription(
|
||||
subscription = subscription,
|
||||
sorts = sorts,
|
||||
filters = filters,
|
||||
offset = offset,
|
||||
limit = limit,
|
||||
keys = keys,
|
||||
afterId = null,
|
||||
beforeId = null,
|
||||
source = source,
|
||||
ignoreWorkspace = null,
|
||||
noDepSubscription = null
|
||||
).results.map { SubscriptionObject(it.id, it) }.toMutableList()
|
||||
|
||||
val objectsFlow =
|
||||
subscribe(
|
||||
listOf(searchParams.subscription)
|
||||
).scan(initial) { dataItems, payload ->
|
||||
var result = dataItems
|
||||
payload.forEach { event ->
|
||||
when (event) {
|
||||
is SubscriptionEvent.Add -> {
|
||||
result = addEventProcessor.process(event, result)
|
||||
}
|
||||
is SubscriptionEvent.Amend -> {
|
||||
result = amendEventProcessor.process(event, result)
|
||||
}
|
||||
is SubscriptionEvent.Position -> {
|
||||
result = positionEventProcessor.process(event, result)
|
||||
}
|
||||
is SubscriptionEvent.Remove -> {
|
||||
result = removeEventProcessor.process(event, result)
|
||||
}
|
||||
is SubscriptionEvent.Set -> {
|
||||
result = setEventProcessor.process(event, result)
|
||||
}
|
||||
is SubscriptionEvent.Unset -> {
|
||||
result = unsetEventProcessor.process(event, result)
|
||||
}
|
||||
else -> {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}.map {
|
||||
it.mapNotNull { item -> item.objectWrapper }
|
||||
}
|
||||
|
||||
emitAll(objectsFlow)
|
||||
}
|
||||
}.flowOn(dispatchers.io)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class SubscriptionObject(
|
||||
val id: Id,
|
||||
val objectWrapper: ObjectWrapper.Basic? = null
|
||||
)
|
|
@ -0,0 +1,26 @@
|
|||
package com.anytypeio.anytype.domain.library.processors
|
||||
|
||||
import com.anytypeio.anytype.core_models.SubscriptionEvent
|
||||
import com.anytypeio.anytype.domain.library.SubscriptionObject
|
||||
|
||||
class EventAddProcessor : SubscriptionEventProcessor<SubscriptionEvent.Add> {
|
||||
|
||||
override fun process(
|
||||
event: SubscriptionEvent.Add,
|
||||
dataItems: MutableList<SubscriptionObject>
|
||||
): MutableList<SubscriptionObject> = with(dataItems) {
|
||||
val afterId = event.afterId
|
||||
if (afterId != null) {
|
||||
val afterIdx = indexOfFirst { afterId == it.id }
|
||||
if (afterIdx != -1) {
|
||||
add(afterIdx.inc(), SubscriptionObject(event.target))
|
||||
} else {
|
||||
add(0, SubscriptionObject(event.target))
|
||||
}
|
||||
} else {
|
||||
add(0, SubscriptionObject(event.target))
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.anytypeio.anytype.domain.library.processors
|
||||
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.SubscriptionEvent
|
||||
import com.anytypeio.anytype.domain.library.SubscriptionObject
|
||||
import com.anytypeio.anytype.domain.`object`.amend
|
||||
|
||||
class EventAmendProcessor: SubscriptionEventProcessor<SubscriptionEvent.Amend> {
|
||||
|
||||
override fun process(
|
||||
event: SubscriptionEvent.Amend,
|
||||
dataItems: MutableList<SubscriptionObject>
|
||||
): MutableList<SubscriptionObject> = with(dataItems) {
|
||||
val item = find { it.id == event.target }
|
||||
if (item?.objectWrapper != null) {
|
||||
set(
|
||||
indexOf(item),
|
||||
SubscriptionObject(
|
||||
id = item.id,
|
||||
objectWrapper = item.objectWrapper.amend(event.diff)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
set(
|
||||
indexOf(item),
|
||||
SubscriptionObject(
|
||||
id = item?.id ?: event.target,
|
||||
objectWrapper = ObjectWrapper.Basic(event.diff)
|
||||
)
|
||||
)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package com.anytypeio.anytype.domain.library.processors
|
||||
|
||||
import com.anytypeio.anytype.core_models.SubscriptionEvent
|
||||
import com.anytypeio.anytype.domain.library.SubscriptionObject
|
||||
|
||||
class EventPositionProcessor: SubscriptionEventProcessor<SubscriptionEvent.Position> {
|
||||
|
||||
override fun process(
|
||||
event: SubscriptionEvent.Position,
|
||||
dataItems: MutableList<SubscriptionObject>
|
||||
): MutableList<SubscriptionObject> = with(dataItems) {
|
||||
val itemToMove = find { it.id == event.target }
|
||||
if (itemToMove != null) {
|
||||
remove(itemToMove)
|
||||
val afterIdx = indexOfFirst { event.afterId == it.id }
|
||||
if (afterIdx != -1) {
|
||||
add(afterIdx.inc(), itemToMove)
|
||||
} else {
|
||||
add(0, itemToMove)
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.anytypeio.anytype.domain.library.processors
|
||||
|
||||
import com.anytypeio.anytype.core_models.SubscriptionEvent
|
||||
import com.anytypeio.anytype.domain.library.SubscriptionObject
|
||||
|
||||
class EventRemoveProcessor : SubscriptionEventProcessor<SubscriptionEvent.Remove> {
|
||||
|
||||
override fun process(
|
||||
event: SubscriptionEvent.Remove,
|
||||
dataItems: MutableList<SubscriptionObject>
|
||||
): MutableList<SubscriptionObject> = with(dataItems) {
|
||||
retainAll {
|
||||
it.id != event.target
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.anytypeio.anytype.domain.library.processors
|
||||
|
||||
import com.anytypeio.anytype.core_models.SubscriptionEvent
|
||||
import com.anytypeio.anytype.domain.library.SubscriptionObject
|
||||
|
||||
class EventSetProcessor : SubscriptionEventProcessor<SubscriptionEvent.Set> {
|
||||
|
||||
override fun process(
|
||||
event: SubscriptionEvent.Set,
|
||||
dataItems: MutableList<SubscriptionObject>
|
||||
): MutableList<SubscriptionObject> = with(dataItems) {
|
||||
val indexOfItem = indexOfFirst { it.id == event.target }
|
||||
if (indexOfItem != -1) {
|
||||
set(
|
||||
indexOfItem,
|
||||
SubscriptionObject(
|
||||
event.target,
|
||||
com.anytypeio.anytype.core_models.ObjectWrapper.Basic(event.data)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
add(
|
||||
0,
|
||||
SubscriptionObject(
|
||||
event.target,
|
||||
com.anytypeio.anytype.core_models.ObjectWrapper.Basic(event.data)
|
||||
)
|
||||
)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.anytypeio.anytype.domain.library.processors
|
||||
|
||||
import com.anytypeio.anytype.core_models.SubscriptionEvent
|
||||
import com.anytypeio.anytype.domain.library.SubscriptionObject
|
||||
import com.anytypeio.anytype.domain.`object`.unset
|
||||
|
||||
class EventUnsetProcessor : SubscriptionEventProcessor<SubscriptionEvent.Unset> {
|
||||
|
||||
override fun process(
|
||||
event: SubscriptionEvent.Unset,
|
||||
dataItems: MutableList<SubscriptionObject>
|
||||
): MutableList<SubscriptionObject> = with(dataItems) {
|
||||
val item = find { it.id == event.target }?.let {
|
||||
SubscriptionObject(it.id, it.objectWrapper.apply {
|
||||
this?.unset(event.keys)
|
||||
})
|
||||
}
|
||||
if (item != null) {
|
||||
set(indexOf(item), item)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.anytypeio.anytype.domain.library.processors
|
||||
|
||||
import com.anytypeio.anytype.core_models.SubscriptionEvent
|
||||
import com.anytypeio.anytype.domain.library.SubscriptionObject
|
||||
|
||||
interface SubscriptionEventProcessor<T: SubscriptionEvent> {
|
||||
fun process(
|
||||
event: T,
|
||||
dataItems: MutableList<SubscriptionObject>
|
||||
): MutableList<SubscriptionObject>
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package com.anytypeio.anytype.presentation.library
|
||||
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.library.LibrarySearchParams
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.presentation.navigation.LibraryView
|
||||
import com.anytypeio.anytype.presentation.objects.toLibraryViews
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
interface LibraryInteractor {
|
||||
|
||||
fun subscribe(searchParams: LibrarySearchParams): Flow<List<LibraryView>>
|
||||
|
||||
class Impl @Inject constructor(
|
||||
private val repo: BlockRepository,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
) : LibraryInteractor {
|
||||
override fun subscribe(searchParams: LibrarySearchParams): Flow<List<LibraryView>> =
|
||||
flow {
|
||||
|
||||
with(searchParams) {
|
||||
val initial = repo.searchObjectsWithSubscription(
|
||||
subscription = subscription,
|
||||
sorts = sorts,
|
||||
filters = filters,
|
||||
offset = offset,
|
||||
limit = limit,
|
||||
keys = keys,
|
||||
afterId = null,
|
||||
beforeId = null,
|
||||
source = source,
|
||||
ignoreWorkspace = null,
|
||||
noDepSubscription = null
|
||||
).results.toLibraryViews(urlBuilder = urlBuilder)
|
||||
|
||||
emit(initial)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.presentation.library
|
||||
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.presentation.navigation.LibraryView
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
@ -12,7 +13,7 @@ interface LibraryListDelegate {
|
|||
val queryFlow: MutableStateFlow<String>
|
||||
val itemsFlow: Flow<LibraryScreenState.Tabs.TabData>
|
||||
|
||||
fun itemsFlow(): Flow<List<LibraryView>>
|
||||
fun itemsFlow(): Flow<List<ObjectWrapper.Basic>>
|
||||
|
||||
@FlowPreview
|
||||
fun queryFlow() = queryFlow
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.anytypeio.anytype.presentation.library.delegates.LibraryRelationsDele
|
|||
import com.anytypeio.anytype.presentation.library.delegates.LibraryTypesDelegate
|
||||
import com.anytypeio.anytype.presentation.library.delegates.MyRelationsDelegate
|
||||
import com.anytypeio.anytype.presentation.library.delegates.MyTypesDelegate
|
||||
import com.anytypeio.anytype.presentation.navigation.LibraryView
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
|
@ -55,10 +56,21 @@ class LibraryViewModel(
|
|||
myRelationsDelegate.itemsFlow,
|
||||
libraryRelationsDelegate.itemsFlow
|
||||
) { myTypes, libTypes, myRel, libRel ->
|
||||
LibraryScreenState(
|
||||
types = LibraryScreenState.Tabs.Types(myTypes, libTypes),
|
||||
relations = LibraryScreenState.Tabs.Relations(myRel, libRel)
|
||||
|
||||
val libTypesItems = updateInstalledValueForTypes(
|
||||
libTypes,
|
||||
myTypes
|
||||
)
|
||||
val libRelItems = updateInstalledValueForRelations(
|
||||
libRel,
|
||||
myRel
|
||||
)
|
||||
|
||||
LibraryScreenState(
|
||||
types = LibraryScreenState.Tabs.Types(myTypes, libTypesItems),
|
||||
relations = LibraryScreenState.Tabs.Relations(myRel, libRelItems)
|
||||
)
|
||||
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(STOP_SUBSCRIPTION_TIMEOUT),
|
||||
|
@ -74,6 +86,40 @@ class LibraryViewModel(
|
|||
)
|
||||
)
|
||||
|
||||
private fun updateInstalledValueForTypes(
|
||||
libTypes: LibraryScreenState.Tabs.TabData,
|
||||
myTypes: LibraryScreenState.Tabs.TabData
|
||||
): LibraryScreenState.Tabs.TabData {
|
||||
return libTypes.copy(
|
||||
items = libTypes.items.map { libType ->
|
||||
if (libType is LibraryView.LibraryTypeView) {
|
||||
libType.copy(installed = myTypes.items.find { myType ->
|
||||
(myType as LibraryView.MyTypeView).sourceObject == libType.id
|
||||
} != null)
|
||||
} else {
|
||||
libType
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateInstalledValueForRelations(
|
||||
libRelations: LibraryScreenState.Tabs.TabData,
|
||||
myRelations: LibraryScreenState.Tabs.TabData
|
||||
): LibraryScreenState.Tabs.TabData {
|
||||
return libRelations.copy(
|
||||
items = libRelations.items.map { libRelation ->
|
||||
if (libRelation is LibraryView.LibraryRelationView) {
|
||||
libRelation.copy(installed = myRelations.items.find { myType ->
|
||||
(myType as LibraryView.MyRelationView).sourceObject == libRelation.id
|
||||
} != null)
|
||||
} else {
|
||||
libRelation
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
class Factory @Inject constructor(
|
||||
private val myTypesDelegate: MyTypesDelegate,
|
||||
private val libraryTypesDelegate: LibraryTypesDelegate,
|
||||
|
|
|
@ -4,13 +4,15 @@ import com.anytypeio.anytype.core_models.DVFilter
|
|||
import com.anytypeio.anytype.core_models.DVFilterCondition
|
||||
import com.anytypeio.anytype.core_models.Marketplace
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.domain.library.LibrarySearchParams
|
||||
import com.anytypeio.anytype.domain.library.StoreSearchParams
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.presentation.dashboard.DEFAULT_KEYS
|
||||
import com.anytypeio.anytype.presentation.library.LibraryInteractor
|
||||
import com.anytypeio.anytype.presentation.library.LibraryListDelegate
|
||||
import com.anytypeio.anytype.presentation.library.LibraryScreenState
|
||||
import com.anytypeio.anytype.presentation.library.QueryListenerLibRelations
|
||||
import com.anytypeio.anytype.presentation.library.filterByQuery
|
||||
import com.anytypeio.anytype.presentation.objects.toLibraryViews
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
@ -18,7 +20,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.flow.combine
|
||||
|
||||
class LibraryRelationsDelegate @Inject constructor(
|
||||
private val interactor: LibraryInteractor
|
||||
private val container: StorelessSubscriptionContainer,
|
||||
private val urlBuilder: UrlBuilder
|
||||
) : LibraryListDelegate, QueryListenerLibRelations {
|
||||
|
||||
override val queryFlow: MutableStateFlow<String> = MutableStateFlow("")
|
||||
|
@ -31,15 +34,19 @@ class LibraryRelationsDelegate @Inject constructor(
|
|||
itemsFlow(),
|
||||
queryFlow()
|
||||
) { items, query ->
|
||||
LibraryScreenState.Tabs.TabData(items.filterByQuery(query))
|
||||
LibraryScreenState.Tabs.TabData(
|
||||
items
|
||||
.toLibraryViews(urlBuilder)
|
||||
.filterByQuery(query)
|
||||
)
|
||||
}
|
||||
|
||||
override fun itemsFlow() = interactor.subscribe(buildSearchParams())
|
||||
override fun itemsFlow() = container.subscribe(buildSearchParams())
|
||||
|
||||
private fun buildSearchParams(): LibrarySearchParams {
|
||||
return LibrarySearchParams(
|
||||
private fun buildSearchParams(): StoreSearchParams {
|
||||
return StoreSearchParams(
|
||||
subscription = SUB_LIBRARY_RELATIONS,
|
||||
keys = DEFAULT_KEYS,
|
||||
keys = DEFAULT_KEYS + listOf(Relations.RELATION_FORMAT),
|
||||
filters = buildList {
|
||||
addAll(ObjectSearchConstants.filterMarketplaceRelations())
|
||||
add(
|
||||
|
|
|
@ -3,14 +3,17 @@ package com.anytypeio.anytype.presentation.library.delegates
|
|||
import com.anytypeio.anytype.core_models.DVFilter
|
||||
import com.anytypeio.anytype.core_models.DVFilterCondition
|
||||
import com.anytypeio.anytype.core_models.Marketplace.MARKETPLACE_ID
|
||||
import com.anytypeio.anytype.core_models.MarketplaceObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.domain.library.LibrarySearchParams
|
||||
import com.anytypeio.anytype.domain.library.StoreSearchParams
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.presentation.dashboard.DEFAULT_KEYS
|
||||
import com.anytypeio.anytype.presentation.library.LibraryInteractor
|
||||
import com.anytypeio.anytype.presentation.library.LibraryListDelegate
|
||||
import com.anytypeio.anytype.presentation.library.LibraryScreenState
|
||||
import com.anytypeio.anytype.presentation.library.QueryListenerLibTypes
|
||||
import com.anytypeio.anytype.presentation.library.filterByQuery
|
||||
import com.anytypeio.anytype.presentation.objects.toLibraryViews
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
@ -18,7 +21,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.flow.combine
|
||||
|
||||
class LibraryTypesDelegate @Inject constructor(
|
||||
private val interactor: LibraryInteractor
|
||||
private val container: StorelessSubscriptionContainer,
|
||||
private val urlBuilder: UrlBuilder
|
||||
) : LibraryListDelegate, QueryListenerLibTypes {
|
||||
|
||||
override val queryFlow: MutableStateFlow<String> = MutableStateFlow("")
|
||||
|
@ -31,17 +35,21 @@ class LibraryTypesDelegate @Inject constructor(
|
|||
itemsFlow(),
|
||||
queryFlow()
|
||||
) { items, query ->
|
||||
LibraryScreenState.Tabs.TabData(items.filterByQuery(query))
|
||||
LibraryScreenState.Tabs.TabData(
|
||||
items
|
||||
.toLibraryViews(urlBuilder)
|
||||
.filterByQuery(query)
|
||||
)
|
||||
}
|
||||
|
||||
override fun itemsFlow() = interactor.subscribe(buildSearchParams())
|
||||
override fun itemsFlow() = container.subscribe(buildSearchParams())
|
||||
|
||||
private fun buildSearchParams(): LibrarySearchParams {
|
||||
return LibrarySearchParams(
|
||||
private fun buildSearchParams(): StoreSearchParams {
|
||||
return StoreSearchParams(
|
||||
subscription = SUB_LIBRARY_TYPES,
|
||||
keys = DEFAULT_KEYS,
|
||||
filters = buildList {
|
||||
ObjectSearchConstants.filterTypes()
|
||||
addAll(ObjectSearchConstants.filterTypes())
|
||||
add(
|
||||
DVFilter(
|
||||
relation = Relations.WORKSPACE_ID,
|
||||
|
@ -49,6 +57,13 @@ class LibraryTypesDelegate @Inject constructor(
|
|||
value = MARKETPLACE_ID
|
||||
)
|
||||
)
|
||||
add(
|
||||
DVFilter(
|
||||
relation = Relations.TYPE,
|
||||
condition = DVFilterCondition.EQUAL,
|
||||
value = MarketplaceObjectTypeIds.OBJECT_TYPE
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,14 +4,16 @@ import com.anytypeio.anytype.core_models.DVFilter
|
|||
import com.anytypeio.anytype.core_models.DVFilterCondition
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.domain.library.LibrarySearchParams
|
||||
import com.anytypeio.anytype.domain.library.StoreSearchParams
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.workspace.WorkspaceManager
|
||||
import com.anytypeio.anytype.presentation.dashboard.DEFAULT_KEYS
|
||||
import com.anytypeio.anytype.presentation.library.LibraryInteractor
|
||||
import com.anytypeio.anytype.presentation.library.LibraryListDelegate
|
||||
import com.anytypeio.anytype.presentation.library.LibraryScreenState
|
||||
import com.anytypeio.anytype.presentation.library.QueryListenerMyRelations
|
||||
import com.anytypeio.anytype.presentation.library.filterByQuery
|
||||
import com.anytypeio.anytype.presentation.objects.toLibraryViews
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
|
@ -22,8 +24,9 @@ import kotlinx.coroutines.flow.flatMapMerge
|
|||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
class MyRelationsDelegate @Inject constructor(
|
||||
private val interactor: LibraryInteractor,
|
||||
private val workspaceManager: WorkspaceManager
|
||||
private val container: StorelessSubscriptionContainer,
|
||||
private val workspaceManager: WorkspaceManager,
|
||||
private val urlBuilder: UrlBuilder
|
||||
) : LibraryListDelegate, QueryListenerMyRelations {
|
||||
|
||||
override val queryFlow: MutableStateFlow<String> = MutableStateFlow("")
|
||||
|
@ -37,7 +40,11 @@ class MyRelationsDelegate @Inject constructor(
|
|||
itemsFlow(),
|
||||
queryFlow()
|
||||
) { items, query ->
|
||||
LibraryScreenState.Tabs.TabData(items.filterByQuery(query))
|
||||
LibraryScreenState.Tabs.TabData(
|
||||
items
|
||||
.toLibraryViews(urlBuilder)
|
||||
.filterByQuery(query)
|
||||
)
|
||||
}
|
||||
|
||||
@FlowPreview
|
||||
|
@ -45,13 +52,17 @@ class MyRelationsDelegate @Inject constructor(
|
|||
emit(workspaceManager.getCurrentWorkspace())
|
||||
}.flatMapMerge {
|
||||
val searchParams = buildSearchParams(it)
|
||||
interactor.subscribe(searchParams)
|
||||
container.subscribe(searchParams)
|
||||
}
|
||||
|
||||
private fun buildSearchParams(workspaceId: Id): LibrarySearchParams {
|
||||
return LibrarySearchParams(
|
||||
private fun buildSearchParams(workspaceId: Id): StoreSearchParams {
|
||||
return StoreSearchParams(
|
||||
subscription = SUB_LIBRARY_MY_RELATIONS,
|
||||
keys = DEFAULT_KEYS,
|
||||
keys = DEFAULT_KEYS + listOf(
|
||||
Relations.SOURCE_OBJECT,
|
||||
Relations.RELATION_FORMAT,
|
||||
Relations.RELATION_READ_ONLY_VALUE
|
||||
),
|
||||
filters = buildList {
|
||||
addAll(ObjectSearchConstants.filterMyRelations())
|
||||
add(
|
||||
|
|
|
@ -3,15 +3,18 @@ package com.anytypeio.anytype.presentation.library.delegates
|
|||
import com.anytypeio.anytype.core_models.DVFilter
|
||||
import com.anytypeio.anytype.core_models.DVFilterCondition
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.domain.library.LibrarySearchParams
|
||||
import com.anytypeio.anytype.domain.library.StoreSearchParams
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.workspace.WorkspaceManager
|
||||
import com.anytypeio.anytype.presentation.dashboard.DEFAULT_KEYS
|
||||
import com.anytypeio.anytype.presentation.library.LibraryInteractor
|
||||
import com.anytypeio.anytype.presentation.library.LibraryListDelegate
|
||||
import com.anytypeio.anytype.presentation.library.LibraryScreenState
|
||||
import com.anytypeio.anytype.presentation.library.QueryListenerMyTypes
|
||||
import com.anytypeio.anytype.presentation.library.filterByQuery
|
||||
import com.anytypeio.anytype.presentation.objects.toLibraryViews
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
|
@ -22,8 +25,9 @@ import kotlinx.coroutines.flow.flatMapMerge
|
|||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
class MyTypesDelegate @Inject constructor(
|
||||
private val interactor: LibraryInteractor,
|
||||
private val workspaceManager: WorkspaceManager
|
||||
private val container: StorelessSubscriptionContainer,
|
||||
private val workspaceManager: WorkspaceManager,
|
||||
private val urlBuilder: UrlBuilder
|
||||
) : LibraryListDelegate, QueryListenerMyTypes {
|
||||
|
||||
override val queryFlow: MutableStateFlow<String> = MutableStateFlow("")
|
||||
|
@ -36,7 +40,11 @@ class MyTypesDelegate @Inject constructor(
|
|||
override val itemsFlow: Flow<LibraryScreenState.Tabs.TabData> = combine(
|
||||
itemsFlow(), queryFlow()
|
||||
) { items, query ->
|
||||
LibraryScreenState.Tabs.TabData(items.filterByQuery(query))
|
||||
LibraryScreenState.Tabs.TabData(
|
||||
items
|
||||
.toLibraryViews(urlBuilder)
|
||||
.filterByQuery(query)
|
||||
)
|
||||
}
|
||||
|
||||
@FlowPreview
|
||||
|
@ -44,13 +52,16 @@ class MyTypesDelegate @Inject constructor(
|
|||
emit(workspaceManager.getCurrentWorkspace())
|
||||
}.flatMapMerge {
|
||||
val searchParams = buildSearchParams(it)
|
||||
interactor.subscribe(searchParams)
|
||||
container.subscribe(searchParams)
|
||||
}
|
||||
|
||||
private fun buildSearchParams(workspaceId: Id): LibrarySearchParams {
|
||||
return LibrarySearchParams(
|
||||
private fun buildSearchParams(workspaceId: Id): StoreSearchParams {
|
||||
return StoreSearchParams(
|
||||
subscription = SUB_LIBRARY_MY_TYPES,
|
||||
keys = DEFAULT_KEYS,
|
||||
keys = DEFAULT_KEYS + listOf(
|
||||
Relations.SOURCE_OBJECT,
|
||||
Relations.RELATION_READ_ONLY_VALUE
|
||||
),
|
||||
filters = buildList {
|
||||
addAll(ObjectSearchConstants.filterTypes())
|
||||
add(
|
||||
|
@ -60,6 +71,13 @@ class MyTypesDelegate @Inject constructor(
|
|||
value = workspaceId
|
||||
)
|
||||
)
|
||||
add(
|
||||
DVFilter(
|
||||
relation = Relations.TYPE,
|
||||
condition = DVFilterCondition.EQUAL,
|
||||
value = ObjectTypeIds.OBJECT_TYPE
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.anytypeio.anytype.presentation.navigation
|
|||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.Struct
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
|
||||
interface DefaultSearchItem
|
||||
|
@ -14,7 +14,7 @@ data class DefaultObjectView(
|
|||
val typeName: String? = null,
|
||||
val layout: ObjectType.Layout? = null,
|
||||
val icon: ObjectIcon = ObjectIcon.None
|
||||
): DefaultSearchItem
|
||||
) : DefaultSearchItem
|
||||
|
||||
data class ObjectView(
|
||||
val id: String,
|
||||
|
@ -31,11 +31,43 @@ fun List<ObjectView>.filterBy(text: String): List<ObjectView> =
|
|||
if (text.isNotEmpty()) this.filter { it.isContainsText(text) } else this
|
||||
|
||||
|
||||
data class LibraryView(
|
||||
val id: Id,
|
||||
val name: String,
|
||||
val type: String? = null,
|
||||
val typeName: String? = null,
|
||||
val layout: ObjectType.Layout? = null,
|
||||
val icon: ObjectIcon = ObjectIcon.None
|
||||
)
|
||||
sealed interface LibraryView {
|
||||
val id: Id
|
||||
val name: String
|
||||
|
||||
class MyTypeView(
|
||||
override val id: Id,
|
||||
override val name: String,
|
||||
val icon: ObjectIcon? = null,
|
||||
val sourceObject: Id? = null,
|
||||
val readOnly: Boolean = false
|
||||
) : LibraryView
|
||||
|
||||
data class LibraryTypeView(
|
||||
override val id: Id,
|
||||
override val name: String,
|
||||
val icon: ObjectIcon? = null,
|
||||
val installed: Boolean = false,
|
||||
) : LibraryView
|
||||
|
||||
class MyRelationView(
|
||||
override val id: Id,
|
||||
override val name: String,
|
||||
val format: RelationFormat,
|
||||
val sourceObject: Id? = null,
|
||||
val readOnly: Boolean = false
|
||||
) : LibraryView
|
||||
|
||||
data class LibraryRelationView(
|
||||
override val id: Id,
|
||||
override val name: String,
|
||||
val format: RelationFormat,
|
||||
val installed: Boolean = false,
|
||||
) : LibraryView
|
||||
|
||||
class UnknownView(
|
||||
override val id: Id = "",
|
||||
override val name: String = "",
|
||||
) : LibraryView
|
||||
|
||||
}
|
|
@ -1,14 +1,18 @@
|
|||
package com.anytypeio.anytype.presentation.objects
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.MarketplaceObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Relations.SOURCE_OBJECT
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.presentation.linking.LinkToItemView
|
||||
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
|
||||
import com.anytypeio.anytype.presentation.navigation.LibraryView
|
||||
import com.anytypeio.anytype.presentation.relations.RelationValueView
|
||||
import com.anytypeio.anytype.presentation.sets.filter.CreateFilterView
|
||||
import timber.log.Timber
|
||||
|
||||
@Deprecated("To be deleted")
|
||||
fun List<ObjectWrapper.Basic>.toView(
|
||||
|
@ -22,7 +26,10 @@ fun List<ObjectWrapper.Basic>.toView(
|
|||
id = obj.id,
|
||||
name = obj.getProperName(),
|
||||
type = typeUrl,
|
||||
typeName = getProperTypeName(id = typeUrl, types = objectTypes),
|
||||
typeName = getProperTypeName(
|
||||
id = typeUrl,
|
||||
types = objectTypes
|
||||
),
|
||||
layout = layout,
|
||||
icon = ObjectIcon.from(
|
||||
obj = obj,
|
||||
|
@ -55,19 +62,55 @@ fun List<ObjectWrapper.Basic>.toViews(
|
|||
fun List<ObjectWrapper.Basic>.toLibraryViews(
|
||||
urlBuilder: UrlBuilder,
|
||||
): List<LibraryView> = map { obj ->
|
||||
val typeUrl = obj.getProperType()
|
||||
val layout = obj.getProperLayout()
|
||||
LibraryView(
|
||||
id = obj.id,
|
||||
name = obj.getProperName(),
|
||||
type = typeUrl,
|
||||
layout = layout,
|
||||
icon = ObjectIcon.from(
|
||||
obj = obj,
|
||||
layout = layout,
|
||||
builder = urlBuilder
|
||||
),
|
||||
)
|
||||
when (obj.getProperType()) {
|
||||
MarketplaceObjectTypeIds.OBJECT_TYPE -> {
|
||||
LibraryView.LibraryTypeView(
|
||||
id = obj.id,
|
||||
name = obj.name ?: "",
|
||||
icon = ObjectIcon.from(
|
||||
obj = obj,
|
||||
layout = obj.getProperLayout(),
|
||||
builder = urlBuilder
|
||||
),
|
||||
installed = false,
|
||||
)
|
||||
}
|
||||
ObjectTypeIds.OBJECT_TYPE -> {
|
||||
LibraryView.MyTypeView(
|
||||
id = obj.id,
|
||||
name = obj.name ?: "",
|
||||
icon = ObjectIcon.from(
|
||||
obj = obj,
|
||||
layout = obj.getProperLayout(),
|
||||
builder = urlBuilder
|
||||
),
|
||||
sourceObject = obj.map[SOURCE_OBJECT]?.toString(),
|
||||
readOnly = obj.relationReadonlyValue ?: false
|
||||
)
|
||||
}
|
||||
ObjectTypeIds.RELATION -> {
|
||||
val relation = ObjectWrapper.Relation(obj.map)
|
||||
LibraryView.MyRelationView(
|
||||
id = obj.id,
|
||||
name = obj.name ?: "",
|
||||
format = relation.format,
|
||||
sourceObject = obj.map[SOURCE_OBJECT]?.toString(),
|
||||
readOnly = relation.isReadonlyValue
|
||||
)
|
||||
}
|
||||
MarketplaceObjectTypeIds.RELATION -> {
|
||||
val relation = ObjectWrapper.Relation(obj.map)
|
||||
LibraryView.LibraryRelationView(
|
||||
id = obj.id,
|
||||
name = obj.name ?: "",
|
||||
format = relation.format
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
Timber.e("Unknown type: ${obj.getProperType()}")
|
||||
LibraryView.UnknownView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun List<ObjectWrapper.Basic>.toLinkToView(
|
||||
|
@ -198,7 +241,8 @@ private fun ObjectWrapper.Basic.getProperType() = type.firstOrNull()
|
|||
private fun ObjectWrapper.Basic.getProperFileExt() = fileExt.orEmpty()
|
||||
private fun ObjectWrapper.Basic.getProperFileMime() = fileMimeType.orEmpty()
|
||||
|
||||
private fun getProperTypeName(id: Id?, types: List<ObjectWrapper.Type>) = types.find { it.id == id }?.name.orEmpty()
|
||||
private fun getProperTypeName(id: Id?, types: List<ObjectWrapper.Type>) =
|
||||
types.find { it.id == id }?.name.orEmpty()
|
||||
|
||||
private fun ObjectWrapper.Basic.getProperFileImage(urlBuilder: UrlBuilder): String? =
|
||||
iconImage?.let { if (it.isBlank()) null else urlBuilder.thumbnail(it) }
|
||||
|
|
|
@ -594,11 +594,6 @@ object ObjectSearchConstants {
|
|||
)
|
||||
|
||||
fun filterTypes() : List<DVFilter> = listOf(
|
||||
DVFilter(
|
||||
relation = Relations.TYPE,
|
||||
condition = DVFilterCondition.EQUAL,
|
||||
value = OBJECT_TYPE
|
||||
),
|
||||
DVFilter(
|
||||
relation = Relations.IS_ARCHIVED,
|
||||
condition = DVFilterCondition.NOT_EQUAL,
|
||||
|
|
Loading…
Reference in New Issue
Block a user