Merge branch 'master' into master-ghc8107

This commit is contained in:
Evgeny Poberezkin 2023-12-02 12:30:36 +00:00
commit f8a469488e
11 changed files with 222 additions and 105 deletions

View File

@ -1426,9 +1426,9 @@ object ChatController {
chatModel.remoteHosts.addAll(hosts)
}
suspend fun startRemoteHost(rhId: Long?, multicast: Boolean = true): Triple<RemoteHostInfo?, String, String>? {
val r = sendCmd(null, CC.StartRemoteHost(rhId, multicast))
if (r is CR.RemoteHostStarted) return Triple(r.remoteHost_, r.invitation, r.ctrlPort)
suspend fun startRemoteHost(rhId: Long?, multicast: Boolean = true, address: RemoteCtrlAddress?, port: Int?): CR.RemoteHostStarted? {
val r = sendCmd(null, CC.StartRemoteHost(rhId, multicast, address, port))
if (r is CR.RemoteHostStarted) return r
apiErrorAlert("startRemoteHost", generalGetString(MR.strings.error_alert_title), r)
return null
}
@ -2248,7 +2248,7 @@ sealed class CC {
// Remote control
class SetLocalDeviceName(val displayName: String): CC()
class ListRemoteHosts(): CC()
class StartRemoteHost(val remoteHostId: Long?, val multicast: Boolean): CC()
class StartRemoteHost(val remoteHostId: Long?, val multicast: Boolean, val address: RemoteCtrlAddress?, val port: Int?): CC()
class SwitchRemoteHost (val remoteHostId: Long?): CC()
class StopRemoteHost(val remoteHostKey: Long?): CC()
class DeleteRemoteHost(val remoteHostId: Long): CC()
@ -2384,7 +2384,7 @@ sealed class CC {
is CancelFile -> "/fcancel $fileId"
is SetLocalDeviceName -> "/set device name $displayName"
is ListRemoteHosts -> "/list remote hosts"
is StartRemoteHost -> "/start remote host " + if (remoteHostId == null) "new" else "$remoteHostId multicast=${onOff(multicast)}"
is StartRemoteHost -> "/start remote host " + (if (remoteHostId == null) "new" else "$remoteHostId multicast=${onOff(multicast)}") + (if (address != null) " addr=${address.address} iface=${address.`interface`}" else "") + (if (port != null) " port=$port" else "")
is SwitchRemoteHost -> "/switch remote host " + if (remoteHostId == null) "local" else "$remoteHostId"
is StopRemoteHost -> "/stop remote host " + if (remoteHostKey == null) "new" else "$remoteHostKey"
is DeleteRemoteHost -> "/delete remote host $remoteHostId"
@ -3606,6 +3606,8 @@ data class RemoteHostInfo(
val remoteHostId: Long,
val hostDeviceName: String,
val storePath: String,
val bindAddress_: RemoteCtrlAddress?,
val bindPort_: Int?,
val sessionState: RemoteHostSessionState?
) {
val activeHost: Boolean
@ -3614,6 +3616,12 @@ data class RemoteHostInfo(
fun activeHost(): Boolean = chatModel.currentRemoteHost.value?.remoteHostId == remoteHostId
}
@Serializable
data class RemoteCtrlAddress(
val address: String,
val `interface`: String
)
@Serializable
sealed class RemoteHostSessionState {
@Serializable @SerialName("starting") object Starting: RemoteHostSessionState()
@ -3848,7 +3856,7 @@ sealed class CR {
// remote events (desktop)
@Serializable @SerialName("remoteHostList") class RemoteHostList(val remoteHosts: List<RemoteHostInfo>): CR()
@Serializable @SerialName("currentRemoteHost") class CurrentRemoteHost(val remoteHost_: RemoteHostInfo?): CR()
@Serializable @SerialName("remoteHostStarted") class RemoteHostStarted(val remoteHost_: RemoteHostInfo?, val invitation: String, val ctrlPort: String): CR()
@Serializable @SerialName("remoteHostStarted") class RemoteHostStarted(val remoteHost_: RemoteHostInfo?, val invitation: String, val localAddrs: List<RemoteCtrlAddress>, val ctrlPort: String): CR()
@Serializable @SerialName("remoteHostSessionCode") class RemoteHostSessionCode(val remoteHost_: RemoteHostInfo?, val sessionCode: String): CR()
@Serializable @SerialName("newRemoteHost") class NewRemoteHost(val remoteHost: RemoteHostInfo): CR()
@Serializable @SerialName("remoteHostConnected") class RemoteHostConnected(val remoteHost: RemoteHostInfo): CR()

View File

@ -152,7 +152,6 @@ fun DefaultConfigurableTextField(
BasicTextField(
value = state.value,
modifier = modifier
.fillMaxWidth()
.background(colors.backgroundColor(enabled).value, shape)
.indicatorLine(enabled, false, interactionSource, colors)
.defaultMinSize(

View File

@ -10,72 +10,87 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.*
import chat.simplex.res.MR
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.usersettings.SettingsActionItemWithContent
@Composable
fun <T> ExposedDropDownSetting(
values: List<Pair<T, String>>,
selection: State<T>,
textColor: Color = MaterialTheme.colors.secondary,
label: String? = null,
enabled: State<Boolean> = mutableStateOf(true),
minWidth: Dp = 200.dp,
maxWidth: Dp = with(LocalDensity.current) { 180.sp.toDp() },
onSelected: (T) -> Unit
) {
val expanded = remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded.value,
onExpandedChange = {
expanded.value = !expanded.value && enabled.value
}
) {
Row(
Modifier.padding(start = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
Text(
values.first { it.first == selection.value }.second + (if (label != null) " $label" else ""),
Modifier.widthIn(max = maxWidth),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = textColor
)
Spacer(Modifier.size(12.dp))
Icon(
if (!expanded.value) painterResource(MR.images.ic_expand_more) else painterResource(MR.images.ic_expand_less),
generalGetString(MR.strings.icon_descr_more_button),
tint = MaterialTheme.colors.secondary
)
}
DefaultExposedDropdownMenu(
modifier = Modifier.widthIn(min = minWidth),
expanded = expanded,
) {
values.forEach { selectionOption ->
DropdownMenuItem(
onClick = {
onSelected(selectionOption.first)
expanded.value = false
},
contentPadding = PaddingValues(horizontal = DEFAULT_PADDING * 1.5f)
) {
Text(
selectionOption.second + (if (label != null) " $label" else ""),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = if (isInDarkTheme()) MenuTextColorDark else Color.Black,
)
}
}
}
}
}
@Composable
fun <T> ExposedDropDownSettingRow(
title: String,
values: List<Pair<T, String>>,
selection: State<T>,
textColor: Color = MaterialTheme.colors.secondary,
label: String? = null,
icon: Painter? = null,
iconTint: Color = MaterialTheme.colors.secondary,
enabled: State<Boolean> = mutableStateOf(true),
minWidth: Dp = 200.dp,
maxWidth: Dp = with(LocalDensity.current) { 180.sp.toDp() },
onSelected: (T) -> Unit
) {
SettingsActionItemWithContent(icon, title, iconColor = iconTint, disabled = !enabled.value) {
val expanded = remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded.value,
onExpandedChange = {
expanded.value = !expanded.value && enabled.value
}
) {
Row(
Modifier.padding(start = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
val maxWidth = with(LocalDensity.current) { 180.sp.toDp() }
Text(
values.first { it.first == selection.value }.second + (if (label != null) " $label" else ""),
Modifier.widthIn(max = maxWidth),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = MaterialTheme.colors.secondary
)
Spacer(Modifier.size(12.dp))
Icon(
if (!expanded.value) painterResource(MR.images.ic_expand_more) else painterResource(MR.images.ic_expand_less),
generalGetString(MR.strings.icon_descr_more_button),
tint = MaterialTheme.colors.secondary
)
}
DefaultExposedDropdownMenu(
modifier = Modifier.widthIn(min = 200.dp),
expanded = expanded,
) {
values.forEach { selectionOption ->
DropdownMenuItem(
onClick = {
onSelected(selectionOption.first)
expanded.value = false
},
contentPadding = PaddingValues(horizontal = DEFAULT_PADDING * 1.5f)
) {
Text(
selectionOption.second + (if (label != null) " $label" else ""),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = if (isInDarkTheme()) MenuTextColorDark else Color.Black,
)
}
}
}
}
ExposedDropDownSetting(values, selection ,textColor, label, enabled, minWidth, maxWidth, onSelected)
}
}

View File

@ -55,7 +55,7 @@ fun annotatedStringResource(id: StringResource): AnnotatedString {
@Composable
fun annotatedStringResource(id: StringResource, vararg args: Any?): AnnotatedString {
val density = LocalDensity.current
return remember(id) {
return remember(id, args) {
escapedHtmlToAnnotatedString(id.localized().format(args = args), density)
}
}

View File

@ -9,6 +9,7 @@ import SectionView
import TextIconSpaced
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.*
import androidx.compose.runtime.*
@ -17,6 +18,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.*
import androidx.compose.ui.text.input.*
@ -32,11 +34,11 @@ import chat.simplex.common.views.chat.item.ItemAction
import chat.simplex.common.views.chatlist.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.newchat.QRCode
import chat.simplex.common.views.usersettings.PreferenceToggle
import chat.simplex.common.views.usersettings.SettingsActionItemWithContent
import chat.simplex.common.views.usersettings.*
import chat.simplex.res.MR
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.flow.distinctUntilChanged
@Composable
fun ConnectMobileView() {
@ -156,7 +158,7 @@ fun DeviceNameField(
DefaultConfigurableTextField(
state = state,
placeholder = generalGetString(MR.strings.enter_this_device_name),
modifier = Modifier.padding(start = DEFAULT_PADDING),
modifier = Modifier.fillMaxWidth().padding(start = DEFAULT_PADDING),
isValid = { true },
)
KeyChangeEffect(state.value) {
@ -172,7 +174,10 @@ private fun ConnectMobileViewLayout(
sessionCode: String?,
port: String?,
staleQrCode: Boolean = false,
refreshQrCode: () -> Unit = {}
editEnabled: Boolean = false,
editClicked: () -> Unit = {},
refreshQrCode: () -> Unit = {},
UnderQrLayout: @Composable () -> Unit = {},
) {
Column(
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
@ -196,7 +201,18 @@ private fun ConnectMobileViewLayout(
}
}
SectionTextFooter(annotatedStringResource(MR.strings.open_on_mobile_and_scan_qr_code), textAlign = TextAlign.Center)
SectionTextFooter(annotatedStringResource(MR.strings.waiting_for_mobile_to_connect_on_port, port), textAlign = TextAlign.Center)
Row(verticalAlignment = Alignment.CenterVertically) {
SectionTextFooter(annotatedStringResource(MR.strings.waiting_for_mobile_to_connect_on_port, port), textAlign = TextAlign.Center)
if (editEnabled) {
Spacer(Modifier.width(4.dp))
IconButton(editClicked, Modifier.size(16.dp)) {
Icon(painterResource(MR.images.ic_edit), stringResource(MR.strings.edit_verb), Modifier.size(16.dp), tint = MaterialTheme.colors.primary)
}
Spacer(Modifier.width(DEFAULT_PADDING))
}
}
UnderQrLayout()
if (remember { controller.appPrefs.developerTools.state }.value) {
val clipboard = LocalClipboardManager.current
@ -259,14 +275,22 @@ private fun showAddingMobileDevice(connecting: MutableState<Boolean>) {
@Composable
fun AddingMobileDevice(showTitle: Boolean, staleQrCode: MutableState<Boolean>, connecting: MutableState<Boolean>, close: () -> Unit) {
val invitation = rememberSaveable { mutableStateOf<String?>(null) }
val port = rememberSaveable { mutableStateOf<String?>(null) }
val cachedR = remember { mutableStateOf<CR.RemoteHostStarted?>(null) }
val customAddress = rememberSaveable { mutableStateOf<RemoteCtrlAddress?>(null) }
val customPort = rememberSaveable { mutableStateOf<Int?>(null) }
var editing by rememberSaveable { mutableStateOf(false) }
val startRemoteHost = suspend {
val r = chatModel.controller.startRemoteHost(null, controller.appPrefs.offerRemoteMulticast.get())
val r = chatModel.controller.startRemoteHost(
rhId = null,
multicast = controller.appPrefs.offerRemoteMulticast.get(),
address = if (customAddress.value?.address != cachedR.address?.address) customAddress.value else cachedR.rh?.bindAddress_,
port = if (customPort.value != cachedR.port) customPort.value else cachedR.rh?.bindPort_
)
if (r != null) {
cachedR.value = r
connecting.value = true
invitation.value = r.second
port.value = r.third
customAddress.value = cachedR.address
customPort.value = cachedR.port
chatModel.remoteHostPairing.value = null to RemoteHostSessionState.Starting
}
}
@ -283,19 +307,23 @@ fun AddingMobileDevice(showTitle: Boolean, staleQrCode: MutableState<Boolean>, c
val remoteDeviceName = pairing.value?.first?.hostDeviceName
ConnectMobileViewLayout(
title = if (!showTitle) null else if (cachedSessionCode == null) stringResource(MR.strings.link_a_mobile) else stringResource(MR.strings.verify_connection),
invitation = invitation.value,
invitation = cachedR.invitation,
deviceName = remoteDeviceName,
sessionCode = cachedSessionCode,
port = port.value,
staleQrCode = staleQrCode.value,
port = cachedR.value?.ctrlPort,
staleQrCode = staleQrCode.value || (cachedR.address != customAddress.value && customAddress.value != null) || (cachedR.port != customPort.value && customPort.value != null),
editEnabled = !editing && cachedR.addresses.isNotEmpty(),
editClicked = { editing = true },
refreshQrCode = {
withBGApi {
if (chatController.stopRemoteHost(null)) {
startRemoteHost()
staleQrCode.value = false
editing = false
}
}
},
UnderQrLayout = { UnderQrLayout(editing, cachedR, customAddress, customPort) }
)
val oldRemoteHostId by remember { mutableStateOf(chatModel.currentRemoteHost.value?.remoteHostId) }
LaunchedEffect(remember { chatModel.currentRemoteHost }.value) {
@ -325,9 +353,26 @@ fun AddingMobileDevice(showTitle: Boolean, staleQrCode: MutableState<Boolean>, c
private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState<Boolean>) {
ModalManager.start.showModalCloseable { close ->
val cachedR = remember { mutableStateOf<CR.RemoteHostStarted?>(null) }
val customAddress = rememberSaveable { mutableStateOf<RemoteCtrlAddress?>(null) }
val customPort = rememberSaveable { mutableStateOf<Int?>(null) }
var editing by rememberSaveable { mutableStateOf(false) }
val startRemoteHost = suspend {
val r = chatModel.controller.startRemoteHost(
rhId = rh.remoteHostId,
multicast = controller.appPrefs.offerRemoteMulticast.get(),
address = if (customAddress.value?.address != cachedR.address?.address) customAddress.value else cachedR.rh?.bindAddress_ ?: rh.bindAddress_,
port = if (customPort.value != cachedR.port) customPort.value else cachedR.rh?.bindPort_ ?: rh.bindPort_
)
if (r != null) {
cachedR.value = r
connecting.value = true
customAddress.value = cachedR.address
customPort.value = cachedR.port
chatModel.remoteHostPairing.value = null to RemoteHostSessionState.Starting
}
}
val pairing = remember { chatModel.remoteHostPairing }
val invitation = rememberSaveable { mutableStateOf<String?>(null) }
val port = rememberSaveable { mutableStateOf<String?>(null) }
val sessionCode = when (val state = pairing.value?.second) {
is RemoteHostSessionState.PendingConfirmation -> state.sessionCode
else -> null
@ -339,25 +384,25 @@ private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState
}
ConnectMobileViewLayout(
title = if (cachedSessionCode == null) stringResource(MR.strings.scan_from_mobile) else stringResource(MR.strings.verify_connection),
invitation = invitation.value,
invitation = cachedR.invitation,
deviceName = pairing.value?.first?.hostDeviceName ?: rh.hostDeviceName,
sessionCode = cachedSessionCode,
port = port.value
port = cachedR.value?.ctrlPort,
staleQrCode = (cachedR.address != customAddress.value && customAddress.value != null) || (cachedR.port != customPort.value && customPort.value != null),
editEnabled = !editing && cachedR.addresses.isNotEmpty(),
editClicked = { editing = true },
refreshQrCode = {
withBGApi {
if (chatController.stopRemoteHost(rh.remoteHostId)) {
startRemoteHost()
editing = false
}
}
},
UnderQrLayout = { UnderQrLayout(editing, cachedR, customAddress, customPort) }
)
var remoteHostId by rememberSaveable { mutableStateOf<Long?>(null) }
LaunchedEffect(Unit) {
val r = chatModel.controller.startRemoteHost(rh.remoteHostId, controller.appPrefs.offerRemoteMulticast.get())
if (r != null) {
val (rh_, inv) = r
connecting.value = true
remoteHostId = rh_?.remoteHostId
invitation.value = inv
port.value = r.third
chatModel.remoteHostPairing.value = null to RemoteHostSessionState.Starting
}
}
LaunchedEffect(remember { chatModel.currentRemoteHost }.value) {
if (remoteHostId != null && chatModel.currentRemoteHost.value?.remoteHostId == remoteHostId) {
if (cachedR.remoteHostId != null && chatModel.currentRemoteHost.value?.remoteHostId == cachedR.remoteHostId) {
close()
}
}
@ -367,10 +412,13 @@ private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState
}
}
DisposableEffect(Unit) {
withBGApi {
startRemoteHost()
}
onDispose {
if (remoteHostId != null && chatModel.currentRemoteHost.value?.remoteHostId != remoteHostId) {
if (cachedR.remoteHostId != null && chatModel.currentRemoteHost.value?.remoteHostId != cachedR.remoteHostId) {
withBGApi {
chatController.stopRemoteHost(remoteHostId)
chatController.stopRemoteHost(cachedR.remoteHostId)
}
}
chatModel.remoteHostPairing.value = null
@ -403,3 +451,50 @@ private fun showConnectedMobileDevice(rh: RemoteHostInfo, disconnectHost: () ->
}
}
}
@Composable
private fun UnderQrLayout(editing: Boolean, cachedR: State<CR.RemoteHostStarted?>, customAddress: MutableState<RemoteCtrlAddress?>, customPort: MutableState<Int?>) {
if (editing) {
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center) {
ExposedDropDownSetting(
cachedR.addresses.map { it to it.address + " (${it.`interface`})" },
customAddress,
textColor = MaterialTheme.colors.onBackground,
minWidth = 250.dp,
maxWidth = with(LocalDensity.current) { 250.sp.toDp() },
onSelected = {
customAddress.value = it
}
)
val portUnsaved = rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue((customPort.value ?: cachedR.port!!).toString()))
}
Spacer(Modifier.width(DEFAULT_PADDING))
DefaultConfigurableTextField(
portUnsaved,
stringResource(MR.strings.port_verb),
modifier = Modifier.widthIn(max = 100.dp),
isValid = { validPort(it) && it.toInt() > 1023 },
keyboardActions = KeyboardActions(onDone = { defaultKeyboardAction(ImeAction.Done) }),
keyboardType = KeyboardType.Number,
)
LaunchedEffect(Unit) {
snapshotFlow { portUnsaved.value.text }
.distinctUntilChanged()
.collect {
if (validPort(it) && it.toInt() > 1023) {
customPort.value = it.toInt()
}
}
}
}
}
}
private val State<CR.RemoteHostStarted?>.rh: RemoteHostInfo? get() = value?.remoteHost_
private val State<CR.RemoteHostStarted?>.remoteHostId: Long? get() = value?.remoteHost_?.remoteHostId
private val State<CR.RemoteHostStarted?>.invitation: String? get() = value?.invitation
private val State<CR.RemoteHostStarted?>.address: RemoteCtrlAddress? get() = value?.localAddrs?.firstOrNull()
private val State<CR.RemoteHostStarted?>.addresses: List<RemoteCtrlAddress> get() =
(if (controller.appPrefs.developerTools.get()) value?.localAddrs else value?.localAddrs?.filterNot { it.address == "127.0.0.1" }) ?: emptyList()
private val State<CR.RemoteHostStarted?>.port: Int? get() = value?.ctrlPort?.toIntOrNull()

View File

@ -305,7 +305,7 @@ fun SockProxySettings(m: ChatModel) {
DefaultConfigurableTextField(
hostUnsaved,
stringResource(MR.strings.host_verb),
modifier = Modifier,
modifier = Modifier.fillMaxWidth(),
isValid = ::validHost,
keyboardActions = KeyboardActions(onNext = { defaultKeyboardAction(ImeAction.Next) }),
keyboardType = KeyboardType.Text,
@ -315,7 +315,7 @@ fun SockProxySettings(m: ChatModel) {
DefaultConfigurableTextField(
portUnsaved,
stringResource(MR.strings.port_verb),
modifier = Modifier,
modifier = Modifier.fillMaxWidth(),
isValid = ::validPort,
keyboardActions = KeyboardActions(onDone = { defaultKeyboardAction(ImeAction.Done); save() }),
keyboardType = KeyboardType.Number,
@ -428,7 +428,7 @@ private fun validHost(s: String): Boolean {
}
// https://ihateregex.io/expr/port/
private fun validPort(s: String): Boolean {
fun validPort(s: String): Boolean {
val validPort = Regex("^(6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{0,5})|([0-9]{1,4})$")
return s.isNotBlank() && s.matches(validPort)
}

View File

@ -9,7 +9,7 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package
type: git
location: https://github.com/simplex-chat/simplexmq.git
tag: 90a8fc91d35c578c3b52ad296a6f1df715da2278
tag: 117168ccce93ef5ac478c02e3f02a018fa8a2200
source-repository-package
type: git

View File

@ -33,7 +33,7 @@ dependencies:
- http-types == 0.12.*
- http2 >= 4.2.2 && < 4.3
- memory >= 0.15 && < 0.19
- mtl >= 2.2 && < 3
- mtl >= 2.3.1 && < 3
- network >= 3.1.2.7 && < 3.2
- network-transport >= 0.5.6 && < 0.6
- optparse-applicative >= 0.15 && < 0.17

View File

@ -1,5 +1,5 @@
{
"https://github.com/simplex-chat/simplexmq.git"."90a8fc91d35c578c3b52ad296a6f1df715da2278" = "1yjixh6b2s1law3kh885fsbr1inv1r7iy4g9g2bn6j4ygdn8vlzy";
"https://github.com/simplex-chat/simplexmq.git"."117168ccce93ef5ac478c02e3f02a018fa8a2200" = "1091c4b8qjp9v29z862b7clda90w3kb4v2aqp5b2jh6yprlga98w";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
"https://github.com/kazu-yamamoto/http2.git"."f5525b755ff2418e6e6ecc69e877363b0d0bcaeb" = "0fyx0047gvhm99ilp212mmz37j84cwrfnpmssib5dw363fyb88b6";
"https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd";

View File

@ -185,7 +185,7 @@ library
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.2 && <3
, mtl >=2.3.1 && <3
, network >=3.1.2.7 && <3.2
, network-transport >=0.5.6 && <0.6
, optparse-applicative >=0.15 && <0.17
@ -237,7 +237,7 @@ executable simplex-bot
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.2 && <3
, mtl >=2.3.1 && <3
, network >=3.1.2.7 && <3.2
, network-transport >=0.5.6 && <0.6
, optparse-applicative >=0.15 && <0.17
@ -290,7 +290,7 @@ executable simplex-bot-advanced
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.2 && <3
, mtl >=2.3.1 && <3
, network >=3.1.2.7 && <3.2
, network-transport >=0.5.6 && <0.6
, optparse-applicative >=0.15 && <0.17
@ -345,7 +345,7 @@ executable simplex-broadcast-bot
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.2 && <3
, mtl >=2.3.1 && <3
, network >=3.1.2.7 && <3.2
, network-transport >=0.5.6 && <0.6
, optparse-applicative >=0.15 && <0.17
@ -399,7 +399,7 @@ executable simplex-chat
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.2 && <3
, mtl >=2.3.1 && <3
, network ==3.1.*
, network-transport >=0.5.6 && <0.6
, optparse-applicative >=0.15 && <0.17
@ -457,7 +457,7 @@ executable simplex-directory-service
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.2 && <3
, mtl >=2.3.1 && <3
, network >=3.1.2.7 && <3.2
, network-transport >=0.5.6 && <0.6
, optparse-applicative >=0.15 && <0.17
@ -541,7 +541,7 @@ test-suite simplex-chat-test
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.2 && <3
, mtl >=2.3.1 && <3
, network ==3.1.*
, network-transport >=0.5.6 && <0.6
, optparse-applicative >=0.15 && <0.17

View File

@ -49,7 +49,7 @@ extra-deps:
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
# - ../simplexmq
- github: simplex-chat/simplexmq
commit: 90a8fc91d35c578c3b52ad296a6f1df715da2278
commit: 117168ccce93ef5ac478c02e3f02a018fa8a2200
- github: kazu-yamamoto/http2
commit: f5525b755ff2418e6e6ecc69e877363b0d0bcaeb
# - ../direct-sqlcipher