Work on new android label selector
This commit is contained in:
@ -136,6 +136,7 @@ dependencies {
|
||||
|
||||
implementation 'com.google.android.gms:play-services-auth:20.4.0'
|
||||
implementation "com.google.accompanist:accompanist-systemuicontroller:0.25.1"
|
||||
implementation "com.google.accompanist:accompanist-flowlayout:0.25.1"
|
||||
|
||||
implementation 'io.coil-kt:coil-compose:2.2.0'
|
||||
|
||||
@ -151,6 +152,7 @@ dependencies {
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
|
||||
implementation 'com.github.jeziellago:compose-markdown:0.3.3'
|
||||
implementation "io.github.dokar3:chiptextfield:0.4.6"
|
||||
}
|
||||
|
||||
apollo {
|
||||
|
||||
@ -3,12 +3,14 @@
|
||||
package app.omnivore.omnivore.ui.components
|
||||
|
||||
import LabelChip
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
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.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ModalBottomSheetLayout
|
||||
@ -23,11 +25,22 @@ import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalViewConfiguration
|
||||
import androidx.compose.ui.platform.ViewConfiguration
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.omnivore.omnivore.models.ServerSyncStatus
|
||||
import app.omnivore.omnivore.persistence.entities.SavedItemLabel
|
||||
import app.omnivore.omnivore.ui.library.LibraryViewModel
|
||||
import com.dokar.chiptextfield.*
|
||||
import com.google.accompanist.flowlayout.FlowRow
|
||||
|
||||
|
||||
@ -93,9 +106,109 @@ import com.google.accompanist.flowlayout.FlowRow
|
||||
// }
|
||||
//}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CircleIcon(colorHex: String){
|
||||
val chipColors = LabelChipColors.fromHex(colorHex)
|
||||
val viewConfiguration = LocalViewConfiguration.current
|
||||
val viewConfigurationOverride = remember(viewConfiguration) {
|
||||
ViewConfigurationOverride(
|
||||
base = viewConfiguration,
|
||||
minimumTouchTargetSize = DpSize(24.dp, 24.dp)
|
||||
)
|
||||
}
|
||||
|
||||
CompositionLocalProvider(LocalViewConfiguration provides viewConfigurationOverride) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(start = 10.dp, end = 2.dp)
|
||||
.padding(vertical = 10.dp)
|
||||
) {
|
||||
Canvas(modifier = Modifier.size(12.dp), onDraw = {
|
||||
drawCircle(color = chipColors.containerColor)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T : Chip> CloseButton(
|
||||
state: ChipTextFieldState<T>,
|
||||
chip: T,
|
||||
modifier: Modifier = Modifier,
|
||||
backgroundColor: Color = Color.Transparent,
|
||||
strokeColor: Color = Color.White,
|
||||
startPadding: Dp = 0.dp,
|
||||
endPadding: Dp = 4.dp
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.padding(start = startPadding, end = endPadding)
|
||||
) {
|
||||
CloseButtonImpl(
|
||||
onClick = { state.removeChip(chip) },
|
||||
backgroundColor = backgroundColor,
|
||||
strokeColor = strokeColor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ViewConfigurationOverride(
|
||||
base: ViewConfiguration,
|
||||
override val doubleTapMinTimeMillis: Long = base.doubleTapMinTimeMillis,
|
||||
override val doubleTapTimeoutMillis: Long = base.doubleTapTimeoutMillis,
|
||||
override val longPressTimeoutMillis: Long = base.longPressTimeoutMillis,
|
||||
override val touchSlop: Float = base.touchSlop,
|
||||
override val minimumTouchTargetSize: DpSize = base.minimumTouchTargetSize
|
||||
) : ViewConfiguration
|
||||
|
||||
@Composable
|
||||
private fun CloseButtonImpl(
|
||||
onClick: () -> Unit,
|
||||
backgroundColor: Color,
|
||||
strokeColor: Color,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val padding = with(LocalDensity.current) { 6.dp.toPx() }
|
||||
val strokeWidth = with(LocalDensity.current) { 1.2.dp.toPx() }
|
||||
val viewConfiguration = LocalViewConfiguration.current
|
||||
val viewConfigurationOverride = remember(viewConfiguration) {
|
||||
ViewConfigurationOverride(
|
||||
base = viewConfiguration,
|
||||
minimumTouchTargetSize = DpSize(24.dp, 24.dp)
|
||||
)
|
||||
}
|
||||
CompositionLocalProvider(LocalViewConfiguration provides viewConfigurationOverride) {
|
||||
Canvas(
|
||||
modifier = modifier
|
||||
.size(18.dp)
|
||||
.clip(CircleShape)
|
||||
.background(backgroundColor)
|
||||
.clickable(onClick = onClick)
|
||||
) {
|
||||
drawLine(
|
||||
color = strokeColor,
|
||||
start = Offset(padding, padding),
|
||||
end = Offset(size.width - padding, size.height - padding),
|
||||
strokeWidth = strokeWidth
|
||||
)
|
||||
drawLine(
|
||||
color = strokeColor,
|
||||
start = Offset(padding, size.height - padding),
|
||||
end = Offset(size.width - padding, padding),
|
||||
strokeWidth = strokeWidth
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LabelChip(label: SavedItemLabel) : Chip(label.name) {
|
||||
val label = label
|
||||
}
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
fun LabelsSelectionSheetContent(
|
||||
// viewModel: LibraryViewModel,
|
||||
isLibraryMode: Boolean,
|
||||
labels: List<SavedItemLabel>,
|
||||
initialSelectedLabels: List<SavedItemLabel>,
|
||||
@ -107,13 +220,32 @@ fun LabelsSelectionSheetContent(
|
||||
val selectedLabels = remember { mutableStateOf(initialSelectedLabels) }
|
||||
var showCreateLabelDialog by remember { mutableStateOf(false ) }
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
|
||||
val titleText = if (isLibraryMode) "Filter by Label" else "Set Labels"
|
||||
|
||||
val findOrCreateLabel: (name: String) -> SavedItemLabel = { name ->
|
||||
val found = labels.find { it.name == name }
|
||||
found
|
||||
?: SavedItemLabel(
|
||||
savedItemLabelId = "",
|
||||
name = name,
|
||||
color = "#FFFFFF",
|
||||
createdAt = "",
|
||||
labelDescription = "",
|
||||
serverSyncStatus = ServerSyncStatus.NEEDS_CREATION.rawValue
|
||||
)
|
||||
}
|
||||
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background),
|
||||
) {
|
||||
var value by remember { mutableStateOf("Initial text") }
|
||||
val state = rememberChipTextFieldState<LabelChip>()
|
||||
|
||||
|
||||
if (showCreateLabelDialog) {
|
||||
LabelCreationDialog(
|
||||
@ -129,9 +261,9 @@ fun LabelsSelectionSheetContent(
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
// .verticalScroll(rememberScrollState())
|
||||
// .verticalScroll(rememberScrollState())
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 0.dp)
|
||||
.padding(horizontal = 5.dp)
|
||||
) {
|
||||
|
||||
Row(
|
||||
@ -139,6 +271,7 @@ fun LabelsSelectionSheetContent(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 5.dp)
|
||||
) {
|
||||
TextButton(onClick = onCancel) {
|
||||
Text(text = "Cancel")
|
||||
@ -150,69 +283,97 @@ fun LabelsSelectionSheetContent(
|
||||
Text(text = if (isLibraryMode) "Search" else "Save")
|
||||
}
|
||||
}
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
items(labels) { label ->
|
||||
val isLabelSelected = selectedLabels.value.contains(label)
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
if (isLabelSelected) {
|
||||
selectedLabels.value =
|
||||
selectedLabels.value.filter { it.savedItemLabelId != label.savedItemLabelId }
|
||||
} else {
|
||||
selectedLabels.value = selectedLabels.value + listOf(label)
|
||||
}
|
||||
}
|
||||
.padding(horizontal = 10.dp, vertical = 6.dp)
|
||||
) {
|
||||
val chipColors = LabelChipColors.fromHex(label.color)
|
||||
ChipTextField(
|
||||
state = state,
|
||||
onSubmit = { LabelChip(findOrCreateLabel(it)) },
|
||||
chipLeadingIcon = { chip -> CircleIcon(colorHex = chip.label.color) },
|
||||
chipTrailingIcon = { chip -> CloseButton(state, chip) },
|
||||
chipStyle = ChipTextFieldDefaults.chipStyle(
|
||||
shape = androidx.compose.material.MaterialTheme.shapes.medium,
|
||||
unfocusedBorderWidth = 0.dp,
|
||||
focusedTextColor = Color(0xFFAEAEAF),
|
||||
focusedBorderColor = Color(0xFF2A2A2A),
|
||||
focusedBackgroundColor = Color(0xFF2A2A2A)
|
||||
),
|
||||
colors = androidx.compose.material.TextFieldDefaults.textFieldColors(
|
||||
textColor = Color(0xFFAEAEAF),
|
||||
backgroundColor = Color(0xFF3D3D3D)
|
||||
),
|
||||
contentPadding = PaddingValues(15.dp),
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minHeight = 45.dp)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 10.dp)
|
||||
.focusRequester(focusRequester)
|
||||
)
|
||||
|
||||
LabelChip(
|
||||
name = label.name,
|
||||
colors = chipColors
|
||||
)
|
||||
if (isLabelSelected) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
Divider(color = MaterialTheme.colorScheme.outlineVariant, thickness = 1.dp)
|
||||
}
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
items(labels) { label ->
|
||||
val isLabelSelected = selectedLabels.value.contains(label)
|
||||
|
||||
if (!isLibraryMode) {
|
||||
item {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { showCreateLabelDialog = true }
|
||||
.padding(horizontal = 6.dp)
|
||||
.padding(vertical = 12.dp)
|
||||
)
|
||||
{
|
||||
Icon(
|
||||
imageVector = Icons.Filled.AddCircle,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
.clickable {
|
||||
if (isLabelSelected) {
|
||||
selectedLabels.value =
|
||||
selectedLabels.value.filter { it.savedItemLabelId != label.savedItemLabelId }
|
||||
} else {
|
||||
selectedLabels.value = selectedLabels.value + listOf(label)
|
||||
state.addChip(app.omnivore.omnivore.ui.components.LabelChip(label))
|
||||
}
|
||||
}
|
||||
.padding(horizontal = 10.dp, vertical = 6.dp)
|
||||
) {
|
||||
val chipColors = LabelChipColors.fromHex(label.color)
|
||||
|
||||
LabelChip(
|
||||
name = label.name,
|
||||
colors = chipColors
|
||||
)
|
||||
Text(text = "Create a new Label")
|
||||
if (isLabelSelected) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
Divider(color = MaterialTheme.colorScheme.outlineVariant, thickness = 1.dp)
|
||||
}
|
||||
|
||||
if (!isLibraryMode) {
|
||||
item {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { showCreateLabelDialog = true }
|
||||
.padding(horizontal = 6.dp)
|
||||
.padding(vertical = 12.dp)
|
||||
)
|
||||
{
|
||||
Icon(
|
||||
imageVector = Icons.Filled.AddCircle,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
)
|
||||
Text(text = "Create a new Label")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ fun LibraryView(
|
||||
|
||||
if (showLabelsSelectionSheet) {
|
||||
coroutineScope.launch {
|
||||
modalBottomSheetState.animateTo(ModalBottomSheetValue.HalfExpanded)
|
||||
modalBottomSheetState.show()
|
||||
}
|
||||
} else {
|
||||
coroutineScope.launch {
|
||||
|
||||
@ -176,6 +176,7 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null,
|
||||
Color(it.backgroundColor ?: 0xFFFFFFFF)
|
||||
}
|
||||
} ?: Color(0xFFFFFFFF)
|
||||
|
||||
val themeTintColor = currentTheme?.let {
|
||||
if (it.themeKey == "System" && isDarkMode) {
|
||||
Color(0xFFFFFFFF)
|
||||
@ -189,27 +190,27 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null,
|
||||
when (bottomSheetState) {
|
||||
BottomSheetState.PREFERENCES -> {
|
||||
coroutineScope.launch {
|
||||
modalBottomSheetState.animateTo(ModalBottomSheetValue.HalfExpanded)
|
||||
modalBottomSheetState.show()
|
||||
}
|
||||
}
|
||||
BottomSheetState.NOTEBOOK -> {
|
||||
coroutineScope.launch {
|
||||
modalBottomSheetState.animateTo(ModalBottomSheetValue.Expanded)
|
||||
modalBottomSheetState.show()
|
||||
}
|
||||
}
|
||||
BottomSheetState.HIGHLIGHTNOTE -> {
|
||||
coroutineScope.launch {
|
||||
modalBottomSheetState.animateTo(ModalBottomSheetValue.Expanded)
|
||||
modalBottomSheetState.show()
|
||||
}
|
||||
}
|
||||
BottomSheetState.LABELS -> {
|
||||
coroutineScope.launch {
|
||||
modalBottomSheetState.animateTo(ModalBottomSheetValue.HalfExpanded)
|
||||
modalBottomSheetState.show()
|
||||
}
|
||||
}
|
||||
BottomSheetState.LINK -> {
|
||||
coroutineScope.launch {
|
||||
modalBottomSheetState.animateTo(ModalBottomSheetValue.HalfExpanded)
|
||||
modalBottomSheetState.show()
|
||||
}
|
||||
}
|
||||
BottomSheetState.NONE -> {
|
||||
@ -346,7 +347,7 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null,
|
||||
IconButton(onClick = {
|
||||
coroutineScope.launch {
|
||||
webReaderViewModel.setBottomSheet(BottomSheetState.NOTEBOOK)
|
||||
modalBottomSheetState.animateTo(ModalBottomSheetValue.Expanded)
|
||||
modalBottomSheetState.show()
|
||||
}
|
||||
}) {
|
||||
Icon(
|
||||
@ -359,7 +360,7 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null,
|
||||
IconButton(onClick = {
|
||||
coroutineScope.launch {
|
||||
webReaderViewModel.setBottomSheet(BottomSheetState.PREFERENCES)
|
||||
modalBottomSheetState.animateTo(ModalBottomSheetValue.HalfExpanded)
|
||||
modalBottomSheetState.show()
|
||||
}
|
||||
}) {
|
||||
Icon(
|
||||
|
||||
Reference in New Issue
Block a user