Skip to content
This repository was archived by the owner on Aug 21, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ internal sealed class ShareEvent {
data class OnEmailSharingChanged(val isShared: Boolean) : ShareEvent()
data class OnPhoneValueChanged(val value: String) : ShareEvent()
data class OnPhoneSharingChanged(val isShared: Boolean) : ShareEvent()
data class OnUserSharePreferencesChanged(val shareFieldType: ShareFieldType) : ShareEvent()
data object OnAboutAppClicked : ShareEvent()
data object OnDismissAboutAppDialog : ShareEvent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package com.gravatar.app.homeUi.presentation.home.share
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
Expand All @@ -16,8 +18,11 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModelStoreOwner
import com.gravatar.app.design.theme.GravatarAppTheme
import com.gravatar.app.homeUi.presentation.home.components.topbar.components.AboutAppDialog
import com.gravatar.app.homeUi.presentation.home.share.components.ItemDivider
import com.gravatar.app.homeUi.presentation.home.share.components.ShareHeader
import com.gravatar.app.homeUi.presentation.home.share.components.SharePrivateContactInfo
import com.gravatar.app.homeUi.presentation.home.share.components.SharePublicContactInfo
import com.gravatar.extensions.defaultProfile
import org.koin.androidx.compose.koinViewModel

@Suppress("UnusedParameter")
Expand All @@ -39,28 +44,43 @@ internal fun ShareScreen(

@Composable
internal fun ShareScreen(uiState: ShareUiState, onEvent: (ShareEvent) -> Unit) {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
ShareHeader(
avatarUrl = uiState.avatarUrl.orEmpty(),
Surface {
Column(
modifier = Modifier
.fillMaxWidth(),
onAboutAppClicked = {
onEvent(ShareEvent.OnAboutAppClicked)
.fillMaxSize()
.imePadding()
.verticalScroll(rememberScrollState())
) {
ShareHeader(
avatarUrl = uiState.avatarUrl.orEmpty(),
onAboutAppClicked = {
onEvent(ShareEvent.OnAboutAppClicked)
},
modifier = Modifier
.fillMaxWidth(),
)
SharePrivateContactInfo(
privateContactInfo = uiState.privateContactInfo,
onEmailValueChange = { onEvent(ShareEvent.OnEmailValueChanged(it)) },
onEmailSwitchCheckedChange = { onEvent(ShareEvent.OnEmailSharingChanged(it)) },
onPhoneValueChange = { onEvent(ShareEvent.OnPhoneValueChanged(it)) },
onPhoneSwitchCheckedChange = { onEvent(ShareEvent.OnPhoneSharingChanged(it)) },
modifier = Modifier.padding(16.dp),
)
ItemDivider()
uiState.profile?.let { profile ->
SharePublicContactInfo(
profile = profile,
userSharePreferences = uiState.userSharePreferences,
onUserPreferenceChanged = { userSharePreferences ->
onEvent(ShareEvent.OnUserSharePreferencesChanged(userSharePreferences))
},
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp)
)
}
)

SharePrivateContactInfo(
privateContactInfo = uiState.privateContactInfo,
onEmailValueChange = { onEvent(ShareEvent.OnEmailValueChanged(it)) },
onEmailSwitchCheckedChange = { onEvent(ShareEvent.OnEmailSharingChanged(it)) },
onPhoneValueChange = { onEvent(ShareEvent.OnPhoneValueChanged(it)) },
onPhoneSwitchCheckedChange = { onEvent(ShareEvent.OnPhoneSharingChanged(it)) },
modifier = Modifier.padding(16.dp),
)
}
}

if (uiState.isAboutAppDialogVisible) {
Expand All @@ -77,7 +97,12 @@ internal fun ShareScreen(uiState: ShareUiState, onEvent: (ShareEvent) -> Unit) {
private fun ShareScreenPreview() {
GravatarAppTheme {
ShareScreen(
uiState = ShareUiState(isAboutAppDialogVisible = false),
uiState = ShareUiState(
profile = defaultProfile(
hash = ""
),
isAboutAppDialogVisible = false
),
onEvent = { }
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,67 @@
package com.gravatar.app.homeUi.presentation.home.share

import com.gravatar.app.usercomponent.domain.model.UserSharePreferences
import com.gravatar.restapi.models.Profile

internal data class ShareUiState(
val profile: Profile? = null,
val avatarUrl: String? = null,
val isAboutAppDialogVisible: Boolean = false,
val privateContactInfo: PrivateContactInfo = PrivateContactInfo(),
)
val userSharePreferences: UserSharePreferences = UserSharePreferences(
name = true,
location = true,
title = true,
organization = true,
description = true,
profileUrl = true
)
) {
fun copyWithUserSharePreferences(
shareFieldType: ShareFieldType,
): ShareUiState = this.copy(
userSharePreferences = userSharePreferences.copy(
name = if (shareFieldType is ShareFieldType.Name) shareFieldType.checked else userSharePreferences.name,
location = if (shareFieldType is ShareFieldType.Location) shareFieldType.checked else userSharePreferences.location,
title = if (shareFieldType is ShareFieldType.Title) shareFieldType.checked else userSharePreferences.title,
organization = if (shareFieldType is ShareFieldType.Organization) shareFieldType.checked else userSharePreferences.organization,
description = if (shareFieldType is ShareFieldType.Description) shareFieldType.checked else userSharePreferences.description,
profileUrl = if (shareFieldType is ShareFieldType.ProfileUrl) shareFieldType.checked else userSharePreferences.profileUrl,
)
Comment on lines +23 to +30
Copy link

Copilot AI Jul 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The copyWithUserSharePreferences function uses repetitive if-else chains that could be simplified. Consider using a when expression or extracting the logic into the ShareFieldType sealed class to reduce code duplication and improve maintainability.

Suggested change
userSharePreferences = userSharePreferences.copy(
name = if (shareFieldType is ShareFieldType.Name) shareFieldType.checked else userSharePreferences.name,
location = if (shareFieldType is ShareFieldType.Location) shareFieldType.checked else userSharePreferences.location,
title = if (shareFieldType is ShareFieldType.Title) shareFieldType.checked else userSharePreferences.title,
organization = if (shareFieldType is ShareFieldType.Organization) shareFieldType.checked else userSharePreferences.organization,
description = if (shareFieldType is ShareFieldType.Description) shareFieldType.checked else userSharePreferences.description,
profileUrl = if (shareFieldType is ShareFieldType.ProfileUrl) shareFieldType.checked else userSharePreferences.profileUrl,
)
userSharePreferences = shareFieldType.updatePreferences(userSharePreferences)

Copilot uses AI. Check for mistakes.
)
}

internal data class PrivateContactInfo(
val emailValue: String = "",
val isEmailShared: Boolean = false,
val phoneValue: String = "",
val isPhoneShared: Boolean = false,
)

internal sealed class ShareFieldType {
abstract val checked: Boolean

data class Name(
override val checked: Boolean
) : ShareFieldType()

data class Location(
override val checked: Boolean
) : ShareFieldType()

data class Title(
override val checked: Boolean
) : ShareFieldType()

data class Organization(
override val checked: Boolean
) : ShareFieldType()

data class Description(
override val checked: Boolean
) : ShareFieldType()

data class ProfileUrl(
override val checked: Boolean
) : ShareFieldType()
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ internal class ShareViewModel(
)
}
}

is ShareEvent.OnEmailSharingChanged -> {
_uiState.update {
it.copy(
Expand All @@ -44,6 +45,7 @@ internal class ShareViewModel(
)
}
}

is ShareEvent.OnPhoneValueChanged -> {
_uiState.update {
it.copy(
Expand All @@ -53,6 +55,7 @@ internal class ShareViewModel(
)
}
}

is ShareEvent.OnPhoneSharingChanged -> {
_uiState.update {
it.copy(
Expand All @@ -62,8 +65,16 @@ internal class ShareViewModel(
)
}
}

is ShareEvent.OnAboutAppClicked -> showAboutAppDialog()
is ShareEvent.OnDismissAboutAppDialog -> hideAboutAppDialog()
is ShareEvent.OnUserSharePreferencesChanged -> updateUserSharePreferences(shareEvent.shareFieldType)
}
}

private fun updateUserSharePreferences(shareFieldType: ShareFieldType) {
_uiState.update { currentState ->
currentState.copyWithUserSharePreferences(shareFieldType)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.gravatar.app.homeUi.presentation.home.share.components

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material3.DividerDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
internal fun ItemDivider(modifier: Modifier = Modifier) {
Column(modifier) {
Spacer(modifier = Modifier.height(8.dp))
HorizontalDivider(thickness = 1.dp, color = DividerDefaults.color.copy(alpha = 0.3f))
Spacer(modifier = Modifier.height(8.dp))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.gravatar.app.homeUi.presentation.home.share.components

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.gravatar.app.design.theme.GravatarAppTheme
import com.gravatar.app.homeUi.R
import com.gravatar.app.homeUi.presentation.home.share.ShareFieldType
import com.gravatar.app.usercomponent.domain.model.UserSharePreferences
import com.gravatar.extensions.defaultProfile
import com.gravatar.restapi.models.Profile

@Composable
internal fun SharePublicContactInfo(
profile: Profile,
userSharePreferences: UserSharePreferences,
onUserPreferenceChanged: (ShareFieldType) -> Unit,
modifier: Modifier = Modifier
) {
val horizontalPadding = 16.dp
Column(
modifier = modifier
) {
ShareSectionTitle(
title = R.string.share_tab_public_info_title,
rightIcon = R.drawable.gravatar,
rightIconTint = MaterialTheme.colorScheme.primary,
Modifier.padding(top = 8.dp, bottom = 28.dp, start = horizontalPadding, end = horizontalPadding)
)
profile.fullName?.let { fullName ->
SharePublicRow(
label = stringResource(R.string.share_tab_name_label),
value = fullName,
checked = userSharePreferences.name,
onCheckedChange = { onUserPreferenceChanged(ShareFieldType.Name(it)) },
modifier = Modifier.padding(horizontal = horizontalPadding),
)
ItemDivider()
}
profile.location.takeIfNotEmpty()?.let { location ->
SharePublicRow(
label = stringResource(R.string.about_field_label_location),
value = location,
checked = userSharePreferences.location,
onCheckedChange = { onUserPreferenceChanged(ShareFieldType.Location(it)) },
modifier = Modifier.padding(horizontal = horizontalPadding),
)
ItemDivider()
}
profile.jobTitle.takeIfNotEmpty()?.let { title ->
SharePublicRow(
label = stringResource(R.string.about_field_label_job_title),
value = title,
checked = userSharePreferences.title,
onCheckedChange = { onUserPreferenceChanged(ShareFieldType.Title(it)) },
modifier = Modifier.padding(horizontal = horizontalPadding),
)
ItemDivider()
}
profile.company.takeIfNotEmpty()?.let { organization ->
SharePublicRow(
label = stringResource(R.string.about_field_label_company),
value = organization,
checked = userSharePreferences.organization,
onCheckedChange = { onUserPreferenceChanged(ShareFieldType.Organization(it)) },
modifier = Modifier.padding(horizontal = horizontalPadding),
)
ItemDivider()
}

profile.description.takeIfNotEmpty()?.let { description ->
SharePublicRow(
label = stringResource(R.string.about_field_label_about_me),
value = description,
checked = userSharePreferences.description,
singleLineValue = false,
onCheckedChange = { onUserPreferenceChanged(ShareFieldType.Description(it)) },
modifier = Modifier.padding(horizontal = horizontalPadding),
)
ItemDivider()
}

profile.profileUrl.toString().takeIfNotEmpty()?.let { profileUrl ->
SharePublicRow(
label = stringResource(R.string.share_tab_profile_url_label),
value = profileUrl,
checked = userSharePreferences.profileUrl,
onCheckedChange = { onUserPreferenceChanged(ShareFieldType.ProfileUrl(it)) },
modifier = Modifier.padding(horizontal = horizontalPadding),
)
ItemDivider()
}
}
}

private val Profile.fullName: String?
get() = "${firstName.orEmpty()} ${lastName.orEmpty()}".trim().ifEmpty {
null
}

private fun String.takeIfNotEmpty(): String? = ifEmpty { null }

@Preview(showBackground = true)
@Composable
private fun SharePublicContactInfoPreview() {
GravatarAppTheme {
SharePublicContactInfo(
profile = defaultProfile(hash = "hash"),
userSharePreferences = UserSharePreferences(
name = true,
location = true,
title = true,
organization = true,
description = true,
profileUrl = true
),
onUserPreferenceChanged = {},
)
}
}
Loading