Coverage Summary for Class: ClickTrackListScreenViewKt (com.vsevolodganin.clicktrack.ui.screen)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| ClickTrackListScreenViewKt |
0%
(0/15)
|
0%
(0/38)
|
0%
(0/62)
|
0%
(0/977)
|
| ClickTrackListScreenViewKt$Content$1$1$2$1 |
0%
(0/1)
|
|
0%
(0/5)
|
0%
(0/56)
|
| ClickTrackListScreenViewKt$Content$1$1$2$1$1$1 |
0%
(0/1)
|
|
0%
(0/1)
|
0%
(0/3)
|
| ClickTrackListScreenViewKt$Content$lambda$2$0$$inlined$items$default$1 |
0%
(0/1)
|
|
| ClickTrackListScreenViewKt$Content$lambda$2$0$$inlined$items$default$2 |
0%
(0/1)
|
|
| ClickTrackListScreenViewKt$Content$lambda$2$0$$inlined$items$default$3 |
0%
(0/1)
|
|
| ClickTrackListScreenViewKt$Content$lambda$2$0$$inlined$items$default$4 |
0%
(0/1)
|
|
| ClickTrackListScreenViewKt$Content$reorderableLazyListState$1$1 |
0%
(0/1)
|
|
0%
(0/1)
|
0%
(0/13)
|
| Total |
0%
(0/22)
|
0%
(0/38)
|
0%
(0/69)
|
0%
(0/1049)
|
package com.vsevolodganin.clicktrack.ui.screen
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FabPosition
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import clicktrack.multiplatform.generated.resources.Res
import clicktrack.multiplatform.generated.resources.click_track_list_screen_title
import com.vsevolodganin.clicktrack.list.ClickTrackListState
import com.vsevolodganin.clicktrack.list.ClickTrackListViewModel
import com.vsevolodganin.clicktrack.model.ClickTrackId
import com.vsevolodganin.clicktrack.model.ClickTrackWithDatabaseId
import com.vsevolodganin.clicktrack.ui.piece.ClickTrackView
import com.vsevolodganin.clicktrack.ui.piece.DarkTopAppBar
import com.vsevolodganin.clicktrack.ui.piece.DragHandle
import com.vsevolodganin.clicktrack.ui.preview.PREVIEW_CLICK_TRACK_1
import com.vsevolodganin.clicktrack.ui.preview.PREVIEW_CLICK_TRACK_2
import com.vsevolodganin.clicktrack.ui.theme.ClickTrackTheme
import com.vsevolodganin.clicktrack.utils.compose.SwipeToDelete
import com.vsevolodganin.clicktrack.utils.compose.withFabPadding
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.serialization.json.Json
import org.jetbrains.compose.resources.stringResource
import org.jetbrains.compose.ui.tooling.preview.Preview
import sh.calvin.reorderable.ReorderableItem
import sh.calvin.reorderable.rememberReorderableLazyListState
@Composable
fun ClickTrackListScreenView(viewModel: ClickTrackListViewModel, modifier: Modifier = Modifier) {
Scaffold(
topBar = { TopBar(viewModel) },
floatingActionButtonPosition = FabPosition.Center,
floatingActionButton = {
FloatingActionButton(
onClick = { viewModel.onAddClick() },
shape = CircleShape,
) {
Icon(imageVector = Icons.Default.Add, contentDescription = null)
}
},
modifier = modifier,
) { paddingValues ->
Content(
viewModel = viewModel,
paddingValues = paddingValues,
)
}
}
@Composable
private fun Content(viewModel: ClickTrackListViewModel, paddingValues: PaddingValues) {
val state by viewModel.state.collectAsState()
val numberOfNonDraggableItems = 1
val lazyListState = rememberLazyListState()
val reorderableLazyListState = rememberReorderableLazyListState(lazyListState) { from, to ->
viewModel.onItemMove(from.index - numberOfNonDraggableItems, to.index - numberOfNonDraggableItems)
}
LazyColumn(
state = lazyListState,
modifier = Modifier.fillMaxSize(),
contentPadding = paddingValues.withFabPadding(),
) {
item {
// FIXME: Dummy item to workaround https://github.com/Calvin-LL/Reorderable/issues/4
Spacer(Modifier.height(1.dp))
}
items(items = state.items, key = { Json.encodeToString(it.id) }) { clickTrack ->
ReorderableItem(state = reorderableLazyListState, key = Json.encodeToString(clickTrack.id)) { isDragging ->
ClickTrackListItem(
viewModel = viewModel,
clickTrack = clickTrack,
dragHandleModifier = Modifier.draggableHandle(
onDragStopped = { viewModel.onItemMoveFinished() },
),
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopBar(viewModel: ClickTrackListViewModel) {
DarkTopAppBar(
title = {
Text(text = stringResource(Res.string.click_track_list_screen_title))
},
navigationIcon = {
IconButton(onClick = viewModel::onMenuClick) {
Icon(imageVector = Icons.Default.Menu, contentDescription = null)
}
},
)
}
@Composable
private fun ClickTrackListItem(
viewModel: ClickTrackListViewModel,
clickTrack: ClickTrackWithDatabaseId,
dragHandleModifier: Modifier,
) {
val contentPadding = 8.dp
SwipeToDelete(
onDeleted = { viewModel.onItemRemove(clickTrack.id) },
contentPadding = contentPadding,
) {
Card(
modifier = Modifier
.padding(contentPadding)
.fillMaxWidth()
.height(100.dp),
) {
Box(modifier = Modifier.fillMaxSize()) {
ClickTrackView(
clickTrack = clickTrack.value,
drawTextMarks = false,
modifier = Modifier
.clickable(onClick = { viewModel.onItemClick(clickTrack.id) }),
)
Row(
modifier = Modifier.align(Alignment.CenterStart),
verticalAlignment = Alignment.CenterVertically,
) {
DragHandle(
modifier = dragHandleModifier,
)
Text(
text = clickTrack.value.name,
)
}
}
}
}
}
@Preview
@Composable
internal fun ClickTrackListScreenPreview() = ClickTrackTheme {
ClickTrackListScreenView(
viewModel = object : ClickTrackListViewModel {
override val state: StateFlow<ClickTrackListState> = MutableStateFlow(
ClickTrackListState(
listOf(
PREVIEW_CLICK_TRACK_1,
PREVIEW_CLICK_TRACK_2,
),
),
)
override fun onAddClick() = Unit
override fun onItemClick(id: ClickTrackId.Database) = Unit
override fun onItemRemove(id: ClickTrackId.Database) = Unit
override fun onMenuClick() = Unit
override fun onItemMove(from: Int, to: Int) = Unit
override fun onItemMoveFinished() = Unit
},
)
}