feat: Add a wordlist from a URL #88
This commit is contained in:
parent
e6d74bd143
commit
998c40dfc0
|
@ -9,6 +9,10 @@ data class AddWordlistRequest(
|
||||||
val uri: String,
|
val uri: String,
|
||||||
) : Wordlist
|
) : Wordlist
|
||||||
|
|
||||||
|
data class FromUrl(
|
||||||
|
val url: String,
|
||||||
|
) : Wordlist
|
||||||
|
|
||||||
data class FromList(
|
data class FromList(
|
||||||
val list: List<String>,
|
val list: List<String>,
|
||||||
) : Wordlist
|
) : Wordlist
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.artemchep.keyguard.common.usecase
|
||||||
|
|
||||||
|
import com.artemchep.keyguard.common.io.IO
|
||||||
|
|
||||||
|
interface ReadWordlistFromUrl : (String) -> IO<List<String>>
|
|
@ -6,16 +6,19 @@ import com.artemchep.keyguard.common.model.AddWordlistRequest
|
||||||
import com.artemchep.keyguard.common.service.wordlist.repo.GeneratorWordlistRepository
|
import com.artemchep.keyguard.common.service.wordlist.repo.GeneratorWordlistRepository
|
||||||
import com.artemchep.keyguard.common.usecase.AddWordlist
|
import com.artemchep.keyguard.common.usecase.AddWordlist
|
||||||
import com.artemchep.keyguard.common.usecase.ReadWordlistFromFile
|
import com.artemchep.keyguard.common.usecase.ReadWordlistFromFile
|
||||||
|
import com.artemchep.keyguard.common.usecase.ReadWordlistFromUrl
|
||||||
import org.kodein.di.DirectDI
|
import org.kodein.di.DirectDI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
|
|
||||||
class AddWordlistImpl(
|
class AddWordlistImpl(
|
||||||
private val generatorWordlistRepository: GeneratorWordlistRepository,
|
private val generatorWordlistRepository: GeneratorWordlistRepository,
|
||||||
private val readWordlistFromFile: ReadWordlistFromFile,
|
private val readWordlistFromFile: ReadWordlistFromFile,
|
||||||
|
private val readWordlistFromUrl: ReadWordlistFromUrl,
|
||||||
) : AddWordlist {
|
) : AddWordlist {
|
||||||
constructor(directDI: DirectDI) : this(
|
constructor(directDI: DirectDI) : this(
|
||||||
generatorWordlistRepository = directDI.instance(),
|
generatorWordlistRepository = directDI.instance(),
|
||||||
readWordlistFromFile = directDI.instance(),
|
readWordlistFromFile = directDI.instance(),
|
||||||
|
readWordlistFromUrl = directDI.instance(),
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun invoke(
|
override fun invoke(
|
||||||
|
@ -29,8 +32,18 @@ class AddWordlistImpl(
|
||||||
.bind()
|
.bind()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is AddWordlistRequest.Wordlist.FromUrl -> {
|
||||||
|
val uri = model.wordlist.url
|
||||||
|
readWordlistFromUrl(uri)
|
||||||
|
.bind()
|
||||||
|
}
|
||||||
|
|
||||||
is AddWordlistRequest.Wordlist.FromList -> model.wordlist.list
|
is AddWordlistRequest.Wordlist.FromList -> model.wordlist.list
|
||||||
}
|
}
|
||||||
|
val invalidWordlist = wordlist.any { it.length > 512 }
|
||||||
|
if (invalidWordlist) {
|
||||||
|
throw IllegalStateException("Failed to parse the wordlist!")
|
||||||
|
}
|
||||||
generatorWordlistRepository
|
generatorWordlistRepository
|
||||||
.post(
|
.post(
|
||||||
name = name,
|
name = name,
|
||||||
|
|
|
@ -5,7 +5,6 @@ import com.artemchep.keyguard.common.io.ioEffect
|
||||||
import com.artemchep.keyguard.common.service.text.TextService
|
import com.artemchep.keyguard.common.service.text.TextService
|
||||||
import com.artemchep.keyguard.common.service.text.readFromFileAsText
|
import com.artemchep.keyguard.common.service.text.readFromFileAsText
|
||||||
import com.artemchep.keyguard.common.usecase.ReadWordlistFromFile
|
import com.artemchep.keyguard.common.usecase.ReadWordlistFromFile
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
|
||||||
import org.kodein.di.DirectDI
|
import org.kodein.di.DirectDI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
|
|
||||||
|
@ -20,15 +19,8 @@ class ReadWordlistFromFileImpl(
|
||||||
uri: String,
|
uri: String,
|
||||||
): IO<List<String>> = ioEffect {
|
): IO<List<String>> = ioEffect {
|
||||||
val content = textService.readFromFileAsText(uri)
|
val content = textService.readFromFileAsText(uri)
|
||||||
content
|
with(content) {
|
||||||
.lineSequence()
|
ReadWordlistUtil.parseAsWordlist()
|
||||||
.filter {
|
}
|
||||||
it.isNotBlank() &&
|
|
||||||
!it.startsWith('#') &&
|
|
||||||
!it.startsWith(';') &&
|
|
||||||
!it.startsWith('-') &&
|
|
||||||
!it.startsWith('/')
|
|
||||||
}
|
|
||||||
.toImmutableList()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.artemchep.keyguard.common.usecase.impl
|
||||||
|
|
||||||
|
import com.artemchep.keyguard.common.io.IO
|
||||||
|
import com.artemchep.keyguard.common.io.ioEffect
|
||||||
|
import com.artemchep.keyguard.common.usecase.ReadWordlistFromUrl
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import io.ktor.client.statement.*
|
||||||
|
import org.kodein.di.DirectDI
|
||||||
|
import org.kodein.di.instance
|
||||||
|
|
||||||
|
class ReadWordlistFromUrlImpl(
|
||||||
|
private val httpClient: HttpClient,
|
||||||
|
) : ReadWordlistFromUrl {
|
||||||
|
constructor(directDI: DirectDI) : this(
|
||||||
|
httpClient = directDI.instance("curl"),
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun invoke(
|
||||||
|
url: String,
|
||||||
|
): IO<List<String>> = ioEffect {
|
||||||
|
val request = httpClient
|
||||||
|
.get(url)
|
||||||
|
val content = request
|
||||||
|
.bodyAsText()
|
||||||
|
with(content) {
|
||||||
|
ReadWordlistUtil.parseAsWordlist()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.artemchep.keyguard.common.usecase.impl
|
||||||
|
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
|
object ReadWordlistUtil {
|
||||||
|
context(String)
|
||||||
|
fun parseAsWordlist(): ImmutableList<String> = lineSequence()
|
||||||
|
.filter {
|
||||||
|
it.isNotBlank() &&
|
||||||
|
!it.startsWith('#') &&
|
||||||
|
!it.startsWith(';') &&
|
||||||
|
!it.startsWith('-') &&
|
||||||
|
!it.startsWith('/')
|
||||||
|
}
|
||||||
|
.toImmutableList()
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
@ -17,6 +18,7 @@ import androidx.compose.material.icons.outlined.Add
|
||||||
import androidx.compose.material.icons.outlined.Book
|
import androidx.compose.material.icons.outlined.Book
|
||||||
import androidx.compose.material.icons.outlined.HelpOutline
|
import androidx.compose.material.icons.outlined.HelpOutline
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
@ -28,8 +30,10 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
@ -53,6 +57,9 @@ import com.artemchep.keyguard.res.Res
|
||||||
import com.artemchep.keyguard.ui.AvatarBuilder
|
import com.artemchep.keyguard.ui.AvatarBuilder
|
||||||
import com.artemchep.keyguard.ui.DefaultFab
|
import com.artemchep.keyguard.ui.DefaultFab
|
||||||
import com.artemchep.keyguard.ui.DefaultSelection
|
import com.artemchep.keyguard.ui.DefaultSelection
|
||||||
|
import com.artemchep.keyguard.ui.DropdownMenuItemFlat
|
||||||
|
import com.artemchep.keyguard.ui.DropdownMinWidth
|
||||||
|
import com.artemchep.keyguard.ui.DropdownScopeImpl
|
||||||
import com.artemchep.keyguard.ui.FabState
|
import com.artemchep.keyguard.ui.FabState
|
||||||
import com.artemchep.keyguard.ui.FlatItemLayout
|
import com.artemchep.keyguard.ui.FlatItemLayout
|
||||||
import com.artemchep.keyguard.ui.FlatItemTextContent
|
import com.artemchep.keyguard.ui.FlatItemTextContent
|
||||||
|
@ -108,6 +115,9 @@ fun WordlistListScreen(
|
||||||
listState.scrollToItem(0, 0)
|
listState.scrollToItem(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val primaryActionsDropdownVisibleState = remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
ScaffoldLazyColumn(
|
ScaffoldLazyColumn(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
@ -147,8 +157,15 @@ fun WordlistListScreen(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionState = run {
|
floatingActionState = run {
|
||||||
val onClick =
|
val actions = loadableState.getOrNull()?.content?.getOrNull()?.getOrNull()?.primaryActions.orEmpty()
|
||||||
loadableState.getOrNull()?.content?.getOrNull()?.getOrNull()?.primaryAction
|
val onClick = if (actions.isNotEmpty()) {
|
||||||
|
// lambda
|
||||||
|
{
|
||||||
|
primaryActionsDropdownVisibleState.value = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
val state = FabState(
|
val state = FabState(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
model = null,
|
model = null,
|
||||||
|
@ -159,6 +176,31 @@ fun WordlistListScreen(
|
||||||
DefaultFab(
|
DefaultFab(
|
||||||
icon = {
|
icon = {
|
||||||
IconBox(main = Icons.Outlined.Add)
|
IconBox(main = Icons.Outlined.Add)
|
||||||
|
|
||||||
|
// Inject the dropdown popup to the bottom of the
|
||||||
|
// content.
|
||||||
|
val onDismissRequest = remember(primaryActionsDropdownVisibleState) {
|
||||||
|
// lambda
|
||||||
|
{
|
||||||
|
primaryActionsDropdownVisibleState.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DropdownMenu(
|
||||||
|
modifier = Modifier
|
||||||
|
.widthIn(min = DropdownMinWidth),
|
||||||
|
expanded = primaryActionsDropdownVisibleState.value,
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
) {
|
||||||
|
val actions = loadableState.getOrNull()?.content?.getOrNull()?.getOrNull()?.primaryActions.orEmpty()
|
||||||
|
val scope = DropdownScopeImpl(this, onDismissRequest = onDismissRequest)
|
||||||
|
with(scope) {
|
||||||
|
actions.forEachIndexed { index, action ->
|
||||||
|
DropdownMenuItemFlat(
|
||||||
|
action = action,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
text = {
|
text = {
|
||||||
Text(
|
Text(
|
||||||
|
|
|
@ -21,7 +21,7 @@ data class WordlistListState(
|
||||||
val revision: Int,
|
val revision: Int,
|
||||||
val items: ImmutableList<Item>,
|
val items: ImmutableList<Item>,
|
||||||
val selection: Selection?,
|
val selection: Selection?,
|
||||||
val primaryAction: (() -> Unit)?,
|
val primaryActions: ImmutableList<ContextItem>,
|
||||||
) {
|
) {
|
||||||
companion object
|
companion object
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
package com.artemchep.keyguard.feature.generator.wordlist.list
|
package com.artemchep.keyguard.feature.generator.wordlist.list
|
||||||
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Add
|
import androidx.compose.material.icons.outlined.AddLink
|
||||||
|
import androidx.compose.material.icons.outlined.AttachFile
|
||||||
import androidx.compose.material.icons.outlined.Delete
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
import androidx.compose.material.icons.outlined.Edit
|
import androidx.compose.material.icons.outlined.Edit
|
||||||
import androidx.compose.material.icons.outlined.ViewList
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import arrow.core.partially1
|
import arrow.core.partially1
|
||||||
import com.artemchep.keyguard.common.io.launchIn
|
|
||||||
import com.artemchep.keyguard.common.model.AddWordlistRequest
|
|
||||||
import com.artemchep.keyguard.common.model.DGeneratorWordlist
|
import com.artemchep.keyguard.common.model.DGeneratorWordlist
|
||||||
import com.artemchep.keyguard.common.model.EditWordlistRequest
|
|
||||||
import com.artemchep.keyguard.common.model.Loadable
|
import com.artemchep.keyguard.common.model.Loadable
|
||||||
import com.artemchep.keyguard.common.usecase.AddWordlist
|
import com.artemchep.keyguard.common.usecase.AddWordlist
|
||||||
import com.artemchep.keyguard.common.usecase.EditWordlist
|
import com.artemchep.keyguard.common.usecase.EditWordlist
|
||||||
|
@ -20,22 +17,18 @@ import com.artemchep.keyguard.common.usecase.RemoveWordlistById
|
||||||
import com.artemchep.keyguard.common.util.flow.persistingStateIn
|
import com.artemchep.keyguard.common.util.flow.persistingStateIn
|
||||||
import com.artemchep.keyguard.feature.attachments.SelectableItemState
|
import com.artemchep.keyguard.feature.attachments.SelectableItemState
|
||||||
import com.artemchep.keyguard.feature.attachments.SelectableItemStateRaw
|
import com.artemchep.keyguard.feature.attachments.SelectableItemStateRaw
|
||||||
import com.artemchep.keyguard.feature.confirmation.ConfirmationResult
|
|
||||||
import com.artemchep.keyguard.feature.confirmation.ConfirmationRoute
|
|
||||||
import com.artemchep.keyguard.feature.confirmation.createConfirmationDialogIntent
|
|
||||||
import com.artemchep.keyguard.feature.crashlytics.crashlyticsAttempt
|
import com.artemchep.keyguard.feature.crashlytics.crashlyticsAttempt
|
||||||
import com.artemchep.keyguard.feature.generator.wordlist.WordlistsRoute
|
import com.artemchep.keyguard.feature.generator.wordlist.WordlistsRoute
|
||||||
import com.artemchep.keyguard.feature.generator.wordlist.util.WordlistUtil
|
import com.artemchep.keyguard.feature.generator.wordlist.util.WordlistUtil
|
||||||
import com.artemchep.keyguard.feature.generator.wordlist.view.WordlistViewRoute
|
import com.artemchep.keyguard.feature.generator.wordlist.view.WordlistViewRoute
|
||||||
import com.artemchep.keyguard.feature.home.vault.model.VaultItemIcon
|
import com.artemchep.keyguard.feature.home.vault.model.VaultItemIcon
|
||||||
import com.artemchep.keyguard.feature.navigation.NavigationIntent
|
import com.artemchep.keyguard.feature.navigation.NavigationIntent
|
||||||
import com.artemchep.keyguard.feature.navigation.registerRouteResultReceiver
|
|
||||||
import com.artemchep.keyguard.feature.navigation.state.produceScreenState
|
import com.artemchep.keyguard.feature.navigation.state.produceScreenState
|
||||||
import com.artemchep.keyguard.res.Res
|
import com.artemchep.keyguard.res.Res
|
||||||
import com.artemchep.keyguard.ui.FlatItemAction
|
import com.artemchep.keyguard.ui.FlatItemAction
|
||||||
import com.artemchep.keyguard.ui.Selection
|
import com.artemchep.keyguard.ui.Selection
|
||||||
import com.artemchep.keyguard.ui.buildContextItems
|
import com.artemchep.keyguard.ui.buildContextItems
|
||||||
import com.artemchep.keyguard.ui.icons.KeyguardWordlist
|
import com.artemchep.keyguard.ui.icons.KeyguardWebsite
|
||||||
import com.artemchep.keyguard.ui.icons.icon
|
import com.artemchep.keyguard.ui.icons.icon
|
||||||
import com.artemchep.keyguard.ui.selection.selectionHandle
|
import com.artemchep.keyguard.ui.selection.selectionHandle
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
@ -250,6 +243,24 @@ fun produceWordlistListState(
|
||||||
cause = e,
|
cause = e,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val primaryActions = buildContextItems {
|
||||||
|
section {
|
||||||
|
this += FlatItemAction(
|
||||||
|
leading = icon(Icons.Outlined.AttachFile),
|
||||||
|
title = translate(Res.strings.wordlist_add_wordlist_via_file_title),
|
||||||
|
onClick = WordlistUtil::onNewFromFile
|
||||||
|
.partially1(this@produceScreenState)
|
||||||
|
.partially1(addWordlist),
|
||||||
|
)
|
||||||
|
this += FlatItemAction(
|
||||||
|
leading = icon(Icons.Outlined.KeyguardWebsite),
|
||||||
|
title = translate(Res.strings.wordlist_add_wordlist_via_url_title),
|
||||||
|
onClick = WordlistUtil::onNewFromUrl
|
||||||
|
.partially1(this@produceScreenState)
|
||||||
|
.partially1(addWordlist),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
val contentFlow = combine(
|
val contentFlow = combine(
|
||||||
selectionFlow,
|
selectionFlow,
|
||||||
itemsFlow,
|
itemsFlow,
|
||||||
|
@ -260,9 +271,7 @@ fun produceWordlistListState(
|
||||||
revision = 0,
|
revision = 0,
|
||||||
items = items,
|
items = items,
|
||||||
selection = selection,
|
selection = selection,
|
||||||
primaryAction = WordlistUtil::onNew
|
primaryActions = primaryActions,
|
||||||
.partially1(this@produceScreenState)
|
|
||||||
.partially1(addWordlist),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Loadable.Ok(contentOrException)
|
Loadable.Ok(contentOrException)
|
||||||
|
|
|
@ -69,7 +69,7 @@ object WordlistUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
context(RememberStateFlowScope)
|
context(RememberStateFlowScope)
|
||||||
fun onNew(
|
fun onNewFromFile(
|
||||||
addWordlist: AddWordlist,
|
addWordlist: AddWordlist,
|
||||||
) {
|
) {
|
||||||
val nameKey = "name"
|
val nameKey = "name"
|
||||||
|
@ -126,6 +126,66 @@ object WordlistUtil {
|
||||||
navigate(intent)
|
navigate(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context(RememberStateFlowScope)
|
||||||
|
fun onNewFromUrl(
|
||||||
|
addWordlist: AddWordlist,
|
||||||
|
) {
|
||||||
|
val nameKey = "name"
|
||||||
|
val nameItem = ConfirmationRoute.Args.Item.StringItem(
|
||||||
|
key = nameKey,
|
||||||
|
value = "",
|
||||||
|
title = translate(Res.strings.generic_name),
|
||||||
|
type = ConfirmationRoute.Args.Item.StringItem.Type.Text,
|
||||||
|
canBeEmpty = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
val urlKey = "url"
|
||||||
|
val urlItem = ConfirmationRoute.Args.Item.StringItem(
|
||||||
|
key = urlKey,
|
||||||
|
value = "",
|
||||||
|
title = translate(Res.strings.url),
|
||||||
|
type = ConfirmationRoute.Args.Item.StringItem.Type.Command,
|
||||||
|
canBeEmpty = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
val items = listOfNotNull(
|
||||||
|
nameItem,
|
||||||
|
urlItem,
|
||||||
|
)
|
||||||
|
val route = registerRouteResultReceiver(
|
||||||
|
route = ConfirmationRoute(
|
||||||
|
args = ConfirmationRoute.Args(
|
||||||
|
icon = icon(
|
||||||
|
main = Icons.Outlined.KeyguardWordlist,
|
||||||
|
secondary = Icons.Outlined.Add,
|
||||||
|
),
|
||||||
|
title = translate(Res.strings.wordlist_add_wordlist_title),
|
||||||
|
items = items,
|
||||||
|
docUrl = null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) { result ->
|
||||||
|
if (result is ConfirmationResult.Confirm) {
|
||||||
|
val name = result.data[nameKey] as? String
|
||||||
|
?: return@registerRouteResultReceiver
|
||||||
|
val url = result.data[urlKey] as? String
|
||||||
|
?: return@registerRouteResultReceiver
|
||||||
|
|
||||||
|
val wordlist = AddWordlistRequest.Wordlist.FromUrl(
|
||||||
|
url = url,
|
||||||
|
)
|
||||||
|
val request = AddWordlistRequest(
|
||||||
|
name = name,
|
||||||
|
wordlist = wordlist,
|
||||||
|
)
|
||||||
|
addWordlist(request)
|
||||||
|
.launchIn(appScope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val intent = NavigationIntent.NavigateToRoute(route)
|
||||||
|
navigate(intent)
|
||||||
|
}
|
||||||
|
|
||||||
context(RememberStateFlowScope)
|
context(RememberStateFlowScope)
|
||||||
fun onDeleteByItems(
|
fun onDeleteByItems(
|
||||||
removeWordlistById: RemoveWordlistById,
|
removeWordlistById: RemoveWordlistById,
|
||||||
|
|
|
@ -520,6 +520,8 @@
|
||||||
<string name="wordlist_delete_many_confirmation_title">Delete the wordlists?</string>
|
<string name="wordlist_delete_many_confirmation_title">Delete the wordlists?</string>
|
||||||
<string name="wordlist_edit_wordlist_title">Edit a wordlist</string>
|
<string name="wordlist_edit_wordlist_title">Edit a wordlist</string>
|
||||||
<string name="wordlist_add_wordlist_title">Add a wordlist</string>
|
<string name="wordlist_add_wordlist_title">Add a wordlist</string>
|
||||||
|
<string name="wordlist_add_wordlist_via_file_title">Load from a file</string>
|
||||||
|
<string name="wordlist_add_wordlist_via_url_title">Load from a URL</string>
|
||||||
<string name="wordlist_empty_label">No wordlists</string>
|
<string name="wordlist_empty_label">No wordlists</string>
|
||||||
<string name="wordlist_word_search_placeholder">Search words</string>
|
<string name="wordlist_word_search_placeholder">Search words</string>
|
||||||
|
|
||||||
|
|
|
@ -187,6 +187,7 @@ import com.artemchep.keyguard.common.usecase.PutVaultSession
|
||||||
import com.artemchep.keyguard.common.usecase.PutWebsiteIcons
|
import com.artemchep.keyguard.common.usecase.PutWebsiteIcons
|
||||||
import com.artemchep.keyguard.common.usecase.PutWriteAccess
|
import com.artemchep.keyguard.common.usecase.PutWriteAccess
|
||||||
import com.artemchep.keyguard.common.usecase.ReadWordlistFromFile
|
import com.artemchep.keyguard.common.usecase.ReadWordlistFromFile
|
||||||
|
import com.artemchep.keyguard.common.usecase.ReadWordlistFromUrl
|
||||||
import com.artemchep.keyguard.common.usecase.RemoveAttachment
|
import com.artemchep.keyguard.common.usecase.RemoveAttachment
|
||||||
import com.artemchep.keyguard.common.usecase.RequestAppReview
|
import com.artemchep.keyguard.common.usecase.RequestAppReview
|
||||||
import com.artemchep.keyguard.common.usecase.ShowMessage
|
import com.artemchep.keyguard.common.usecase.ShowMessage
|
||||||
|
@ -303,6 +304,7 @@ import com.artemchep.keyguard.common.usecase.impl.PutVaultSessionImpl
|
||||||
import com.artemchep.keyguard.common.usecase.impl.PutWebsiteIconsImpl
|
import com.artemchep.keyguard.common.usecase.impl.PutWebsiteIconsImpl
|
||||||
import com.artemchep.keyguard.common.usecase.impl.PutWriteAccessImpl
|
import com.artemchep.keyguard.common.usecase.impl.PutWriteAccessImpl
|
||||||
import com.artemchep.keyguard.common.usecase.impl.ReadWordlistFromFileImpl
|
import com.artemchep.keyguard.common.usecase.impl.ReadWordlistFromFileImpl
|
||||||
|
import com.artemchep.keyguard.common.usecase.impl.ReadWordlistFromUrlImpl
|
||||||
import com.artemchep.keyguard.common.usecase.impl.RemoveAttachmentImpl
|
import com.artemchep.keyguard.common.usecase.impl.RemoveAttachmentImpl
|
||||||
import com.artemchep.keyguard.common.usecase.impl.RequestAppReviewImpl
|
import com.artemchep.keyguard.common.usecase.impl.RequestAppReviewImpl
|
||||||
import com.artemchep.keyguard.common.usecase.impl.UnlockUseCaseImpl
|
import com.artemchep.keyguard.common.usecase.impl.UnlockUseCaseImpl
|
||||||
|
@ -846,6 +848,11 @@ fun globalModuleJvm() = DI.Module(
|
||||||
directDI = this,
|
directDI = this,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
bindSingleton<ReadWordlistFromUrl> {
|
||||||
|
ReadWordlistFromUrlImpl(
|
||||||
|
directDI = this,
|
||||||
|
)
|
||||||
|
}
|
||||||
bindSingleton<PutWriteAccess> {
|
bindSingleton<PutWriteAccess> {
|
||||||
PutWriteAccessImpl(
|
PutWriteAccessImpl(
|
||||||
directDI = this,
|
directDI = this,
|
||||||
|
|
Loading…
Reference in New Issue