Skip to content

Commit cd4e574

Browse files
committed
feat: add cross-platform URL opener for issue tracker token links
- Implement UrlOpener expect/actual for JVM, Android, iOS, JS, and WASM targets - Integrate clickable token creation links in IssueTrackerConfigDialog for GitHub, GitLab, and Jira
1 parent 5fcadb4 commit cd4e574

File tree

7 files changed

+149
-3
lines changed

7 files changed

+149
-3
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package cc.unitmesh.devins.ui.platform
2+
3+
import android.content.Intent
4+
import android.net.Uri
5+
import cc.unitmesh.config.ConfigManager
6+
7+
/**
8+
* Android implementation of UrlOpener
9+
* Uses Intent.ACTION_VIEW to open URLs in the system's default browser
10+
*/
11+
actual object UrlOpener {
12+
actual fun openUrl(url: String) {
13+
try {
14+
val context = ConfigManager.appContext
15+
if (context != null) {
16+
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
17+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
18+
context.startActivity(intent)
19+
} else {
20+
println("Failed to open URL: Context not available")
21+
}
22+
} catch (e: Exception) {
23+
println("Failed to open URL: $url - ${e.message}")
24+
}
25+
}
26+
}
27+

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/codereview/IssueTrackerConfigDialog.kt

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cc.unitmesh.devins.ui.compose.agent.codereview
22

3+
import androidx.compose.foundation.clickable
34
import androidx.compose.foundation.layout.*
45
import androidx.compose.foundation.rememberScrollState
56
import androidx.compose.foundation.verticalScroll
@@ -13,6 +14,7 @@ import androidx.compose.ui.text.input.VisualTransformation
1314
import androidx.compose.ui.unit.dp
1415
import androidx.compose.ui.window.Dialog
1516
import cc.unitmesh.devins.ui.compose.icons.AutoDevComposeIcons
17+
import cc.unitmesh.devins.ui.platform.UrlOpener
1618
import cc.unitmesh.config.ConfigManager
1719
import cc.unitmesh.config.IssueTrackerConfig
1820
import kotlinx.coroutines.launch
@@ -197,21 +199,37 @@ fun IssueTrackerConfigDialog(
197199
text = "Create token at: github.com/settings/tokens",
198200
style = MaterialTheme.typography.bodySmall,
199201
color = MaterialTheme.colorScheme.primary,
200-
textDecoration = androidx.compose.ui.text.style.TextDecoration.Underline
202+
textDecoration = androidx.compose.ui.text.style.TextDecoration.Underline,
203+
modifier = Modifier.clickable {
204+
UrlOpener.openUrl("https://github.com/settings/tokens")
205+
}
201206
)
202207
}
203208
"gitlab" -> {
204209
Text(
205210
text = "Create a personal access token in your GitLab settings",
206211
style = MaterialTheme.typography.bodySmall,
207-
color = MaterialTheme.colorScheme.onSurfaceVariant
212+
color = MaterialTheme.colorScheme.primary,
213+
textDecoration = androidx.compose.ui.text.style.TextDecoration.Underline,
214+
modifier = Modifier.clickable {
215+
val url = if (serverUrl.isNotBlank()) {
216+
"$serverUrl/-/profile/personal_access_tokens"
217+
} else {
218+
"https://gitlab.com/-/profile/personal_access_tokens"
219+
}
220+
UrlOpener.openUrl(url)
221+
}
208222
)
209223
}
210224
"jira" -> {
211225
Text(
212226
text = "Create an API token in your Atlassian account settings",
213227
style = MaterialTheme.typography.bodySmall,
214-
color = MaterialTheme.colorScheme.onSurfaceVariant
228+
color = MaterialTheme.colorScheme.primary,
229+
textDecoration = androidx.compose.ui.text.style.TextDecoration.Underline,
230+
modifier = Modifier.clickable {
231+
UrlOpener.openUrl("https://id.atlassian.com/manage-profile/security/api-tokens")
232+
}
215233
)
216234
}
217235
else -> {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package cc.unitmesh.devins.ui.platform
2+
3+
/**
4+
* Cross-platform URL opener
5+
* Opens URLs in the system's default browser
6+
*/
7+
expect object UrlOpener {
8+
/**
9+
* Open a URL in the system's default browser
10+
* @param url The URL to open (e.g., "https://github.com/settings/tokens")
11+
*/
12+
fun openUrl(url: String)
13+
}
14+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package cc.unitmesh.devins.ui.platform
2+
3+
import platform.Foundation.NSURL
4+
import platform.UIKit.UIApplication
5+
6+
/**
7+
* iOS implementation of UrlOpener
8+
* Uses UIApplication.sharedApplication.openURL to open URLs in Safari
9+
*/
10+
actual object UrlOpener {
11+
actual fun openUrl(url: String) {
12+
try {
13+
val nsUrl = NSURL.URLWithString(url)
14+
if (nsUrl != null) {
15+
UIApplication.sharedApplication.openURL(nsUrl)
16+
}
17+
} catch (e: Exception) {
18+
println("Failed to open URL: $url - ${e.message}")
19+
}
20+
}
21+
}
22+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package cc.unitmesh.devins.ui.platform
2+
3+
import kotlinx.browser.window
4+
5+
/**
6+
* JS implementation of UrlOpener
7+
* Uses window.open() to open URLs in a new browser tab
8+
*/
9+
actual object UrlOpener {
10+
actual fun openUrl(url: String) {
11+
try {
12+
window.open(url, "_blank")
13+
} catch (e: Exception) {
14+
println("Failed to open URL: $url - ${e.message}")
15+
}
16+
}
17+
}
18+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package cc.unitmesh.devins.ui.platform
2+
3+
import java.awt.Desktop
4+
import java.net.URI
5+
6+
/**
7+
* JVM implementation of UrlOpener
8+
* Uses java.awt.Desktop to open URLs in the system's default browser
9+
*/
10+
actual object UrlOpener {
11+
actual fun openUrl(url: String) {
12+
try {
13+
if (Desktop.isDesktopSupported()) {
14+
val desktop = Desktop.getDesktop()
15+
if (desktop.isSupported(Desktop.Action.BROWSE)) {
16+
desktop.browse(URI(url))
17+
}
18+
}
19+
} catch (e: Exception) {
20+
println("Failed to open URL: $url - ${e.message}")
21+
}
22+
}
23+
}
24+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package cc.unitmesh.devins.ui.platform
2+
3+
/**
4+
* WASM-JS implementation of UrlOpener
5+
* Uses external JS function to open URLs
6+
*/
7+
actual object UrlOpener {
8+
actual fun openUrl(url: String) {
9+
try {
10+
openUrlExternal(url)
11+
} catch (e: Exception) {
12+
println("Failed to open URL: $url - ${e.message}")
13+
}
14+
}
15+
}
16+
17+
/**
18+
* External JS function to open URL in new tab
19+
*/
20+
private fun openUrlExternal(url: String) {
21+
js("window.open(url, '_blank')")
22+
}
23+

0 commit comments

Comments
 (0)