Skip to content

Commit 3bb76dd

Browse files
authored
feat: add edit repository dialog with key rename support (#6)
Add key field to UpdateRepositoryRequest SDK model and EditRepositoryDialog composable to RepositoryDetailScreen. Supports renaming key, name, description, and visibility.
1 parent 97ed93c commit 3bb76dd

2 files changed

Lines changed: 154 additions & 4 deletions

File tree

app/src/main/java/com/artifactkeeper/android/ui/screens/repositories/RepositoryDetailScreen.kt

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import androidx.compose.material.icons.Icons
1010
import androidx.compose.material.icons.automirrored.filled.ArrowBack
1111
import androidx.compose.material.icons.automirrored.filled.ArrowForward
1212
import androidx.compose.material.icons.filled.Download
13+
import androidx.compose.material.icons.filled.Edit
1314
import androidx.compose.material.icons.filled.Groups
1415
import androidx.compose.material.icons.filled.Search
1516
import androidx.compose.material.icons.filled.Shield
@@ -25,6 +26,7 @@ import com.artifactkeeper.android.data.api.ApiClient
2526
import com.artifactkeeper.android.data.api.unwrap
2627
import com.artifactkeeper.android.data.models.Artifact
2728
import com.artifactkeeper.android.data.models.Repository
29+
import com.artifactkeeper.client.models.UpdateRepositoryRequest
2830
import com.artifactkeeper.android.ui.util.formatBytes
2931
import com.artifactkeeper.android.ui.util.formatDownloadCount
3032
import com.artifactkeeper.android.ui.util.formatRelativeTime
@@ -44,6 +46,7 @@ fun RepositoryDetailScreen(
4446
var isRefreshing by remember { mutableStateOf(false) }
4547
var errorMessage by remember { mutableStateOf<String?>(null) }
4648
var searchQuery by remember { mutableStateOf("") }
49+
var showEditDialog by remember { mutableStateOf(false) }
4750
val coroutineScope = rememberCoroutineScope()
4851

4952
fun loadData(refresh: Boolean = false) {
@@ -78,6 +81,11 @@ fun RepositoryDetailScreen(
7881
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
7982
}
8083
},
84+
actions = {
85+
IconButton(onClick = { showEditDialog = true }) {
86+
Icon(Icons.Default.Edit, contentDescription = "Edit Repository")
87+
}
88+
},
8189
)
8290

8391
when {
@@ -185,6 +193,22 @@ fun RepositoryDetailScreen(
185193
}
186194
}
187195
}
196+
197+
if (showEditDialog && repository != null) {
198+
EditRepositoryDialog(
199+
repository = repository!!,
200+
onDismiss = { showEditDialog = false },
201+
onSaved = { updatedRepo, originalKey ->
202+
showEditDialog = false
203+
if (updatedRepo.key != originalKey) {
204+
onBack()
205+
} else {
206+
repository = updatedRepo
207+
loadData(refresh = true)
208+
}
209+
},
210+
)
211+
}
188212
}
189213
}
190214

@@ -381,3 +405,125 @@ private fun VirtualMembersCard(onClick: () -> Unit) {
381405
}
382406
}
383407
}
408+
409+
@Composable
410+
private fun EditRepositoryDialog(
411+
repository: Repository,
412+
onDismiss: () -> Unit,
413+
onSaved: (updatedRepo: Repository, originalKey: String) -> Unit,
414+
) {
415+
var key by remember { mutableStateOf(repository.key) }
416+
var name by remember { mutableStateOf(repository.name) }
417+
var description by remember { mutableStateOf(repository.description ?: "") }
418+
var isPublic by remember { mutableStateOf(repository.isPublic) }
419+
var isSaving by remember { mutableStateOf(false) }
420+
var errorMessage by remember { mutableStateOf<String?>(null) }
421+
val coroutineScope = rememberCoroutineScope()
422+
423+
AlertDialog(
424+
onDismissRequest = { if (!isSaving) onDismiss() },
425+
title = { Text("Edit Repository") },
426+
text = {
427+
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
428+
OutlinedTextField(
429+
value = key,
430+
onValueChange = { key = it.lowercase() },
431+
label = { Text("Key") },
432+
modifier = Modifier.fillMaxWidth(),
433+
singleLine = true,
434+
enabled = !isSaving,
435+
)
436+
437+
if (key != repository.key) {
438+
Text(
439+
text = "Changing the repository key will update all URLs. This may break existing client configurations.",
440+
style = MaterialTheme.typography.bodySmall,
441+
color = MaterialTheme.colorScheme.error,
442+
)
443+
}
444+
445+
OutlinedTextField(
446+
value = name,
447+
onValueChange = { name = it },
448+
label = { Text("Name") },
449+
modifier = Modifier.fillMaxWidth(),
450+
singleLine = true,
451+
enabled = !isSaving,
452+
)
453+
454+
OutlinedTextField(
455+
value = description,
456+
onValueChange = { description = it },
457+
label = { Text("Description") },
458+
modifier = Modifier.fillMaxWidth(),
459+
minLines = 2,
460+
maxLines = 4,
461+
enabled = !isSaving,
462+
)
463+
464+
Row(
465+
modifier = Modifier.fillMaxWidth(),
466+
horizontalArrangement = Arrangement.SpaceBetween,
467+
verticalAlignment = Alignment.CenterVertically,
468+
) {
469+
Text("Public", style = MaterialTheme.typography.bodyLarge)
470+
Switch(
471+
checked = isPublic,
472+
onCheckedChange = { isPublic = it },
473+
enabled = !isSaving,
474+
)
475+
}
476+
477+
if (errorMessage != null) {
478+
Text(
479+
text = errorMessage ?: "",
480+
style = MaterialTheme.typography.bodySmall,
481+
color = MaterialTheme.colorScheme.error,
482+
)
483+
}
484+
}
485+
},
486+
confirmButton = {
487+
TextButton(
488+
onClick = {
489+
coroutineScope.launch {
490+
isSaving = true
491+
errorMessage = null
492+
try {
493+
val request = UpdateRepositoryRequest(
494+
key = if (key != repository.key) key else null,
495+
name = name,
496+
description = description.ifBlank { null },
497+
isPublic = isPublic,
498+
)
499+
val updatedRepo = ApiClient.reposApi.updateRepository(
500+
repository.key,
501+
request,
502+
).unwrap()
503+
onSaved(updatedRepo, repository.key)
504+
} catch (e: Exception) {
505+
errorMessage = e.message ?: "Failed to update repository"
506+
} finally {
507+
isSaving = false
508+
}
509+
}
510+
},
511+
enabled = !isSaving,
512+
) {
513+
if (isSaving) {
514+
CircularProgressIndicator(modifier = Modifier.size(16.dp))
515+
} else {
516+
Text("Save")
517+
}
518+
}
519+
},
520+
dismissButton = {
521+
TextButton(
522+
onClick = onDismiss,
523+
enabled = !isSaving,
524+
) {
525+
Text("Cancel")
526+
}
527+
},
528+
)
529+
}

sdk/src/main/kotlin/com/artifactkeeper/client/models/UpdateRepositoryRequest.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,19 @@ import kotlinx.serialization.Contextual
2323
/**
2424
*
2525
*
26-
* @param description
27-
* @param isPublic
28-
* @param name
29-
* @param quotaBytes
26+
* @param key
27+
* @param description
28+
* @param isPublic
29+
* @param name
30+
* @param quotaBytes
3031
*/
3132
@Serializable
3233

3334
data class UpdateRepositoryRequest (
3435

36+
@SerialName(value = "key")
37+
val key: kotlin.String? = null,
38+
3539
@SerialName(value = "description")
3640
val description: kotlin.String? = null,
3741

0 commit comments

Comments
 (0)