api and types

This commit is contained in:
Diogo
2024-11-15 22:01:00 +00:00
parent b605ebfd2a
commit 79d3504de1
6 changed files with 476 additions and 107 deletions
@@ -3,11 +3,11 @@ package chat.simplex.common.views.usersettings
import android.Manifest
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import chat.simplex.common.model.ServerCfg
import chat.simplex.common.model.UserServer
import com.google.accompanist.permissions.rememberPermissionState
@Composable
actual fun ScanProtocolServer(rhId: Long?, onNext: (ServerCfg) -> Unit) {
actual fun ScanProtocolServer(rhId: Long?, onNext: (UserServer) -> Unit) {
val cameraPermissionState = rememberPermissionState(permission = Manifest.permission.CAMERA)
LaunchedEffect(Unit) {
cameraPermissionState.launchPermissionRequest()
@@ -955,36 +955,6 @@ object ChatController {
return null
}
suspend fun getUserProtoServers(rh: Long?, serverProtocol: ServerProtocol): UserProtocolServers? {
val userId = kotlin.runCatching { currentUserId("getUserProtoServers") }.getOrElse { return null }
val r = sendCmd(rh, CC.APIGetUserProtoServers(userId, serverProtocol))
return if (r is CR.UserProtoServers) { if (rh == null) r.servers else r.servers.copy(protoServers = r.servers.protoServers.map { it.copy(remoteHostId = rh) }) }
else {
Log.e(TAG, "getUserProtoServers bad response: ${r.responseType} ${r.details}")
AlertManager.shared.showAlertMsg(
generalGetString(if (serverProtocol == ServerProtocol.SMP) MR.strings.error_loading_smp_servers else MR.strings.error_loading_xftp_servers),
"${r.responseType}: ${r.details}"
)
null
}
}
suspend fun setUserProtoServers(rh: Long?, serverProtocol: ServerProtocol, servers: List<ServerCfg>): Boolean {
val userId = kotlin.runCatching { currentUserId("setUserProtoServers") }.getOrElse { return false }
val r = sendCmd(rh, CC.APISetUserProtoServers(userId, serverProtocol, servers))
return when (r) {
is CR.CmdOk -> true
else -> {
Log.e(TAG, "setUserProtoServers bad response: ${r.responseType} ${r.details}")
AlertManager.shared.showAlertMsg(
generalGetString(if (serverProtocol == ServerProtocol.SMP) MR.strings.error_saving_smp_servers else MR.strings.error_saving_xftp_servers),
generalGetString(if (serverProtocol == ServerProtocol.SMP) MR.strings.ensure_smp_server_address_are_correct_format_and_unique else MR.strings.ensure_xftp_server_address_are_correct_format_and_unique)
)
false
}
}
}
suspend fun testProtoServer(rh: Long?, server: String): ProtocolTestFailure? {
val userId = currentUserId("testProtoServer")
val r = sendCmd(rh, CC.APITestProtoServer(userId, server))
@@ -997,6 +967,97 @@ object ChatController {
}
}
suspend fun getServerOperators(rh: Long?): ServerOperatorConditionsDetail? {
val r = sendCmd(rh, CC.ApiGetServerOperators())
return when (r) {
is CR.ServerOperatorConditions -> r.conditions
else -> {
Log.e(TAG, "getServerOperators bad response: ${r.responseType} ${r.details}")
null
}
}
}
suspend fun setServerOperators(rh: Long?, operators: List<ServerOperator>): ServerOperatorConditionsDetail? {
val r = sendCmd(rh, CC.ApiSetServerOperators(operators))
return when (r) {
is CR.ServerOperatorConditions -> r.conditions
else -> {
Log.e(TAG, "setServerOperators bad response: ${r.responseType} ${r.details}")
null
}
}
}
suspend fun getUserServers(rh: Long?): List<UserOperatorServers>? {
val userId = currentUserId("getUserServers")
val r = sendCmd(rh, CC.ApiGetUserServers(userId))
return when (r) {
is CR.UserServers -> r.userServers
else -> {
Log.e(TAG, "getUserServers bad response: ${r.responseType} ${r.details}")
null
}
}
}
suspend fun setUserServers(rh: Long?, userServers: List<UserOperatorServers>): Boolean {
val userId = currentUserId("setUserServers")
val r = sendCmd(rh, CC.ApiSetUserServers(userId, userServers))
return when (r) {
is CR.CmdOk -> true
else -> {
Log.e(TAG, "setUserServers bad response: ${r.responseType} ${r.details}")
false
}
}
}
suspend fun validateServers(rh: Long?, userServers: List<UserOperatorServers>): List<UserServersError>? {
val r = sendCmd(rh, CC.ApiValidateServers(userServers))
return when (r) {
is CR.UserServersValidation -> r.serverErrors
else -> {
Log.e(TAG, "validateServers bad response: ${r.responseType} ${r.details}")
null
}
}
}
suspend fun getUsageConditions(rh: Long?): Triple<UsageConditionsDetail, String?, CR.UsageConditions?>? {
val r = sendCmd(rh, CC.ApiGetUsageConditions())
return when (r) {
is CR.UsageConditions -> Triple(r.usageConditions, r.conditionsText, r.acceptedConditions)
else -> {
Log.e(TAG, "getUsageConditions bad response: ${r.responseType} ${r.details}")
null
}
}
}
suspend fun setConditionsNotified(rh: Long?, conditionsId: Long): Boolean {
val r = sendCmd(rh, CC.ApiSetConditionsNotified(conditionsId))
return when (r) {
is CR.CmdOk -> true
else -> {
Log.e(TAG, "setConditionsNotified bad response: ${r.responseType} ${r.details}")
false
}
}
}
suspend fun acceptConditions(rh: Long?, conditionsId: Long, operatorIds: List<Long>): CR.ServerOperatorConditions? {
val r = sendCmd(rh, CC.ApiAcceptConditions(conditionsId, operatorIds))
return when (r) {
is CR.ServerOperatorConditions -> r
else -> {
Log.e(TAG, "acceptConditions bad response: ${r.responseType} ${r.details}")
null
}
}
}
suspend fun getChatItemTTL(rh: Long?): ChatItemTTL {
val userId = currentUserId("getChatItemTTL")
val r = sendCmd(rh, CC.APIGetChatItemTTL(userId))
@@ -3029,9 +3090,15 @@ sealed class CC {
class APIGetGroupLink(val groupId: Long): CC()
class APICreateMemberContact(val groupId: Long, val groupMemberId: Long): CC()
class APISendMemberContactInvitation(val contactId: Long, val mc: MsgContent): CC()
class APIGetUserProtoServers(val userId: Long, val serverProtocol: ServerProtocol): CC()
class APISetUserProtoServers(val userId: Long, val serverProtocol: ServerProtocol, val servers: List<ServerCfg>): CC()
class APITestProtoServer(val userId: Long, val server: String): CC()
class ApiGetServerOperators(): CC()
class ApiSetServerOperators(val operators: List<ServerOperator>): CC()
class ApiGetUserServers(val userId: Long): CC()
class ApiSetUserServers(val userId: Long, val userServers: List<UserOperatorServers>): CC()
class ApiValidateServers(val userServers: List<UserOperatorServers>): CC()
class ApiGetUsageConditions(): CC()
class ApiSetConditionsNotified(val conditionsId: Long): CC()
class ApiAcceptConditions(val conditionsId: Long, val operatorIds: List<Long>): CC()
class APISetChatItemTTL(val userId: Long, val seconds: Long?): CC()
class APIGetChatItemTTL(val userId: Long): CC()
class APISetNetworkConfig(val networkConfig: NetCfg): CC()
@@ -3189,9 +3256,15 @@ sealed class CC {
is APIGetGroupLink -> "/_get link #$groupId"
is APICreateMemberContact -> "/_create member contact #$groupId $groupMemberId"
is APISendMemberContactInvitation -> "/_invite member contact @$contactId ${mc.cmdString}"
is APIGetUserProtoServers -> "/_servers $userId ${serverProtocol.name.lowercase()}"
is APISetUserProtoServers -> "/_servers $userId ${serverProtocol.name.lowercase()} ${protoServersStr(servers)}"
is APITestProtoServer -> "/_server test $userId $server"
is ApiGetServerOperators -> "/_operators"
is ApiSetServerOperators -> "/_operators ${json.encodeToString(operators)}}"
is ApiGetUserServers -> "/_servers $userId"
is ApiSetUserServers -> "/_servers $userId ${json.encodeToString(userServers)}"
is ApiValidateServers -> "/_validate_servers ${json.encodeToString(userServers)}"
is ApiGetUsageConditions -> "/_conditions"
is ApiSetConditionsNotified -> "/_conditions_notified ${conditionsId}"
is ApiAcceptConditions -> "/_accept_conditions ${conditionsId} ${operatorIds.joinToString(",")}"
is APISetChatItemTTL -> "/_ttl $userId ${chatItemTTLStr(seconds)}"
is APIGetChatItemTTL -> "/_ttl $userId"
is APISetNetworkConfig -> "/_network ${json.encodeToString(networkConfig)}"
@@ -3334,9 +3407,15 @@ sealed class CC {
is APIGetGroupLink -> "apiGetGroupLink"
is APICreateMemberContact -> "apiCreateMemberContact"
is APISendMemberContactInvitation -> "apiSendMemberContactInvitation"
is APIGetUserProtoServers -> "apiGetUserProtoServers"
is APISetUserProtoServers -> "apiSetUserProtoServers"
is APITestProtoServer -> "testProtoServer"
is ApiGetServerOperators -> "apiGetServerOperators"
is ApiSetServerOperators -> "apiSetServerOperators"
is ApiGetUserServers -> "apiGetUserServers"
is ApiSetUserServers -> "apiSetUserServers"
is ApiValidateServers -> "apiValidateServers"
is ApiGetUsageConditions -> "apiGetUsageConditions"
is ApiSetConditionsNotified -> "apiSetConditionsNotified"
is ApiAcceptConditions -> "apiAcceptConditions"
is APISetChatItemTTL -> "apiSetChatItemTTL"
is APIGetChatItemTTL -> "apiGetChatItemTTL"
is APISetNetworkConfig -> "apiSetNetworkConfig"
@@ -3451,8 +3530,6 @@ sealed class CC {
companion object {
fun chatRef(chatType: ChatType, id: Long) = "${chatType.type}${id}"
fun protoServersStr(servers: List<ServerCfg>) = json.encodeToString(ProtoServersConfig(servers))
}
}
@@ -3498,24 +3575,291 @@ enum class ServerProtocol {
}
@Serializable
data class ProtoServersConfig(
val servers: List<ServerCfg>
enum class OperatorTag {
@SerialName("simplex") SimpleX,
@SerialName("flux") Flux,
@SerialName("xyz") XYZ,
@SerialName("demo") Demo
}
@Serializable
data class ServerOperatorInfo(
val description: String,
val website: String,
val logo: String,
val largeLogo: String,
val logoDarkMode: String,
val largeLogoDarkMode: String
)
val operatorsInfo: Map<OperatorTag, ServerOperatorInfo> = mapOf(
OperatorTag.SimpleX to ServerOperatorInfo(
description = "SimpleX Chat preset servers",
website = "https://simplex.chat",
logo = "decentralized",
largeLogo = "logo",
logoDarkMode = "decentralized-light",
largeLogoDarkMode = "logo-light"
),
OperatorTag.Flux to ServerOperatorInfo(
description = "Flux is the largest decentralized cloud infrastructure, leveraging a global network of user-operated computational nodes. Flux offers a powerful, scalable, and affordable platform designed to support individuals, businesses, and cutting-edge technologies like AI. With high uptime and worldwide distribution, Flux ensures reliable, accessible cloud computing for all.",
website = "https://runonflux.com",
logo = "flux_logo_symbol",
largeLogo = "flux_logo",
logoDarkMode = "flux_logo_symbol",
largeLogoDarkMode = "flux_logo-light"
),
OperatorTag.XYZ to ServerOperatorInfo(
description = "XYZ servers",
website = "XYZ website",
logo = "shield",
largeLogo = "logo",
logoDarkMode = "shield",
largeLogoDarkMode = "logo-light"
),
OperatorTag.Demo to ServerOperatorInfo(
description = "Demo operator",
website = "Demo website",
logo = "decentralized",
largeLogo = "logo",
logoDarkMode = "decentralized-light",
largeLogoDarkMode = "logo-light"
)
)
@Serializable
data class UserProtocolServers(
val serverProtocol: ServerProtocol,
val protoServers: List<ServerCfg>,
val presetServers: List<ServerCfg>,
data class UsageConditionsDetail(
val conditionsId: Long,
val conditionsCommit: String,
val notifiedAt: Instant?,
val createdAt: Instant
) {
companion object {
val sampleData = UsageConditionsDetail(
conditionsId = 1,
conditionsCommit = "11a44dc1fd461a93079f897048b46998db55da5c",
notifiedAt = null,
createdAt = Clock.System.now()
)
}
}
@Serializable
sealed class UsageConditionsAction {
data class Review(val operators: List<ServerOperator>, val deadline: Date?, private val showNoticeFlag: Boolean) : UsageConditionsAction() {
val showNotice: Boolean
get() = showNoticeFlag
}
data class Accepted(val operators: List<ServerOperator>) : UsageConditionsAction() {
val showNotice: Boolean
get() = false
}
}
@Serializable
data class ServerOperatorConditionsDetail(
val serverOperators: List<ServerOperator>,
val currentConditions: UsageConditionsDetail,
val conditionsAction: UsageConditionsAction?
) {
companion object {
val empty = ServerOperatorConditionsDetail(
serverOperators = emptyList(),
currentConditions = UsageConditionsDetail(conditionsId = 0, conditionsCommit = "empty", notifiedAt = null, createdAt = Clock.System.now()),
conditionsAction = null
)
}
}
@Serializable()
sealed class ConditionsAcceptance {
data class Accepted(val acceptedAt: Date?) : ConditionsAcceptance()
data class Required(val deadline: Date?) : ConditionsAcceptance()
val conditionsAccepted: Boolean
get() = when (this) {
is Accepted -> true
is Required -> false
}
val usageAllowed: Boolean
get() = when (this) {
is Accepted -> true
is Required -> this.deadline != null
}
}
@Serializable
data class ServerOperator(
val operatorId: Long,
val operatorTag: OperatorTag?,
val tradeName: String,
val legalName: String?,
val serverDomains: List<String>,
val conditionsAcceptance: ConditionsAcceptance,
val enabled: Boolean,
val smpRoles: ServerRoles,
val xftpRoles: ServerRoles,
) {
companion object {
val dummyOperatorInfo = ServerOperatorInfo(
description = "Default",
website = "Default",
logo = "decentralized",
largeLogo = "logo",
logoDarkMode = "decentralized-light",
largeLogoDarkMode = "logo-light"
)
val sampleData1 = ServerOperator(
operatorId = 1,
operatorTag = OperatorTag.SimpleX,
tradeName = "SimpleX Chat",
legalName = "SimpleX Chat Ltd",
serverDomains = listOf("simplex.im"),
conditionsAcceptance = ConditionsAcceptance.Accepted(acceptedAt = null),
enabled = true,
smpRoles = ServerRoles(storage = true, proxy = true),
xftpRoles = ServerRoles(storage = true, proxy = true)
)
val sampleData2 = ServerOperator(
operatorId = 2,
operatorTag = OperatorTag.XYZ,
tradeName = "XYZ",
legalName = null,
serverDomains = listOf("xyz.com"),
conditionsAcceptance = ConditionsAcceptance.Required(deadline = null),
enabled = false,
smpRoles = ServerRoles(storage = false, proxy = true),
xftpRoles = ServerRoles(storage = false, proxy = true)
)
val sampleData3 = ServerOperator(
operatorId = 3,
operatorTag = OperatorTag.Demo,
tradeName = "Demo",
legalName = null,
serverDomains = listOf("demo.com"),
conditionsAcceptance = ConditionsAcceptance.Required(deadline = null),
enabled = false,
smpRoles = ServerRoles(storage = true, proxy = false),
xftpRoles = ServerRoles(storage = true, proxy = false)
)
}
val id: Long
get() = operatorId
override fun equals(other: Any?): Boolean {
if (other !is ServerOperator) return false
return other.operatorId == this.operatorId &&
other.operatorTag == this.operatorTag &&
other.tradeName == this.tradeName &&
other.legalName == this.legalName &&
other.serverDomains == this.serverDomains &&
other.conditionsAcceptance == this.conditionsAcceptance &&
other.enabled == this.enabled &&
other.smpRoles == this.smpRoles &&
other.xftpRoles == this.xftpRoles
}
override fun hashCode(): Int {
var result = operatorId.hashCode()
result = 31 * result + (operatorTag?.hashCode() ?: 0)
result = 31 * result + tradeName.hashCode()
result = 31 * result + (legalName?.hashCode() ?: 0)
result = 31 * result + serverDomains.hashCode()
result = 31 * result + conditionsAcceptance.hashCode()
result = 31 * result + enabled.hashCode()
result = 31 * result + smpRoles.hashCode()
result = 31 * result + xftpRoles.hashCode()
return result
}
val legalName_: String
get() = legalName ?: tradeName
val info: ServerOperatorInfo get() {
return if (this.operatorTag != null) {
operatorsInfo[this.operatorTag] ?: dummyOperatorInfo
} else {
dummyOperatorInfo
}
}
val logo: String get() {
return if (CurrentColors.value.base == DefaultTheme.LIGHT) info.logo else info.logoDarkMode
}
val largeLogo: String get() {
return if (CurrentColors.value.base == DefaultTheme.LIGHT) info.largeLogo else info.largeLogoDarkMode
}
}
@Serializable
data class ServerRoles(
val storage: Boolean,
val proxy: Boolean
)
@Serializable
data class ServerCfg(
data class UserOperatorServers(
var operator: ServerOperator?,
val smpServers: List<UserServer>,
val xftpServers: List<UserServer>
) {
val id: String
get() = operator?.operatorId?.toString() ?: "nil operator"
var operator_: ServerOperator
get() = operator ?: ServerOperator(
operatorId = 0,
operatorTag = null,
tradeName = "",
legalName = null,
serverDomains = emptyList(),
conditionsAcceptance = ConditionsAcceptance.Accepted(null),
enabled = false,
smpRoles = ServerRoles(storage = true, proxy = true),
xftpRoles = ServerRoles(storage = true, proxy = true)
)
set(value) {
operator = value
}
companion object {
val sampleData1 = UserOperatorServers(
operator = ServerOperator.sampleData1,
smpServers = listOf(UserServer.sampleData.preset),
xftpServers = listOf(UserServer.sampleData.xftpPreset)
)
val sampleDataNilOperator = UserOperatorServers(
operator = null,
smpServers = listOf(UserServer.sampleData.preset),
xftpServers = listOf(UserServer.sampleData.xftpPreset)
)
}
}
@Serializable
sealed class UserServersError {
class StorageMissing: UserServersError()
class ProxyMissing: UserServersError()
data class DuplicateSMP(val server: String) : UserServersError()
data class DuplicateXFTP(val server: String) : UserServersError()
}
@Serializable
data class UserServer(
val remoteHostId: Long?,
val serverId: Long?,
val server: String,
val preset: Boolean,
val tested: Boolean? = null,
val enabled: Boolean
val enabled: Boolean,
val deleted: Boolean
) {
@Transient
private val createdAt: Date = Date()
@@ -3529,35 +3873,51 @@ data class ServerCfg(
get() = server.isBlank()
companion object {
val empty = ServerCfg(remoteHostId = null, server = "", preset = false, tested = null, enabled = false)
val empty = UserServer(remoteHostId = null, serverId = null, server = "", preset = false, tested = null, enabled = false, deleted = false)
class SampleData(
val preset: ServerCfg,
val custom: ServerCfg,
val untested: ServerCfg
val preset: UserServer,
val custom: UserServer,
val untested: UserServer,
val xftpPreset: UserServer
)
val sampleData = SampleData(
preset = ServerCfg(
preset = UserServer(
remoteHostId = null,
serverId = 1,
server = "smp://abcd@smp8.simplex.im",
preset = true,
tested = true,
enabled = true
enabled = true,
deleted = false
),
custom = ServerCfg(
custom = UserServer(
remoteHostId = null,
serverId = 2,
server = "smp://abcd@smp9.simplex.im",
preset = false,
tested = false,
enabled = false
enabled = false,
deleted = false
),
untested = ServerCfg(
untested = UserServer(
remoteHostId = null,
serverId = 3,
server = "smp://abcd@smp10.simplex.im",
preset = false,
tested = null,
enabled = true
enabled = true,
deleted = false
),
xftpPreset = UserServer(
remoteHostId = null,
serverId = 4,
server = "xftp://abcd@xftp8.simplex.im",
preset = true,
tested = true,
enabled = true,
deleted = false
)
)
}
@@ -4916,8 +5276,11 @@ sealed class CR {
@Serializable @SerialName("apiChats") class ApiChats(val user: UserRef, val chats: List<Chat>): CR()
@Serializable @SerialName("apiChat") class ApiChat(val user: UserRef, val chat: Chat): CR()
@Serializable @SerialName("chatItemInfo") class ApiChatItemInfo(val user: UserRef, val chatItem: AChatItem, val chatItemInfo: ChatItemInfo): CR()
@Serializable @SerialName("userProtoServers") class UserProtoServers(val user: UserRef, val servers: UserProtocolServers): CR()
@Serializable @SerialName("serverTestResult") class ServerTestResult(val user: UserRef, val testServer: String, val testFailure: ProtocolTestFailure? = null): CR()
@Serializable @SerialName("serverOperatorConditions") class ServerOperatorConditions(val conditions: ServerOperatorConditionsDetail): CR()
@Serializable @SerialName("userServers") class UserServers(val user: UserRef, val userServers: List<UserOperatorServers>): CR()
@Serializable @SerialName("userServersValidation") class UserServersValidation(val serverErrors: List<UserServersError>): CR()
@Serializable @SerialName("usageConditions") class UsageConditions(val usageConditions: UsageConditionsDetail, val conditionsText: String, val acceptedConditions: UsageConditions?): CR()
@Serializable @SerialName("chatItemTTL") class ChatItemTTL(val user: UserRef, val chatItemTTL: Long? = null): CR()
@Serializable @SerialName("networkConfig") class NetworkConfig(val networkConfig: NetCfg): CR()
@Serializable @SerialName("contactInfo") class ContactInfo(val user: UserRef, val contact: Contact, val connectionStats_: ConnectionStats? = null, val customUserProfile: Profile? = null): CR()
@@ -5096,8 +5459,11 @@ sealed class CR {
is ApiChats -> "apiChats"
is ApiChat -> "apiChat"
is ApiChatItemInfo -> "chatItemInfo"
is UserProtoServers -> "userProtoServers"
is ServerTestResult -> "serverTestResult"
is ServerOperatorConditions -> "serverOperatorConditions"
is UserServers -> "userServers"
is UserServersValidation -> "userServersValidation"
is UsageConditions -> "usageConditions"
is ChatItemTTL -> "chatItemTTL"
is NetworkConfig -> "networkConfig"
is ContactInfo -> "contactInfo"
@@ -5266,8 +5632,11 @@ sealed class CR {
is ApiChats -> withUser(user, json.encodeToString(chats))
is ApiChat -> withUser(user, json.encodeToString(chat))
is ApiChatItemInfo -> withUser(user, "chatItem: ${json.encodeToString(chatItem)}\n${json.encodeToString(chatItemInfo)}")
is UserProtoServers -> withUser(user, "servers: ${json.encodeToString(servers)}")
is ServerTestResult -> withUser(user, "server: $testServer\nresult: ${json.encodeToString(testFailure)}")
is ServerOperatorConditions -> "conditions: ${json.encodeToString(conditions)}"
is UserServers -> withUser(user, "userServers: ${json.encodeToString(userServers)}")
is UserServersValidation -> "serverErrors: ${json.encodeToString(serverErrors)}"
is UsageConditions -> "usageConditions: ${json.encodeToString(usageConditions)}\nnacceptedConditions: ${json.encodeToString(acceptedConditions)}"
is ChatItemTTL -> withUser(user, json.encodeToString(chatItemTTL))
is NetworkConfig -> json.encodeToString(networkConfig)
is ContactInfo -> withUser(user, "contact: ${json.encodeToString(contact)}\nconnectionStats: ${json.encodeToString(connectionStats_)}")
@@ -31,7 +31,7 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.flow.distinctUntilChanged
@Composable
fun ProtocolServerView(m: ChatModel, server: ServerCfg, serverProtocol: ServerProtocol, onUpdate: (ServerCfg) -> Unit, onDelete: () -> Unit) {
fun ProtocolServerView(m: ChatModel, server: UserServer, serverProtocol: ServerProtocol, onUpdate: (UserServer) -> Unit, onDelete: () -> Unit) {
var testing by remember { mutableStateOf(false) }
ProtocolServerLayout(
testing,
@@ -69,10 +69,10 @@ fun ProtocolServerView(m: ChatModel, server: ServerCfg, serverProtocol: ServerPr
@Composable
private fun ProtocolServerLayout(
testing: Boolean,
server: ServerCfg,
server: UserServer,
serverProtocol: ServerProtocol,
testServer: () -> Unit,
onUpdate: (ServerCfg) -> Unit,
onUpdate: (UserServer) -> Unit,
onDelete: () -> Unit,
) {
ColumnWithScrollBar {
@@ -90,9 +90,9 @@ private fun ProtocolServerLayout(
@Composable
private fun PresetServer(
testing: Boolean,
server: ServerCfg,
server: UserServer,
testServer: () -> Unit,
onUpdate: (ServerCfg) -> Unit,
onUpdate: (UserServer) -> Unit,
onDelete: () -> Unit,
) {
SectionView(stringResource(MR.strings.smp_servers_preset_address).uppercase()) {
@@ -114,10 +114,10 @@ private fun PresetServer(
@Composable
private fun CustomServer(
testing: Boolean,
server: ServerCfg,
server: UserServer,
serverProtocol: ServerProtocol,
testServer: () -> Unit,
onUpdate: (ServerCfg) -> Unit,
onUpdate: (UserServer) -> Unit,
onDelete: () -> Unit,
) {
val serverAddress = remember { mutableStateOf(server.server) }
@@ -162,9 +162,9 @@ private fun CustomServer(
private fun UseServerSection(
valid: Boolean,
testing: Boolean,
server: ServerCfg,
server: UserServer,
testServer: () -> Unit,
onUpdate: (ServerCfg) -> Unit,
onUpdate: (UserServer) -> Unit,
onDelete: () -> Unit,
) {
SectionView(stringResource(MR.strings.smp_servers_use_server).uppercase()) {
@@ -189,14 +189,14 @@ private fun UseServerSection(
}
@Composable
fun ShowTestStatus(server: ServerCfg, modifier: Modifier = Modifier) =
fun ShowTestStatus(server: UserServer, modifier: Modifier = Modifier) =
when (server.tested) {
true -> Icon(painterResource(MR.images.ic_check), null, modifier, tint = SimplexGreen)
false -> Icon(painterResource(MR.images.ic_close), null, modifier, tint = MaterialTheme.colors.error)
else -> Icon(painterResource(MR.images.ic_check), null, modifier, tint = Color.Transparent)
}
suspend fun testServerConnection(server: ServerCfg, m: ChatModel): Pair<ServerCfg, ProtocolTestFailure?> =
suspend fun testServerConnection(server: UserServer, m: ChatModel): Pair<UserServer, ProtocolTestFailure?> =
try {
val r = m.controller.testProtoServer(server.remoteHostId, server.server)
server.copy(tested = r == null) to r
@@ -28,8 +28,8 @@ import chat.simplex.res.MR
@Composable
fun ModalData.ProtocolServersView(m: ChatModel, rhId: Long?, serverProtocol: ServerProtocol, close: () -> Unit) {
var presetServers by remember(rhId) { mutableStateOf(emptyList<ServerCfg>()) }
var servers by remember { stateGetOrPut("servers") { emptyList<ServerCfg>() } }
var presetServers by remember(rhId) { mutableStateOf(emptyList<UserServer>()) }
var servers by remember { stateGetOrPut("servers") { emptyList<UserServer>() } }
var serversAlreadyLoaded by remember { stateGetOrPut("serversAlreadyLoaded") { false } }
val currServers = remember(rhId) { mutableStateOf(servers) }
val testing = rememberSaveable(rhId) { mutableStateOf(false) }
@@ -55,19 +55,19 @@ fun ModalData.ProtocolServersView(m: ChatModel, rhId: Long?, serverProtocol: Ser
LaunchedEffect(rhId) {
withApi {
val res = m.controller.getUserProtoServers(rhId, serverProtocol)
if (res != null) {
currServers.value = res.protoServers
presetServers = res.presetServers
if (servers.isEmpty() && !serversAlreadyLoaded) {
servers = currServers.value
serversAlreadyLoaded = true
}
}
// val res = m.controller.getUserServers(rhId)
// if (res != null) {
// currServers.value = res.protoServers
// presetServers = res.presetServers
// if (servers.isEmpty() && !serversAlreadyLoaded) {
// servers = currServers.value
// serversAlreadyLoaded = true
// }
// }
}
}
val testServersJob = CancellableOnGoneJob()
fun showServer(server: ServerCfg) {
fun showServer(server: UserServer) {
ModalManager.start.showModalCloseable(true) { close ->
var old by remember { mutableStateOf(server) }
val index = servers.indexOf(old)
@@ -111,7 +111,7 @@ fun ModalData.ProtocolServersView(m: ChatModel, rhId: Long?, serverProtocol: Ser
Column {
SectionItemView({
AlertManager.shared.hideAlert()
servers = servers + ServerCfg.empty
servers = servers + UserServer.empty
// No saving until something will be changed on the next screen to prevent blank servers on the list
showServer(servers.last())
}) {
@@ -181,7 +181,7 @@ fun ModalData.ProtocolServersView(m: ChatModel, rhId: Long?, serverProtocol: Ser
private fun ProtocolServersLayout(
serverProtocol: ServerProtocol,
testing: Boolean,
servers: List<ServerCfg>,
servers: List<UserServer>,
serversUnchanged: Boolean,
saveDisabled: Boolean,
allServersDisabled: Boolean,
@@ -190,7 +190,7 @@ private fun ProtocolServersLayout(
testServers: () -> Unit,
resetServers: () -> Unit,
saveSMPServers: () -> Unit,
showServer: (ServerCfg) -> Unit,
showServer: (UserServer) -> Unit,
) {
ColumnWithScrollBar {
AppBarTitle(stringResource(if (serverProtocol == ServerProtocol.SMP) MR.strings.your_SMP_servers else MR.strings.your_XFTP_servers))
@@ -263,7 +263,7 @@ private fun ProtocolServersLayout(
}
@Composable
private fun ProtocolServerView(serverProtocol: ServerProtocol, srv: ServerCfg, servers: List<ServerCfg>, disabled: Boolean) {
private fun ProtocolServerView(serverProtocol: ServerProtocol, srv: UserServer, servers: List<UserServer>, disabled: Boolean) {
val address = parseServerAddress(srv.server)
when {
address == null || !address.valid || address.serverProtocol != serverProtocol || !uniqueAddress(srv, address, servers) -> InvalidServer()
@@ -296,17 +296,17 @@ fun InvalidServer() {
Icon(painterResource(MR.images.ic_error), null, tint = MaterialTheme.colors.error)
}
private fun uniqueAddress(s: ServerCfg, address: ServerAddress, servers: List<ServerCfg>): Boolean = servers.all { srv ->
private fun uniqueAddress(s: UserServer, address: ServerAddress, servers: List<UserServer>): Boolean = servers.all { srv ->
address.hostnames.all { host ->
srv.id == s.id || !srv.server.contains(host)
}
}
private fun hasAllPresets(presetServers: List<ServerCfg>, servers: List<ServerCfg>, m: ChatModel): Boolean =
private fun hasAllPresets(presetServers: List<UserServer>, servers: List<UserServer>, m: ChatModel): Boolean =
presetServers.all { hasPreset(it, servers) } ?: true
private fun addAllPresets(rhId: Long?, presetServers: List<ServerCfg>, servers: List<ServerCfg>, m: ChatModel): List<ServerCfg> {
val toAdd = ArrayList<ServerCfg>()
private fun addAllPresets(rhId: Long?, presetServers: List<UserServer>, servers: List<UserServer>, m: ChatModel): List<UserServer> {
val toAdd = ArrayList<UserServer>()
for (srv in presetServers) {
if (!hasPreset(srv, servers)) {
toAdd.add(srv)
@@ -315,10 +315,10 @@ private fun addAllPresets(rhId: Long?, presetServers: List<ServerCfg>, servers:
return toAdd
}
private fun hasPreset(srv: ServerCfg, servers: List<ServerCfg>): Boolean =
private fun hasPreset(srv: UserServer, servers: List<UserServer>): Boolean =
servers.any { it.server == srv.server }
private suspend fun testServers(testing: MutableState<Boolean>, servers: List<ServerCfg>, m: ChatModel, onUpdated: (List<ServerCfg>) -> Unit) {
private suspend fun testServers(testing: MutableState<Boolean>, servers: List<UserServer>, m: ChatModel, onUpdated: (List<UserServer>) -> Unit) {
val resetStatus = resetTestStatus(servers)
onUpdated(resetStatus)
testing.value = true
@@ -333,7 +333,7 @@ private suspend fun testServers(testing: MutableState<Boolean>, servers: List<Se
}
}
private fun resetTestStatus(servers: List<ServerCfg>): List<ServerCfg> {
private fun resetTestStatus(servers: List<UserServer>): List<UserServer> {
val copy = ArrayList(servers)
for ((index, server) in servers.withIndex()) {
if (server.enabled) {
@@ -344,9 +344,9 @@ private fun resetTestStatus(servers: List<ServerCfg>): List<ServerCfg> {
return copy
}
private suspend fun runServersTest(servers: List<ServerCfg>, m: ChatModel, onUpdated: (List<ServerCfg>) -> Unit): Map<String, ProtocolTestFailure> {
private suspend fun runServersTest(servers: List<UserServer>, m: ChatModel, onUpdated: (List<UserServer>) -> Unit): Map<String, ProtocolTestFailure> {
val fs: MutableMap<String, ProtocolTestFailure> = mutableMapOf()
val updatedServers = ArrayList<ServerCfg>(servers)
val updatedServers = ArrayList<UserServer>(servers)
for ((index, server) in servers.withIndex()) {
if (server.enabled) {
interruptIfCancelled()
@@ -363,11 +363,11 @@ private suspend fun runServersTest(servers: List<ServerCfg>, m: ChatModel, onUpd
return fs
}
private fun saveServers(rhId: Long?, protocol: ServerProtocol, currServers: MutableState<List<ServerCfg>>, servers: List<ServerCfg>, m: ChatModel, afterSave: () -> Unit = {}) {
private fun saveServers(rhId: Long?, protocol: ServerProtocol, currServers: MutableState<List<UserServer>>, servers: List<UserServer>, m: ChatModel, afterSave: () -> Unit = {}) {
withBGApi {
if (m.controller.setUserProtoServers(rhId, protocol, servers)) {
currServers.value = servers
}
// if (m.controller.setUserServers(rhId, protocol, servers)) {
// currServers.value = servers
// }
afterSave()
}
}
@@ -6,7 +6,7 @@ import androidx.compose.ui.Modifier
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.unit.dp
import chat.simplex.common.model.ServerAddress.Companion.parseServerAddress
import chat.simplex.common.model.ServerCfg
import chat.simplex.common.model.UserServer
import chat.simplex.common.platform.ColumnWithScrollBar
import chat.simplex.common.ui.theme.DEFAULT_PADDING
import chat.simplex.common.views.helpers.*
@@ -14,16 +14,16 @@ import chat.simplex.common.views.newchat.QRCodeScanner
import chat.simplex.res.MR
@Composable
expect fun ScanProtocolServer(rhId: Long?, onNext: (ServerCfg) -> Unit)
expect fun ScanProtocolServer(rhId: Long?, onNext: (UserServer) -> Unit)
@Composable
fun ScanProtocolServerLayout(rhId: Long?, onNext: (ServerCfg) -> Unit) {
fun ScanProtocolServerLayout(rhId: Long?, onNext: (UserServer) -> Unit) {
ColumnWithScrollBar {
AppBarTitle(stringResource(MR.strings.smp_servers_scan_qr))
QRCodeScanner { text ->
val res = parseServerAddress(text)
if (res != null) {
onNext(ServerCfg(remoteHostId = rhId, text, false, null, false))
onNext(UserServer(remoteHostId = rhId, null, text, false, null, false, false))
} else {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.smp_servers_invalid_address),
@@ -1,9 +1,9 @@
package chat.simplex.common.views.usersettings
import androidx.compose.runtime.Composable
import chat.simplex.common.model.ServerCfg
import chat.simplex.common.model.UserServer
@Composable
actual fun ScanProtocolServer(rhId: Long?, onNext: (ServerCfg) -> Unit) {
actual fun ScanProtocolServer(rhId: Long?, onNext: (UserServer) -> Unit) {
ScanProtocolServerLayout(rhId, onNext)
}