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

DROID-2731 Merge branch 'refs/heads/main' into droid-2731-epic-release-7-new-vault-5

This commit is contained in:
Evgenii Kozlov 2024-09-04 12:27:33 +02:00
commit 53f23b002c
33 changed files with 241 additions and 85 deletions

View file

@ -99,6 +99,7 @@ import com.anytypeio.anytype.core_utils.ext.lastDecorator
import com.anytypeio.anytype.core_utils.ext.safeNavigate
import com.anytypeio.anytype.core_utils.ext.screen
import com.anytypeio.anytype.core_utils.ext.show
import com.anytypeio.anytype.core_utils.ext.startMarketPageOrWeb
import com.anytypeio.anytype.core_utils.ext.subscribe
import com.anytypeio.anytype.core_utils.ext.syncTranslationWithImeVisibility
import com.anytypeio.anytype.core_utils.ext.throttleFirst
@ -745,7 +746,8 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
SpaceSyncStatusScreen(
uiState = vm.syncStatusWidget.collectAsStateWithLifecycle().value,
onDismiss = vm::onSyncWidgetDismiss,
scope = lifecycleScope
scope = lifecycleScope,
onUpdateAppClick = vm::onUpdateAppClick
)
}
}
@ -949,6 +951,9 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
toast("Couldn't parse url: ${command.url}")
}
}
is Command.OpenAppStore -> {
startMarketPageOrWeb()
}
is Command.OpenDocumentMenu -> {
hideKeyboard()
runCatching {

View file

@ -78,6 +78,7 @@ import com.anytypeio.anytype.core_utils.ext.hideKeyboard
import com.anytypeio.anytype.core_utils.ext.hideSoftInput
import com.anytypeio.anytype.core_utils.ext.invisible
import com.anytypeio.anytype.core_utils.ext.safeNavigate
import com.anytypeio.anytype.core_utils.ext.startMarketPageOrWeb
import com.anytypeio.anytype.core_utils.ext.subscribe
import com.anytypeio.anytype.core_utils.ext.syncFocusWithImeVisibility
import com.anytypeio.anytype.core_utils.ext.syncTranslationWithImeVisibility
@ -452,7 +453,8 @@ open class ObjectSetFragment :
SpaceSyncStatusScreen(
uiState = vm.syncStatusWidget.collectAsStateWithLifecycle().value,
onDismiss = vm::onSyncWidgetDismiss,
scope = lifecycleScope
scope = lifecycleScope,
onUpdateAppClick = vm::onUpdateAppClick
)
}
}
@ -1228,6 +1230,9 @@ open class ObjectSetFragment :
getString(R.string.multiplayer_read_only_access_error)
)
}
ObjectSetCommand.Intent.OpenAppStore -> {
startMarketPageOrWeb()
}
}
}

View file

@ -4,7 +4,9 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
@ -46,6 +48,7 @@ import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncStatus
import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncUpdate
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.foundation.Divider
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.core_ui.views.BodyRegular
import com.anytypeio.anytype.core_ui.views.Relations3
import com.anytypeio.anytype.core_ui.views.Title2
@ -60,7 +63,8 @@ import kotlinx.coroutines.launch
fun SpaceSyncStatusScreen(
uiState: SyncStatusWidgetState,
onDismiss: () -> Unit,
scope: CoroutineScope
scope: CoroutineScope,
onUpdateAppClick: () -> Unit
) {
val isVisible = uiState is SyncStatusWidgetState.Success || uiState is SyncStatusWidgetState.Error
@ -112,7 +116,8 @@ fun SpaceSyncStatusScreen(
SyncStatusWidgetState.Hidden -> LoadingState()
is SyncStatusWidgetState.Success -> SuccessState(
spaceSyncUpdate = uiState.spaceSyncUpdate,
p2pStatus = uiState.p2PStatusUpdate
p2pStatus = uiState.p2PStatusUpdate,
onUpdateAppClick = onUpdateAppClick
)
}
}
@ -145,9 +150,13 @@ private fun ColumnScope.ErrorState() {
}
@Composable
private fun SuccessState(spaceSyncUpdate: SpaceSyncUpdate, p2pStatus: P2PStatusUpdate) {
private fun SuccessState(
spaceSyncUpdate: SpaceSyncUpdate,
p2pStatus: P2PStatusUpdate,
onUpdateAppClick: () -> Unit
) {
if (spaceSyncUpdate is SpaceSyncUpdate.Update) {
SpaceSyncStatusItem(spaceSyncUpdate)
SpaceSyncStatusItem(spaceSyncUpdate, onUpdateAppClick)
Divider()
}
if (p2pStatus is P2PStatusUpdate.Update) {
@ -197,7 +206,8 @@ private fun P2PStatusItem(
@Composable
private fun SpaceSyncStatusItem(
spaceSyncUpdate: SpaceSyncUpdate.Update
spaceSyncUpdate: SpaceSyncUpdate.Update,
onUpdateAppClick: () -> Unit = {}
) {
val networkCardSettings = getNetworkCardSettings(
syncStatus = spaceSyncUpdate.status,
@ -205,15 +215,21 @@ private fun SpaceSyncStatusItem(
error = spaceSyncUpdate.error,
syncingObjectsCounter = spaceSyncUpdate.syncingObjectsCounter
)
Row(
Box(
modifier = Modifier
.fillMaxWidth()
.height(72.dp)
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically
.noRippleClickable {
if (spaceSyncUpdate.status == SpaceSyncStatus.NETWORK_UPDATE_NEEDED) {
onUpdateAppClick()
}
}
) {
Image(
modifier = Modifier.wrapContentSize(),
modifier = Modifier
.wrapContentSize()
.padding(start = 16.dp)
.align(Alignment.CenterStart),
painter = networkCardSettings.icon,
contentDescription = "dfas",
alpha = networkCardSettings.alpha
@ -221,7 +237,8 @@ private fun SpaceSyncStatusItem(
Column(
modifier = Modifier
.fillMaxWidth()
.padding(start = 12.dp)
.padding(start = 76.dp)
.align(Alignment.CenterStart)
) {
Text(
text = networkCardSettings.mainText,
@ -236,6 +253,16 @@ private fun SpaceSyncStatusItem(
)
}
}
if (spaceSyncUpdate.status == SpaceSyncStatus.NETWORK_UPDATE_NEEDED) {
Image(
modifier = Modifier
.padding(end = 20.dp)
.wrapContentSize()
.align(Alignment.CenterEnd),
painter = painterResource(id = R.drawable.ic_arrow_top_end),
contentDescription = "update app icon"
)
}
}
}
@ -318,7 +345,11 @@ private fun getNetworkCardSettings(
)
}
SpaceSyncStatus.NETWORK_UPDATE_NEEDED -> {
TODO()
CardSettings(
icon = painterResource(R.drawable.ic_sync_limitations),
mainText = stringResource(id = R.string.sync_status_anytype_network),
secondaryText = stringResource(id = R.string.sync_status_anytype_sync_slow)
)
}
}
@ -362,7 +393,13 @@ private fun getNetworkCardSettings(
secondaryText = stringResource(id = R.string.sync_status_anytype_network_no_connecting)
)
}
SpaceSyncStatus.NETWORK_UPDATE_NEEDED -> TODO()
SpaceSyncStatus.NETWORK_UPDATE_NEEDED -> {
CardSettings(
icon = painterResource(R.drawable.ic_sync_limitations),
mainText = stringResource(id = R.string.sync_status_self_host),
secondaryText = stringResource(id = R.string.sync_status_anytype_sync_slow)
)
}
}
}
SpaceSyncNetwork.LOCAL_ONLY -> {
@ -540,4 +577,17 @@ fun SpaceSyncStatusPreview12() {
spaceId = "1"
)
P2PStatusItem(p2pStatus = p2pStatus)
}
@Preview(name = "AnytypeNetworkNeedUpdate", showBackground = true)
@Composable
fun SpaceSyncStatusPreview13() {
val spaceSyncUpdate = SpaceSyncUpdate.Update(
id = "1",
status = SpaceSyncStatus.NETWORK_UPDATE_NEEDED,
network = SpaceSyncNetwork.ANYTYPE,
error = SpaceSyncError.NULL,
syncingObjectsCounter = 0
)
SpaceSyncStatusItem(spaceSyncUpdate = spaceSyncUpdate)
}

View file

@ -52,7 +52,8 @@ class StatusBadgeWidget @JvmOverloads constructor(
setImageResource(R.drawable.ic_sync_grey_10)
}
SpaceSyncStatus.NETWORK_UPDATE_NEEDED -> {
// TODO
visible()
setImageResource(R.drawable.ic_sync_slow_10)
}
}
}

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="12dp"
android:viewportWidth="12"
android:viewportHeight="12">
<path
android:pathData="M3.5,1H11M11,1V8.5M11,1L1,11"
android:strokeWidth="1.25"
android:fillColor="#00000000"
android:strokeColor="@color/glyph_active"/>
</vector>

View file

@ -4,11 +4,7 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M16.9047,7.8655L11.6709,17.2312L6.9697,12.53L8.0303,11.4693L11.3291,14.7681L15.5953,7.1338L16.9047,7.8655Z"
android:fillColor="@color/glyph_active"
android:fillType="evenOdd"/>
<path
android:pathData="M12,20.5C16.6944,20.5 20.5,16.6944 20.5,12C20.5,7.3056 16.6944,3.5 12,3.5C7.3056,3.5 3.5,7.3056 3.5,12C3.5,16.6944 7.3056,20.5 12,20.5ZM12,22C17.5228,22 22,17.5228 22,12C22,6.4771 17.5228,2 12,2C6.4771,2 2,6.4771 2,12C2,17.5228 6.4771,22 12,22Z"
android:pathData="M7.5,4.5H16.5C18.157,4.5 19.5,5.843 19.5,7.5V16.5C19.5,18.157 18.157,19.5 16.5,19.5H7.5C5.843,19.5 4.5,18.157 4.5,16.5V7.5C4.5,5.843 5.843,4.5 7.5,4.5ZM3,7.5C3,5.015 5.015,3 7.5,3H16.5C18.985,3 21,5.015 21,7.5V16.5C21,18.985 18.985,21 16.5,21H7.5C5.015,21 3,18.985 3,16.5V7.5ZM11.671,17.232L16.905,7.866L15.595,7.134L11.329,14.768L8.03,11.47L6.97,12.53L11.671,17.232Z"
android:fillColor="@color/glyph_active"
android:fillType="evenOdd"/>
</vector>

View file

@ -4,11 +4,11 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,20.5C16.694,20.5 20.5,16.694 20.5,12C20.5,7.306 16.694,3.5 12,3.5C7.306,3.5 3.5,7.306 3.5,12C3.5,16.694 7.306,20.5 12,20.5ZM12,22C17.523,22 22,17.523 22,12C22,6.477 17.523,2 12,2C6.477,2 2,6.477 2,12C2,17.523 6.477,22 12,22Z"
android:pathData="M5.47,14.47C5.763,14.177 6.237,14.177 6.53,14.47L12,19.939L17.47,14.47C17.763,14.177 18.237,14.177 18.53,14.47C18.823,14.763 18.823,15.237 18.53,15.53L12,22.061L5.47,15.53C5.177,15.237 5.177,14.763 5.47,14.47Z"
android:fillColor="@color/glyph_active"
android:fillType="evenOdd"/>
<path
android:pathData="M7.47,10.47C7.763,10.177 8.237,10.177 8.53,10.47L12,13.939L15.47,10.47C15.763,10.177 16.237,10.177 16.53,10.47C16.823,10.763 16.823,11.237 16.53,11.53L12,16.061L7.47,11.53C7.177,11.237 7.177,10.763 7.47,10.47Z"
android:pathData="M5.47,9.53C5.763,9.823 6.237,9.823 6.53,9.53L12,4.061L17.47,9.53C17.763,9.823 18.237,9.823 18.53,9.53C18.823,9.237 18.823,8.763 18.53,8.47L12,1.939L5.47,8.47C5.177,8.763 5.177,9.237 5.47,9.53Z"
android:fillColor="@color/glyph_active"
android:fillType="evenOdd"/>
</vector>

View file

@ -15,9 +15,6 @@
<path
android:pathData="M19.5,6m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"
android:fillColor="#B6B6B6"/>
<path
android:pathData="M19.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"
android:fillColor="#B6B6B6"/>
<path
android:pathData="M19.5,18m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"
android:fillColor="#B6B6B6"/>

View file

@ -0,0 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:pathData="M24,0L24,0A24,24 0,0 1,48 24L48,24A24,24 0,0 1,24 48L24,48A24,24 0,0 1,0 24L0,24A24,24 0,0 1,24 0z"
android:fillColor="@color/palette_light_yellow"/>
<path
android:pathData="M20.5,16L20.5,16A0.5,0.5 0,0 1,21 16.5L21,19.5A0.5,0.5 0,0 1,20.5 20L20.5,20A0.5,0.5 0,0 1,20 19.5L20,16.5A0.5,0.5 0,0 1,20.5 16z"
android:fillColor="@color/palette_dark_yellow"/>
<path
android:pathData="M27.5,16L27.5,16A0.5,0.5 0,0 1,28 16.5L28,19.5A0.5,0.5 0,0 1,27.5 20L27.5,20A0.5,0.5 0,0 1,27 19.5L27,16.5A0.5,0.5 0,0 1,27.5 16z"
android:fillColor="@color/palette_dark_yellow"/>
<path
android:strokeWidth="1"
android:pathData="M16,29H32"
android:fillColor="#00000000"
android:strokeColor="@color/palette_dark_yellow"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="10dp"
android:height="10dp"
android:viewportWidth="10"
android:viewportHeight="10">
<path
android:pathData="M5,5m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:fillColor="@color/palette_system_yellow"/>
</vector>

View file

@ -1,8 +1,11 @@
package com.anytypeio.anytype.core_utils.ext
import android.content.Intent
import android.net.Uri
import android.os.Parcelable
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.anytypeio.anytype.core_utils.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
@ -54,4 +57,22 @@ fun <T> CoroutineScope.subscribe(flow: Flow<T>, body: suspend (T) -> Unit): Job
flow.cancellable().onEach { body(it) }.launchIn(this)
fun <T> Fragment.subscribe(flow: Flow<T>, body: (T) -> Unit): Job =
flow.cancellable().onEach { body(it) }.launchIn(viewLifecycleOwner.lifecycleScope)
flow.cancellable().onEach { body(it) }.launchIn(viewLifecycleOwner.lifecycleScope)
fun Fragment.startMarketPageOrWeb() {
runCatching {
startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse("${getString(R.string.play_market_url)}${context?.packageName}")
)
)
}.onFailure {
startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse(getString(R.string.download_anytype_url))
)
)
}
}

View file

@ -1,3 +1,5 @@
<resources>
<string name="app_name">CoreUtils</string>
<string name="download_anytype_url" translatable="false">https://download.anytype.io</string>
<string name="play_market_url" translatable="false">market://details?id=</string>
</resources>

View file

@ -2,15 +2,26 @@ package com.anytypeio.anytype.domain.account
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filterIsInstance
interface AwaitAccountStartManager {
fun isStarted(): Flow<Boolean>
fun setIsStarted(isStarted: Boolean)
fun state(): Flow<State>
fun setState(state: State)
fun awaitStart(): Flow<State.Started>
fun awaitStopped(): Flow<State.Stopped>
object Default: AwaitAccountStartManager {
private val isStarted = MutableStateFlow(false)
override fun isStarted(): Flow<Boolean> = isStarted
override fun setIsStarted(isStarted: Boolean) {
this.isStarted.value = isStarted
}
private val state = MutableStateFlow<State>(State.Init)
override fun state(): Flow<State> = state
override fun setState(state: State) { this.state.value = state }
override fun awaitStart(): Flow<State.Started> = state.filterIsInstance()
override fun awaitStopped(): Flow<State.Stopped> = state.filterIsInstance()
}
sealed class State {
data object Init : State()
data object Started : State()
data object Stopped : State()
}
}

View file

@ -47,7 +47,7 @@ open class CreateAccount @Inject constructor(
}
configStorage.set(setup.config)
spaceManager.set(setup.config.space)
awaitAccountStartManager.setIsStarted(true)
awaitAccountStartManager.setState(AwaitAccountStartManager.State.Started)
return setup.account
}

View file

@ -5,7 +5,6 @@ import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.domain.account.AwaitAccountStartManager
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import com.anytypeio.anytype.domain.device.PathProvider
@ -57,7 +56,7 @@ class LaunchAccount @Inject constructor(
} else {
spaceManager.set(setup.config.space)
}
awaitAccountStartManager.setIsStarted(true)
awaitAccountStartManager.setState(AwaitAccountStartManager.State.Started)
setup.config.analytics
}
}

View file

@ -26,7 +26,7 @@ class Logout @Inject constructor(
user.clear()
config.clear()
spaceManager.clear()
awaitAccountStartManager.setIsStarted(false)
awaitAccountStartManager.setState(AwaitAccountStartManager.State.Stopped)
}
class Params(

View file

@ -61,7 +61,7 @@ class ResumeAccount @Inject constructor(
}
setup.account.id
}
awaitAccountStartManager.setIsStarted(true)
awaitAccountStartManager.setState(AwaitAccountStartManager.State.Started)
result
}
}

View file

@ -9,7 +9,6 @@ import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.platform.MetricsProvider
import javax.inject.Inject
import kotlinx.coroutines.delay
/**
* Use case for selecting user account.
@ -42,7 +41,7 @@ class SelectAccount @Inject constructor(
setCurrentAccount(setup.account.id)
}
configStorage.set(config = setup.config)
awaitAccountStartManager.setIsStarted(true)
awaitAccountStartManager.setState(AwaitAccountStartManager.State.Started)
StartAccountResult(setup.config.analytics, setup.status)
}

View file

@ -43,11 +43,18 @@ interface ActiveSpaceMemberSubscriptionContainer {
init {
scope.launch {
awaitAccountStart.isStarted().collect { isStarted ->
if (isStarted)
start()
else
stop()
awaitAccountStart.state().collect { state ->
when(state) {
AwaitAccountStartManager.State.Init -> {
// Do nothing
}
AwaitAccountStartManager.State.Started -> {
start()
}
AwaitAccountStartManager.State.Stopped -> {
stop()
}
}
}
}
}

View file

@ -51,11 +51,18 @@ interface SpaceViewSubscriptionContainer {
init {
scope.launch {
awaitAccountStart.isStarted().collect { isStarted ->
if (isStarted)
start()
else
stop()
awaitAccountStart.state().collect { state ->
when(state) {
AwaitAccountStartManager.State.Init -> {
// Do nothing
}
AwaitAccountStartManager.State.Started -> {
start()
}
AwaitAccountStartManager.State.Stopped -> {
stop()
}
}
}
}
}

View file

@ -127,7 +127,7 @@ class CreateAccountTest {
verifyNoMoreInteractions(repo)
verify(configStorage, times(1)).set(setup.config)
verify(spaceManager, times(1)).set(setup.config.space)
verify(awaitAccountStartManager, times(1)).setIsStarted(true)
verify(awaitAccountStartManager, times(1)).setState(AwaitAccountStartManager.State.Started)
}
private fun stubMetricsProvider(version: String, platform: String) {

View file

@ -916,7 +916,6 @@
<string name="anytype_update_alert_description">Some of your data was managed in a newer version of Anytype. Please update the app to work with all your docs and the latest features.</string>
<string name="anytype_update_alert_title">It\'s time to update</string>
<string name="download_anytype_url" translatable="false">https://download.anytype.io</string>
<string name="space_type_default">Entry</string>
<string name="space_type_default_space">Entry space</string>
@ -1734,6 +1733,7 @@ Please provide specific details of your needs here.</string>
</plurals>
<string name="sync_status_anytype_network_connecting">Network Connecting...</string>
<string name="sync_status_anytype_network_no_connecting">No connection</string>
<string name="sync_status_anytype_sync_slow">Sync might be slow. Update the app.</string>
<string name="sync_status_storage_limit_exceed">Storage limit reached</string>
<string name="sync_status_incompatible_version">Incompatible version</string>

View file

@ -112,7 +112,7 @@ class MembershipProviderTest {
}
whenever(localeProvider.language()).thenReturn("en")
awaitAccountStartManager.setIsStarted(true)
awaitAccountStartManager.setState(AwaitAccountStartManager.State.Started)
val membershipProviderFlow = provider.status().testIn(backgroundScope)
val membershipProviderFlow1 = provider.status().testIn(backgroundScope)
val membershipProviderFlow2 = provider.activeTier().testIn(backgroundScope)

View file

@ -7413,6 +7413,10 @@ class EditorViewModel(
fun onSyncWidgetDismiss() {
syncStatusWidget.value = SyncStatusWidgetState.Hidden
}
fun onUpdateAppClick() {
dispatch(command = Command.OpenAppStore)
}
//endregion
data class Params(

View file

@ -57,6 +57,8 @@ sealed class Command {
val url: Url
) : Command()
data object OpenAppStore: Command()
data class OpenDocumentMenu(
val ctx: Id,
val space: Id,

View file

@ -12,7 +12,6 @@ import com.anytypeio.anytype.core_models.NotificationPayload
import com.anytypeio.anytype.core_models.NotificationStatus
import com.anytypeio.anytype.core_models.Wallpaper
import com.anytypeio.anytype.core_models.exceptions.NeedToUpdateApplicationException
import com.anytypeio.anytype.core_models.membership.TierId
import com.anytypeio.anytype.core_utils.ext.cancel
import com.anytypeio.anytype.domain.account.AwaitAccountStartManager
import com.anytypeio.anytype.domain.account.InterceptAccountStatus
@ -41,7 +40,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch
@ -306,16 +304,11 @@ class MainViewModel(
deepLinkJobs.cancel()
deepLinkJobs += viewModelScope.launch {
awaitAccountStartManager
.isStarted()
.filter { isStarted -> isStarted }
.awaitStart()
.onEach { delay(NEW_DEEP_LINK_DELAY) }
.take(1)
.collect { isStarted ->
if (isStarted) {
proceedWithNewDeepLink(deeplink)
} else {
Timber.w("Account not started")
}
.collect {
proceedWithNewDeepLink(deeplink)
}
}
}

View file

@ -2,15 +2,15 @@ package com.anytypeio.anytype.presentation.membership.provider
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.membership.Membership
import com.anytypeio.anytype.core_models.membership.MembershipStatus
import com.anytypeio.anytype.core_models.membership.MembershipTierData
import com.anytypeio.anytype.core_models.membership.TierId
import com.anytypeio.anytype.domain.account.AwaitAccountStartManager
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.misc.DateProvider
import com.anytypeio.anytype.domain.misc.LocaleProvider
import com.anytypeio.anytype.domain.workspace.MembershipChannel
import com.anytypeio.anytype.core_models.membership.MembershipStatus
import com.anytypeio.anytype.core_models.membership.TierId
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
@ -38,13 +38,13 @@ interface MembershipProvider {
@OptIn(ExperimentalCoroutinesApi::class)
override fun status(): Flow<MembershipStatus> {
return awaitAccountStartManager.isStarted().flatMapLatest { isStarted ->
if (isStarted) {
buildStatusFlow(
return awaitAccountStartManager.state().flatMapLatest { state ->
when(state) {
AwaitAccountStartManager.State.Started -> buildStatusFlow(
initial = proceedWithGettingMembership()
)
} else {
emptyFlow()
AwaitAccountStartManager.State.Init -> emptyFlow()
AwaitAccountStartManager.State.Stopped -> emptyFlow()
}
}.catch { e -> Timber.e(e) }
.flowOn(dispatchers.io)
@ -52,13 +52,13 @@ interface MembershipProvider {
@OptIn(ExperimentalCoroutinesApi::class)
override fun activeTier(): Flow<TierId> {
return awaitAccountStartManager.isStarted().flatMapLatest { isStarted ->
if (isStarted) {
buildActiveTierFlow(
return awaitAccountStartManager.state().flatMapLatest { state ->
when(state) {
AwaitAccountStartManager.State.Started -> buildActiveTierFlow(
initial = proceedWithGettingMembership()
)
} else {
emptyFlow()
AwaitAccountStartManager.State.Init -> emptyFlow()
AwaitAccountStartManager.State.Stopped -> emptyFlow()
}
}.catch { e -> Timber.e(e) }
.flowOn(dispatchers.io)

View file

@ -42,8 +42,11 @@ interface NotificationsProvider {
@OptIn(ExperimentalCoroutinesApi::class)
private fun observe(): Flow<List<Notification.Event>> {
return awaitAccountStartManager.isStarted().flatMapLatest { isStarted ->
if (isStarted) notificationsChannel.observe() else emptyFlow()
return awaitAccountStartManager.state().flatMapLatest { state ->
if (state is AwaitAccountStartManager.State.Started)
notificationsChannel.observe()
else
emptyFlow()
}
}
}

View file

@ -115,6 +115,7 @@ sealed class ObjectSetCommand {
data class GoTo(val url: String) : Intent()
data class MailTo(val email: String) : Intent()
data class Call(val phone: String) : Intent()
data object OpenAppStore : Intent()
}
data object ShowOnlyAccessError : ObjectSetCommand()

View file

@ -2865,6 +2865,10 @@ class ObjectSetViewModel(
fun onSyncWidgetDismiss() {
syncStatusWidget.value = SyncStatusWidgetState.Hidden
}
fun onUpdateAppClick() {
dispatch(command = ObjectSetCommand.Intent.OpenAppStore)
}
//endregion
companion object {

View file

@ -51,7 +51,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.onSubscription
import kotlinx.coroutines.flow.shareIn
@ -105,8 +104,7 @@ class AddToAnytypeViewModel(
}
viewModelScope.launch {
awaitAccountStartManager
.isStarted()
.filter { isStarted -> isStarted }
.awaitStart()
.flatMapLatest {
combine(
spaces,

View file

@ -139,6 +139,7 @@ class CollectionCreateAndAddObjectTest: ObjectSetViewModelTestSetup() {
@Test
fun `create pre-populated record in Collection`() = runTest {
// SETUP
val filters = mockObjectCollection.filters
@ -169,13 +170,19 @@ class CollectionCreateAndAddObjectTest: ObjectSetViewModelTestSetup() {
)
// TESTING
proceedWithStartingViewModel()
// ASSERT DATA VIEW STATE
viewModel.currentViewer.test {
val first = awaitItem()
assertIs<DataViewViewState.Init>(first)
advanceUntilIdle()
cancelAndIgnoreRemainingEvents()
advanceUntilIdle()
@ -191,7 +198,10 @@ class CollectionCreateAndAddObjectTest: ObjectSetViewModelTestSetup() {
val spaceId = SpaceId(mockObjectCollection.spaceId)
val command = Command.CreateObject(
prefilled = mapOf(filters[0].relation to filters[0].value, filters[1].relation to filters[1].value),
prefilled = mapOf(
filters[0].relation to filters[0].value,
filters[1].relation to filters[1].value
),
internalFlags = listOf(InternalFlags.ShouldSelectTemplate),
space = spaceId,
typeKey = TypeKey(newObjectTypeKey),

View file

@ -64,7 +64,7 @@ class NotificationsProviderTest {
// Arrange
val eventList = listOf(testEvent)
whenever(notificationsChannel.observe()).thenReturn(flowOf(eventList))
awaitAccountStartManager.setIsStarted(true)
awaitAccountStartManager.setState(AwaitAccountStartManager.State.Started)
// Act
notificationsProvider.events.test {
@ -78,8 +78,8 @@ class NotificationsProviderTest {
// Arrange
val eventList = listOf(testEvent)
whenever(notificationsChannel.observe()).thenReturn(flowOf(eventList))
awaitAccountStartManager.setIsStarted(true) // Start and then stop
awaitAccountStartManager.setIsStarted(false)
awaitAccountStartManager.setState(AwaitAccountStartManager.State.Started) // Start and then stop
awaitAccountStartManager.setState(AwaitAccountStartManager.State.Stopped)
// Act & Assert
notificationsProvider.events.test(timeout = Duration.parse("2s")) {
@ -94,7 +94,7 @@ class NotificationsProviderTest {
// Arrange
val eventFlow = flowOf(listOf(testEvent))
whenever(notificationsChannel.observe()).thenReturn(eventFlow)
awaitAccountStartManager.setIsStarted(true)
awaitAccountStartManager.setState(AwaitAccountStartManager.State.Started)
// Act & Assert
notificationsProvider.events.test(timeout = Duration.parse("2s")) {