Skip to content

Commit

Permalink
Determine invite org state correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
hueachilles committed Jan 4, 2024
1 parent 143ca94 commit 0c3eb92
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 13 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ plugins {

android {
defaultConfig {
val buildVersion = 177
val buildVersion = 178
applicationId = "com.crisiscleanup"
versionCode = buildVersion
versionName = "0.9.${buildVersion - 168}"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.crisiscleanup.feature.organizationmanage

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.ImageBitmap
Expand All @@ -16,6 +17,7 @@ import com.crisiscleanup.core.common.log.CrisisCleanupLoggers.Onboarding
import com.crisiscleanup.core.common.log.Logger
import com.crisiscleanup.core.common.network.CrisisCleanupDispatchers.IO
import com.crisiscleanup.core.common.network.Dispatcher
import com.crisiscleanup.core.common.throttleLatest
import com.crisiscleanup.core.data.IncidentSelectManager
import com.crisiscleanup.core.data.repository.AccountDataRepository
import com.crisiscleanup.core.data.repository.OrgVolunteerRepository
Expand All @@ -33,7 +35,6 @@ import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
Expand All @@ -47,7 +48,6 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds

@HiltViewModel
class InviteTeammateViewModel @Inject constructor(
Expand All @@ -65,9 +65,9 @@ class InviteTeammateViewModel @Inject constructor(
) : ViewModel() {
private val inviteUrl = "${settingsProvider.baseUrl}/mobile_app_user_invite"

val isValidatingAccount = MutableStateFlow(false)
private val isValidatingAccount = MutableStateFlow(false)

val accountData = accountDataRepository.accountData
private val accountData = accountDataRepository.accountData
.shareIn(
scope = viewModelScope,
replay = 1,
Expand All @@ -84,7 +84,7 @@ class InviteTeammateViewModel @Inject constructor(

val inviteToAnotherOrg = MutableStateFlow(false)
private val affiliateOrganizationIds = MutableStateFlow<Set<Long>?>(null)
val selectedOtherOrg = MutableStateFlow(OrganizationIdName(0, ""))
private val selectedOtherOrg = MutableStateFlow(OrganizationIdName(0, ""))
val organizationNameQuery = MutableStateFlow("")
private val isSearchingLocalOrganizations = MutableStateFlow(false)
private val isSearchingNetworkOrganizations = MutableStateFlow(false)
Expand Down Expand Up @@ -163,7 +163,7 @@ class InviteTeammateViewModel @Inject constructor(
)

private val qFlow = organizationNameQuery
.debounce(0.3.seconds)
.throttleLatest(300)
.map(String::trim)
.shareIn(
scope = viewModelScope,
Expand Down Expand Up @@ -200,7 +200,7 @@ class InviteTeammateViewModel @Inject constructor(

val incidents = MutableStateFlow(emptyList<Incident>())
val incidentLookup = MutableStateFlow(emptyMap<Long, Incident>())
var selectedIncidentId by mutableStateOf(EmptyIncident.id)
var selectedIncidentId by mutableLongStateOf(EmptyIncident.id)

// TODO Size QR codes relative to min screen dimension
private val qrCodeSize = 512 + 256
Expand Down Expand Up @@ -479,6 +479,23 @@ class InviteTeammateViewModel @Inject constructor(
joinMyOrgInvite.value = it
}
.launchIn(viewModelScope)

inviteOrgState
.throttleLatest(300)
.onEach {
clearErrors()
}
.launchIn(viewModelScope)
}

private fun clearErrors() {
emailAddressError = ""
phoneNumberError = ""
firstNameError = ""
lastNameError = ""
selectedIncidentError = ""

sendInviteErrorMessage.value = ""
}

private fun makeInviteUrl(userId: Long, invite: JoinOrgInvite): String {
Expand Down Expand Up @@ -528,11 +545,14 @@ class InviteTeammateViewModel @Inject constructor(
break
}
}
if (selectedOtherOrg.value.id != matchingOrg.id) {
val selectedOrgId = selectedOtherOrg.value.id
if ((selectedOrgId == 0L || selectedOrgId != matchingOrg.id) &&
matchingOrg.id == 0L
) {
matchingOrg = OrganizationIdName(0, q)
selectedOtherOrg.value = matchingOrg
organizationNameQuery.value = matchingOrg.name
}
selectedOtherOrg.value = matchingOrg
organizationNameQuery.value = matchingOrg.name
}

private suspend fun inviteToOrgOrAffiliate(
Expand Down Expand Up @@ -579,7 +599,7 @@ class InviteTeammateViewModel @Inject constructor(
sendInvites()
} catch (e: Exception) {
sendInviteErrorMessage.value =
translator("~~Invites are broken. Sorry for the inconvenience. Please try again later.")
translator("~~Invites are not working at the moment. Please try again later.")
logger.logException(e)
} finally {
isSendingInvite.value = false
Expand Down Expand Up @@ -649,8 +669,9 @@ class InviteTeammateViewModel @Inject constructor(
}

var isSentToOrgOrAffiliate = false
var isInviteSuccessful = false
if (inviteToAnotherOrg.value) {
if (inviteState.new && emailAddresses.size == 1) {
if (inviteState.new) {
val organizationName = q.trim()
val emailContact = emailAddresses[0]
val isRegisterNewOrganization = orgVolunteerRepository.createOrganization(
Expand All @@ -672,20 +693,29 @@ class InviteTeammateViewModel @Inject constructor(
text = translator("registerOrg.we_will_finalize_registration")
.replace("{email}", emailContact),
)

isInviteSuccessful = true
}
} else if (inviteState.affiliate) {
isSentToOrgOrAffiliate =
inviteToOrgOrAffiliate(emailAddresses, selectedOtherOrg.value.id)
isInviteSuccessful = isSentToOrgOrAffiliate
} else if (inviteState.nonAffiliate) {
// TODO Finish when API supports a corresponding endpoint
}
} else {
isSentToOrgOrAffiliate = inviteToOrgOrAffiliate(emailAddresses)
isInviteSuccessful = isSentToOrgOrAffiliate
}

if (isSentToOrgOrAffiliate) {
onInviteSentToOrgOrAffiliate(emailAddresses)
}

if (!isInviteSuccessful) {
sendInviteErrorMessage.value =
translator("~~Invites are not working at the moment. Please try again later.")
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.layout.onGloballyPositioned
Expand Down Expand Up @@ -158,6 +159,10 @@ fun InviteTeammateContent(
val onDismissOrgOptions = remember(viewModel) {
{
dismissOrganizationQuery = organizationNameQuery
}
}
val onQueryFocusOut = remember(viewModel) {
{
viewModel.onOrgQueryClose()
}
}
Expand Down Expand Up @@ -233,6 +238,7 @@ fun InviteTeammateContent(
organizations = matchingOrganizations,
onOrgSelect = onOrgSelect,
onDismissDropdown = onDismissOrgOptions,
onFocusOut = onQueryFocusOut,
)

if (inviteToAnotherOrg) {
Expand Down Expand Up @@ -358,6 +364,7 @@ private fun OrgQueryInput(
organizations: List<OrganizationIdName>,
onOrgSelect: (OrganizationIdName) -> Unit,
onDismissDropdown: () -> Unit,
onFocusOut: () -> Unit,
) {
val t = LocalAppTranslator.current

Expand All @@ -369,6 +376,11 @@ private fun OrgQueryInput(
.fillMaxWidth()
.onGloballyPositioned {
contentSize = it.size.toSize()
}
.onFocusChanged {
if (!it.isFocused) {
onFocusOut()
}
},
label = t("profileOrg.organization_name"),
value = organizationNameQuery,
Expand Down

0 comments on commit 0c3eb92

Please sign in to comment.