refactor: extract duplicated string literals into named constants

This commit is contained in:
davotoula
2026-07-01 09:38:52 +02:00
parent c66d1db0de
commit 0adee41711
4 changed files with 18 additions and 12 deletions
@@ -145,6 +145,8 @@ class MainActivity : AppCompatActivity() {
}
}
private const val NOSTR_URI_PREFIX = "nostr:"
fun isNotificationRoute(uri: String) = uri.startsWith("notifications", true) || uri.startsWith("nostr:notifications", true)
fun isHashtagRoute(uri: String) = uri.startsWith("hashtag?id=") || uri.startsWith("nostr:hashtag?id=")
@@ -161,7 +163,7 @@ fun isConnectedAppRoute(uri: String) = uri.startsWith("connectedapp?coordinate="
fun connectedAppRoute(uri: String): Route.ConnectedAppDetail? {
val coordinate =
runCatching {
val raw = java.net.URI(uri.removePrefix("nostr:")).findParameterValue("coordinate") ?: return null
val raw = java.net.URI(uri.removePrefix(NOSTR_URI_PREFIX)).findParameterValue("coordinate") ?: return null
URLDecoder.decode(raw, Charsets.UTF_8.name())
}.getOrNull()?.takeIf { it.isNotBlank() } ?: return null
@@ -171,7 +173,7 @@ fun connectedAppRoute(uri: String): Route.ConnectedAppDetail? {
fun urlRoute(uri: String): Route.Url? {
val url =
runCatching {
val rawUrl = java.net.URI(uri.removePrefix("nostr:")).findParameterValue("id") ?: return null
val rawUrl = java.net.URI(uri.removePrefix(NOSTR_URI_PREFIX)).findParameterValue("id") ?: return null
URLDecoder.decode(rawUrl, Charsets.UTF_8.name())
}.getOrNull() ?: return null
@@ -189,11 +191,11 @@ fun uriToRoute(
account: Account,
): Route? {
if (isNotificationRoute(uri)) {
val scrollTo = runCatching { java.net.URI(uri.removePrefix("nostr:")).findParameterValue("scrollTo") }.getOrNull()
val scrollTo = runCatching { java.net.URI(uri.removePrefix(NOSTR_URI_PREFIX)).findParameterValue("scrollTo") }.getOrNull()
return Route.Notification(scrollToEventId = scrollTo)
}
if (isHashtagRoute(uri)) {
return Route.Hashtag(uri.removePrefix("nostr:").removePrefix("hashtag?id=").lowercase())
return Route.Hashtag(uri.removePrefix(NOSTR_URI_PREFIX).removePrefix("hashtag?id=").lowercase())
}
if (isUrlRoute(uri)) {
return urlRoute(uri)
@@ -51,6 +51,8 @@ import okhttp3.OkHttpClient
* which only parses). The signer side lives in [BunkerCommand].
*/
object NostrConnect {
private const val NOSTRCONNECT_SCHEME = "nostrconnect://"
data class Offer(
val clientPubkey: String,
val relays: Set<NormalizedRelayUrl>,
@@ -60,8 +62,8 @@ object NostrConnect {
/** Parse `nostrconnect://<client-pubkey>?relay=…&secret=…&name=…` (percent-decoded). */
fun parseOffer(uri: String): Offer? {
if (!uri.startsWith("nostrconnect://")) return null
val parts = uri.removePrefix("nostrconnect://").split("?", limit = 2)
if (!uri.startsWith(NOSTRCONNECT_SCHEME)) return null
val parts = uri.removePrefix(NOSTRCONNECT_SCHEME).split("?", limit = 2)
val clientPubkey = parts[0].lowercase()
if (clientPubkey.length != 64 || clientPubkey.any { it !in "0123456789abcdef" }) return null
val relays = mutableSetOf<NormalizedRelayUrl>()
@@ -89,7 +91,7 @@ object NostrConnect {
): String {
val enc = { s: String -> java.net.URLEncoder.encode(s, "UTF-8") }
return buildString {
append("nostrconnect://").append(clientPubkey)
append(NOSTRCONNECT_SCHEME).append(clientPubkey)
append("?").append(relays.joinToString("&") { "relay=${enc(it.url)}" })
append("&secret=").append(enc(secret))
if (name != null) append("&name=").append(enc(name))
@@ -153,7 +153,7 @@ class NappletBrowserService : Service() {
BrowserTab(
sessionId = sessionId,
clientMessenger = msg.replyTo,
url = data.getString(NappletBrowserContract.KEY_URL)?.ifBlank { "about:blank" } ?: "about:blank",
url = data.getString(NappletBrowserContract.KEY_URL)?.ifBlank { ABOUT_BLANK } ?: ABOUT_BLANK,
proxyPort = data.getInt(NappletBrowserContract.KEY_PROXY_PORT, -1),
useTor = data.getBoolean(NappletBrowserContract.KEY_USE_TOR, false),
bgColor = data.getInt(NappletBrowserContract.KEY_BG_COLOR, android.graphics.Color.WHITE),
@@ -590,9 +590,10 @@ class NappletBrowserService : Service() {
private fun readContractAsset(path: String): ByteArray = assets.open(NappletWebContract.RESOURCE_ASSET_ROOT + path).use { it.readBytes() }
/** Address-bar text → URL via the shared [OmniboxInput] rules (bare domain → https, else search). */
private fun normalizeUrl(input: String): String = OmniboxInput.resolve(input)?.url ?: "about:blank"
private fun normalizeUrl(input: String): String = OmniboxInput.resolve(input)?.url ?: ABOUT_BLANK
private companion object {
private const val TAG = "NappletBrowserService"
private const val ABOUT_BLANK = "about:blank"
}
}
@@ -121,7 +121,7 @@ class NappletContentServer(
if (url == NappletWebContract.SHELL_URL) return serveShell()
if (url == appOrigin || url.startsWith("$appOrigin/")) {
// A document navigation accepts text/html; a sub-resource (js/css/img) does not.
val acceptsHtml = request.requestHeaders["Accept"]?.contains("text/html", ignoreCase = true) == true
val acceptsHtml = request.requestHeaders["Accept"]?.contains(MIME_HTML, ignoreCase = true) == true
return serveAppResource(url, acceptsHtml)
}
// Off-origin: a locked napplet 404s (connect-src 'none' means it shouldn't ask). An nSite in
@@ -134,7 +134,7 @@ class NappletContentServer(
// origin so the shell frames exactly this applet (and the CSP frame-src is pinned to it too).
val html = shellHtmlBytes.decodeToString().replace(NappletWebContract.APP_ORIGIN_PLACEHOLDER, appOrigin).encodeToByteArray()
return WebResourceResponse(
"text/html",
MIME_HTML,
"utf-8",
200,
"OK",
@@ -167,7 +167,7 @@ class NappletContentServer(
if (resolution !is StaticSiteResolution.Resolved) return notFound()
val (mime, charset) = splitContentType(resolution.contentType)
val isHtml = mime.equals("text/html", ignoreCase = true)
val isHtml = mime.equals(MIME_HTML, ignoreCase = true)
val bytes = if (isHtml) injectShim(resolution.bytes) else resolution.bytes
// Locked napplets get the strict app CSP (connect-src 'none', etc.). An nSite in website mode
@@ -222,5 +222,6 @@ class NappletContentServer(
companion object {
// Marker "server" for a Resolved served from the local content-addressed cache.
private const val CACHE_SERVER = "cache"
private const val MIME_HTML = "text/html"
}
}