mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 07:29:14 +01:00
select a server on tap and refuse to delete the active one
This commit is contained in:
@@ -20,9 +20,6 @@ interface SourcesDao {
|
|||||||
@Query("SELECT * FROM sources WHERE id = :id")
|
@Query("SELECT * FROM sources WHERE id = :id")
|
||||||
suspend fun sourceOnce(id: Long): Source?
|
suspend fun sourceOnce(id: Long): Source?
|
||||||
|
|
||||||
@Query("SELECT COUNT(*) FROM sources")
|
|
||||||
suspend fun sourceCount(): Int
|
|
||||||
|
|
||||||
@Query("SELECT id FROM sources WHERE isActive = 1 LIMIT 1")
|
@Query("SELECT id FROM sources WHERE isActive = 1 LIMIT 1")
|
||||||
fun activeSourceId(): Flow<Long?>
|
fun activeSourceId(): Flow<Long?>
|
||||||
|
|
||||||
@@ -32,9 +29,6 @@ interface SourcesDao {
|
|||||||
@Query("SELECT id FROM sources WHERE isActive = 1 LIMIT 1")
|
@Query("SELECT id FROM sources WHERE isActive = 1 LIMIT 1")
|
||||||
suspend fun activeSourceIdOnce(): Long?
|
suspend fun activeSourceIdOnce(): Long?
|
||||||
|
|
||||||
@Query("SELECT id FROM sources ORDER BY createdAt LIMIT 1")
|
|
||||||
suspend fun firstSourceId(): Long?
|
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insertSource(source: Source): Long
|
suspend fun insertSource(source: Source): Long
|
||||||
|
|
||||||
|
|||||||
@@ -175,13 +175,8 @@ class SourceRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteSource(id: Long): Boolean {
|
suspend fun deleteSource(id: Long): Boolean {
|
||||||
val dao = db.sourcesDao()
|
if (db.sourcesDao().activeSourceIdOnce() == id) return false
|
||||||
if (dao.sourceCount() <= 1) return false
|
db.sourcesDao().deleteSource(id)
|
||||||
val wasActive = dao.activeSourceIdOnce() == id
|
|
||||||
dao.deleteSource(id)
|
|
||||||
if (wasActive) {
|
|
||||||
dao.firstSourceId()?.let { dao.setActiveSource(it) }
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ fun AddSourceScreen(
|
|||||||
)
|
)
|
||||||
if (state.isEditing && !state.canDelete) {
|
if (state.isEditing && !state.canDelete) {
|
||||||
Text(
|
Text(
|
||||||
"This is the only server. Add another before you can remove this one.",
|
"This is the active server. Switch to another server before deleting it.",
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ class AddSourceViewModel(
|
|||||||
}
|
}
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
sourceRepository.sources().collect { sources ->
|
sourceRepository.sources().collect { sources ->
|
||||||
_state.update { it.copy(canDelete = sources.size > 1) }
|
val active = sources.any { it.id == sourceId && it.isActive }
|
||||||
|
_state.update { it.copy(canDelete = !active) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,7 +146,7 @@ class AddSourceViewModel(
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
if (!sourceRepository.deleteSource(id)) {
|
if (!sourceRepository.deleteSource(id)) {
|
||||||
_state.update {
|
_state.update {
|
||||||
it.copy(busy = false, message = "At least one server is required", isError = true)
|
it.copy(busy = false, message = "The active server can't be deleted; switch to another server first", isError = true)
|
||||||
}
|
}
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
||||||
import androidx.compose.material.icons.automirrored.rounded.KeyboardArrowRight
|
|
||||||
import androidx.compose.material.icons.rounded.Add
|
import androidx.compose.material.icons.rounded.Add
|
||||||
|
import androidx.compose.material.icons.rounded.Edit
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@@ -140,9 +140,11 @@ fun SettingsScreen(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
Icon(Icons.AutoMirrored.Rounded.KeyboardArrowRight, contentDescription = null)
|
IconButton(onClick = { onEditServer(source.id) }) {
|
||||||
|
Icon(Icons.Rounded.Edit, contentDescription = "Edit ${source.name}")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.clickable(onClickLabel = "Edit ${source.name}") { onEditServer(source.id) },
|
modifier = Modifier.clickable(onClickLabel = "Use ${source.name}") { onSelectSource(source.id) },
|
||||||
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
|
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,19 +105,21 @@ class SourceRepositoryTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun theLastSourceCannotBeDeleted() =
|
fun theActiveSourceCannotBeDeleted() =
|
||||||
runTest {
|
runTest {
|
||||||
repository.addSource("only", "http://a.example", "u", "p", true)
|
repository.addSource("first", "http://a.example", "u", "p", true)
|
||||||
val id = repository.activeSourceId().first()!!
|
val firstId = repository.activeSourceId().first()!!
|
||||||
|
|
||||||
assertFalse("the last source must survive deletion", repository.deleteSource(id))
|
|
||||||
assertEquals(1, repository.sources().first().size)
|
|
||||||
|
|
||||||
repository.addSource("second", "http://b.example", "u", "p", true)
|
repository.addSource("second", "http://b.example", "u", "p", true)
|
||||||
|
val secondId = repository.activeSourceId().first()!!
|
||||||
|
|
||||||
assertTrue(repository.deleteSource(id))
|
assertFalse("the active source must survive deletion", repository.deleteSource(secondId))
|
||||||
assertEquals(listOf("second"), repository.sources().first().map { it.name })
|
assertEquals(2, repository.sources().first().size)
|
||||||
assertEquals("second", repository.activeConfig().first()?.name)
|
|
||||||
|
repository.selectSource(firstId)
|
||||||
|
|
||||||
|
assertTrue(repository.deleteSource(secondId))
|
||||||
|
assertEquals(listOf("first"), repository.sources().first().map { it.name })
|
||||||
|
assertEquals("first", repository.activeConfig().first()?.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user