-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# *Feature Contact* ## ♻️ Current situation & Problem * #115 * The feature was also missing in the Engage-HF app ## ⚙️ Release Notes ### Contact Module <img src="https://github.com/user-attachments/assets/c269291f-bc53-40a6-aa1f-c45efa433610" width="200" alt="Contact Screen"/> <img src="https://github.com/user-attachments/assets/21495328-5c9b-41e1-a079-a1be3b83da6a" width="200" alt="Contact Screen"/> ### Engage-HF <img src="https://github.com/user-attachments/assets/eb05d2a5-bcd7-4ed3-a23b-6c625bc3db8a" width="200" alt="Contact Screen"/> ## 📝 Code of Conduct & Contributing Guidelines By submitting creating this pull request, you agree to follow our [Code of Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md): - [x] I agree to follow the [Code of Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md). --------- Signed-off-by: Basler182 <[email protected]>
- Loading branch information
Showing
24 changed files
with
713 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
app/src/main/kotlin/edu/stanford/bdh/engagehf/contact/data/ContactDocumentToContactMapper.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package edu.stanford.bdh.engagehf.contact.data | ||
|
||
import com.google.firebase.firestore.DocumentSnapshot | ||
import edu.stanford.spezi.core.design.component.StringResource | ||
import edu.stanford.spezi.modules.contact.model.Contact | ||
import edu.stanford.spezi.modules.contact.model.ContactOption | ||
import edu.stanford.spezi.modules.contact.model.PersonNameComponents | ||
import edu.stanford.spezi.modules.contact.model.call | ||
import edu.stanford.spezi.modules.contact.model.email | ||
import javax.inject.Inject | ||
|
||
class ContactDocumentToContactMapper @Inject constructor() { | ||
|
||
fun map(document: DocumentSnapshot): Result<Contact> = runCatching { | ||
val contactName = document.getString(CONTACT_NAME_FIELD) | ||
val organisationName = document.getString(ORGANISATION_NAME_FIELD) | ||
if (contactName == null || organisationName == null) { | ||
error("Missing required data, contactName: $contactName, organisation: $organisationName") | ||
} | ||
val components = contactName.split(", ") | ||
val nameComponents = components.firstOrNull()?.split(" ") | ||
val personNameComponents = PersonNameComponents( | ||
givenName = nameComponents?.getOrNull(0), | ||
familyName = nameComponents?.drop(1) | ||
?.joinToString(" ") // assigning everything besides given name here | ||
) | ||
val title = components.lastOrNull() | ||
val contactEmail = document.getString(CONTACT_EMAIL_FIELD) | ||
val phone = document.getString(CONTACT_PHONE_FIELD) | ||
Contact( | ||
name = personNameComponents, | ||
title = title?.let { StringResource(it) }, | ||
organization = StringResource(organisationName), | ||
options = listOfNotNull( | ||
phone?.let { ContactOption.call(it) }, | ||
contactEmail?.let { ContactOption.email(listOf(contactEmail)) }, | ||
), | ||
) | ||
} | ||
|
||
private companion object { | ||
const val CONTACT_NAME_FIELD = "contactName" | ||
const val CONTACT_EMAIL_FIELD = "emailAddress" | ||
const val CONTACT_PHONE_FIELD = "phoneNumber" | ||
const val ORGANISATION_NAME_FIELD = "name" | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
app/src/main/kotlin/edu/stanford/bdh/engagehf/contact/data/EngageContactRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package edu.stanford.bdh.engagehf.contact.data | ||
|
||
import com.google.firebase.firestore.FirebaseFirestore | ||
import edu.stanford.spezi.core.logging.speziLogger | ||
import edu.stanford.spezi.module.account.manager.UserSessionManager | ||
import edu.stanford.spezi.modules.contact.model.Contact | ||
import kotlinx.coroutines.tasks.await | ||
import javax.inject.Inject | ||
|
||
class EngageContactRepository @Inject constructor( | ||
private val firebaseFirestore: FirebaseFirestore, | ||
private val userSessionManager: UserSessionManager, | ||
private val contactDocumentToContactMapper: ContactDocumentToContactMapper, | ||
) { | ||
private val logger by speziLogger() | ||
|
||
suspend fun getContact(): Result<Contact> = runCatching { | ||
val uid = userSessionManager.getUserUid() ?: error("User not available") | ||
val organization = firebaseFirestore | ||
.collection(USERS_PATH) | ||
.document(uid) | ||
.get() | ||
.await() | ||
.getString(ORGANISATION_FIELD) ?: error("Organization not found") | ||
val contactDocument = firebaseFirestore.collection(ORGANISATION_PATH) | ||
.document(organization) | ||
.get() | ||
.await() | ||
|
||
contactDocumentToContactMapper.map(contactDocument).fold( | ||
onSuccess = { contact -> | ||
contact | ||
}, | ||
onFailure = { error -> | ||
logger.e(error) { "Failed to map contact" } | ||
error("Failed to map contact") | ||
} | ||
) | ||
} | ||
|
||
private companion object { | ||
const val ORGANISATION_FIELD = "organization" | ||
const val ORGANISATION_PATH = "organizations" | ||
const val USERS_PATH = "users" | ||
} | ||
} |
144 changes: 144 additions & 0 deletions
144
app/src/main/kotlin/edu/stanford/bdh/engagehf/contact/ui/ContactScreen.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package edu.stanford.bdh.engagehf.contact.ui | ||
|
||
import android.location.Address | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.material.icons.Icons | ||
import androidx.compose.material.icons.automirrored.filled.ArrowBack | ||
import androidx.compose.material.icons.filled.AccountBox | ||
import androidx.compose.material3.CircularProgressIndicator | ||
import androidx.compose.material3.Icon | ||
import androidx.compose.material3.IconButton | ||
import androidx.compose.material3.Scaffold | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.collectAsState | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.text.style.TextAlign | ||
import androidx.compose.ui.tooling.preview.PreviewParameter | ||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider | ||
import androidx.hilt.navigation.compose.hiltViewModel | ||
import edu.stanford.spezi.core.design.component.AppTopAppBar | ||
import edu.stanford.spezi.core.design.component.CenteredBoxContent | ||
import edu.stanford.spezi.core.design.component.StringResource | ||
import edu.stanford.spezi.core.design.theme.Colors.primary | ||
import edu.stanford.spezi.core.design.theme.Spacings | ||
import edu.stanford.spezi.core.design.theme.SpeziTheme | ||
import edu.stanford.spezi.core.design.theme.TextStyles | ||
import edu.stanford.spezi.core.design.theme.ThemePreviews | ||
import edu.stanford.spezi.core.notification.R | ||
import edu.stanford.spezi.modules.contact.ContactComposable | ||
import edu.stanford.spezi.modules.contact.model.Contact | ||
import edu.stanford.spezi.modules.contact.model.ContactOption | ||
import edu.stanford.spezi.modules.contact.model.PersonNameComponents | ||
import edu.stanford.spezi.modules.contact.model.call | ||
import edu.stanford.spezi.modules.contact.model.email | ||
import edu.stanford.spezi.modules.contact.model.website | ||
import java.util.Locale | ||
|
||
@Composable | ||
internal fun ContactScreen() { | ||
val viewModel = hiltViewModel<ContactScreenViewModel>() | ||
val uiState by viewModel.uiState.collectAsState() | ||
ContactScreen( | ||
onAction = viewModel::onAction, | ||
uiState = uiState, | ||
) | ||
} | ||
|
||
@Composable | ||
private fun ContactScreen( | ||
onAction: (ContactScreenViewModel.Action) -> Unit, | ||
uiState: ContactScreenViewModel.UiState, | ||
) { | ||
Scaffold(topBar = { | ||
AppTopAppBar(title = { | ||
Text( | ||
text = stringResource(edu.stanford.bdh.engagehf.R.string.contact), | ||
) | ||
}, navigationIcon = { | ||
IconButton(onClick = { | ||
onAction(ContactScreenViewModel.Action.Back) | ||
}) { | ||
Icon( | ||
Icons.AutoMirrored.Filled.ArrowBack, | ||
contentDescription = stringResource(R.string.back) | ||
) | ||
} | ||
}) | ||
}, content = { paddingValues -> | ||
Column( | ||
modifier = Modifier | ||
.padding(paddingValues) | ||
.padding(horizontal = Spacings.medium) | ||
) { | ||
when (uiState) { | ||
is ContactScreenViewModel.UiState.Error -> { | ||
CenteredBoxContent { | ||
Text( | ||
text = uiState.message, | ||
style = TextStyles.headlineMedium, | ||
textAlign = TextAlign.Center, | ||
) | ||
} | ||
} | ||
|
||
ContactScreenViewModel.UiState.Loading -> { | ||
CenteredBoxContent { | ||
CircularProgressIndicator(color = primary) | ||
} | ||
} | ||
|
||
is ContactScreenViewModel.UiState.ContactLoaded -> { | ||
val contact = uiState.contact | ||
ContactComposable( | ||
contact = contact, | ||
) | ||
} | ||
} | ||
} | ||
}) | ||
} | ||
|
||
private class ContactUiStateProvider : PreviewParameterProvider<ContactScreenViewModel.UiState> { | ||
override val values = sequenceOf( | ||
ContactScreenViewModel.UiState.Loading, | ||
ContactScreenViewModel.UiState.Error("An error occurred"), | ||
ContactScreenViewModel.UiState.ContactLoaded( | ||
contact = Contact( | ||
name = PersonNameComponents(givenName = "Leland", familyName = "Stanford"), | ||
image = Icons.Default.AccountBox, | ||
title = StringResource("University Founder"), | ||
description = StringResource( | ||
"""Leland Stanford (March 9, 1824 – June 21, 1893) was an American industrialist and politician.""" | ||
), | ||
organization = StringResource("Stanford University"), | ||
address = Address(Locale.US).apply { | ||
setAddressLine(0, "450 Jane Stanford Way") | ||
locality = "Stanford" | ||
adminArea = "CA" | ||
}, | ||
options = listOf( | ||
ContactOption.call("+49 123 456 789"), | ||
ContactOption.email(listOf("[email protected]")), | ||
ContactOption.website("https://www.google.com") | ||
) | ||
) | ||
) | ||
) | ||
} | ||
|
||
@ThemePreviews | ||
@Composable | ||
private fun ContactScreenPreview( | ||
@PreviewParameter(ContactUiStateProvider::class) uiState: ContactScreenViewModel.UiState, | ||
) { | ||
SpeziTheme { | ||
ContactScreen( | ||
onAction = {}, | ||
uiState = uiState | ||
) | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
app/src/main/kotlin/edu/stanford/bdh/engagehf/contact/ui/ContactScreenViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package edu.stanford.bdh.engagehf.contact.ui | ||
|
||
import androidx.lifecycle.ViewModel | ||
import androidx.lifecycle.viewModelScope | ||
import dagger.hilt.android.lifecycle.HiltViewModel | ||
import edu.stanford.bdh.engagehf.contact.data.EngageContactRepository | ||
import edu.stanford.spezi.core.logging.speziLogger | ||
import edu.stanford.spezi.core.navigation.NavigationEvent | ||
import edu.stanford.spezi.core.navigation.Navigator | ||
import edu.stanford.spezi.modules.contact.model.Contact | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.asStateFlow | ||
import kotlinx.coroutines.launch | ||
import javax.inject.Inject | ||
|
||
@HiltViewModel | ||
internal class ContactScreenViewModel @Inject constructor( | ||
private val engageContactRepository: EngageContactRepository, | ||
private val navigator: Navigator, | ||
) : ViewModel() { | ||
private val logger by speziLogger() | ||
|
||
private val _uiState = MutableStateFlow<UiState>(UiState.Loading) | ||
val uiState = _uiState.asStateFlow() | ||
|
||
init { | ||
loadContact() | ||
} | ||
|
||
private fun loadContact() { | ||
viewModelScope.launch { | ||
engageContactRepository.getContact().fold(onSuccess = { contact -> | ||
_uiState.value = UiState.ContactLoaded(contact) | ||
}, onFailure = { error -> | ||
_uiState.value = UiState.Error(error.message ?: "Failed to load contact") | ||
logger.e(error) { "Failed to load contact" } | ||
}) | ||
} | ||
} | ||
|
||
fun onAction(action: Action) { | ||
when (action) { | ||
Action.Back -> navigator.navigateTo(NavigationEvent.PopBackStack) | ||
} | ||
} | ||
|
||
sealed interface Action { | ||
data object Back : Action | ||
} | ||
|
||
sealed interface UiState { | ||
data object Loading : UiState | ||
data class Error(val message: String) : UiState | ||
data class ContactLoaded(val contact: Contact) : UiState | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.