Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4694d9d
fix: align profile create and edit with figma
jvsena42 Sep 23, 2026
bad792d
fix: blur only the cancel button in profile edit
jvsena42 Sep 23, 2026
03becd1
fix: remove tags section from own profile screen
jvsena42 Sep 23, 2026
b592ef4
chore: update changelog fragment
jvsena42 Sep 23, 2026
e5c9108
fix: address profile design review divergences
jvsena42 Sep 23, 2026
53ce425
fix: position pubky choice background as in figma
jvsena42 Sep 23, 2026
6ed6d83
chore: fix detekt spacing
jvsena42 Sep 23, 2026
540232a
fix: focus profile name only when empty
jvsena42 Sep 24, 2026
2f05ea3
fix: match profile icons and pubky choice copy to figma
jvsena42 Sep 24, 2026
5c2403e
fix: size profile qr to figma insets
jvsena42 Sep 24, 2026
0d351a5
fix: remove links from own profile screen
jvsena42 Sep 24, 2026
a0180fb
fix: match contact add tag and action icons to figma
jvsena42 Sep 24, 2026
c2f6c83
feat: open valid contact links
jvsena42 Sep 24, 2026
9fa83f6
fix: accept mixed-case link schemes
jvsena42 Sep 24, 2026
1742e66
fix: open phone and explicit mailto/tel contact links
jvsena42 Sep 24, 2026
5ea5e21
refactor: simplify contact link parsing
jvsena42 Sep 24, 2026
7d7b2e9
fix: open formatted tel links and dotted phone numbers
jvsena42 Sep 24, 2026
942de3e
fix: keep ip addresses out of phone link detection
jvsena42 Sep 24, 2026
aea0987
fix: restore profile links and tags, add copy popup and transparent h…
jvsena42 Sep 24, 2026
817cf58
fix: require plus or phone label for bare phone links
jvsena42 Sep 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions app/src/main/java/to/bitkit/ui/components/AddLinkSheet.kt
Original file line number Diff line number Diff line change
Expand Up @@ -165,17 +165,27 @@ internal fun SuggestionsContent(
.sheetHeight(SheetSize.COMPACT, isModal = true)
.gradientBackground()
.navigationBarsPadding()
.padding(horizontal = 16.dp),
) {
SheetTopBar(titleText = title, onBack = onBack)
SheetTopBar(
titleText = title,
onBack = onBack,
modifier = Modifier.padding(horizontal = 4.dp)
)
VerticalSpacer(16.dp)
FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.fillMaxWidth()
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
suggestions.forEach { suggestion ->
TagButton(text = suggestion, onClick = { onSelect(suggestion) })
PrimaryButton(
text = suggestion,
onClick = { onSelect(suggestion) },
size = ButtonSize.Small,
fullWidth = false,
)
}
}
VerticalSpacer(24.dp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ fun CenteredProfileHeader(
)

if (bio.isNotEmpty()) {
VerticalSpacer(8.dp)
VerticalSpacer(16.dp)
BodyM(
text = bio,
color = Colors.White64,
Expand Down
57 changes: 56 additions & 1 deletion app/src/main/java/to/bitkit/ui/components/LinkRow.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
package to.bitkit.ui.components

import android.content.Intent
import android.net.Uri
import android.util.Patterns
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import to.bitkit.ui.shared.modifiers.clickableAlpha
import to.bitkit.ui.theme.Colors
import to.bitkit.ui.utils.isValidEmail
import to.bitkit.utils.Logger

private const val TAG = "LinkRow"

@Composable
fun LinkRow(
Expand All @@ -16,6 +27,9 @@ fun LinkRow(
linkIndex: Int,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val uri = remember(label, value) { value.toLinkUri(label) }

Column(modifier = modifier.fillMaxWidth()) {
VerticalSpacer(16.dp)
Text13Up(
Expand All @@ -26,9 +40,50 @@ fun LinkRow(
VerticalSpacer(8.dp)
BodySSB(
text = value,
modifier = Modifier.testTag("ProfileLinkValue_$linkIndex"),
modifier = Modifier
.clickableAlpha(
onClick = uri?.let { linkUri ->
{
runCatching { context.startActivity(Intent(Intent.ACTION_VIEW, linkUri)) }
.onFailure { Logger.warn("Failed to open link '$linkUri'", it, context = TAG) }
}
}
)
.testTag("ProfileLinkValue_$linkIndex"),
)
VerticalSpacer(16.dp)
HorizontalDivider()
}
}

private val PHONE_REGEX = Regex("""^\+?[0-9 ().-]{7,}$""")
Comment thread
jvsena42 marked this conversation as resolved.
private const val MIN_PHONE_DIGITS = 7
private val PHONE_LABELS = setOf("phone", "tel", "telephone", "mobile", "cell")

private fun String.toLinkUri(label: String): Uri? {
val trimmed = trim()
val scheme = trimmed.toUri().scheme?.lowercase()
return when {
trimmed.isEmpty() -> null
trimmed.isValidEmail() -> "mailto:$trimmed".toUri()
(trimmed.startsWith('+') || label.trim().lowercase() in PHONE_LABELS) && trimmed.isPhoneNumber() ->
trimmed.toTelUri()
scheme == "tel" -> trimmed.substringAfter(':').trim().takeIf { it.isPhoneNumber() }?.toTelUri()
trimmed.contains(' ') -> null
Comment thread
jvsena42 marked this conversation as resolved.
scheme == "mailto" -> trimmed.toUri()
Patterns.WEB_URL.matcher(trimmed).matches() -> trimmed.toWebUri()
else -> null
}
}

private fun String.isPhoneNumber() = matches(PHONE_REGEX) && count(Char::isDigit) >= MIN_PHONE_DIGITS

private fun String.toTelUri() = "tel:${filter { it.isDigit() || it == '+' }}".toUri()

private fun String.toWebUri(): Uri? {
val withScheme = if (contains("://")) this else "https://$this"
return withScheme.toUri().takeIf {
val isHttp = it.scheme.equals("http", ignoreCase = true) || it.scheme.equals("https", ignoreCase = true)
isHttp && it.host?.substringAfterLast('.').orEmpty().any(Char::isLetter)
}
}
Loading
Loading