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

DROID-3142 Set | Fix | A view list is not editable after clicking on the edit (#1922)

This commit is contained in:
Konstantin Ivanov 2024-12-14 12:02:40 +01:00 committed by Evgenii Kozlov
parent 07878fc27c
commit 998cd1cf2d
8 changed files with 541 additions and 712 deletions

View file

@ -411,7 +411,6 @@ open class ObjectSetFragment :
ViewersWidget(
state = vm.viewersWidgetState.collectAsStateWithLifecycle().value,
action = vm::onViewersWidgetAction,
scope = lifecycleScope
)
}
}
@ -422,7 +421,6 @@ open class ObjectSetFragment :
ViewerEditWidget(
state = vm.viewerEditWidgetState.collectAsStateWithLifecycle().value,
action = vm::onViewerEditWidgetAction,
scope = lifecycleScope
)
}
}
@ -433,7 +431,6 @@ open class ObjectSetFragment :
ViewerLayoutWidget(
uiState = vm.viewerLayoutWidgetState.collectAsStateWithLifecycle().value,
action = vm::onViewerLayoutWidgetAction,
scope = lifecycleScope
)
}
}

View file

@ -1,23 +1,16 @@
package com.anytypeio.anytype.core_ui.widgets.dv
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
@ -25,12 +18,11 @@ import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
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.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -45,13 +37,10 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.boundsInRoot
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.SoftwareKeyboardController
@ -62,103 +51,57 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import com.anytypeio.anytype.core_models.DVViewerType
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.foundation.noRippleThrottledClickable
import com.anytypeio.anytype.core_ui.views.Caption1Medium
import com.anytypeio.anytype.core_ui.views.Title1
import com.anytypeio.anytype.core_ui.views.UXBody
import com.anytypeio.anytype.core_ui.widgets.DragStates
import com.anytypeio.anytype.presentation.sets.ViewEditAction
import com.anytypeio.anytype.presentation.sets.ViewerEditWidgetUi
import com.anytypeio.anytype.presentation.sets.isVisible
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class, ExperimentalComposeUiApi::class)
@OptIn(ExperimentalMaterialApi::class, ExperimentalComposeUiApi::class,
ExperimentalMaterial3Api::class
)
@Composable
fun ViewerEditWidget(
state: ViewerEditWidgetUi,
action: (ViewEditAction) -> Unit,
scope: CoroutineScope
) {
val bottomSheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
)
val focusRequester = remember { FocusRequester() }
val focusManager = LocalFocusManager.current
val keyboardController = LocalSoftwareKeyboardController.current
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.BottomStart,
) {
val currentState by rememberUpdatedState(state)
val swipeableState = rememberSwipeableState(DragStates.VISIBLE)
val keyboardController = LocalSoftwareKeyboardController.current
AnimatedVisibility(
visible = currentState.isVisible(),
enter = fadeIn(),
exit = fadeOut(tween(100))
) {
Box(
Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.4f))
.noRippleClickable { action(ViewEditAction.Dismiss) }
)
}
if (swipeableState.isAnimationRunning && swipeableState.targetValue == DragStates.DISMISSED) {
DisposableEffect(Unit) {
onDispose {
keyboardController?.hide()
focusManager.clearFocus()
action(ViewEditAction.Dismiss)
}
}
}
if (!currentState.isVisible()) {
DisposableEffect(Unit) {
onDispose {
scope.launch { swipeableState.snapTo(DragStates.VISIBLE) }
}
}
}
val sizePx =
with(LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
AnimatedVisibility(
visible = currentState.isVisible(),
enter = slideInVertically { it },
exit = slideOutVertically { it },
if (state is ViewerEditWidgetUi.Data) {
ModalBottomSheet(
modifier = Modifier
.swipeable(state = swipeableState,
orientation = Orientation.Vertical,
anchors = mapOf(
0f to DragStates.VISIBLE, sizePx to DragStates.DISMISSED
),
thresholds = { _, _ -> FractionalThreshold(0.3f) })
.offset { IntOffset(0, swipeableState.offset.value.roundToInt()) }
) {
if (state is ViewerEditWidgetUi.Data) {
.windowInsetsPadding(WindowInsets.ime)
.padding(start = 8.dp, end = 8.dp, bottom = 30.dp)
.fillMaxWidth()
.wrapContentHeight(),
scrimColor = colorResource(id = R.color.modal_screen_outside_background),
containerColor = colorResource(id = R.color.background_secondary),
shape = RoundedCornerShape(16.dp),
onDismissRequest = { action(ViewEditAction.Dismiss) },
sheetState = bottomSheetState,
dragHandle = { DragHandle() },
content = {
ViewerEditWidgetContent(state, focusRequester, keyboardController) {
focusManager.clearFocus()
keyboardController?.hide()
action.invoke(it)
}
}
}
)
}
}
@ -179,11 +122,7 @@ fun ViewerEditWidgetContent(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(start = 8.dp, end = 8.dp, bottom = 15.dp)
.background(
color = colorResource(id = R.color.background_secondary),
shape = RoundedCornerShape(size = 16.dp)
),
//.padding(start = 8.dp, end = 8.dp, bottom = 15.dp)
) {
Column(
modifier = Modifier
@ -191,12 +130,6 @@ fun ViewerEditWidgetContent(
.wrapContentHeight()
.padding(bottom = 16.dp, start = 20.dp, end = 20.dp)
) {
Box(modifier = Modifier
.fillMaxWidth()
.padding(top = 6.dp, bottom = 6.dp)
) {
Dragger(modifier = Modifier.align(Alignment.Center))
}
Box(
modifier = Modifier
.fillMaxWidth()
@ -477,5 +410,5 @@ fun PreviewViewerEditWidget() {
id = "1",
isActive = false
)
ViewerEditWidget(state = state, action = {}, scope = CoroutineScope(Dispatchers.Main))
ViewerEditWidget(state = state, action = {})
}

View file

@ -1,46 +1,33 @@
package com.anytypeio.anytype.core_ui.widgets.dv
import androidx.annotation.DrawableRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
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.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
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.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.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
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 com.anytypeio.anytype.core_models.DVViewerType
import com.anytypeio.anytype.core_models.Relations
@ -49,93 +36,80 @@ import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.foundation.Divider
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
import com.anytypeio.anytype.core_ui.views.BodyRegular
import com.anytypeio.anytype.core_ui.widgets.DragStates
import com.anytypeio.anytype.core_ui.views.Title1
import com.anytypeio.anytype.presentation.sets.ViewerLayoutWidgetUi
import com.anytypeio.anytype.presentation.sets.ViewerLayoutWidgetUi.Action.Dismiss
import com.anytypeio.anytype.presentation.sets.ViewerLayoutWidgetUi.State.ImagePreview
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class)
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
@Composable
fun ViewerLayoutCoverWidget(
uiState: ViewerLayoutWidgetUi,
action: (ViewerLayoutWidgetUi.Action) -> Unit,
scope: CoroutineScope
) {
val swipeableState = rememberSwipeableState(DragStates.VISIBLE)
val sizePx = with(LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
val bottomSheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
)
val lazyListState = rememberLazyListState()
if (swipeableState.isAnimationRunning && swipeableState.targetValue == DragStates.DISMISSED) {
DisposableEffect(Unit) {
onDispose {
action(ViewerLayoutWidgetUi.Action.DismissCoverMenu)
}
}
}
if (!uiState.showCoverMenu) {
DisposableEffect(Unit) {
onDispose {
scope.launch { swipeableState.snapTo(DragStates.VISIBLE) }
}
}
}
AnimatedVisibility(
visible = uiState.showCoverMenu,
enter = slideInVertically { it },
exit = slideOutVertically(tween(200)) { it },
modifier = Modifier
.swipeable(state = swipeableState,
orientation = Orientation.Vertical,
anchors = mapOf(
0f to DragStates.VISIBLE, sizePx to DragStates.DISMISSED
),
thresholds = { _, _ -> FractionalThreshold(0.3f) })
.offset { IntOffset(0, swipeableState.offset.value.roundToInt()) }
) {
val shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
Box(
if (uiState.showCoverMenu) {
ModalBottomSheet(
modifier = Modifier
.windowInsetsPadding(WindowInsets.ime)
.fillMaxWidth()
.wrapContentHeight()
.background(
color = colorResource(id = R.color.background_secondary),
shape = shape
.wrapContentHeight(),
scrimColor = colorResource(id = R.color.modal_screen_outside_background),
containerColor = colorResource(id = R.color.background_secondary),
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
onDismissRequest = { action(Dismiss) },
sheetState = bottomSheetState,
dragHandle = { DragHandle() },
content = {
Content(
uiState = uiState,
action = action
)
.clip(shape)
) {
WidgetHeader(title = stringResource(R.string.view_layout_cover_widget_title))
LazyColumn(
state = lazyListState,
modifier = Modifier
.fillMaxWidth()
.padding(top = 64.dp, bottom = 250.dp)
}
)
}
}
@Composable
private fun ColumnScope.Content(
uiState: ViewerLayoutWidgetUi,
action: (ViewerLayoutWidgetUi.Action) -> Unit,
) {
val lazyListState = rememberLazyListState()
Spacer(modifier = Modifier.height(12.dp))
Text(
modifier = Modifier.align(Alignment.CenterHorizontally),
text = stringResource(R.string.view_layout_cover_widget_title),
style = Title1,
color = colorResource(R.color.text_primary)
)
LazyColumn(
state = lazyListState,
modifier = Modifier
.fillMaxWidth()
.padding(top = 12.dp, bottom = 250.dp)
) {
items(
count = uiState.imagePreviewItems.size,
key = { index -> uiState.imagePreviewItems[index].relationKey.key }
) { idx ->
val item = uiState.imagePreviewItems[idx]
val title = item.getTitle()
val iconDrawableRes = when (item) {
is ImagePreview.None -> null
is ImagePreview.PageCover -> null
is ImagePreview.Custom -> R.drawable.ic_relation_attachment_24
}
CoverItem(
text = title,
checked = item.isChecked,
iconDrawableRes = iconDrawableRes
) {
items(
count = uiState.imagePreviewItems.size,
key = { index -> uiState.imagePreviewItems[index].relationKey.key }
) { idx ->
val item = uiState.imagePreviewItems[idx]
val title = item.getTitle()
val iconDrawableRes = when (item) {
is ImagePreview.None -> null
is ImagePreview.PageCover -> null
is ImagePreview.Custom -> R.drawable.ic_relation_attachment_24
}
CoverItem(
text = title,
checked = item.isChecked,
iconDrawableRes = iconDrawableRes
) {
action(ViewerLayoutWidgetUi.Action.ImagePreviewUpdate(item))
}
}
action(ViewerLayoutWidgetUi.Action.ImagePreviewUpdate(item))
}
}
}
@ -217,9 +191,6 @@ fun PreviewLayoutCoverWidget() {
)
)
),
action = {},
scope = CoroutineScope(
Dispatchers.Main
)
action = {}
)
}

View file

@ -21,6 +21,7 @@ import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
import com.anytypeio.anytype.core_ui.foundation.Divider
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
import com.anytypeio.anytype.core_ui.views.BodyCallout
@ -36,7 +37,7 @@ fun ViewerLayoutListMenu(
if (show) {
Column(
modifier = Modifier
.offset(x = offsetX - 220.dp, y = (-52).dp)
.offset(x = offsetX - 220.dp, y = 246.dp)
.width(220.dp)
.wrapContentHeight()
.shadow(
@ -88,4 +89,14 @@ fun ViewerLayoutListMenu(
)
}
}
}
@Composable
@DefaultPreviews
fun ViewerLayoutListMenuPreview() {
ViewerLayoutListMenu(
show = true,
action = {},
coordinates = Rect(0f, 0f, 0f, 0f)
)
}

View file

@ -1,230 +1,201 @@
package com.anytypeio.anytype.core_ui.widgets.dv
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.FractionalThreshold
import androidx.compose.material.Switch
import androidx.compose.material.SwitchDefaults
import androidx.compose.material.Text
import androidx.compose.material.rememberSwipeableState
import androidx.compose.material.swipeable
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.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.boundsInRoot
import androidx.compose.ui.layout.onGloballyPositioned
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.stringResource
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_models.DVViewerType
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.foundation.noRippleThrottledClickable
import com.anytypeio.anytype.core_ui.views.Caption2Medium
import com.anytypeio.anytype.core_ui.views.Caption2Regular
import com.anytypeio.anytype.core_ui.views.Title1
import com.anytypeio.anytype.core_ui.views.UXBody
import com.anytypeio.anytype.core_ui.widgets.DragStates
import com.anytypeio.anytype.presentation.sets.ViewerLayoutWidgetUi
import com.anytypeio.anytype.presentation.sets.ViewerLayoutWidgetUi.Action.CardSizeMenu
import com.anytypeio.anytype.presentation.sets.ViewerLayoutWidgetUi.Action.Dismiss
import com.anytypeio.anytype.presentation.sets.ViewerLayoutWidgetUi.Action.FitImage
import com.anytypeio.anytype.presentation.sets.ViewerLayoutWidgetUi.Action.Icon
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import com.anytypeio.anytype.presentation.sets.ViewerLayoutWidgetUi.State.CardSize
@OptIn(ExperimentalMaterialApi::class)
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
@Composable
fun ViewerLayoutWidget(
uiState: ViewerLayoutWidgetUi,
action: (ViewerLayoutWidgetUi.Action) -> Unit,
scope: CoroutineScope
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.BottomStart,
) {
val currentState by rememberUpdatedState(uiState)
val swipeableState = rememberSwipeableState(DragStates.VISIBLE)
val bottomSheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
)
AnimatedVisibility(
visible = currentState.showWidget,
enter = fadeIn(),
exit = fadeOut(
tween(200)
)
) {
Box(
Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.4f))
.noRippleClickable { action(Dismiss) }
)
}
var currentCoordinates: Rect by remember {
mutableStateOf(Rect.Zero)
}
if (swipeableState.isAnimationRunning && swipeableState.targetValue == DragStates.DISMISSED) {
DisposableEffect(Unit) {
onDispose {
action(Dismiss)
}
}
}
if (!currentState.showWidget) {
DisposableEffect(Unit) {
onDispose {
scope.launch { swipeableState.snapTo(DragStates.VISIBLE) }
}
}
}
val sizePx = with(LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
var currentCoordinates: androidx.compose.ui.geometry.Rect by remember {
mutableStateOf(androidx.compose.ui.geometry.Rect.Zero)
}
var currentCoverItem by remember {
mutableStateOf(uiState.getActiveImagePreviewItem())
}
LaunchedEffect(key1 = uiState) {
currentCoverItem = uiState.getActiveImagePreviewItem()
}
AnimatedVisibility(
visible = currentState.showWidget,
enter = slideInVertically { it },
exit = slideOutVertically(tween(200)) { it },
if (uiState.showWidget) {
ModalBottomSheet(
modifier = Modifier
.swipeable(state = swipeableState,
orientation = Orientation.Vertical,
anchors = mapOf(
0f to DragStates.VISIBLE, sizePx to DragStates.DISMISSED
),
thresholds = { _, _ -> FractionalThreshold(0.3f) })
.offset { IntOffset(0, swipeableState.offset.value.roundToInt()) }
) {
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.background(
color = colorResource(id = R.color.background_secondary),
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
),
) {
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(bottom = 20.dp)
) {
WidgetHeader(title = stringResource(R.string.view_layout_widget_title))
Spacer(modifier = Modifier.height(12.dp))
LayoutIcons(uiState = currentState, action = action)
Spacer(modifier = Modifier.height(8.dp))
LayoutSwitcherItem(
text = stringResource(id = R.string.icon),
checked = currentState.withIcon.toggled,
onCheckedChanged = { action(Icon(it)) }
)
val isGallery = currentState.layoutType == DVViewerType.GALLERY
Divider(visible = isGallery)
ColumnItem(
.windowInsetsPadding(WindowInsets.ime)
.fillMaxWidth()
.wrapContentHeight(),
scrimColor = colorResource(id = R.color.modal_screen_outside_background),
containerColor = colorResource(id = R.color.background_secondary),
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
onDismissRequest = { action(Dismiss) },
sheetState = bottomSheetState,
dragHandle = { DragHandle() },
content = {
var currentCoordinates: Rect by remember {
mutableStateOf(Rect.Zero)
}
Box(modifier = Modifier.fillMaxWidth()) {
ViewerLayoutContent(
modifier = Modifier
.padding(start = 20.dp, end = 20.dp)
.alpha(if (isGallery) 1f else 0f),
title = stringResource(id = R.string.card_size),
value = when (currentState.cardSize) {
ViewerLayoutWidgetUi.State.CardSize.Large -> stringResource(id = R.string.large)
ViewerLayoutWidgetUi.State.CardSize.Small -> stringResource(id = R.string.small)
},
onClick = {
action(ViewerLayoutWidgetUi.Action.CardSizeMenu)
},
arrow = painterResource(id = R.drawable.ic_list_arrow_18),
imageModifier = Modifier
.onGloballyPositioned { coordinates ->
if (coordinates.isAttached) {
with(coordinates.boundsInRoot()) {
currentCoordinates = this
}
} else {
currentCoordinates = androidx.compose.ui.geometry.Rect.Zero
}
}
.fillMaxWidth()
.wrapContentHeight()
.padding(bottom = 20.dp),
currentState = uiState,
action = action,
updateCurrentCoordinates = { currentCoordinates = it }
)
Divider(visible = isGallery)
ColumnItem(
modifier = Modifier
.padding(start = 20.dp, end = 20.dp)
.alpha(if (isGallery) 1f else 0f),
title = stringResource(id = R.string.cover),
value = currentCoverItem.getTitle(),
onClick = {
action(ViewerLayoutWidgetUi.Action.CoverMenu)
},
arrow = painterResource(id = R.drawable.ic_arrow_disclosure_18)
)
Divider(visible = isGallery)
LayoutSwitcherItem(
modifier = Modifier.alpha(if (isGallery) 1f else 0f),
text = stringResource(id = R.string.fit_image),
checked = currentState.fitImage.toggled,
onCheckedChanged = { action(FitImage(it)) }
ViewerLayoutListMenu(
show = uiState.showCardSize,
action = action,
coordinates = currentCoordinates
)
}
}
}
ViewerLayoutListMenu(
show = currentState.showCardSize,
action = action,
coordinates = currentCoordinates
)
ViewerLayoutCoverWidget(
uiState = uiState,
action = action,
scope = scope
)
}
}
@Composable
private fun ViewerLayoutContent(
modifier: Modifier,
currentState: ViewerLayoutWidgetUi,
action: (ViewerLayoutWidgetUi.Action) -> Unit,
updateCurrentCoordinates: (Rect) -> Unit = {}
) {
var currentCoverItem by remember {
mutableStateOf(currentState.getActiveImagePreviewItem())
}
LaunchedEffect(key1 = currentState) {
currentCoverItem = currentState.getActiveImagePreviewItem()
}
Column(
modifier = modifier
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(48.dp),
contentAlignment = Alignment.Center
) {
Text(
modifier = Modifier,
text = stringResource(R.string.view_layout_widget_title),
style = Title1,
color = colorResource(R.color.text_primary)
)
}
Spacer(modifier = Modifier.height(12.dp))
LayoutIcons(uiState = currentState, action = action)
Spacer(modifier = Modifier.height(8.dp))
LayoutSwitcherItem(
text = stringResource(id = R.string.icon),
checked = currentState.withIcon.toggled,
onCheckedChanged = { action(Icon(it)) }
)
val isGallery = currentState.layoutType == DVViewerType.GALLERY
Divider(visible = isGallery)
ColumnItem(
modifier = Modifier
.padding(start = 20.dp, end = 20.dp)
.alpha(if (isGallery) 1f else 0f),
title = stringResource(id = R.string.card_size),
value = when (currentState.cardSize) {
CardSize.Large -> stringResource(id = R.string.large)
CardSize.Small -> stringResource(id = R.string.small)
},
onClick = {
action(CardSizeMenu)
},
arrow = painterResource(id = R.drawable.ic_list_arrow_18),
imageModifier = Modifier
.onGloballyPositioned { coordinates ->
if (coordinates.isAttached) {
with(coordinates.boundsInRoot()) {
updateCurrentCoordinates(this)
}
} else {
updateCurrentCoordinates(Rect.Zero)
}
}
)
Divider(visible = isGallery)
ColumnItem(
modifier = Modifier
.padding(start = 20.dp, end = 20.dp)
.alpha(if (isGallery) 1f else 0f),
title = stringResource(id = R.string.cover),
value = currentCoverItem.getTitle(),
onClick = {
action(ViewerLayoutWidgetUi.Action.CoverMenu)
},
arrow = painterResource(id = R.drawable.ic_arrow_disclosure_18)
)
Divider(visible = isGallery)
LayoutSwitcherItem(
modifier = Modifier.alpha(if (isGallery) 1f else 0f),
text = stringResource(id = R.string.fit_image),
checked = currentState.fitImage.toggled,
onCheckedChanged = { action(FitImage(it)) }
)
}
}
@ -321,7 +292,7 @@ fun LayoutIcons(uiState: ViewerLayoutWidgetUi, action: (ViewerLayoutWidgetUi.Act
layoutType = DVViewerType.GRID,
imageResource = R.drawable.ic_layout_grid,
imageResourceSelected = R.drawable.ic_layout_grid_selected,
contentDescription = "Grid",
contentDescription = stringResource(id = R.string.view_grid),
click = { action(ViewerLayoutWidgetUi.Action.Type(DVViewerType.GRID)) }
)
LayoutIcon(
@ -330,7 +301,7 @@ fun LayoutIcons(uiState: ViewerLayoutWidgetUi, action: (ViewerLayoutWidgetUi.Act
layoutType = DVViewerType.GALLERY,
imageResourceSelected = R.drawable.ic_layout_gallery_selected,
imageResource = R.drawable.ic_layout_gallery,
contentDescription = "Gallery",
contentDescription = stringResource(id = R.string.view_gallery),
click = { action(ViewerLayoutWidgetUi.Action.Type(DVViewerType.GALLERY)) }
)
LayoutIcon(
@ -339,7 +310,7 @@ fun LayoutIcons(uiState: ViewerLayoutWidgetUi, action: (ViewerLayoutWidgetUi.Act
layoutType = DVViewerType.LIST,
imageResourceSelected = R.drawable.ic_layout_list_selected,
imageResource = R.drawable.ic_layout_list,
contentDescription = "List",
contentDescription = stringResource(id = R.string.view_list),
click = { action(ViewerLayoutWidgetUi.Action.Type(DVViewerType.LIST)) }
)
LayoutIcon(
@ -348,7 +319,7 @@ fun LayoutIcons(uiState: ViewerLayoutWidgetUi, action: (ViewerLayoutWidgetUi.Act
layoutType = DVViewerType.BOARD,
imageResourceSelected = R.drawable.ic_layout_kanban_selected,
imageResource = R.drawable.ic_layout_kanban,
contentDescription = "Kanban",
contentDescription = stringResource(id = R.string.view_kanban),
click = { action(ViewerLayoutWidgetUi.Action.Type(DVViewerType.BOARD)) }
)
LayoutIcon(
@ -421,7 +392,7 @@ fun PreviewLayoutScreen() {
ViewerLayoutWidget(
uiState = ViewerLayoutWidgetUi(
showWidget = true,
layoutType = DVViewerType.GRID,
layoutType = DVViewerType.GALLERY,
withIcon = ViewerLayoutWidgetUi.State.Toggle.WithIcon(
toggled = true
),
@ -429,15 +400,12 @@ fun PreviewLayoutScreen() {
toggled = false
),
cardSize = ViewerLayoutWidgetUi.State.CardSize.Small,
showCardSize = false,
showCardSize = true,
viewer = "",
showCoverMenu = false,
imagePreviewItems = emptyList()
),
action = {},
scope = CoroutineScope(
Dispatchers.Main
)
action = {}
)
}

View file

@ -1,386 +1,188 @@
package com.anytypeio.anytype.core_ui.widgets.dv
import android.view.HapticFeedbackConstants
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
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.offset
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
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.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.constraintlayout.compose.Visibility
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.extensions.swapList
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.foundation.noRippleThrottledClickable
import com.anytypeio.anytype.core_ui.views.BodyRegular
import com.anytypeio.anytype.core_ui.views.Caption2Regular
import com.anytypeio.anytype.core_ui.views.HeadlineSubheading
import com.anytypeio.anytype.core_ui.views.Title1
import com.anytypeio.anytype.core_ui.widgets.DragStates
import com.anytypeio.anytype.presentation.sets.ViewersWidgetUi
import com.anytypeio.anytype.presentation.sets.ViewersWidgetUi.Action.Delete
import com.anytypeio.anytype.presentation.sets.ViewersWidgetUi.Action.Dismiss
import com.anytypeio.anytype.presentation.sets.ViewersWidgetUi.Action.DoneMode
import com.anytypeio.anytype.presentation.sets.ViewersWidgetUi.Action.Edit
import com.anytypeio.anytype.presentation.sets.ViewersWidgetUi.Action.EditMode
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import com.anytypeio.anytype.presentation.sets.viewer.ViewerView
import org.burnoutcrew.reorderable.ReorderableItem
import org.burnoutcrew.reorderable.ReorderableLazyListState
import org.burnoutcrew.reorderable.detectReorder
import org.burnoutcrew.reorderable.rememberReorderableLazyListState
import org.burnoutcrew.reorderable.reorderable
@OptIn(ExperimentalMaterialApi::class)
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
@Composable
fun ViewersWidget(
state: ViewersWidgetUi,
action: (ViewersWidgetUi.Action) -> Unit,
scope: CoroutineScope
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.BottomStart,
) {
val bottomSheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
)
val currentState by rememberUpdatedState(state)
val swipeableState = rememberSwipeableState(DragStates.VISIBLE)
AnimatedVisibility(
visible = currentState.showWidget,
enter = fadeIn(),
exit = fadeOut(tween(100))
) {
Box(
Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.4f))
.noRippleClickable { action(Dismiss) }
)
}
if (swipeableState.isAnimationRunning && swipeableState.targetValue == DragStates.DISMISSED) {
DisposableEffect(Unit) {
onDispose {
action(Dismiss)
}
}
}
if (!currentState.showWidget) {
DisposableEffect(Unit) {
onDispose {
scope.launch { swipeableState.snapTo(DragStates.VISIBLE) }
}
}
}
val sizePx =
with(LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
AnimatedVisibility(
visible = currentState.showWidget,
enter = slideInVertically { it },
exit = slideOutVertically { it },
if (state.showWidget) {
ModalBottomSheet(
modifier = Modifier
.swipeable(
state = swipeableState,
orientation = Orientation.Vertical,
anchors = mapOf(
0f to DragStates.VISIBLE, sizePx to DragStates.DISMISSED
),
thresholds = { _, _ -> FractionalThreshold(0.3f) })
.offset { IntOffset(0, swipeableState.offset.value.roundToInt()) }
) {
ViewersWidgetContent(state, action)
}
.windowInsetsPadding(WindowInsets.ime)
.padding(start = 8.dp, end = 8.dp, bottom = 30.dp)
.fillMaxWidth()
.wrapContentHeight(),
scrimColor = colorResource(id = R.color.modal_screen_outside_background),
containerColor = colorResource(id = R.color.background_secondary),
shape = RoundedCornerShape(16.dp),
onDismissRequest = { action(Dismiss) },
sheetState = bottomSheetState,
dragHandle = { DragHandle() },
content = {
ViewersWidgetContent(
modifier = Modifier.padding(bottom = 168.dp),
state = state,
action = action
)
}
)
}
}
@Composable
fun DragHandle() {
Column {
Spacer(modifier = Modifier.height(6.dp))
Dragger()
Spacer(modifier = Modifier.height(6.dp))
}
}
@Composable
private fun ViewersWidgetContent(
modifier: Modifier,
state: ViewersWidgetUi,
action: (ViewersWidgetUi.Action) -> Unit
) {
val currentState by rememberUpdatedState(state)
val views = remember { mutableStateOf(currentState.items) }
views.value = currentState.items
val views = remember { mutableStateListOf<ViewerView>() }
views.swapList(state.items)
val isEditing = remember { mutableStateOf(currentState.isEditing && !state.isReadOnly) }
isEditing.value = currentState.isEditing && !state.isReadOnly
Box(
modifier = Modifier
val lazyListState = rememberReorderableLazyListState(
onMove = { from, to ->
val newList = views.toMutableList().apply {
add(to.index, removeAt(from.index))
}
views.swapList(newList)
},
onDragEnd = { from, to ->
action(
ViewersWidgetUi.Action.OnMove(
currentViews = views,
from = from,
to = to
)
)
}
)
Column(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(start = 8.dp, end = 8.dp, bottom = 15.dp, top = 24.dp)
.background(
color = colorResource(id = R.color.background_secondary),
shape = RoundedCornerShape(size = 16.dp)
),
) {
Column(
Header(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(bottom = 16.dp)
) {
Box(modifier = Modifier
.height(48.dp),
isEditingMode = state.isEditing,
isReadOnlyState = state.isReadOnly,
action = action
)
LazyColumn(
state = lazyListState.listState,
modifier = Modifier
.reorderable(lazyListState)
.fillMaxWidth()
.padding(top = 6.dp, bottom = 6.dp)
) {
Dragger(modifier = Modifier.align(Alignment.Center))
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
) {
if (!state.isReadOnly) {
Box(
modifier = Modifier
.align(Alignment.CenterStart),
) {
if (currentState.isEditing) {
ActionText(
text = stringResource(id = R.string.done),
click = { action(DoneMode) }
)
} else {
ActionText(
text = stringResource(id = R.string.edit),
click = { action(EditMode) }
)
}
.wrapContentHeight()
) {
items(
count = views.size,
key = { index -> views[index].id },
) { index ->
ReorderableItem(
modifier = Modifier.animateItem(),
reorderableState = lazyListState,
key = views[index].id
) { isDragging ->
val currentItem = LocalView.current
if (isDragging) {
currentItem.isHapticFeedbackEnabled = true
currentItem.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
}
}
Box(modifier = Modifier.align(Alignment.Center)) {
Text(
text = stringResource(R.string.views),
style = Title1,
color = colorResource(R.color.text_primary)
Item(
modifier = Modifier,
lazyListState = lazyListState,
isDragging = isDragging,
isEditing = state.isEditing,
action = action,
view = views[index]
)
}
if (!state.isReadOnly) {
Box(
modifier = Modifier
.align(Alignment.CenterEnd)
.noRippleThrottledClickable {
action.invoke(ViewersWidgetUi.Action.Plus)
}
) {
Image(
modifier = Modifier.padding(
start = 16.dp,
top = 12.dp,
bottom = 12.dp,
end = 16.dp
),
painter = painterResource(id = R.drawable.ic_default_plus),
contentDescription = null
)
}
}
}
val lazyListState = rememberReorderableLazyListState(
onMove = { from, to ->
views.value = views.value.toMutableList().apply {
add(to.index, removeAt(from.index))
}
},
onDragEnd = { from, to ->
action(
ViewersWidgetUi.Action.OnMove(
currentViews = views.value,
from = from,
to = to
)
)
}
)
LazyColumn(
state = lazyListState.listState,
modifier = Modifier
.reorderable(lazyListState)
.fillMaxWidth()
.wrapContentHeight()
) {
itemsIndexed(
items = views.value,
key = { _, item -> item.id }) { index, view ->
ReorderableItem(
reorderableState = lazyListState,
key = view.id
) { isDragging ->
val currentItem = LocalView.current
if (isDragging) {
currentItem.isHapticFeedbackEnabled = true
currentItem.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
}
val alpha =
animateFloatAsState(if (isDragging) 0.8f else 1.0f, label = "")
ConstraintLayout(
modifier = Modifier
.height(52.dp)
.fillMaxWidth()
.padding(start = 20.dp, end = 20.dp)
.animateContentSize(
animationSpec = spring(
stiffness = Spring.StiffnessLow
)
)
.alpha(alpha.value)
) {
val (delete, text, edit, dnd, unsupported) = createRefs()
Image(
modifier = Modifier
.noRippleThrottledClickable {
action.invoke(Delete(view.id))
}
.constrainAs(delete) {
start.linkTo(parent.start)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
visibility =
if (isEditing.value && !view.isActive) Visibility.Visible else Visibility.Gone
},
painter = painterResource(id = R.drawable.ic_relation_delete),
contentDescription = "Delete view"
)
Image(
modifier = Modifier
.detectReorder(lazyListState)
.constrainAs(dnd) {
end.linkTo(parent.end)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
visibility =
if (isEditing.value) Visibility.Visible else Visibility.Gone
},
painter = painterResource(id = R.drawable.ic_dnd),
contentDescription = "Dnd view"
)
Image(
modifier = Modifier
.noRippleThrottledClickable {
action.invoke(Edit(id = view.id))
}
.constrainAs(edit) {
end.linkTo(dnd.start, margin = 16.dp)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
visibility =
if (isEditing.value) Visibility.Visible else Visibility.Gone
},
painter = painterResource(id = R.drawable.ic_edit_24),
contentDescription = "Edit view"
)
Text(
modifier = Modifier
.constrainAs(unsupported) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
end.linkTo(edit.start)
visibility =
if (!isEditing.value && view.isUnsupported) Visibility.Visible else Visibility.Gone
},
text = stringResource(id = R.string.unsupported),
color = colorResource(id = R.color.text_secondary),
style = Caption2Regular,
textAlign = TextAlign.Left
)
Text(
modifier = Modifier
.noRippleThrottledClickable {
if (!isEditing.value) {
action.invoke(
ViewersWidgetUi.Action.SetActive(
id = view.id,
type = view.type
)
)
}
}
.constrainAs(text) {
start.linkTo(
delete.end,
margin = 12.dp,
goneMargin = 0.dp
)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
end.linkTo(
unsupported.start,
margin = 8.dp,
goneMargin = 38.dp
)
width = Dimension.fillToConstraints
},
text = view.name.ifBlank { stringResource(id = R.string.untitled) },
color = colorResource(id = if (view.isActive) R.color.text_primary else R.color.glyph_active),
style = HeadlineSubheading,
textAlign = TextAlign.Left,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
if (index != views.value.size - 1) {
Divider()
}
if (index != views.size - 1) {
Divider()
}
}
}
@ -388,14 +190,195 @@ private fun ViewersWidgetContent(
}
@Composable
private fun ActionText(text: String, click: () -> Unit) {
private fun Item(
modifier: Modifier,
lazyListState: ReorderableLazyListState,
isDragging: Boolean,
isEditing: Boolean,
action: (ViewersWidgetUi.Action) -> Unit,
view: ViewerView
) {
val alpha = animateFloatAsState(if (isDragging) 0.8f else 1.0f, label = "")
ConstraintLayout(
modifier = modifier
.height(52.dp)
.fillMaxWidth()
.padding(start = 20.dp, end = 20.dp)
.animateContentSize(
animationSpec = spring(
stiffness = Spring.StiffnessLow
)
)
.alpha(alpha.value)
) {
val (delete, text, edit, dnd, unsupported) = createRefs()
Image(
modifier = Modifier
.noRippleThrottledClickable {
action.invoke(Delete(view.id))
}
.constrainAs(delete) {
start.linkTo(parent.start)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
visibility =
if (isEditing && !view.isActive) Visibility.Visible else Visibility.Gone
},
painter = painterResource(id = R.drawable.ic_relation_delete),
contentDescription = "Delete view"
)
Image(
modifier = Modifier
.detectReorder(lazyListState)
.constrainAs(dnd) {
end.linkTo(parent.end)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
visibility =
if (isEditing) Visibility.Visible else Visibility.Gone
},
painter = painterResource(id = R.drawable.ic_dnd),
contentDescription = "Dnd view"
)
Image(
modifier = Modifier
.noRippleThrottledClickable {
action.invoke(Edit(id = view.id))
}
.constrainAs(edit) {
end.linkTo(dnd.start, margin = 16.dp)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
visibility =
if (isEditing) Visibility.Visible else Visibility.Gone
},
painter = painterResource(id = R.drawable.ic_edit_24),
contentDescription = "Edit view"
)
Text(
modifier = Modifier
.constrainAs(unsupported) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
end.linkTo(edit.start)
visibility =
if (!isEditing && view.isUnsupported) Visibility.Visible else Visibility.Gone
},
text = stringResource(id = R.string.unsupported),
color = colorResource(id = R.color.text_secondary),
style = Caption2Regular,
textAlign = TextAlign.Left
)
Text(
modifier = Modifier
.noRippleThrottledClickable {
if (!isEditing) {
action.invoke(
ViewersWidgetUi.Action.SetActive(
id = view.id, type = view.type
)
)
}
}
.constrainAs(text) {
start.linkTo(
delete.end, margin = 12.dp, goneMargin = 0.dp
)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
end.linkTo(
unsupported.start, margin = 8.dp, goneMargin = 38.dp
)
width = Dimension.fillToConstraints
},
text = view.name.ifBlank { stringResource(id = R.string.untitled) },
color = colorResource(id = if (view.isActive) R.color.text_primary else R.color.glyph_active),
style = HeadlineSubheading,
textAlign = TextAlign.Left,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
@Composable
private fun Header(
modifier: Modifier,
isReadOnlyState: Boolean,
isEditingMode: Boolean,
action: (ViewersWidgetUi.Action) -> Unit
) {
Box(modifier = modifier) {
if (!isReadOnlyState) {
ActionButtons(
modifier = Modifier.align(Alignment.CenterStart),
isEditingMode = isEditingMode,
action = action
)
}
Text(
modifier = Modifier.align(Alignment.Center),
text = stringResource(R.string.views),
style = Title1,
color = colorResource(R.color.text_primary)
)
if (!isReadOnlyState) {
PlusButton(
modifier = Modifier
.align(Alignment.CenterEnd)
.noRippleThrottledClickable {
action.invoke(ViewersWidgetUi.Action.Plus)
}
)
}
}
}
@Composable
private fun ActionButtons(
modifier: Modifier,
isEditingMode: Boolean,
action: (ViewersWidgetUi.Action) -> Unit
) {
if (isEditingMode) {
ActionText(
modifier = modifier,
text = stringResource(id = R.string.done),
click = { action(DoneMode) }
)
} else {
ActionText(
modifier = modifier,
text = stringResource(id = R.string.edit),
click = { action(EditMode) }
)
}
}
@Composable
private fun BoxScope.PlusButton(
modifier: Modifier
) {
Image(
modifier = modifier.padding(
start = 16.dp,
top = 12.dp,
bottom = 12.dp,
end = 16.dp
),
painter = painterResource(id = R.drawable.ic_default_plus),
contentDescription = null
)
}
@Composable
private fun ActionText(modifier: Modifier, text: String, click: () -> Unit) {
Text(
modifier = Modifier
modifier = modifier
.padding(
start = 16.dp,
top = 12.dp,
bottom = 12.dp,
end = 16.dp
start = 16.dp, top = 12.dp, bottom = 12.dp, end = 16.dp
)
.noRippleThrottledClickable { click() },
text = text,

View file

@ -1,32 +0,0 @@
package com.anytypeio.anytype.core_ui.widgets.dv
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.foundation.Dragger
import com.anytypeio.anytype.core_ui.views.Title1
@Composable
fun WidgetHeader(title: String) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(6.dp))
Dragger()
Spacer(modifier = Modifier.height(18.dp))
Text(
text = title,
style = Title1,
color = colorResource(R.color.text_primary)
)
}
}

View file

@ -43,6 +43,4 @@ data class ViewersWidgetUi(
data object Plus : Action()
}
}
}