mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-2802 Sync status | Ui fixes (#1934)
This commit is contained in:
parent
5b803ac85c
commit
d173139b54
10 changed files with 131 additions and 103 deletions
|
@ -24,8 +24,10 @@ import androidx.annotation.RequiresApi
|
|||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
@ -756,11 +758,12 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
|
|||
SpaceSyncStatusScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(WindowInsets.ime)
|
||||
.padding(bottom = 6.dp, start = 8.dp, end = 8.dp),
|
||||
.wrapContentHeight()
|
||||
.padding(bottom = 16.dp)
|
||||
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||
modifierCard = Modifier.padding(start = 8.dp, end = 8.dp),
|
||||
uiState = vm.syncStatusWidget.collectAsStateWithLifecycle().value,
|
||||
onDismiss = vm::onSyncWidgetDismiss,
|
||||
scope = lifecycleScope,
|
||||
onUpdateAppClick = vm::onUpdateAppClick
|
||||
)
|
||||
}
|
||||
|
|
|
@ -24,8 +24,10 @@ import androidx.appcompat.widget.AppCompatEditText
|
|||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
@ -454,11 +456,12 @@ open class ObjectSetFragment :
|
|||
SpaceSyncStatusScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(WindowInsets.ime)
|
||||
.padding(bottom = 6.dp, start = 8.dp, end = 8.dp),
|
||||
.wrapContentHeight()
|
||||
.padding(bottom = 16.dp)
|
||||
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||
modifierCard = Modifier.padding(start = 8.dp, end = 8.dp),
|
||||
uiState = vm.syncStatusWidget.collectAsStateWithLifecycle().value,
|
||||
onDismiss = vm::onSyncWidgetDismiss,
|
||||
scope = lifecycleScope,
|
||||
onUpdateAppClick = vm::onUpdateAppClick
|
||||
)
|
||||
}
|
||||
|
|
|
@ -201,7 +201,6 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.motion.widget.MotionLayout>
|
|
@ -1,46 +1,35 @@
|
|||
package com.anytypeio.anytype.core_ui.syncstatus
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.Image
|
||||
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
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.CircularProgressIndicator
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.FractionalThreshold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.rememberSwipeableState
|
||||
import androidx.compose.material.swipeable
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_models.multiplayer.P2PStatus
|
||||
import com.anytypeio.anytype.core_models.multiplayer.P2PStatusUpdate
|
||||
|
@ -50,82 +39,62 @@ 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.Dragger
|
||||
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
|
||||
import com.anytypeio.anytype.core_ui.widgets.DragStates
|
||||
import com.anytypeio.anytype.presentation.sync.SyncStatusWidgetState
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SpaceSyncStatusScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
modifierCard: Modifier = Modifier,
|
||||
uiState: SyncStatusWidgetState,
|
||||
onDismiss: () -> Unit,
|
||||
scope: CoroutineScope,
|
||||
onUpdateAppClick: () -> Unit
|
||||
) {
|
||||
val isVisible = uiState is SyncStatusWidgetState.Success || uiState is SyncStatusWidgetState.Error
|
||||
|
||||
val swappableState = rememberSwipeableState(DragStates.VISIBLE)
|
||||
if (swappableState.isAnimationRunning && swappableState.targetValue == DragStates.DISMISSED) {
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
val bottomSheetState = rememberModalBottomSheetState(
|
||||
skipPartiallyExpanded = true
|
||||
)
|
||||
|
||||
if (!isVisible) {
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
scope.launch { swappableState.snapTo(DragStates.VISIBLE) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val sizePx = with(LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = isVisible,
|
||||
enter = slideInVertically { it },
|
||||
exit = slideOutVertically { it },
|
||||
modifier = Modifier
|
||||
.swipeable(
|
||||
state = swappableState,
|
||||
orientation = Orientation.Vertical,
|
||||
anchors = mapOf(0f to DragStates.VISIBLE, sizePx to DragStates.DISMISSED),
|
||||
thresholds = { _, _ -> FractionalThreshold(0.3f) })
|
||||
.offset { IntOffset(0, swappableState.offset.value.roundToInt()) }
|
||||
) {
|
||||
ElevatedCard(
|
||||
if (uiState is SyncStatusWidgetState.Success || uiState is SyncStatusWidgetState.Error) {
|
||||
ModalBottomSheet(
|
||||
modifier = modifier,
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = colorResource(id = R.color.background_secondary)
|
||||
),
|
||||
elevation = CardDefaults.elevatedCardElevation(
|
||||
defaultElevation = 16.dp
|
||||
)
|
||||
) {
|
||||
when (uiState) {
|
||||
is SyncStatusWidgetState.Error -> ErrorState()
|
||||
SyncStatusWidgetState.Hidden -> LoadingState()
|
||||
is SyncStatusWidgetState.Success -> SuccessState(
|
||||
spaceSyncUpdate = uiState.spaceSyncUpdate,
|
||||
p2pStatus = uiState.p2PStatusUpdate,
|
||||
onUpdateAppClick = onUpdateAppClick
|
||||
)
|
||||
scrimColor = colorResource(id = R.color.transparent_black),
|
||||
containerColor = colorResource(id = R.color.transparent_black),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
tonalElevation = 120.dp,
|
||||
onDismissRequest = { onDismiss() },
|
||||
sheetState = bottomSheetState,
|
||||
dragHandle = null,
|
||||
content = {
|
||||
ElevatedCard(
|
||||
modifier = modifierCard,
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = colorResource(id = R.color.background_secondary)
|
||||
),
|
||||
elevation = CardDefaults.elevatedCardElevation(
|
||||
defaultElevation = 120.dp
|
||||
)
|
||||
) {
|
||||
when (uiState) {
|
||||
is SyncStatusWidgetState.Error -> ErrorState()
|
||||
SyncStatusWidgetState.Hidden -> LoadingState()
|
||||
is SyncStatusWidgetState.Success -> SuccessState(
|
||||
spaceSyncUpdate = uiState.spaceSyncUpdate,
|
||||
p2pStatus = uiState.p2PStatusUpdate,
|
||||
onUpdateAppClick = onUpdateAppClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum class MenuVisibilityState(val fraction: Float) { Hidden(0f), Visible(1f) }
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.LoadingState() {
|
||||
CircularProgressIndicator(
|
||||
|
@ -150,11 +119,14 @@ private fun ColumnScope.ErrorState() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun SuccessState(
|
||||
private fun ColumnScope.SuccessState(
|
||||
spaceSyncUpdate: SpaceSyncUpdate,
|
||||
p2pStatus: P2PStatusUpdate,
|
||||
onUpdateAppClick: () -> Unit
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
Dragger(modifier = Modifier.align(Alignment.CenterHorizontally))
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
if (spaceSyncUpdate is SpaceSyncUpdate.Update) {
|
||||
SpaceSyncStatusItem(spaceSyncUpdate, onUpdateAppClick)
|
||||
Divider()
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.anytypeio.anytype.core_ui.widgets
|
|||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncAndP2PStatusState
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncError
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncStatus
|
||||
|
@ -17,6 +18,9 @@ class StatusBadgeWidget @JvmOverloads constructor(
|
|||
attrs: AttributeSet? = null
|
||||
) : AppCompatImageView(context, attrs) {
|
||||
|
||||
private var animatedDrawable: AnimatedVectorDrawableCompat? =
|
||||
AnimatedVectorDrawableCompat.create(context, R.drawable.animated_pulsing_circle)
|
||||
|
||||
fun bind(status: SpaceSyncAndP2PStatusState?) {
|
||||
when (status) {
|
||||
is SpaceSyncAndP2PStatusState.Error -> {
|
||||
|
@ -41,7 +45,9 @@ class StatusBadgeWidget @JvmOverloads constructor(
|
|||
}
|
||||
SpaceSyncStatus.SYNCING -> {
|
||||
visible()
|
||||
setImageResource(R.drawable.ic_syncing)
|
||||
setImageDrawable(animatedDrawable)
|
||||
animatedDrawable?.start()
|
||||
Unit
|
||||
}
|
||||
SpaceSyncStatus.ERROR -> {
|
||||
visible()
|
||||
|
|
|
@ -68,10 +68,6 @@ fun ViewerLayoutWidget(
|
|||
skipPartiallyExpanded = true
|
||||
)
|
||||
|
||||
var currentCoordinates: Rect by remember {
|
||||
mutableStateOf(Rect.Zero)
|
||||
}
|
||||
|
||||
if (uiState.showWidget) {
|
||||
ModalBottomSheet(
|
||||
modifier = Modifier
|
||||
|
|
22
core-ui/src/main/res/animator/pulse_anim.xml
Normal file
22
core-ui/src/main/res/animator/pulse_anim.xml
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Animate scaleX -->
|
||||
<objectAnimator
|
||||
android:propertyName="scaleX"
|
||||
android:valueFrom="0.0"
|
||||
android:valueTo="1.0"
|
||||
android:duration="600"
|
||||
android:repeatCount="infinite"
|
||||
android:repeatMode="reverse"
|
||||
android:valueType="floatType"/>
|
||||
|
||||
<!-- Animate scaleY -->
|
||||
<objectAnimator
|
||||
android:propertyName="scaleY"
|
||||
android:valueFrom="0.0"
|
||||
android:valueTo="1.0"
|
||||
android:duration="600"
|
||||
android:repeatCount="infinite"
|
||||
android:repeatMode="reverse"
|
||||
android:valueType="floatType"/>
|
||||
</set>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<animated-vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/ic_syncing">
|
||||
|
||||
<target
|
||||
android:name="animatable_group"
|
||||
android:animation="@animator/pulse_anim" />
|
||||
</animated-vector>
|
|
@ -1,19 +1,39 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
|
||||
<!-- Group containing the outer two circles that we will scale -->
|
||||
<group
|
||||
android:name="animatable_group"
|
||||
android:pivotX="10"
|
||||
android:pivotY="10"
|
||||
android:scaleX="0"
|
||||
android:scaleY="0">
|
||||
|
||||
<!-- Outer circle (radius = 10) -->
|
||||
<path
|
||||
android:name="outer_circle"
|
||||
android:pathData="M10,10m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
|
||||
android:fillColor="@color/palette_system_green"
|
||||
android:fillAlpha="0.2" />
|
||||
|
||||
<!-- Middle circle (radius = 7) -->
|
||||
<path
|
||||
android:name="middle_circle"
|
||||
android:pathData="M10,10m-7,0a7,7 0,1 1,14 0a7,7 0,1 1,-14 0"
|
||||
android:fillColor="@color/palette_system_green"
|
||||
android:fillAlpha="0.4" />
|
||||
|
||||
</group>
|
||||
|
||||
<!-- Center circle (radius = 4), remains static -->
|
||||
<path
|
||||
android:pathData="M10,10m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
|
||||
android:strokeAlpha="0.2"
|
||||
android:fillColor="@color/palette_system_green"
|
||||
android:fillAlpha="0.2"/>
|
||||
<path
|
||||
android:pathData="M10,10m-7,0a7,7 0,1 1,14 0a7,7 0,1 1,-14 0"
|
||||
android:strokeAlpha="0.4"
|
||||
android:fillColor="@color/palette_system_green"
|
||||
android:fillAlpha="0.4"/>
|
||||
<path
|
||||
android:name="center_circle"
|
||||
android:pathData="M10,10m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
|
||||
android:fillColor="@color/palette_system_green"/>
|
||||
</vector>
|
||||
android:fillColor="@color/palette_system_green" />
|
||||
|
||||
</vector>
|
|
@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.WindowInsets
|
|||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
|
@ -198,11 +197,10 @@ fun DateMainScreen(
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.windowInsetsPadding(WindowInsets.ime)
|
||||
.padding(bottom = 27.dp, start = 8.dp, end = 8.dp),
|
||||
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||
modifierCard = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 16.dp),
|
||||
uiState = uiSyncStatusState,
|
||||
onDismiss = { onDateEvent(DateEvent.SyncStatusWidget.OnSyncStatusDismiss) },
|
||||
scope = scope,
|
||||
onUpdateAppClick = {}
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue