Compare commits

...

22 Commits

Author SHA1 Message Date
uburoiubu
06d6e81b65
v0.24.7 2023-09-05 11:25:08 +02:00
Konstantin Ivanov
3684f79d29
DROID-1663 App | Tech | Update target sdk to 33 (#303) 2023-09-05 11:24:18 +02:00
uburoiubu
58a0b10183
v0.24.6 2023-09-05 10:52:46 +02:00
Brayan Oliveira
7c6b4684d5
DROID Editor | Fix | Enable auto-correct by fixing IME input and action modes (#283) 2023-09-05 10:51:45 +02:00
uburoiubu
ed9f41ac02
v0.24.5 2023-08-18 11:47:03 +02:00
Konstantin Ivanov
705367d56f
DROID-1593 App | Fix | Create object, template internal flag (#297) 2023-08-17 16:19:37 +02:00
Mikhail
3ae86e0c65
DROID-1615 Main | Widgets | Change the sources descriptions (#280) 2023-08-17 15:39:39 +02:00
Evgenii Kozlov
9d12987a9f
DROID-1633 Widgets | Fix | Profile icon is not displayed correctly in compact list and tree widgets (#294) 2023-08-17 11:28:39 +02:00
uburoiubu
9e951951db
v0.24.4 2023-08-15 22:02:55 +02:00
Evgenii Kozlov
8a5454869f
DROID-1504 App | Tech | Fix crashes in 0.23.17 (#293) 2023-08-15 22:00:45 +02:00
Evgenii Kozlov
f91807f89a
DROID-1648 Onboarding | Analytics | Fix order of onboarding steps (#292) 2023-08-15 21:12:30 +02:00
konstantiniiv
454e586222 Bump version 2023-08-15 08:51:41 +02:00
Konstantin Ivanov
32c740f246
DROID-1649 Protocol | Enhancement | MW 0.27.20 (#291) 2023-08-15 08:36:09 +02:00
Konstantin Ivanov
cfcac55a48
DROID-1586 Set | Enhancement | Template button integration, empty templates (#290) 2023-08-14 21:32:11 +02:00
Konstantin Ivanov
91591e116c
DROID -1642 Editor | Fix | 'Cancel' button on the template selection screen does not work (#289) 2023-08-14 20:09:40 +02:00
Konstantin Ivanov
9bdf830a70
DROID-1630 App | Bump app version 0.24.2 (#287) 2023-08-14 12:56:03 +02:00
Konstantin Ivanov
6a5ab68eda
DROID-1644 Protocol | Enhancement | MW 0.27.19 (#286) 2023-08-14 12:27:04 +02:00
Konstantin Ivanov
33e6488d54 DROID-1593 Editor | Feature | InternalFlags, selectTemplate, selectType (#261) 2023-08-08 22:28:17 +02:00
Konstantin Ivanov
c50be0a8e5
DROID-1630 App | Bump version (#276) 2023-08-08 22:20:17 +02:00
Konstantin Ivanov
b47dd59a21
DROID-1638 Protocol | Enhancement | MW 0.27.18 (#275) 2023-08-08 21:52:25 +02:00
Konstantin Ivanov
6563e138a6
DROID-1497 Analytics | Templates (#274) 2023-08-08 21:34:16 +02:00
konstantiniiv
3949a47a66 DROID-1616 spam error deleted 2023-08-07 16:49:01 +02:00
40 changed files with 704 additions and 159 deletions

View File

@ -148,6 +148,14 @@ object EventsDictionary {
const val screenHome = "ScreenHome"
const val selectHomeTab = "SelectHomeTab"
//Templates
const val selectTemplate = "SelectTemplate"
const val clickNewOption = "ClickNewOption"
const val changeDefaultTemplate = "ChangeDefaultTemplate"
const val editTemplate = "EditTemplate"
const val duplicateTemplate = "DuplicateTemplate"
const val createTemplate = "CreateTemplate"
// Onboarding events
const val screenOnboarding = "ScreenOnboarding"
const val clickOnboarding = "ClickOnboarding"

View File

@ -36,6 +36,9 @@ class AmplitudeTracker(
analytics
.observeEvents()
.collect { event ->
if (BuildConfig.DEBUG) {
Timber.d("Analytics Amplitude(event = $event)")
}
if (BuildConfig.SEND_EVENTS && event is EventAnalytics.Anytype) {
val props = event.props.getEventProperties(
startTime = event.duration?.start,
@ -43,9 +46,6 @@ class AmplitudeTracker(
renderTime = event.duration?.render
)
tracker.logEvent(event.name, props)
if (BuildConfig.DEBUG) {
Timber.d("Analytics Amplitude(event = $event)")
}
}
}
}

View File

@ -1,4 +1,4 @@
version.versionMajor=0
version.versionMinor=24
version.versionPatch=0
version.versionPatch=7
version.useDatedVersionName=false

View File

@ -60,6 +60,7 @@ import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.ConvertObjectToSet
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.DefaultStoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.DefaultStoreOfRelations
@ -269,6 +270,9 @@ open class EditorTestSetup {
@Mock
lateinit var fileLimitsEventChannel: FileLimitsEventChannel
@Mock
lateinit var setObjectInternalFlags: SetObjectInternalFlags
lateinit var interceptFileLimitEvents: InterceptFileLimitEvents
lateinit var addRelationToObject: AddRelationToObject
@ -466,7 +470,8 @@ open class EditorTestSetup {
getObjectTypes = getObjectTypes,
objectToCollection = objectToCollection,
interceptFileLimitEvents = interceptFileLimitEvents,
addRelationToObject = addRelationToObject
addRelationToObject = addRelationToObject,
setObjectInternalFlags = setObjectInternalFlags
)
}

View File

@ -56,6 +56,7 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.ConvertObjectToSet
import com.anytypeio.anytype.domain.`object`.DuplicateObject
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.SetObjectIsArchived
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
@ -233,6 +234,14 @@ object EditorSessionModule {
dispatchers: AppCoroutineDispatchers
) : InterceptFileLimitEvents = InterceptFileLimitEvents(channel, dispatchers)
@JvmStatic
@Provides
@PerScreen
fun provideSetObjectInternalFlags(
repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
) : SetObjectInternalFlags = SetObjectInternalFlags(repo, dispatchers)
@JvmStatic
@Provides
fun providePageViewModelFactory(
@ -271,7 +280,8 @@ object EditorSessionModule {
getObjectTypes: GetObjectTypes,
objectToCollection: ConvertObjectToCollection,
interceptFileLimitEvents: InterceptFileLimitEvents,
addRelationToObject: AddRelationToObject
addRelationToObject: AddRelationToObject,
setObjectInternalFlags: SetObjectInternalFlags
): EditorViewModelFactory = EditorViewModelFactory(
openPage = openPage,
closeObject = closePage,
@ -308,7 +318,8 @@ object EditorSessionModule {
getObjectTypes = getObjectTypes,
objectToCollection = objectToCollection,
interceptFileLimitEvents = interceptFileLimitEvents,
addRelationToObject = addRelationToObject
addRelationToObject = addRelationToObject,
setObjectInternalFlags = setObjectInternalFlags
)

View File

@ -59,6 +59,7 @@ import dagger.Module
import dagger.Provides
import javax.inject.Named
import javax.inject.Singleton
import timber.log.Timber
@Module(includes = [DataModule.Bindings::class])
object DataModule {
@ -162,8 +163,22 @@ object DataModule {
@Named("encrypted")
fun provideEncryptedSharedPreferences(
context: Context
): SharedPreferences = EncryptedSharedPreferences.create(
"encrypted_prefs",
): SharedPreferences = try {
initializeEncryptedPrefs(context)
} catch (e: Exception) {
// https://issuetracker.google.com/issues/164901843
Timber.e(e, "Error while initializing encrypted prefs")
// Clearing pre-existing prefs
context
.getSharedPreferences(ENCRYPTED_PREFS_NAME, Context.MODE_PRIVATE)
.edit()
.clear()
.commit()
initializeEncryptedPrefs(context)
}
private fun initializeEncryptedPrefs(context: Context) = EncryptedSharedPreferences.create(
ENCRYPTED_PREFS_NAME,
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
@ -311,4 +326,6 @@ object DataModule {
@Singleton
fun bindMiddlewareService(middleware: MiddlewareServiceImplementation): MiddlewareService
}
}
}
const val ENCRYPTED_PREFS_NAME = "encrypted_prefs"

View File

@ -538,7 +538,6 @@ class OnboardingFragment : Fragment() {
navController.navigate(
route = OnboardingNavigation.createSoulAnim
)
vm.sendAnalyticsOnboardingScreen()
}
}
}

View File

@ -5,9 +5,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
@ -20,7 +18,6 @@ import com.anytypeio.anytype.databinding.FragmentTemplateSelectBinding
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.templates.TemplateSelectViewModel
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.launch
import javax.inject.Inject
class TemplateSelectFragment :
@ -40,9 +37,15 @@ class TemplateSelectFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupViewPagerAndTabs()
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
setupClickEventHandlers()
with(lifecycleScope) {
subscribe(binding.btnSkip.clicks()) {
vm.onSkipButtonClicked()
}
subscribe(binding.btnUseTemplate.clicks()) {
vm.onUseTemplateButtonPressed(
currentItem = binding.templateViewPager.currentItem,
ctx = ctx
)
}
}
}
@ -53,11 +56,6 @@ class TemplateSelectFragment :
TabLayoutMediator(binding.tabs, binding.templateViewPager) { _, _ -> }.attach()
}
private suspend fun setupClickEventHandlers() {
setupUseTemplateClicks()
setupCancelClicks()
}
override fun onStart() {
jobs += lifecycleScope.subscribe(vm.viewState) { render(it) }
jobs += lifecycleScope.subscribe(vm.isDismissed) { if (it) exit() }
@ -67,10 +65,9 @@ class TemplateSelectFragment :
private fun render(viewState: TemplateSelectViewModel.ViewState) {
when (viewState) {
TemplateSelectViewModel.ViewState.ErrorGettingType -> TODO()
TemplateSelectViewModel.ViewState.Init -> {
binding.tvTemplateCountOrTutorial.text = null
binding.btnCancel.isEnabled = true
binding.btnSkip.isEnabled = true
binding.btnUseTemplate.isEnabled = false
}
@ -86,19 +83,6 @@ class TemplateSelectFragment :
}
}
private suspend fun setupUseTemplateClicks() {
binding.btnUseTemplate.clicks().collect {
vm.onUseTemplateButtonPressed(
currentItem = binding.templateViewPager.currentItem,
ctx = ctx
)
}
}
private suspend fun setupCancelClicks() {
binding.btnCancel.clicks().collect { exit() }
}
private fun exit() {
findNavController().popBackStack()
}

View File

@ -40,9 +40,9 @@ fun TreeWidgetObjectIcon(
is ObjectIcon.Profile.Avatar -> {
Box(
modifier = modifier
.padding(start = paddingStart, end = paddingEnd)
.height(18.dp)
.width(18.dp)
.padding(start = paddingStart, end = paddingEnd)
.background(
shape = CircleShape,
color = colorResource(id = R.color.shape_primary)

View File

@ -5,7 +5,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/defaultCanvasColor"
android:orientation="vertical">
android:orientation="vertical"
tools:context="com.anytypeio.anytype.ui.templates.TemplateSelectFragment">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
@ -44,14 +45,14 @@
android:orientation="horizontal">
<com.anytypeio.anytype.core_ui.views.ButtonSecondaryLarge
android:id="@+id/btnCancel"
android:id="@+id/btnSkip"
android:layout_width="0dp"
android:layout_height="@dimen/dp_48"
android:layout_gravity="center_vertical"
android:layout_marginStart="20dp"
android:layout_marginEnd="4dp"
android:layout_weight="1"
android:text="@string/cancel" />
android:text="@string/skip" />
<com.anytypeio.anytype.core_ui.views.ButtonPrimaryLarge
android:id="@+id/btnUseTemplate"

View File

@ -2,7 +2,7 @@ import com.android.build.gradle.LibraryPlugin
buildscript {
ext.compile_sdk = 33
ext.target_sdk = 32
ext.target_sdk = 33
ext.min_sdk = 26
ext.application_id = 'io.anytype.app'

View File

@ -421,4 +421,6 @@ sealed class Command {
data class AddObjectToCollection(val ctx: Id, val afterId: Id, val ids: List<Id>)
data class SetQueryToSet(val ctx: Id, val query: String)
data class SetInternalFlags(val ctx: Id, val flags: List<InternalFlags>)
}

View File

@ -373,14 +373,20 @@ interface TextBlockHolder : TextHolder {
private fun setupCustomInsertionActionMode(isWithBookmark: Boolean = true) {
content.customInsertionActionModeCallback = object : ActionMode.Callback2() {
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
if (getLink() != null) {
menu.addLink()
if (isWithBookmark) {
menu.add(0, R.id.menuBookmark, 3, R.string.bookmark)
return try {
if (getLink() != null) {
menu.addLink()
if (isWithBookmark) {
menu.add(0, R.id.menuBookmark, 3, R.string.bookmark)
}
menu.pasteToText()
}
true
} catch (e: Exception) {
false.also {
Timber.d(e, "Error while creating action mode")
}
menu.pasteToText()
}
return true
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
@ -417,11 +423,17 @@ interface TextBlockHolder : TextHolder {
}
content.customSelectionActionModeCallback = object : ActionMode.Callback2() {
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
if (getLink() != null) {
menu.addLink()
menu.pasteToText()
return try {
if (getLink() != null) {
menu.addLink()
menu.pasteToText()
}
return true
} catch (e: Exception) {
false.also {
Timber.d(e, "Error while creating action mode")
}
}
return true
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {

View File

@ -143,7 +143,7 @@ class BundledWidgetSourceHolder(
BundledWidgetSourceView.Favorites -> {
with(binding) {
tvTitle.setText(R.string.favorites)
tvSubtitle.setText(R.string.your_favorite_objects)
tvSubtitle.text = ""
ivIcon.setIcon(ObjectIcon.Basic.Emoji("⭐️"))
}
}
@ -158,7 +158,7 @@ class BundledWidgetSourceHolder(
BundledWidgetSourceView.RecentLocal -> {
with(binding) {
tvTitle.setText(R.string.recently_opened)
tvSubtitle.setText(R.string.recently_opened_objects)
tvSubtitle.setText(R.string.on_this_device)
ivIcon.setIcon(ObjectIcon.Basic.Emoji("📅"))
}
}
@ -166,14 +166,14 @@ class BundledWidgetSourceHolder(
BundledWidgetSourceView.Sets -> {
with(binding) {
tvTitle.setText(R.string.sets)
tvSubtitle.setText(R.string.sets_of_objects)
tvSubtitle.text = ""
ivIcon.setIcon(ObjectIcon.Basic.Emoji("📚"))
}
}
BundledWidgetSourceView.Collections -> {
with(binding) {
tvTitle.setText(R.string.collections)
tvSubtitle.setText(R.string.collection_widget_description)
tvSubtitle.text = ""
ivIcon.setIcon(ObjectIcon.Basic.Emoji("📂"))
}
}

View File

@ -5,6 +5,7 @@ import android.R.id.paste
import android.content.Context
import android.graphics.Canvas
import android.os.Parcelable
import android.text.InputType
import android.text.Spanned
import android.text.TextWatcher
import android.text.util.Linkify
@ -98,7 +99,12 @@ class TextInputWidget : AppCompatEditText {
}
fun enableEditMode() {
multilineIme(action = inputAction.toIMECode())
setRawInputType(
InputType.TYPE_CLASS_TEXT
or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
or InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
)
imeOptions = inputAction.toIMECode()
setTextIsSelectable(true)
inReadMode = false
}

View File

@ -113,6 +113,7 @@
<string name="modal_table">Table</string>
<string name="modal_list">List</string>
<string name="cancel">Cancel</string>
<string name="skip">Skip</string>
<string name="add">Add</string>
<string name="duplicate">Duplicate</string>
<string name="customize_view">Customize view</string>
@ -610,7 +611,7 @@
<string name="object_search_recently_opened_section_title">Recently opened</string>
<string name="widget_source_anytype_library">Anytype Library</string>
<string name="your_favorite_objects">Your favorite objects</string>
<string name="recently_opened_objects">Recently opened objects</string>
<string name="on_this_device">On this device</string>
<string name="sets_of_objects">Sets of objects from your workspace</string>
<string name="content_description_customize_view_button">Customize-view button</string>

View File

@ -841,4 +841,8 @@ class BlockDataRepository(
override suspend fun fileSpaceUsage(): FileLimits {
return remote.fileSpaceUsage()
}
override suspend fun setInternalFlags(command: Command.SetInternalFlags): Payload {
return remote.setInternalFlags(command)
}
}

View File

@ -360,4 +360,6 @@ interface BlockRemote {
suspend fun addObjectToCollection(command: Command.AddObjectToCollection): Payload
suspend fun setQueryToSet(command: Command.SetQueryToSet): Payload
suspend fun fileSpaceUsage(): FileLimits
suspend fun setInternalFlags(command: Command.SetInternalFlags): Payload
}

View File

@ -412,4 +412,5 @@ interface BlockRepository {
suspend fun addObjectToCollection(command: Command.AddObjectToCollection): Payload
suspend fun setQueryToSet(command: Command.SetQueryToSet): Payload
suspend fun fileSpaceUsage(): FileLimits
suspend fun setInternalFlags(command: Command.SetInternalFlags): Payload
}

View File

@ -0,0 +1,28 @@
package com.anytypeio.anytype.domain.`object`
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.InternalFlags
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.block.repo.BlockRepository
class SetObjectInternalFlags(
private val repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
) : ResultInteractor<SetObjectInternalFlags.Params, Payload>(dispatchers.io) {
override suspend fun doWork(params: Params): Payload {
val command = Command.SetInternalFlags(
ctx = params.ctx,
flags = params.flags
)
return repo.setInternalFlags(command)
}
data class Params(
val ctx: Id,
val flags: List<InternalFlags>
)
}

View File

@ -25,19 +25,9 @@ class CreateObject(
val type = params.type ?: getDefaultPageType.run(Unit).type
val objectTemplates = if (type != null) {
getTemplates.run(GetTemplates.Params(type = type))
} else {
null
}
val internalFlags = buildList {
if (!objectTemplates.isNullOrEmpty()) {
add(InternalFlags.ShouldSelectType)
add(InternalFlags.ShouldSelectTemplate)
} else {
add(InternalFlags.ShouldSelectType)
}
add(InternalFlags.ShouldSelectType)
add(InternalFlags.ShouldSelectTemplate)
add(InternalFlags.ShouldEmptyDelete)
}

View File

@ -68,13 +68,17 @@ class CreateObjectTest {
val commands = Command.CreateObject(
prefilled = emptyMap(),
template = null,
internalFlags = listOf(InternalFlags.ShouldSelectType, InternalFlags.ShouldEmptyDelete)
internalFlags = listOf(
InternalFlags.ShouldSelectType,
InternalFlags.ShouldSelectTemplate,
InternalFlags.ShouldEmptyDelete
)
)
verifyBlocking(repo, times(1)) { createObject(commands) }
}
@Test
fun `when type is null and default type is note without template - should send proper params`() =
fun `when type is null and default type is note - should send proper params`() =
runBlocking {
//SETUP
@ -90,12 +94,12 @@ class CreateObjectTest {
createObject.run(params)
//ASSERT
verifyBlocking(getTemplates, times(1)) { run(GetTemplates.Params(defaultType)) }
val commands = Command.CreateObject(
prefilled = buildMap { put(Relations.TYPE, defaultType) },
template = null,
internalFlags = listOf(
InternalFlags.ShouldSelectType,
InternalFlags.ShouldSelectTemplate,
InternalFlags.ShouldEmptyDelete
)
)
@ -122,7 +126,6 @@ class CreateObjectTest {
createObject.run(params)
//ASSERT
verifyBlocking(getTemplates, times(1)) { run(GetTemplates.Params(defaultType)) }
val commands = Command.CreateObject(
prefilled = buildMap { put(Relations.TYPE, defaultType) },
template = null,
@ -150,12 +153,12 @@ class CreateObjectTest {
//ASSERT
verifyNoInteractions(getDefaultPageType)
verifyBlocking(getTemplates, times(1)) { run(GetTemplates.Params(type)) }
val commands = Command.CreateObject(
prefilled = buildMap { put(Relations.TYPE, type) },
template = null,
internalFlags = listOf(
InternalFlags.ShouldSelectType,
InternalFlags.ShouldSelectTemplate,
InternalFlags.ShouldEmptyDelete
)
)
@ -180,7 +183,6 @@ class CreateObjectTest {
//ASSERT
verifyNoInteractions(getDefaultPageType)
verifyBlocking(getTemplates, times(1)) { run(GetTemplates.Params(type)) }
val commands = Command.CreateObject(
prefilled = buildMap { put(Relations.TYPE, type) },
template = null,
@ -219,7 +221,6 @@ class CreateObjectTest {
//ASSERT
verifyNoInteractions(getDefaultPageType)
verifyBlocking(getTemplates, times(1)) { run(GetTemplates.Params(type)) }
val commands = Command.CreateObject(
prefilled = buildMap { put(Relations.TYPE, type) },
template = null,

View File

@ -1,5 +1,5 @@
[versions]
middlewareVersion = "v0.27.15"
middlewareVersion = "v0.27.20"
kotlinVersion = '1.7.10'
androidxCoreVersion = "1.10.1"
@ -26,7 +26,7 @@ kotlinCoroutinesVersion = '1.6.4'
androidxTestCoreVersion = '1.5.0'
androidxCoreTestingVersion = '2.2.0'
androidxSecurityCryptoVersion = '1.0.0'
androidxPreferenceVersion = '1.2.0'
androidxPreferenceVersion = '1.2.1'
constraintLayoutVersion = '2.1.4'
recyclerviewVersion = '1.3.0'
cardviewVersion = '1.0.0'

View File

@ -792,4 +792,8 @@ class BlockMiddleware(
override suspend fun fileSpaceUsage(): FileLimits {
return middleware.fileSpaceUsage()
}
override suspend fun setInternalFlags(command: Command.SetInternalFlags): Payload {
return middleware.setInternalFlags(command)
}
}

View File

@ -12,7 +12,6 @@ class ResolveListener(
) : NsdManager.ResolveListener {
override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
Timber.e("Mdns discovery resolve failed: $serviceInfo, error: $errorCode")
try {
semaphore.release()
} catch (e: Exception) {

View File

@ -2185,6 +2185,20 @@ class Middleware @Inject constructor(
return response.event.toPayload()
}
@Throws(Exception::class)
fun setInternalFlags(
command: Command.SetInternalFlags
): Payload {
val request = Rpc.Object.SetInternalFlags.Request(
contextId = command.ctx,
internalFlags = command.flags.toMiddlewareModel()
)
if (BuildConfig.DEBUG) logRequest(request)
val response = service.setInternalFlags(request)
if (BuildConfig.DEBUG) logResponse(response)
return response.event.toPayload()
}
fun addObjectToCollection(command: Command.AddObjectToCollection): Payload {
val request = Rpc.ObjectCollection.Add.Request(
contextId = command.ctx,

View File

@ -143,6 +143,9 @@ interface MiddlewareService {
@Throws(Exception::class)
fun setObjectSource(request: Rpc.Object.SetSource.Request): Rpc.Object.SetSource.Response
@Throws(Exception::class)
fun setInternalFlags(request: Rpc.Object.SetInternalFlags.Request): Rpc.Object.SetInternalFlags.Response
//endregion
//region OBJECT'S RELATIONS command

View File

@ -1611,4 +1611,17 @@ class MiddlewareServiceImplementation @Inject constructor(
return response
}
}
override fun setInternalFlags(request: Rpc.Object.SetInternalFlags.Request): Rpc.Object.SetInternalFlags.Response {
val encoded = Service.objectSetInternalFlags(
Rpc.Object.SetInternalFlags.Request.ADAPTER.encode(request)
)
val response = Rpc.Object.SetInternalFlags.Response.ADAPTER.decode(encoded)
val error = response.error
if (error != null && error.code != Rpc.Object.SetInternalFlags.Response.Error.Code.NULL) {
throw Exception(error.description)
} else {
return response
}
}
}

View File

@ -73,6 +73,7 @@ import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.ConvertObjectToSet
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
@ -94,6 +95,7 @@ import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.presentation.common.StateReducer
import com.anytypeio.anytype.presentation.common.SupportCommand
import com.anytypeio.anytype.presentation.editor.ControlPanelMachine.Interactor
import com.anytypeio.anytype.presentation.editor.ControlPanelMachine.Event.ObjectTypesWidgetEvent
import com.anytypeio.anytype.presentation.editor.Editor.Restore
import com.anytypeio.anytype.presentation.editor.editor.Command
import com.anytypeio.anytype.presentation.editor.editor.Intent
@ -286,7 +288,8 @@ class EditorViewModel(
private val workspaceManager: WorkspaceManager,
private val getObjectTypes: GetObjectTypes,
private val interceptFileLimitEvents: InterceptFileLimitEvents,
private val addRelationToObject: AddRelationToObject
private val addRelationToObject: AddRelationToObject,
private val setObjectInternalFlags: SetObjectInternalFlags
) : ViewStateViewModel<ViewState>(),
PickerListener,
SupportNavigation<EventWrapper<AppNavigation.Command>>,
@ -1212,9 +1215,7 @@ class EditorViewModel(
)
viewModelScope.launch { orchestrator.stores.views.update(new) }
viewModelScope.launch { orchestrator.proxies.changes.send(update) }
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
}
fun onDescriptionBlockTextChanged(view: BlockView.Description) {
@ -1229,9 +1230,7 @@ class EditorViewModel(
)
viewModelScope.launch { orchestrator.stores.views.update(new) }
viewModelScope.launch { orchestrator.proxies.changes.send(update) }
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
}
fun onTextBlockTextChanged(view: BlockView.Text) {
@ -1257,9 +1256,7 @@ class EditorViewModel(
}
viewModelScope.launch { orchestrator.proxies.changes.send(update) }
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
}
fun onSelectionChanged(id: String, selection: IntRange) {
@ -1438,9 +1435,7 @@ class EditorViewModel(
) {
Timber.d("onEndLineEnterClicked, id:[$id] text:[$text] marks:[$marks]")
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
val target = blocks.first { it.id == id }
@ -2908,9 +2903,7 @@ class EditorViewModel(
fun onOutsideClicked() {
Timber.d("onOutsideClicked, ")
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
if (mode is EditorMode.Styling) {
onExitBlockStyleToolbarClicked()
@ -4289,11 +4282,9 @@ class EditorViewModel(
analytics = analytics,
objType = storeOfObjectTypes.get(type)
)
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
if (applyTemplate) {
proceedWithTemplateSelection(type)
proceedWithCheckingInternalFlagShouldSelectTemplate(objTypeId = type)
}
}
}
@ -5353,9 +5344,7 @@ class EditorViewModel(
Timber.d("onKeyPressedEvent, event:[$event]")
when (event) {
is KeyPressedEvent.OnTitleBlockEnterKeyEvent -> {
if (isObjectTypesWidgetVisible) {
proceedWithHidingObjectTypeWidget()
}
sendHideObjectTypeWidgetEvent()
proceedWithTitleEnterClicked(
title = event.target,
text = event.text,
@ -5883,28 +5872,17 @@ class EditorViewModel(
fun onObjectTypesWidgetDoneClicked() {
Timber.d("onObjectTypesWidgetDoneClicked, ")
proceedWithHidingObjectTypeWidget()
val details = orchestrator.stores.details.current()
val wrapper = ObjectWrapper.Basic(details.details[context]?.map ?: emptyMap())
if (wrapper.internalFlags.contains(InternalFlags.ShouldSelectTemplate)) {
if (wrapper.type.isNotEmpty()) {
proceedWithTemplateSelection(typeId = wrapper.type.first())
}
}
sendHideObjectTypeWidgetEvent()
proceedWithCheckingInternalFlagShouldSelectTemplate()
}
private fun proceedWithShowingObjectTypesWidget() {
val restrictions = orchestrator.stores.objectRestrictions.current()
if (restrictions.contains(ObjectRestriction.TYPE_CHANGE)) {
Timber.d("proceedWithShowingObjectTypesWidget, type change is restricted")
return
}
val details = orchestrator.stores.details.current()
val objectDetails = ObjectWrapper.Basic(details.details[context]?.map ?: emptyMap())
val internalFlags = objectDetails.internalFlags
if (internalFlags.contains(InternalFlags.ShouldSelectType)) {
proceedWithGettingObjectTypesForObjectTypeWidget()
}
proceedWithCheckingInternalFlagShouldSelectType()
}
private fun proceedWithGettingObjectTypesForObjectTypeWidget() {
@ -5959,10 +5937,6 @@ class EditorViewModel(
)
}
private fun proceedWithHidingObjectTypeWidget() {
controlPanelInteractor.onEvent(ControlPanelMachine.Event.ObjectTypesWidgetEvent.Hide)
}
private fun proceedWithOpeningSelectingObjectTypeScreen() {
val excludeTypes = orchestrator.stores.details.current().details[context]?.type
val command = if (isObjectTypesWidgetVisible) {
@ -5976,6 +5950,10 @@ class EditorViewModel(
}
dispatch(command)
}
private fun sendHideObjectTypeWidgetEvent() {
if (isObjectTypesWidgetVisible) controlPanelInteractor.onEvent(ObjectTypesWidgetEvent.Hide)
}
//endregion
//region OBJECT APPEARANCE SETTING
@ -6186,23 +6164,28 @@ class EditorViewModel(
viewModelScope.launch { onEvent(SelectTemplateEvent.OnSkipped) }
}
private fun proceedWithTemplateSelection(typeId: Id) {
private fun proceedWithStartTemplateEvent(objTypeId: Id) {
viewModelScope.launch {
val objType = storeOfObjectTypes.get(typeId)
val objType = storeOfObjectTypes.get(objTypeId)
if (objType != null) {
onEvent(
SelectTemplateEvent.OnStart(
ctx = context,
type = typeId,
type = objTypeId,
typeName = objType.name.orEmpty()
)
)
} else {
Timber.e("Error while getting object type from storeOfObjectTypes by id: $typeId")
Timber.e("Error while getting object type from storeOfObjectTypes by id: $objTypeId")
}
}
}
private fun getObjectTypeFromDetails(): Id? {
val details = orchestrator.stores.details.current()
val wrapper = ObjectWrapper.Basic(details.details[context]?.map ?: emptyMap())
return wrapper.getProperType()
}
//endregion
//region SIMPLE TABLES
@ -6892,6 +6875,84 @@ class EditorViewModel(
}
}
//endregion
//region INTERNAL FLAGS
private fun proceedWithCheckingInternalFlagShouldSelectType() {
val internalFlags = getInternalFlagsFromDetails()
if (internalFlags.contains(InternalFlags.ShouldSelectType)) {
//We use this flag to show object type widget and then we don't need it anymore
proceedWithGettingObjectTypesForObjectTypeWidget()
proceedWithOptOutTypeInternalFlag()
} else {
Timber.d("Object doesn't have internal flag: ShouldSelectType")
}
}
private fun proceedWithCheckingInternalFlagShouldSelectTemplate(objTypeId: Id? = null) {
val internalFlags = getInternalFlagsFromDetails()
if (internalFlags.contains(InternalFlags.ShouldSelectTemplate)) {
//We use this flag to show template widget and then we don't need it anymore
val properObjTypeId = objTypeId ?: getObjectTypeFromDetails() ?: return
proceedWithStartTemplateEvent(objTypeId = properObjTypeId)
proceedWithOptOutTemplateInternalFlag()
} else {
Timber.d("Object doesn't have internal flag: ShouldSelectTemplate")
}
}
private fun proceedWithOptOutTypeInternalFlag() {
val internalFlags = getInternalFlagsFromDetails()
if (!internalFlags.contains(InternalFlags.ShouldSelectType)) return
val flagsWithoutType = filterOutInternalFlags(
flags = internalFlags,
out = InternalFlags.ShouldSelectType
)
updateFlagsAndProceed(
flags = flagsWithoutType,
action = this::sendHideObjectTypeWidgetEvent
)
}
private fun proceedWithOptOutTemplateInternalFlag() {
val internalFlags = getInternalFlagsFromDetails()
if (!internalFlags.contains(InternalFlags.ShouldSelectTemplate)) return
val flagsWithoutTemplate = filterOutInternalFlags(
flags = internalFlags,
out = InternalFlags.ShouldSelectTemplate
)
updateFlagsAndProceed(flags = flagsWithoutTemplate)
}
private fun getInternalFlagsFromDetails(): List<InternalFlags> {
val details = orchestrator.stores.details.current()
val obj = ObjectWrapper.Basic(details.details[context]?.map ?: emptyMap())
return obj.internalFlags
}
private fun filterOutInternalFlags(flags: List<InternalFlags>, out: InternalFlags): List<InternalFlags> {
return flags.filter { it != out }
}
private fun updateFlagsAndProceed(flags: List<InternalFlags>, action: () -> Unit = {}) {
viewModelScope.launch {
val params = SetObjectInternalFlags.Params(
ctx = context,
flags = flags
)
setObjectInternalFlags.async(params).fold(
onSuccess = {
dispatcher.send(it)
Timber.d("Internal flags updated")
action.invoke()
},
onFailure = {
Timber.e(it, "Error while updating internal flags")
action.invoke()
}
)
}
}
//endregion
}
private const val NO_POSITION = -1

View File

@ -18,6 +18,7 @@ import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.ConvertObjectToSet
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
@ -79,7 +80,8 @@ open class EditorViewModelFactory(
private val getObjectTypes: GetObjectTypes,
private val objectToCollection: ConvertObjectToCollection,
private val interceptFileLimitEvents: InterceptFileLimitEvents,
private val addRelationToObject: AddRelationToObject
private val addRelationToObject: AddRelationToObject,
private val setObjectInternalFlags: SetObjectInternalFlags
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@ -120,7 +122,8 @@ open class EditorViewModelFactory(
workspaceManager = workspaceManager,
getObjectTypes = getObjectTypes,
interceptFileLimitEvents = interceptFileLimitEvents,
addRelationToObject = addRelationToObject
addRelationToObject = addRelationToObject,
setObjectInternalFlags = setObjectInternalFlags
) as T
}
}

View File

@ -8,6 +8,7 @@ import com.anytypeio.anytype.analytics.base.EventsDictionary.addView
import com.anytypeio.anytype.analytics.base.EventsDictionary.changeFilterValue
import com.anytypeio.anytype.analytics.base.EventsDictionary.changeSortValue
import com.anytypeio.anytype.analytics.base.EventsDictionary.changeViewType
import com.anytypeio.anytype.analytics.base.EventsDictionary.clickNewOption
import com.anytypeio.anytype.analytics.base.EventsDictionary.collectionScreenShow
import com.anytypeio.anytype.analytics.base.EventsDictionary.duplicateView
import com.anytypeio.anytype.analytics.base.EventsDictionary.objectCreate
@ -18,6 +19,7 @@ import com.anytypeio.anytype.analytics.base.EventsDictionary.removeFilter
import com.anytypeio.anytype.analytics.base.EventsDictionary.removeSort
import com.anytypeio.anytype.analytics.base.EventsDictionary.removeView
import com.anytypeio.anytype.analytics.base.EventsDictionary.repositionView
import com.anytypeio.anytype.analytics.base.EventsDictionary.selectTemplate
import com.anytypeio.anytype.analytics.base.EventsDictionary.setScreenShow
import com.anytypeio.anytype.analytics.base.EventsDictionary.setSelectQuery
import com.anytypeio.anytype.analytics.base.EventsDictionary.switchView
@ -1201,6 +1203,37 @@ fun CoroutineScope.logEvent(
)
)
}
ObjectStateAnalyticsEvent.SELECT_TEMPLATE -> {
val route = when (state) {
is ObjectState.DataView.Collection -> EventsDictionary.Routes.objCreateCollection
is ObjectState.DataView.Set -> EventsDictionary.Routes.objCreateSet
}
scope.sendEvent(
analytics = analytics,
eventName = selectTemplate,
startTime = startTime,
middleTime = middleTime,
props = buildProps(
route = route
)
)
}
ObjectStateAnalyticsEvent.SHOW_TEMPLATES -> {
val route = when (state) {
is ObjectState.DataView.Collection -> EventsDictionary.Routes.objCreateCollection
is ObjectState.DataView.Set -> EventsDictionary.Routes.objCreateSet
}
scope.sendEvent(
analytics = analytics,
eventName = clickNewOption,
startTime = startTime,
middleTime = middleTime,
props = buildProps(
route = route
)
)
}
}
}
@ -1242,7 +1275,9 @@ enum class ObjectStateAnalyticsEvent {
ADD_SORT,
CHANGE_SORT_VALUE,
REMOVE_SORT,
OBJECT_CREATE
OBJECT_CREATE,
SELECT_TEMPLATE,
SHOW_TEMPLATES
}
fun CoroutineScope.sendEditWidgetsEvent(
@ -1681,4 +1716,18 @@ fun CoroutineScope.sendAnalyticsOnboardingLoginEvent(
}
)
)
}
fun CoroutineScope.sendAnalyticsSelectTemplateEvent(
analytics: Analytics
) {
sendEvent(
analytics = analytics,
eventName = selectTemplate,
props = Props(
buildMap {
put(EventsPropertiesKey.route, "Navigation")
}
)
)
}

View File

@ -35,6 +35,9 @@ class OnboardingSoulCreationViewModel @Inject constructor(
val config = configStorage.getOrNull()
if (config != null) {
viewModelScope.launch {
sendAnalyticsOnboardingScreenEvent(analytics,
EventsDictionary.ScreenOnboardingStep.SOUL_CREATING
)
setObjectDetails.async(
SetObjectDetails.Params(
ctx = config.profile, details = mapOf(Relations.NAME to name)
@ -59,6 +62,10 @@ class OnboardingSoulCreationViewModel @Inject constructor(
val config = configStorage.getOrNull()
if (config != null) {
viewModelScope.launch {
sendAnalyticsOnboardingScreenEvent(
analytics = analytics,
step = EventsDictionary.ScreenOnboardingStep.SPACE_CREATING
)
setObjectDetails.async(
SetObjectDetails.Params(
ctx = config.workspace,
@ -69,10 +76,6 @@ class OnboardingSoulCreationViewModel @Inject constructor(
Timber.e(it, "Error while updating object details")
},
onSuccess = {
sendAnalyticsOnboardingScreenEvent(
analytics = analytics,
step = EventsDictionary.ScreenOnboardingStep.SPACE_CREATING
)
_navigationFlow.emit(Navigation.OpenSoulCreationAnim(name))
}
)
@ -88,12 +91,6 @@ class OnboardingSoulCreationViewModel @Inject constructor(
viewModelScope.launch { toasts.emit(msg) }
}
fun sendAnalyticsOnboardingScreen() {
viewModelScope.sendAnalyticsOnboardingScreenEvent(analytics,
EventsDictionary.ScreenOnboardingStep.SOUL_CREATING
)
}
sealed interface Navigation {
class OpenSoulCreationAnim(val name: String): Navigation
}

View File

@ -446,4 +446,11 @@ fun ObjectWrapper.Basic.toTemplateViewBlank(typeId: Id): TemplateView.Blank {
typeId = typeId,
layout = layout?.code ?: ObjectType.Layout.BASIC.code
)
}
fun ObjectWrapper.Type.toTemplateViewBlank(): TemplateView.Blank {
return TemplateView.Blank(
typeId = id,
layout = recommendedLayout?.code ?: ObjectType.Layout.BASIC.code
)
}

View File

@ -539,24 +539,22 @@ class ObjectSetViewModel(
query.isEmpty() || setOfValue.isEmpty() -> DataViewViewState.Set.NoQuery
render == null -> DataViewViewState.Set.NoView
render.isEmpty() -> {
val isTemplatesAllowed = templates.isNotEmpty() &&
objectState.isTemplatesAllowed(
setOfValue,
storeOfObjectTypes,
getDefaultPageType
)
val isTemplatesAllowed = objectState.isTemplatesAllowed(
setOfValue,
storeOfObjectTypes,
getDefaultPageType
)
DataViewViewState.Set.NoItems(
title = render.title,
hasTemplates = isTemplatesAllowed
)
}
else -> {
val isTemplatesAllowed = templates.isNotEmpty() &&
objectState.isTemplatesAllowed(
setOfValue,
storeOfObjectTypes,
getDefaultPageType
)
val isTemplatesAllowed = objectState.isTemplatesAllowed(
setOfValue,
storeOfObjectTypes,
getDefaultPageType
)
DataViewViewState.Set.Default(
viewer = render,
hasTemplates = isTemplatesAllowed
@ -882,6 +880,14 @@ class ObjectSetViewModel(
items = _templateViews.value,
showWidget = true
)
viewModelScope.launch {
logEvent(
state = stateReducer.state.value,
analytics = analytics,
event = ObjectStateAnalyticsEvent.SHOW_TEMPLATES
)
}
}
fun onDismissTemplatesWidget() {
@ -1494,6 +1500,8 @@ class ObjectSetViewModel(
_templateViews.value =
listOf(templates.first().toTemplateViewBlank(objectType.id)) +
templates.map { it.toTemplateView(typeId = objectType.id) }
} else {
_templateViews.value = listOf(objectType.toTemplateViewBlank())
}
},
onFailure = { e ->
@ -1519,10 +1527,24 @@ class ObjectSetViewModel(
when(item) {
is TemplateView.Blank -> {
templatesWidgetState.value = TemplatesWidgetUiState.empty()
viewModelScope.launch {
logEvent(
state = stateReducer.state.value,
analytics = analytics,
event = ObjectStateAnalyticsEvent.SELECT_TEMPLATE
)
}
proceedWithCreatingNewDataViewObject()
}
is TemplateView.Template -> {
templatesWidgetState.value = TemplatesWidgetUiState.empty()
viewModelScope.launch {
logEvent(
state = stateReducer.state.value,
analytics = analytics,
event = ObjectStateAnalyticsEvent.SELECT_TEMPLATE
)
}
proceedWithCreatingNewDataViewObject(templatesId = item.id)
}
}

View File

@ -4,6 +4,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.ObjectWrapper
@ -13,6 +14,7 @@ import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.templates.ApplyTemplate
import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.presentation.common.BaseViewModel
import com.anytypeio.anytype.presentation.extension.sendAnalyticsSelectTemplateEvent
import com.anytypeio.anytype.presentation.navigation.AppNavigation
import com.anytypeio.anytype.presentation.navigation.SupportNavigation
import kotlinx.coroutines.launch
@ -24,7 +26,8 @@ import timber.log.Timber
class TemplateSelectViewModel(
private val storeOfObjectTypes: StoreOfObjectTypes,
private val getTemplates: GetTemplates,
private val applyTemplate: ApplyTemplate
private val applyTemplate: ApplyTemplate,
private val analytics: Analytics
) : BaseViewModel(), SupportNavigation<EventWrapper<AppNavigation.Command>> {
val isDismissed = MutableStateFlow(false)
@ -102,6 +105,9 @@ class TemplateSelectViewModel(
proceedWithApplyingTemplate(ctx, template)
}
}
viewModelScope.launch {
sendAnalyticsSelectTemplateEvent(analytics)
}
}
else -> {
Timber.e("onUseTemplate: unexpected state $state")
@ -127,10 +133,15 @@ class TemplateSelectViewModel(
}
}
fun onSkipButtonClicked() {
isDismissed.value = true
}
class Factory @Inject constructor(
private val applyTemplate: ApplyTemplate,
private val getTemplates: GetTemplates,
private val storeOfObjectTypes: StoreOfObjectTypes
private val storeOfObjectTypes: StoreOfObjectTypes,
private val analytics: Analytics
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@ -138,7 +149,8 @@ class TemplateSelectViewModel(
return TemplateSelectViewModel(
applyTemplate = applyTemplate,
getTemplates = getTemplates,
storeOfObjectTypes = storeOfObjectTypes
storeOfObjectTypes = storeOfObjectTypes,
analytics = analytics
) as T
}
}
@ -149,7 +161,6 @@ class TemplateSelectViewModel(
) : ViewState()
object Init : ViewState()
object ErrorGettingType : ViewState()
}
}

View File

@ -61,6 +61,7 @@ import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.ConvertObjectToSet
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.DefaultStoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.DefaultStoreOfRelations
@ -352,6 +353,7 @@ open class EditorViewModelTest {
private lateinit var objectToSet: ConvertObjectToSet
private lateinit var clearBlockContent: ClearBlockContent
private lateinit var clearBlockStyle: ClearBlockStyle
private lateinit var setObjectInternalFlags: SetObjectInternalFlags
val root = MockDataFactory.randomUuid()
@ -3812,6 +3814,7 @@ open class EditorViewModelTest {
clearBlockContent = ClearBlockContent(repo)
clearBlockStyle = ClearBlockStyle(repo)
interceptFileLimitEvents = InterceptFileLimitEvents(fileLimitsEventChannel, dispatchers)
setObjectInternalFlags = SetObjectInternalFlags(repo, dispatchers)
workspaceManager = WorkspaceManager.DefaultWorkspaceManager()
runBlocking {
@ -3904,7 +3907,8 @@ open class EditorViewModelTest {
workspaceManager = workspaceManager,
getObjectTypes = getObjectTypes,
interceptFileLimitEvents = interceptFileLimitEvents,
addRelationToObject = addRelationToObject
addRelationToObject = addRelationToObject,
setObjectInternalFlags = setObjectInternalFlags
)
}

View File

@ -10,6 +10,7 @@ import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.StubHeader
import com.anytypeio.anytype.core_models.StubSmartBlock
import com.anytypeio.anytype.core_models.StubTitle
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.presentation.util.DefaultCoroutineTestRule
import kotlin.test.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -19,6 +20,10 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.times
import org.mockito.kotlin.verifyBlocking
import org.mockito.kotlin.verifyNoInteractions
import org.mockito.kotlin.verifyNoMoreInteractions
@ExperimentalCoroutinesApi
class EditorInternalFlagsTest : EditorPresentationTestSetup() {
@ -99,4 +104,203 @@ class EditorInternalFlagsTest : EditorPresentationTestSetup() {
assertEquals(expected = expectedFlags, actual = actualFlags)
}
@Test
fun `should remove type flag on show object event with type flag in details`() = runTest {
val title = StubTitle()
val header = StubHeader(children = listOf(title.id))
val page = StubSmartBlock(id = root, children = listOf(header.id))
val document = listOf(page, header, title)
stubInterceptEvents()
val detailsList = Block.Details(
details = mapOf(
root to Block.Fields(
mapOf(
Relations.TYPE to ObjectTypeIds.PAGE,
Relations.LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
Relations.INTERNAL_FLAGS to listOf(
InternalFlags.ShouldSelectTemplate.code.toDouble(),
InternalFlags.ShouldEmptyDelete.code.toDouble(),
InternalFlags.ShouldSelectType.code.toDouble(),
)
)
)
)
)
stubOpenDocument(document = document, details = detailsList)
stubGetObjectTypes(types = emptyList())
stubGetDefaultObjectType()
val vm = buildViewModel()
stubFileLimitEvents()
stubSetInternalFlags()
vm.onStart(root)
advanceUntilIdle()
verifyBlocking(setObjectInternalFlags, times(1)) {
async(
params = SetObjectInternalFlags.Params(
ctx = root,
flags = listOf(
InternalFlags.ShouldSelectTemplate,
InternalFlags.ShouldEmptyDelete
)
)
)
}
coroutineTestRule.advanceTime(100)
}
@Test
fun `should not remove type flag on show object event without type flag in details`() = runTest {
val title = StubTitle()
val header = StubHeader(children = listOf(title.id))
val page = StubSmartBlock(id = root, children = listOf(header.id))
val document = listOf(page, header, title)
stubInterceptEvents()
val detailsList = Block.Details(
details = mapOf(
root to Block.Fields(
mapOf(
Relations.TYPE to ObjectTypeIds.PAGE,
Relations.LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
Relations.INTERNAL_FLAGS to listOf(
InternalFlags.ShouldSelectTemplate.code.toDouble(),
InternalFlags.ShouldEmptyDelete.code.toDouble()
)
)
)
)
)
stubOpenDocument(document = document, details = detailsList)
stubGetObjectTypes(types = emptyList())
stubGetDefaultObjectType()
val vm = buildViewModel()
stubFileLimitEvents()
stubSetInternalFlags()
vm.onStart(root)
advanceUntilIdle()
verifyNoInteractions(setObjectInternalFlags)
coroutineTestRule.advanceTime(100)
}
@Test
fun `should remove template flag on start template selection widget`() = runTest {
val title = StubTitle()
val header = StubHeader(children = listOf(title.id))
val page = StubSmartBlock(id = root, children = listOf(header.id))
val document = listOf(page, header, title)
stubInterceptEvents()
val detailsList = Block.Details(
details = mapOf(
root to Block.Fields(
mapOf(
Relations.TYPE to ObjectTypeIds.PAGE,
Relations.LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
Relations.INTERNAL_FLAGS to listOf(
InternalFlags.ShouldSelectTemplate.code.toDouble(),
InternalFlags.ShouldEmptyDelete.code.toDouble(),
)
)
)
)
)
stubOpenDocument(document = document, details = detailsList)
stubGetObjectTypes(types = emptyList())
stubGetDefaultObjectType()
val vm = buildViewModel()
stubFileLimitEvents()
stubSetInternalFlags()
vm.onStart(root)
advanceUntilIdle()
vm.onObjectTypesWidgetDoneClicked()
advanceUntilIdle()
verifyBlocking(setObjectInternalFlags, times(1)) {
async(
params = SetObjectInternalFlags.Params(
ctx = root,
flags = listOf(
InternalFlags.ShouldEmptyDelete
)
)
)
}
coroutineTestRule.advanceTime(100)
}
@Test
fun `should not remove template flag on start template selection widget when flag isn't present`() = runTest {
val title = StubTitle()
val header = StubHeader(children = listOf(title.id))
val page = StubSmartBlock(id = root, children = listOf(header.id))
val document = listOf(page, header, title)
stubInterceptEvents()
val detailsList = Block.Details(
details = mapOf(
root to Block.Fields(
mapOf(
Relations.TYPE to ObjectTypeIds.PAGE,
Relations.LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
Relations.INTERNAL_FLAGS to listOf(
InternalFlags.ShouldSelectType.code.toDouble(),
InternalFlags.ShouldEmptyDelete.code.toDouble(),
)
)
)
)
)
stubOpenDocument(document = document, details = detailsList)
stubGetObjectTypes(types = emptyList())
stubGetDefaultObjectType()
val vm = buildViewModel()
stubFileLimitEvents()
stubSetInternalFlags()
vm.onStart(root)
advanceUntilIdle()
vm.onObjectTypesWidgetDoneClicked()
advanceUntilIdle()
verifyBlocking(setObjectInternalFlags, times(1)) {
async(
params = SetObjectInternalFlags.Params(
ctx = root,
flags = listOf(
InternalFlags.ShouldEmptyDelete
)
)
)
}
advanceUntilIdle()
verifyNoMoreInteractions(setObjectInternalFlags)
coroutineTestRule.advanceTime(100)
}
}

View File

@ -50,6 +50,7 @@ import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.ConvertObjectToSet
import com.anytypeio.anytype.domain.`object`.SetObjectInternalFlags
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.DefaultStoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.DefaultStoreOfRelations
@ -346,6 +347,9 @@ open class EditorPresentationTestSetup {
lateinit var fileLimitsEventChannel: FileLimitsEventChannel
lateinit var interceptFileLimitEvents: InterceptFileLimitEvents
@Mock
lateinit var setObjectInternalFlags: SetObjectInternalFlags
open fun buildViewModel(urlBuilder: UrlBuilder = builder): EditorViewModel {
val storage = Editor.Storage()
@ -467,7 +471,8 @@ open class EditorPresentationTestSetup {
workspaceManager = workspaceManager,
getObjectTypes = getObjectTypes,
interceptFileLimitEvents = interceptFileLimitEvents,
addRelationToObject = addRelationToObject
addRelationToObject = addRelationToObject,
setObjectInternalFlags = setObjectInternalFlags
)
}
@ -746,4 +751,16 @@ open class EditorPresentationTestSetup {
} doReturn Resultat.success(types)
}
}
fun stubFileLimitEvents() {
interceptFileLimitEvents.stub {
onBlocking { run(Unit) } doReturn emptyFlow()
}
}
fun stubSetInternalFlags() {
setObjectInternalFlags.stub {
onBlocking { async(any()) } doReturn Resultat.success(Payload(root, emptyList()))
}
}
}

View File

@ -257,8 +257,7 @@ message Rpc {
}
string storePath = 3; // Path to local storage
int64 icon = 4; // Option of pre-installed icon
string alphaInviteCode = 20; // DEPRECATED
bool disableLocalNetworkSync = 5; // Disable local network discovery
}
/**
@ -363,6 +362,7 @@ message Rpc {
option (no_auth) = true;
string id = 1; // Id of a selected account
string rootPath = 2; // Root path is optional, set if this is a first request
bool disableLocalNetworkSync = 3; // Disable local network discovery
}
/**
@ -548,6 +548,27 @@ message Rpc {
}
}
}
message EnableLocalNetworkSync {
message Request {
}
message Response {
Error error = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
ACCOUNT_IS_NOT_RUNNING = 4;
}
}
}
}
}
message Workspace {
@ -1498,6 +1519,7 @@ message Rpc {
}
}
}
message SetInternalFlags {
message Request {
string contextId = 1;
@ -1739,6 +1761,28 @@ message Rpc {
}
}
message ListSetObjectType {
message Request {
repeated string objectIds = 1;
string objectTypeId = 2;
}
message Response {
Error error = 1;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
UNKNOWN_OBJECT_TYPE_URL = 3;
}
}
}
}
message ApplyTemplate {
message Request {
string contextId = 1;
@ -5364,6 +5408,26 @@ message Rpc {
}
}
message OpenedObjects {
message Request {}
message Response {
Error error = 1;
repeated string objectIDs = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
}
}
}
}
message Ping {
message Request {
int32 index = 1;

View File

@ -581,6 +581,7 @@ message Account {
string localStoragePath = 103; // path to local storage
string timeZone = 104; // time zone from config
string analyticsId = 105;
string networkId = 106; // network id to which anytype is connected
}
}