Add settings page to manage tags
This commit is contained in:
+2
-1
@@ -10,6 +10,7 @@ import de.mm20.launcher2.icons.LauncherIcon
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import kotlin.coroutines.coroutineContext
|
||||
@@ -85,6 +86,6 @@ class CustomizeSearchableSheetVM(
|
||||
}
|
||||
|
||||
suspend fun autocompleteTags(query: String): List<String> {
|
||||
return customAttributesRepository.getAllTags(startsWith = query)
|
||||
return customAttributesRepository.getAllTags(startsWith = query).first()
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,7 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent {
|
||||
availableTags.value =
|
||||
customAttributesRepository
|
||||
.getAllTags()
|
||||
.first()
|
||||
.filter {t -> pinnedTags.none { it.tag == t } }
|
||||
.sortedBy { it.normalize() }
|
||||
.map { Tag(it) }
|
||||
|
||||
@@ -1,11 +1,265 @@
|
||||
package de.mm20.launcher2.ui.settings.tags
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Warning
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedCard
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.icons.LauncherIcon
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
|
||||
import de.mm20.launcher2.ui.component.SmallMessage
|
||||
import de.mm20.launcher2.ui.ktx.toPixels
|
||||
|
||||
@Composable
|
||||
fun EditTagSheet(
|
||||
tag: String?,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val viewModel: EditTagSheetVM = viewModel()
|
||||
|
||||
val isCreatingNewTag = tag == null
|
||||
|
||||
LaunchedEffect(tag) {
|
||||
viewModel.init(tag)
|
||||
}
|
||||
|
||||
if (viewModel.loading) return
|
||||
|
||||
BottomSheetDialog(
|
||||
title = {
|
||||
Text(
|
||||
stringResource(
|
||||
if (viewModel.page == EditTagSheetPage.CustomizeTag || !isCreatingNewTag) R.string.edit_tag_title
|
||||
else R.string.create_tag_title
|
||||
)
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
if (viewModel.page == EditTagSheetPage.CustomizeTag) {
|
||||
OutlinedButton(onClick = {
|
||||
viewModel.save()
|
||||
onDismiss()
|
||||
}) {
|
||||
Text(stringResource(R.string.close))
|
||||
}
|
||||
} else if (isCreatingNewTag) {
|
||||
Button(
|
||||
enabled = (viewModel.tagName.isNotBlank() && viewModel.page == EditTagSheetPage.CreateTag && !viewModel.tagNameExists)
|
||||
|| (viewModel.page == EditTagSheetPage.PickItems && viewModel.taggedItems.isNotEmpty()),
|
||||
onClick = { viewModel.onClickContinue() }) {
|
||||
Text(stringResource(R.string.action_next))
|
||||
}
|
||||
} else {
|
||||
OutlinedButton(onClick = { viewModel.closeItemPicker() }) {
|
||||
Text(stringResource(id = R.string.ok))
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
onDismissRequest = {
|
||||
if (viewModel.page == EditTagSheetPage.CustomizeTag) viewModel.save()
|
||||
onDismiss()
|
||||
},
|
||||
swipeToDismiss = {
|
||||
!(!isCreatingNewTag && viewModel.page == EditTagSheetPage.PickItems)
|
||||
},
|
||||
dismissOnBackPress = {
|
||||
!(!isCreatingNewTag && viewModel.page == EditTagSheetPage.PickItems)
|
||||
}
|
||||
) {
|
||||
when (viewModel.page) {
|
||||
EditTagSheetPage.CreateTag -> CreateNewTagPage(viewModel)
|
||||
EditTagSheetPage.PickItems -> PickItems(viewModel)
|
||||
EditTagSheetPage.CustomizeTag -> CustomizeTag(viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CreateNewTagPage(viewModel: EditTagSheetVM) {
|
||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
singleLine = true,
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = {
|
||||
viewModel.onClickContinue()
|
||||
}
|
||||
),
|
||||
isError = viewModel.tagNameExists,
|
||||
supportingText = { if (viewModel.tagNameExists) Text(stringResource(id = R.string.tag_exists_error)) },
|
||||
label = { Text(stringResource(R.string.tag_name)) },
|
||||
value = viewModel.tagName,
|
||||
onValueChange = { viewModel.tagName = it }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PickItems(viewModel: EditTagSheetVM) {
|
||||
LazyColumn(modifier = Modifier.fillMaxWidth()) {
|
||||
item {
|
||||
Text(stringResource(id = R.string.tag_select_items),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
items(viewModel.taggableApps) {
|
||||
val iconSize = 32.dp.toPixels()
|
||||
val icon by remember(it.item.key) {
|
||||
viewModel.getIcon(it.item, iconSize.toInt())
|
||||
}.collectAsState(null)
|
||||
ListItem(item = it, icon = icon, onTagChanged = { tagged ->
|
||||
if (tagged) viewModel.tagItem(it.item)
|
||||
else viewModel.untagItem(it.item)
|
||||
})
|
||||
}
|
||||
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 8.dp)
|
||||
.background(MaterialTheme.colorScheme.outlineVariant)
|
||||
.fillMaxWidth()
|
||||
.height(1.dp)
|
||||
)
|
||||
}
|
||||
|
||||
items(viewModel.taggableOther) {
|
||||
val iconSize = 32.dp.toPixels()
|
||||
val icon by remember(it.item.key) {
|
||||
viewModel.getIcon(it.item, iconSize.toInt())
|
||||
}.collectAsState(null)
|
||||
ListItem(item = it, icon = icon, onTagChanged = { tagged ->
|
||||
if (tagged) viewModel.tagItem(it.item)
|
||||
else viewModel.untagItem(it.item)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ListItem(
|
||||
item: TaggableItem,
|
||||
icon: LauncherIcon?,
|
||||
onTagChanged: (Boolean) -> Unit
|
||||
) {
|
||||
|
||||
OutlinedCard(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 4.dp)
|
||||
.fillMaxWidth(),
|
||||
onClick = {
|
||||
onTagChanged(!item.isTagged)
|
||||
}
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
ShapedLauncherIcon(icon = { icon }, size = 32.dp, modifier = Modifier.padding(4.dp))
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 12.dp)
|
||||
.weight(1f),
|
||||
text = item.item.label,
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
Checkbox(checked = item.isTagged, onCheckedChange = { checked ->
|
||||
onTagChanged(checked)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun CustomizeTag(viewModel: EditTagSheetVM) {
|
||||
val iconSize = 32.dp.toPixels()
|
||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
singleLine = true,
|
||||
label = { Text(stringResource(R.string.tag_name)) },
|
||||
value = viewModel.tagName,
|
||||
onValueChange = { viewModel.tagName = it }
|
||||
)
|
||||
val icon1 = remember(viewModel.taggedItems.getOrNull(0)?.key) {
|
||||
viewModel.taggedItems.getOrNull(0)?.let {
|
||||
viewModel.getIcon(it, iconSize.toInt())
|
||||
}
|
||||
}?.collectAsState(null)
|
||||
val icon2 = remember(viewModel.taggedItems.getOrNull(1)?.key) {
|
||||
viewModel.taggedItems.getOrNull(1)?.let {
|
||||
viewModel.getIcon(it, iconSize.toInt())
|
||||
}
|
||||
}?.collectAsState(null)
|
||||
val icon3 = remember(viewModel.taggedItems.getOrNull(2)?.key) {
|
||||
viewModel.taggedItems.getOrNull(2)?.let {
|
||||
viewModel.getIcon(it, iconSize.toInt())
|
||||
}
|
||||
}?.collectAsState(null)
|
||||
TextButton(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 16.dp).fillMaxWidth(),
|
||||
onClick = { viewModel.openItemPicker() }) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = pluralStringResource(
|
||||
R.plurals.tag_selected_items,
|
||||
viewModel.taggedItems.size,
|
||||
viewModel.taggedItems.size
|
||||
)
|
||||
)
|
||||
Box(modifier = Modifier.padding(start = 8.dp).width(64.dp).height(32.dp), contentAlignment = Alignment.CenterEnd) {
|
||||
ShapedLauncherIcon(size = 32.dp, icon = { icon1?.value }, modifier = Modifier.offset(x = -0.dp))
|
||||
ShapedLauncherIcon(size = 32.dp, icon = { icon2?.value }, modifier = Modifier.offset(x = -16.dp))
|
||||
ShapedLauncherIcon(size = 32.dp, icon = { icon3?.value }, modifier = Modifier.offset(x = -32.dp))
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(viewModel.tagNameExists || viewModel.taggedItems.isEmpty()) {
|
||||
SmallMessage(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
icon = Icons.Rounded.Warning,
|
||||
text = stringResource(
|
||||
if (viewModel.taggedItems.isEmpty()) R.string.tag_no_items_message else R.string.tag_exists_message
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package de.mm20.launcher2.ui.settings.tags
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.applications.AppRepository
|
||||
import de.mm20.launcher2.icons.IconRepository
|
||||
import de.mm20.launcher2.icons.LauncherIcon
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.search.SearchService
|
||||
import de.mm20.launcher2.services.tags.TagsService
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class EditTagSheetVM : ViewModel(), KoinComponent {
|
||||
|
||||
private val tagService: TagsService by inject()
|
||||
private val searchService: SearchService by inject()
|
||||
private val iconRepository: IconRepository by inject()
|
||||
private val appRepository: AppRepository by inject()
|
||||
|
||||
private var oldTagName by mutableStateOf<String?>(null)
|
||||
private var allTags by mutableStateOf(emptySet<String>())
|
||||
var tagName by mutableStateOf("")
|
||||
|
||||
var loading by mutableStateOf(true)
|
||||
|
||||
var page by mutableStateOf(EditTagSheetPage.CreateTag)
|
||||
|
||||
var taggedItems by mutableStateOf(emptyList<SavableSearchable>())
|
||||
var taggableApps by mutableStateOf(emptyList<TaggableItem>())
|
||||
var taggableOther by mutableStateOf(emptyList<TaggableItem>())
|
||||
|
||||
val tagNameExists by derivedStateOf {
|
||||
tagName != oldTagName && allTags.contains(tagName)
|
||||
}
|
||||
|
||||
|
||||
fun init(tag: String?) {
|
||||
Log.d("MM20", "Init with tag: $tag")
|
||||
loading = true
|
||||
this.oldTagName = tag
|
||||
this.tagName = tag ?: ""
|
||||
this.page = if (tag == null) EditTagSheetPage.CreateTag else EditTagSheetPage.CustomizeTag
|
||||
this.taggedItems = emptyList()
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
allTags = tagService.getAllTags().first().toSet()
|
||||
val items = if (tag != null) tagService.getTaggedItems(tag).first() else emptyList()
|
||||
val apps = appRepository.getAllInstalledApps().first().sorted()
|
||||
taggedItems = items
|
||||
taggableApps = apps.map { app -> TaggableItem(app, items.any { app.key == it.key }) }
|
||||
taggableOther = items.mapNotNull { item ->
|
||||
if (apps.any { item.key == it.key }) null
|
||||
else TaggableItem(item, true)
|
||||
}.sortedBy { it.item }
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
fun save() {
|
||||
val oldName = oldTagName
|
||||
val newName = tagName
|
||||
if (taggedItems.isEmpty() && oldName != null) tagService.deleteTag(oldName)
|
||||
else if (oldName != null) tagService.updateTag(oldName, newName = newName, items = taggedItems)
|
||||
else tagService.createTag(tagName, taggedItems)
|
||||
loading = true
|
||||
}
|
||||
|
||||
fun onClickContinue() {
|
||||
if (page == EditTagSheetPage.CreateTag && tagNameExists) return
|
||||
page = if (page == EditTagSheetPage.CreateTag) EditTagSheetPage.PickItems else EditTagSheetPage.CustomizeTag
|
||||
oldTagName = tagName
|
||||
}
|
||||
|
||||
fun getIcon(item: SavableSearchable, size: Int): Flow<LauncherIcon> {
|
||||
return iconRepository.getIcon(item, size)
|
||||
}
|
||||
|
||||
fun openItemPicker() {
|
||||
page = EditTagSheetPage.PickItems
|
||||
}
|
||||
|
||||
fun closeItemPicker() {
|
||||
page = EditTagSheetPage.CustomizeTag
|
||||
}
|
||||
|
||||
fun tagItem(item: SavableSearchable) {
|
||||
taggedItems = taggedItems + item
|
||||
taggableApps =
|
||||
taggableApps.map { app -> app.copy(isTagged = taggedItems.any { it.key == app.item.key }) }
|
||||
taggableOther =
|
||||
taggableOther.map { oth -> oth.copy(isTagged = taggedItems.any { it.key == oth.item.key }) }
|
||||
}
|
||||
|
||||
fun untagItem(item: SavableSearchable) {
|
||||
taggedItems = taggedItems.filter { it.key != item.key }
|
||||
taggableApps =
|
||||
taggableApps.map { app -> app.copy(isTagged = taggedItems.any { it.key == app.item.key }) }
|
||||
taggableOther =
|
||||
taggableOther.map { oth -> oth.copy(isTagged = taggedItems.any { it.key == oth.item.key }) }
|
||||
}
|
||||
}
|
||||
|
||||
enum class EditTagSheetPage {
|
||||
CreateTag,
|
||||
PickItems,
|
||||
CustomizeTag,
|
||||
}
|
||||
|
||||
@Stable
|
||||
data class TaggableItem(val item: SavableSearchable, val isTagged: Boolean)
|
||||
@@ -2,12 +2,26 @@ package de.mm20.launcher2.ui.settings.tags
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.ContentCopy
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material.icons.rounded.MoreVert
|
||||
import androidx.compose.material.icons.rounded.Tag
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
@@ -16,27 +30,71 @@ import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
fun TagsSettingsScreen() {
|
||||
val viewModel: TagsSettingsScreenVM = viewModel()
|
||||
|
||||
LaunchedEffect(null) {
|
||||
viewModel.update()
|
||||
}
|
||||
val tags by remember { viewModel.tags }.collectAsState(emptyList())
|
||||
|
||||
PreferenceScreen(
|
||||
title = "Tags",
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(onClick = { /*TODO*/ }) {
|
||||
FloatingActionButton(onClick = { viewModel.createTag.value = true }) {
|
||||
Icon(Icons.Rounded.Add, null)
|
||||
}
|
||||
}
|
||||
) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
for (tag in viewModel.tags.value) {
|
||||
for (tag in tags) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
Preference(
|
||||
icon = Icons.Rounded.Tag,
|
||||
title = tag,
|
||||
onClick = {
|
||||
viewModel.editTag.value = tag
|
||||
},
|
||||
controls = {
|
||||
IconButton(onClick = { showMenu = true }) {
|
||||
Icon(Icons.Rounded.MoreVert, null)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.duplicate)) },
|
||||
leadingIcon = { Icon(Icons.Rounded.ContentCopy, null) },
|
||||
onClick = {
|
||||
viewModel.duplicateTag(tag)
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.menu_delete)) },
|
||||
leadingIcon = { Icon(Icons.Rounded.Delete, null) },
|
||||
onClick = {
|
||||
viewModel.deleteTag(tag)
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (viewModel.editTag.value != null) {
|
||||
EditTagSheet(
|
||||
tag = viewModel.editTag.value,
|
||||
onDismiss = {
|
||||
viewModel.editTag.value = null
|
||||
viewModel.createTag.value = false
|
||||
}
|
||||
)
|
||||
} else if(viewModel.createTag.value) {
|
||||
EditTagSheet(
|
||||
tag = null,
|
||||
onDismiss = {
|
||||
viewModel.createTag.value = false
|
||||
viewModel.editTag.value = null
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,35 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.data.customattrs.CustomAttributesRepository
|
||||
import de.mm20.launcher2.services.tags.TagsService
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class TagsSettingsScreenVM: ViewModel(), KoinComponent {
|
||||
private val customAttributesRepository: CustomAttributesRepository by inject()
|
||||
private val tagsService: TagsService by inject()
|
||||
|
||||
val tags = mutableStateOf(emptyList<String>())
|
||||
val tags = tagsService.getAllTags()
|
||||
|
||||
suspend fun update() {
|
||||
tags.value = customAttributesRepository.getAllTags()
|
||||
var editTag = mutableStateOf<String?>(null)
|
||||
var createTag = mutableStateOf(false)
|
||||
|
||||
fun duplicateTag(tag: String) {
|
||||
viewModelScope.launch {
|
||||
val allTags = tags.first()
|
||||
var i = 2
|
||||
var newName = "$tag ($i)"
|
||||
while(allTags.contains(newName)) {
|
||||
i++
|
||||
newName = "$tag ($i)"
|
||||
}
|
||||
tagsService.cloneTag(tag, newName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun deleteTag(tag: String) {
|
||||
tagsService.deleteTag(tag)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user