mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-23 10:43:05 +00:00
refactor core-UI API to NameWarning with logic in core
This commit is contained in:
1 parent
b78d76ae64
commit
38b79929ca
19 files changed
+697
-627
No files matched your search
+10
-29
@@ -5253,38 +5253,19 @@ enum class SimplexTLD {
|
||||
}
|
||||
|
||||
@Serializable
|
||||
sealed class NameRegistration {
|
||||
// held by someone; expires/graceUntil are absent from an older router, which means "not known", not "live forever"
|
||||
@Serializable @SerialName("registered") class Registered(
|
||||
val expires: Long? = null,
|
||||
val graceUntil: Long? = null,
|
||||
val reservedReason_: String? = null
|
||||
): NameRegistration()
|
||||
@Serializable @SerialName("available") class Available(val pricing: NamePricing): NameRegistration()
|
||||
@Serializable @SerialName("reserved") class Reserved(val reservedReason: String): NameRegistration()
|
||||
|
||||
fun expired(now: Long): Boolean = this is Registered && expires != null && expires < now
|
||||
|
||||
val reservedForCommunity: Boolean get() = when (this) {
|
||||
is Reserved -> reservedReason == RESERVED_COMMUNITY
|
||||
is Registered -> reservedReason_ == RESERVED_COMMUNITY
|
||||
is Available -> false
|
||||
}
|
||||
|
||||
companion object {
|
||||
// the registry may add reasons after this version, so any other value is just "not registrable"
|
||||
const val RESERVED_COMMUNITY = "community"
|
||||
}
|
||||
sealed class NameWarning {
|
||||
@Serializable @SerialName("expired") class Expired(val expiredAt: Instant, val graceUntil: Instant? = null): NameWarning()
|
||||
@Serializable @SerialName("ownExpired") class OwnExpired(val expiredAt: Instant, val graceUntil: Instant? = null): NameWarning()
|
||||
@Serializable @SerialName("available") class Available(val price: NamePrice): NameWarning()
|
||||
@Serializable @SerialName("noLongerRegistered") class NoLongerRegistered(val price: NamePrice): NameWarning()
|
||||
@Serializable @SerialName("ownAvailable") class OwnAvailable(val price: NamePrice): NameWarning()
|
||||
@Serializable @SerialName("reservedForCommunity") object ReservedForCommunity: NameWarning()
|
||||
@Serializable @SerialName("notRegistered") object NotRegistered: NameWarning()
|
||||
@Serializable @SerialName("noValidLink") object NoValidLink: NameWarning()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class NamePricing(
|
||||
val registrationPrices: Map<String, Long> = emptyMap(),
|
||||
val basePrice: Long,
|
||||
val minLabelLength: Int
|
||||
) {
|
||||
fun centsPerYear(labelLength: Int): Long = registrationPrices[labelLength.toString()] ?: basePrice
|
||||
}
|
||||
data class NamePrice(val amount: Long, val years: Int)
|
||||
|
||||
@Serializable
|
||||
enum class SimplexNameType {
|
||||
|
||||
+7
-7
@@ -7441,15 +7441,15 @@ enum class PlanResolveMode {
|
||||
@Serializable
|
||||
sealed class ConnectionPlan {
|
||||
@Serializable @SerialName("invitationLink") class InvitationLink(val invitationLinkPlan: InvitationLinkPlan): ConnectionPlan()
|
||||
@Serializable @SerialName("contactAddress") class ContactAddress(val contactAddressPlan: ContactAddressPlan, val nameRegistration_: NameRegistration? = null): ConnectionPlan()
|
||||
@Serializable @SerialName("groupLink") class GroupLink(val groupLinkPlan: GroupLinkPlan, val nameRegistration_: NameRegistration? = null): ConnectionPlan()
|
||||
@Serializable @SerialName("nameNotConnectable") class NameNotConnectable(val simplexDomain: SimplexDomain, val nameRegistration: NameRegistration): ConnectionPlan()
|
||||
@Serializable @SerialName("contactAddress") class ContactAddress(val contactAddressPlan: ContactAddressPlan, val nameWarning_: NameWarning? = null): ConnectionPlan()
|
||||
@Serializable @SerialName("groupLink") class GroupLink(val groupLinkPlan: GroupLinkPlan, val nameWarning_: NameWarning? = null): ConnectionPlan()
|
||||
@Serializable @SerialName("nameNotConnectable") class NameNotConnectable(val simplexDomain: SimplexDomain, val nameWarning: NameWarning): ConnectionPlan()
|
||||
@Serializable @SerialName("error") class Error(val chatError: ChatError): ConnectionPlan()
|
||||
|
||||
fun nameRegistration(): NameRegistration? = when (this) {
|
||||
is ContactAddress -> nameRegistration_
|
||||
is GroupLink -> nameRegistration_
|
||||
is NameNotConnectable -> nameRegistration
|
||||
fun nameWarning(): NameWarning? = when (this) {
|
||||
is ContactAddress -> nameWarning_
|
||||
is GroupLink -> nameWarning_
|
||||
is NameNotConnectable -> nameWarning
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -300,6 +300,7 @@ class AlertManager {
|
||||
connectOtherButton: String? = null,
|
||||
onConnectOther: (() -> Unit)? = null,
|
||||
dismissText: String = generalGetString(MR.strings.cancel_verb),
|
||||
onDismissButton: (() -> Unit)? = null,
|
||||
onDismiss: (() -> Unit)? = null,
|
||||
) {
|
||||
showAlert {
|
||||
@@ -413,7 +414,7 @@ class AlertManager {
|
||||
}
|
||||
}
|
||||
TextButton(onClick = {
|
||||
onDismiss?.invoke()
|
||||
(onDismissButton ?: onDismiss)?.invoke()
|
||||
hideAlert()
|
||||
}, if (confirmText == null) Modifier.focusRequester(focusRequester) else Modifier) {
|
||||
Text(dismissText)
|
||||
|
||||
+88
-82
@@ -60,29 +60,25 @@ suspend fun planAndConnect(
|
||||
return planAndConnectTask(rhId, shortOrFullLink, linkOwnerSig, close, cleanup, filterKnownContact, filterKnownGroup, inProgress)
|
||||
}
|
||||
|
||||
private fun nameDate(seconds: Long): String =
|
||||
Instant.fromEpochSeconds(seconds).toLocalDateTime(TimeZone.currentSystemDefault()).date.toString()
|
||||
private fun nameDate(t: Instant): String =
|
||||
t.toLocalDateTime(TimeZone.currentSystemDefault()).date.toString()
|
||||
|
||||
private fun nameCentsPerYear(pricing: NamePricing, domain: SimplexDomain): String {
|
||||
val cents = pricing.centsPerYear(domain.domain.length)
|
||||
return "$" + (cents / 100).toString() + (if (cents % 100 == 0L) "" else ".%02d".format(cents % 100))
|
||||
private fun namePrice(price: NamePrice): String {
|
||||
val dollars = "$" + (price.amount / 100).toString() + (if (price.amount % 100 == 0L) "" else ".%02d".format(price.amount % 100))
|
||||
return String.format(generalGetString(MR.strings.simplex_name_price_for_years), dollars, price.years)
|
||||
}
|
||||
|
||||
private fun openNameHowTo(uriHandler: UriHandler) = openBrowserAlert(SIMPLEX_NAMES_HOWTO_URL, uriHandler)
|
||||
|
||||
private const val SIMPLEX_NAMES_HOWTO_URL = "https://simplex.domains/#testing"
|
||||
|
||||
private fun showNameRegistrationAlert(
|
||||
private fun showNameWarningAlert(
|
||||
domain: SimplexDomain,
|
||||
reg: NameRegistration,
|
||||
isOwn: Boolean,
|
||||
notConnectable: Boolean,
|
||||
hasLocalChat: Boolean,
|
||||
warning: NameWarning,
|
||||
openExistingChat: (() -> Unit)?,
|
||||
cleanup: (() -> Unit)?
|
||||
): Boolean {
|
||||
) {
|
||||
val nameStr = domain.fullDomainName
|
||||
val now = Clock.System.now().epochSeconds
|
||||
val ok = generalGetString(MR.strings.ok)
|
||||
fun dismiss() {
|
||||
AlertManager.privacySensitive.hideAlert()
|
||||
@@ -109,62 +105,44 @@ private fun showNameRegistrationAlert(
|
||||
fun alert(title: String, text: String, action: Pair<String, (UriHandler) -> Unit>? = null) {
|
||||
AlertManager.privacySensitive.showAlertDialogButtonsColumn(title = title, text = text, onDismissRequest = { cleanup?.invoke() }, buttons = buttons(action))
|
||||
}
|
||||
val expired = reg.expired(now)
|
||||
return when {
|
||||
expired && reg is NameRegistration.Registered -> {
|
||||
val until = reg.graceUntil
|
||||
if (isOwn) {
|
||||
val text = if (until != null)
|
||||
String.format(generalGetString(MR.strings.simplex_name_own_expired_desc), nameStr, nameDate(reg.expires!!), nameDate(until))
|
||||
else String.format(generalGetString(MR.strings.simplex_name_own_expired_no_date_desc), nameStr)
|
||||
alert(generalGetString(MR.strings.simplex_name_own_expired), text, generalGetString(MR.strings.simplex_name_renew) to { uh: UriHandler -> openNameHowTo(uh) })
|
||||
} else {
|
||||
val text = if (until != null)
|
||||
String.format(generalGetString(MR.strings.simplex_name_expired_desc), nameStr, nameDate(reg.expires!!), nameDate(until))
|
||||
else String.format(generalGetString(MR.strings.simplex_name_expired_no_date_desc), nameStr)
|
||||
alert(generalGetString(MR.strings.simplex_name_expired), text)
|
||||
}
|
||||
true
|
||||
}
|
||||
reg is NameRegistration.Available && domain.domain.length >= reg.pricing.minLabelLength -> {
|
||||
val price = nameCentsPerYear(reg.pricing, domain)
|
||||
if (isOwn) {
|
||||
alert(
|
||||
generalGetString(MR.strings.simplex_name_own_expired),
|
||||
String.format(generalGetString(MR.strings.simplex_name_own_available_desc), nameStr, price),
|
||||
generalGetString(MR.strings.simplex_name_re_register) to { uh: UriHandler -> openNameHowTo(uh) }
|
||||
)
|
||||
} else {
|
||||
alert(
|
||||
generalGetString(if (hasLocalChat) MR.strings.simplex_name_no_longer_registered else MR.strings.simplex_name_not_registered),
|
||||
String.format(generalGetString(MR.strings.simplex_name_available_desc), nameStr, price),
|
||||
generalGetString(MR.strings.simplex_name_register) to { uh: UriHandler -> openNameHowTo(uh) }
|
||||
)
|
||||
}
|
||||
true
|
||||
}
|
||||
reg.reservedForCommunity -> {
|
||||
alert(
|
||||
generalGetString(MR.strings.simplex_name_not_registered),
|
||||
String.format(generalGetString(MR.strings.simplex_name_reserved_community_desc), nameStr),
|
||||
generalGetString(MR.strings.simplex_name_connect_simplex_team) to { uh: UriHandler ->
|
||||
uh.openVerifiedSimplexUri(simplexTeamUri)
|
||||
}
|
||||
)
|
||||
true
|
||||
}
|
||||
(reg is NameRegistration.Reserved || reg is NameRegistration.Available) && !hasLocalChat && !isOwn -> {
|
||||
alert(generalGetString(MR.strings.simplex_name_not_registered), generalGetString(MR.strings.simplex_name_not_found_desc))
|
||||
true
|
||||
}
|
||||
reg is NameRegistration.Registered && notConnectable && !hasLocalChat && !isOwn -> {
|
||||
alert(
|
||||
generalGetString(MR.strings.simplex_name_no_valid_link),
|
||||
String.format(generalGetString(MR.strings.simplex_name_no_valid_link_desc), nameStr)
|
||||
)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
val register = generalGetString(MR.strings.simplex_name_register) to { uh: UriHandler -> openNameHowTo(uh) }
|
||||
when (warning) {
|
||||
is NameWarning.Expired -> alert(
|
||||
generalGetString(MR.strings.simplex_name_expired),
|
||||
if (warning.graceUntil != null) String.format(generalGetString(MR.strings.simplex_name_expired_desc), nameStr, nameDate(warning.expiredAt), nameDate(warning.graceUntil))
|
||||
else String.format(generalGetString(MR.strings.simplex_name_expired_no_date_desc), nameStr)
|
||||
)
|
||||
is NameWarning.OwnExpired -> alert(
|
||||
generalGetString(MR.strings.simplex_name_own_expired),
|
||||
if (warning.graceUntil != null) String.format(generalGetString(MR.strings.simplex_name_own_expired_desc), nameStr, nameDate(warning.expiredAt), nameDate(warning.graceUntil))
|
||||
else String.format(generalGetString(MR.strings.simplex_name_own_expired_no_date_desc), nameStr),
|
||||
generalGetString(MR.strings.simplex_name_renew) to { uh: UriHandler -> openNameHowTo(uh) }
|
||||
)
|
||||
is NameWarning.Available -> alert(
|
||||
generalGetString(MR.strings.simplex_name_not_registered),
|
||||
String.format(generalGetString(MR.strings.simplex_name_available_desc), nameStr, namePrice(warning.price)),
|
||||
register
|
||||
)
|
||||
is NameWarning.NoLongerRegistered -> alert(
|
||||
generalGetString(MR.strings.simplex_name_no_longer_registered),
|
||||
String.format(generalGetString(MR.strings.simplex_name_available_desc), nameStr, namePrice(warning.price)),
|
||||
register
|
||||
)
|
||||
is NameWarning.OwnAvailable -> alert(
|
||||
generalGetString(MR.strings.simplex_name_own_expired),
|
||||
String.format(generalGetString(MR.strings.simplex_name_own_available_desc), nameStr, namePrice(warning.price)),
|
||||
generalGetString(MR.strings.simplex_name_re_register) to { uh: UriHandler -> openNameHowTo(uh) }
|
||||
)
|
||||
NameWarning.ReservedForCommunity -> alert(
|
||||
generalGetString(MR.strings.simplex_name_not_registered),
|
||||
String.format(generalGetString(MR.strings.simplex_name_reserved_community_desc), nameStr),
|
||||
generalGetString(MR.strings.simplex_name_connect_simplex_team) to { uh: UriHandler -> uh.openVerifiedSimplexUri(simplexTeamUri) }
|
||||
)
|
||||
NameWarning.NotRegistered -> alert(generalGetString(MR.strings.simplex_name_not_registered), generalGetString(MR.strings.simplex_name_not_found_desc))
|
||||
NameWarning.NoValidLink -> alert(
|
||||
generalGetString(MR.strings.simplex_name_no_valid_link),
|
||||
String.format(generalGetString(MR.strings.simplex_name_no_valid_link_desc), nameStr)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,16 +179,14 @@ private suspend fun planAndConnectTask(
|
||||
val label = if (it.nameType == SimplexNameType.publicGroup) MR.strings.connect_plan_join_name else MR.strings.connect_plan_connect_to_name
|
||||
generalGetString(label).format(it.shortStr)
|
||||
}
|
||||
val nameReg = connectionPlan.nameRegistration()
|
||||
val nameWarning = connectionPlan.nameWarning()
|
||||
val nameDomain = when (connectionPlan) {
|
||||
is ConnectionPlan.NameNotConnectable -> connectionPlan.simplexDomain
|
||||
else -> planSimplexName?.nameDomain
|
||||
}
|
||||
if (nameReg != null && nameDomain != null) {
|
||||
if (nameWarning != null && nameDomain != null) {
|
||||
val knownContact = (connectionPlan as? ConnectionPlan.ContactAddress)?.contactAddressPlan.let { it as? ContactAddressPlan.Known }?.contact
|
||||
val knownGroup = (connectionPlan as? ConnectionPlan.GroupLink)?.groupLinkPlan.let { it as? GroupLinkPlan.Known }?.groupInfo
|
||||
val isOwnName = (connectionPlan as? ConnectionPlan.ContactAddress)?.contactAddressPlan is ContactAddressPlan.OwnLink ||
|
||||
(connectionPlan as? ConnectionPlan.GroupLink)?.groupLinkPlan is GroupLinkPlan.OwnLink
|
||||
var openExisting: (() -> Unit)? = null
|
||||
if (knownContact != null) {
|
||||
if (filterKnownContact != null) filterKnownContact(knownContact)
|
||||
@@ -219,16 +195,8 @@ private suspend fun planAndConnectTask(
|
||||
if (filterKnownGroup != null) filterKnownGroup(knownGroup)
|
||||
else openExisting = { openKnownGroup(chatModel, rhId, close, knownGroup) }
|
||||
}
|
||||
val handled = showNameRegistrationAlert(
|
||||
domain = nameDomain,
|
||||
reg = nameReg,
|
||||
isOwn = isOwnName,
|
||||
notConnectable = connectionPlan is ConnectionPlan.NameNotConnectable,
|
||||
hasLocalChat = knownContact != null || knownGroup != null,
|
||||
openExistingChat = openExisting,
|
||||
cleanup = cleanup
|
||||
)
|
||||
if (handled) return completable
|
||||
showNameWarningAlert(nameDomain, nameWarning, openExisting, cleanup)
|
||||
return completable
|
||||
}
|
||||
val connectionLink = connectionLink_ ?: run {
|
||||
cleanup()
|
||||
@@ -312,6 +280,9 @@ private suspend fun planAndConnectTask(
|
||||
connectOtherButton = connectOtherButton,
|
||||
connectOtherLink = connectOtherLink,
|
||||
addressChanged = connectionPlan.contactAddressPlan.addressChanged,
|
||||
openExistingChat = if (filterKnownContact == null && connectionPlan.contactAddressPlan.addressChanged && planSimplexName != null) {
|
||||
{ openLocalNameChat(rhId, planSimplexName, close, cleanup) }
|
||||
} else null,
|
||||
close,
|
||||
cleanup
|
||||
)
|
||||
@@ -373,6 +344,7 @@ private suspend fun planAndConnectTask(
|
||||
}
|
||||
if (filterKnownContact != null) {
|
||||
filterKnownContact(contact)
|
||||
if (planSimplexName != null && otherSimplexName != null && connectOtherButton != null) showOtherNameAlert(rhId, planSimplexName, otherSimplexName, connectOtherButton, close, cleanup)
|
||||
} else {
|
||||
showOpenKnownContactAlert(chatModel, rhId, close, contact, planSimplexName = planSimplexName, connectOtherButton = connectOtherButton, connectOtherLink = connectOtherLink)
|
||||
cleanup()
|
||||
@@ -478,6 +450,7 @@ private suspend fun planAndConnectTask(
|
||||
}
|
||||
if (filterKnownGroup != null) {
|
||||
filterKnownGroup(groupInfo)
|
||||
if (planSimplexName != null && otherSimplexName != null && connectOtherButton != null) showOtherNameAlert(rhId, planSimplexName, otherSimplexName, connectOtherButton, close, cleanup)
|
||||
} else {
|
||||
showOpenKnownGroupAlert(chatModel, rhId, close, groupInfo, planSimplexName = planSimplexName, connectOtherButton = connectOtherButton, connectOtherLink = connectOtherLink)
|
||||
cleanup()
|
||||
@@ -856,6 +829,7 @@ fun showPrepareContactAlert(
|
||||
connectOtherButton: String? = null,
|
||||
connectOtherLink: String? = null,
|
||||
addressChanged: Boolean = false,
|
||||
openExistingChat: (() -> Unit)? = null,
|
||||
close: (() -> Unit)?,
|
||||
cleanup: (() -> Unit)?
|
||||
) {
|
||||
@@ -895,12 +869,44 @@ fun showPrepareContactAlert(
|
||||
},
|
||||
connectOtherButton = connectOtherButton,
|
||||
onConnectOther = connectOtherLink?.let { link -> { withBGApi { planAndConnect(rhId, link, close = close, cleanup = cleanup) } } },
|
||||
dismissText = generalGetString(if (openExistingChat != null) MR.strings.connect_plan_open_existing_chat else MR.strings.cancel_verb),
|
||||
onDismissButton = openExistingChat,
|
||||
onDismiss = {
|
||||
cleanup?.invoke()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// a name tapped in a message has no filtered list behind the alert, so the chat the name had is found and opened here
|
||||
private fun openLocalNameChat(rhId: Long?, name: SimplexNameInfo, close: (() -> Unit)?, cleanup: (() -> Unit)?) {
|
||||
withBGApi {
|
||||
val chatId = knownChatId(rhId, chatModel.controller.apiConnectPlan(rhId, name.shortStr, PlanResolveMode.PRMNever, inProgress = mutableStateOf(false)))
|
||||
val chat = chatId?.let { chatModel.getChat(it) }
|
||||
if (chat != null) withContext(Dispatchers.Main) { openChat_(chatModel, rhId, close, chat) }
|
||||
cleanup?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
// a bare name found as a local chat also leads to the other kind, which the user does not have
|
||||
private fun showOtherNameAlert(rhId: Long?, planSimplexName: SimplexNameInfo, otherSimplexName: SimplexNameInfo, connectOtherButton: String, close: (() -> Unit)?, cleanup: (() -> Unit)?) {
|
||||
AlertManager.privacySensitive.showAlertDialogButtonsColumn(
|
||||
title = String.format(generalGetString(MR.strings.simplex_name_also_leads_to), planSimplexName.nameDomain.fullDomainName, otherSimplexName.shortStr),
|
||||
buttons = {
|
||||
Column {
|
||||
SectionItemView({
|
||||
AlertManager.privacySensitive.hideAlert()
|
||||
withBGApi { planAndConnect(rhId, otherSimplexName.shortStr, close = close, cleanup = cleanup) }
|
||||
}) {
|
||||
Text(connectOtherButton, Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
|
||||
}
|
||||
SectionItemView({ AlertManager.privacySensitive.hideAlert() }) {
|
||||
Text(generalGetString(MR.strings.ok), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun showPrepareGroupAlert(
|
||||
rhId: Long?,
|
||||
connectionLink: CreatedConnLink,
|
||||
|
||||
@@ -211,12 +211,14 @@
|
||||
<string name="simplex_name_own_expired">Your name has expired</string>
|
||||
<string name="simplex_name_own_expired_desc">Your name %1$s expired on %2$s. Renew it before %3$s.</string>
|
||||
<string name="simplex_name_own_expired_no_date_desc">Your name %1$s has expired. Renew it before the grace period ends.</string>
|
||||
<string name="simplex_name_own_available_desc">Your name %1$s is no longer registered. It is available for registration from %2$s per year.</string>
|
||||
<string name="simplex_name_own_available_desc">Your name %1$s is no longer registered. It is available for registration for %2$s.</string>
|
||||
<string name="simplex_name_not_registered">Name not registered</string>
|
||||
<string name="simplex_name_no_longer_registered">Name no longer registered</string>
|
||||
<string name="simplex_name_available_desc">%1$s is available for registration from %2$s per year.</string>
|
||||
<string name="simplex_name_available_desc">%1$s is available for registration for %2$s.</string>
|
||||
<string name="simplex_name_price_for_years">%1$s for %2$d years</string>
|
||||
<string name="simplex_name_reserved_community_desc">%1$s is reserved for community. Please contact SimpleX Chat if you are interested in using it.</string>
|
||||
<string name="simplex_name_address_changed">%1$s now leads to a new address.</string>
|
||||
<string name="simplex_name_also_leads_to">%1$s also leads to %2$s</string>
|
||||
<string name="simplex_name_register">Register</string>
|
||||
<string name="simplex_name_re_register">Re-register</string>
|
||||
<string name="simplex_name_renew">Renew</string>
|
||||
|
||||
Reference in new issue
Block a user