Skip to content

Commit

Permalink
add info to preference screen
Browse files Browse the repository at this point in the history
  • Loading branch information
danielyrovas committed Feb 19, 2024
1 parent 5b090b7 commit a3cb47e
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 21 deletions.
161 changes: 148 additions & 13 deletions app/src/main/java/org/yrovas/linklater/ui/common/PreferenceComponents.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
package org.yrovas.linklater.ui.common

import android.content.Context
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.*
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import org.yrovas.linklater.readClipboard
import org.yrovas.linklater.ui.screens.ThemePreview
import org.yrovas.linklater.ui.theme.AppTheme
import org.yrovas.linklater.ui.theme.padding
Expand All @@ -24,7 +32,9 @@ fun TextPreference(
icon: ImageVector,
name: String,
placeholder: String = "",
secure: Boolean = true,
infoPreview: String = "",
infoTitle: String = "",
info: (@Composable () -> Unit)? = null,
state: State<String>,
onSave: (String) -> Unit,
onCheck: (String) -> Boolean,
Expand All @@ -33,7 +43,9 @@ fun TextPreference(
icon = { Icon(icon) },
name = name,
placeholder = placeholder,
secure = secure,
infoPreview = infoPreview,
infoTitle = infoTitle,
info = info,
state = state,
onSave = onSave,
onCheck = onCheck,
Expand All @@ -44,20 +56,31 @@ fun TextPreference(
private fun TextPreference(
icon: @Composable () -> Unit = {},
name: String,
infoPreview: String,
infoTitle: String,
info: (@Composable () -> Unit)?,
placeholder: String,
secure: Boolean,
state: State<String>,
onSave: (String) -> Unit,
onCheck: (String) -> Boolean,
) {
var showDialog by remember { mutableStateOf(false) }

if (showDialog) {
Dialog(properties = DialogProperties(
dismissOnBackPress = true,
dismissOnClickOutside = true,
securePolicy = if (secure) SecureFlagPolicy.SecureOn else SecureFlagPolicy.SecureOff,
), onDismissRequest = { showDialog = false }) {
TextEditDialog(name, placeholder, state, onSave, onCheck) {
TextEditDialog(
name,
placeholder,
infoPreview,
infoTitle,
info,
state,
onSave,
onCheck
) {
showDialog = false
}
}
Expand All @@ -72,7 +95,8 @@ private fun TextPreference(
Column {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
horizontalArrangement = Arrangement.Start,
modifier = Modifier.fillMaxWidth()
) {
icon()
Spacer(modifier = Modifier.width(padding.standard))
Expand Down Expand Up @@ -101,13 +125,18 @@ private fun TextPreference(
private fun TextEditDialog(
name: String,
placeholder: String,
infoPreview: String,
infoTitle: String,
info: (@Composable () -> Unit)?,
storedValue: State<String>,
onSave: (String) -> Unit,
onCheck: (String) -> Boolean,
onDismiss: () -> Unit,
) {
val context: Context = LocalContext.current
var currentInput by remember { mutableStateOf(TextFieldValue(storedValue.value)) }
var isValid by remember { mutableStateOf(onCheck(storedValue.value)) }
var showInfo by remember { mutableStateOf(false) }

Surface(
color = MaterialTheme.colorScheme.surfaceContainer,
Expand All @@ -116,7 +145,9 @@ private fun TextEditDialog(
Column(
modifier = Modifier
.wrapContentHeight()
.padding(vertical = padding.standard, horizontal = padding.standard)
.padding(
vertical = padding.standard, horizontal = padding.standard
)
.fillMaxWidth()
) {
Text(
Expand All @@ -136,8 +167,59 @@ private fun TextEditDialog(
currentInput = it
})
Spacer(modifier = Modifier.height(padding.standard))

if (info != null) {
Column(
modifier = Modifier
.clip(RoundedCornerShape(5.dp))
.background(MaterialTheme.colorScheme.secondaryContainer)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start,
modifier = Modifier.fillMaxWidth()
.clickable { showInfo = !showInfo }
.padding(padding.standard)
) {
Crossfade(
label = "Info Title", targetState = showInfo
) {
if (it) Text(text = infoTitle, color = MaterialTheme.colorScheme.onSecondaryContainer)
else Text(text = infoPreview, color = MaterialTheme.colorScheme.onSecondaryContainer)
}
Spacer(modifier = Modifier.weight(1f))
Icon(
imageVector = if (showInfo) Icons.Default.Info else Icons.Outlined.Info
)
}
AnimatedVisibility(
modifier = Modifier.padding(
start = padding.standard,
end = padding.standard,
bottom = padding.standard,
), visible = showInfo
) {
info()
}
}
Spacer(modifier = Modifier.height(padding.standard))
}

Row {
Spacer(modifier = Modifier.weight(1f))

Button(
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant,
contentColor = MaterialTheme.colorScheme.onSurfaceVariant
),
onClick = {
currentInput = TextFieldValue(readClipboard(context))
},
) {
Icon(imageVector = Icons.Default.ContentPasteGo)
}
Spacer(modifier = Modifier.width(padding.standard))
Button(
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
Expand All @@ -154,9 +236,31 @@ private fun TextEditDialog(
}
}

@ThemePreview
@Composable
private fun TextPreferenceURLPreview() {
val state = remember { mutableStateOf("") }
AppTheme {
Surface {
Row(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.padding(padding.standard)
) {
TextPreference(icon = Icons.Default.Build,
name = "LinkDing API URL",
state = state,
onSave = {},
onCheck = { true })
}
}
}
}

@ThemePreview
@Composable
private fun TextPreferencePreview() {
val state = remember { mutableStateOf("APITokenstringVeryLongAndRandom") }
AppTheme {
Surface {
Row(
Expand All @@ -166,7 +270,7 @@ private fun TextPreferencePreview() {
) {
TextPreference(icon = Icons.Default.Build,
name = "LinkDing API Token",
state = derivedStateOf { "APITokenstringVeryLongAndRandom" },
state = state,
onSave = {},
onCheck = { true })
}
Expand All @@ -177,16 +281,47 @@ private fun TextPreferencePreview() {
@ThemePreview
@Composable
private fun TextEditDialogPreview() {
val state = remember { mutableStateOf("") }
AppTheme {
Surface {
Row(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.padding(padding.double)
) {
TextEditDialog(name = "LinkDing API Token",
placeholder = "Enter your API token...",
storedValue = derivedStateOf { "" },
TextEditDialog(name = "LinkDing API URL",
placeholder = "Enter your LinkDing instance URL...",
infoPreview = "include /api",
infoTitle = "Enter the LinkDing API URL",
// the protocol (https://), and the path (`/api`). e.g. `https://demo.linkding.link/api`",
info = {
Column {
Text("Include the protocol (https://).", color = MaterialTheme.colorScheme.onSecondaryContainer)
Text("Include the /api path.", color = MaterialTheme.colorScheme.onSecondaryContainer)
Text("Include the port if necessary.", color = MaterialTheme.colorScheme.onSecondaryContainer)
Text("For example", color = MaterialTheme.colorScheme.onSecondaryContainer)
Row(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(6.dp))
.background(MaterialTheme.colorScheme.surfaceContainer)
.padding(padding.half)
) {
Text("https://demo.linkding.link/api", color = MaterialTheme.colorScheme.onSurface)
}
Text("or", color = MaterialTheme.colorScheme.onSecondaryContainer)
Row(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(6.dp))
.background(MaterialTheme.colorScheme.surfaceContainer)
.padding(padding.half)
) {
Text("http://192.168.0.47:8000/api", color = MaterialTheme.colorScheme.onSurface)
}
}
},
storedValue = state,
onSave = {},
onCheck = { true }) {}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package org.yrovas.linklater.ui.screens

import android.content.Context
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.Create
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
Expand Down Expand Up @@ -47,6 +47,56 @@ fun PreferencesScreen(
name = "LinkDing URL",
placeholder = "URL/IP incl. port and https://",
icon = Icons.Default.Create,
infoPreview = "include /api",
infoTitle = "Enter the LinkDing API URL",
info = {
Column {
Text(
"Include the protocol (https://).",
color = MaterialTheme.colorScheme.onSecondaryContainer
)
Text(
"Include the /api path.",
color = MaterialTheme.colorScheme.onSecondaryContainer
)
Text(
"Include the port if necessary.",
color = MaterialTheme.colorScheme.onSecondaryContainer
)
Text(
"For example",
color = MaterialTheme.colorScheme.onSecondaryContainer
)
Row(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(6.dp))
.background(MaterialTheme.colorScheme.surfaceContainer)
.padding(padding.half)
) {
Text(
"https://demo.linkding.link/api",
color = MaterialTheme.colorScheme.onSurface
)
}
Text(
"or",
color = MaterialTheme.colorScheme.onSecondaryContainer
)
Row(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(6.dp))
.background(MaterialTheme.colorScheme.surfaceContainer)
.padding(padding.half)
) {
Text(
"http://192.168.0.47:8000/api",
color = MaterialTheme.colorScheme.onSurface
)
}
}
},
state = mainActivityState.bookmarkURL.collectAsState(),
onSave = {
scope.launch {
Expand All @@ -57,8 +107,22 @@ fun PreferencesScreen(
)
TextPreference(
name = "LinkDing API Token",
placeholder = "Enter your LinkDing API Token",
placeholder = "Enter your REST API Token",
icon = Icons.Default.Build,
infoPreview = "Settings > Integrations",
infoTitle = "Go to your Instance Settings",
info = {
Column {
Text(
"Select Integrations",
color = MaterialTheme.colorScheme.onSecondaryContainer
)
Text(
"Copy the token under REST API.",
color = MaterialTheme.colorScheme.onSecondaryContainer
)
}
},
state = mainActivityState.bookmarkAPIToken.collectAsState(),
onSave = {
scope.launch {
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/org/yrovas/linklater/utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,14 @@ fun openBrowser(context: Context, uri: Uri) {
val browserIntent = Intent(Intent.ACTION_VIEW, uri)
context.startActivity(browserIntent);
}

fun readClipboard(context: Context): String {
val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
if (clipboardManager.hasPrimaryClip()) {
val clipData = clipboardManager.primaryClip
if (clipData != null && clipData.itemCount > 0) {
return clipData.getItemAt(0).text?.toString() ?: ""
}
}
return ""
}
Loading

0 comments on commit a3cb47e

Please sign in to comment.