Show some more info in the about sheet.

This commit is contained in:
Greyson Parrelli
2024-02-13 12:27:09 -05:00
committed by Cody Henthorne
parent 47cdc50a81
commit 57ac7cb328
7 changed files with 251 additions and 58 deletions

View File

@@ -5,6 +5,7 @@
package org.thoughtcrime.securesms.recipients.ui.about
import android.content.res.Configuration
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -26,7 +27,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
@@ -38,6 +38,7 @@ import androidx.core.widget.TextViewCompat
import org.signal.core.ui.BottomSheets
import org.signal.core.ui.theme.SignalTheme
import org.signal.core.util.getParcelableCompat
import org.signal.core.util.isNotNullOrBlank
import org.thoughtcrime.securesms.AvatarPreviewActivity
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.avatar.AvatarImage
@@ -81,11 +82,27 @@ class AboutSheet : ComposeBottomSheetDialogFragment() {
override fun SheetContent() {
val recipient by viewModel.recipient
val groupsInCommonCount by viewModel.groupsInCommonCount
val verified by viewModel.verified
if (recipient.isPresent) {
AboutSheetContent(
recipient = recipient.get(),
groupsInCommonCount = groupsInCommonCount,
Content(
model = AboutModel(
isSelf = recipient.get().isSelf,
hasAvatar = recipient.get().profileAvatarFileDetails.hasFile(),
displayName = recipient.get().getDisplayName(requireContext()),
shortName = recipient.get().getShortDisplayName(requireContext()),
about = recipient.get().about,
verified = verified,
recipientForAvatar = recipient.get(),
formattedE164 = if (recipient.get().hasE164() && recipient.get().shouldShowE164()) {
PhoneNumberFormatter.get(requireContext()).prettyPrintFormat(recipient.get().requireE164())
} else {
null
},
groupsInCommon = groupsInCommonCount,
profileSharing = recipient.get().isProfileSharing,
systemContact = recipient.get().isSystemContact
),
onClickSignalConnections = this::openSignalConnectionsSheet,
onAvatarClicked = this::openProfilePhotoViewer
)
@@ -102,25 +119,23 @@ class AboutSheet : ComposeBottomSheetDialogFragment() {
}
}
@Preview
@Composable
private fun AboutSheetContentPreview() {
SignalTheme {
Surface {
AboutSheetContent(
recipient = Recipient.UNKNOWN,
groupsInCommonCount = 0,
onClickSignalConnections = {},
onAvatarClicked = {}
)
}
}
}
private data class AboutModel(
val isSelf: Boolean,
val displayName: String,
val shortName: String,
val about: String?,
val verified: Boolean,
val hasAvatar: Boolean,
val recipientForAvatar: Recipient,
val formattedE164: String?,
val profileSharing: Boolean,
val systemContact: Boolean,
val groupsInCommon: Int
)
@Composable
private fun AboutSheetContent(
recipient: Recipient,
groupsInCommonCount: Int,
private fun Content(
model: AboutModel,
onClickSignalConnections: () -> Unit,
onAvatarClicked: () -> Unit
) {
@@ -131,8 +146,8 @@ private fun AboutSheetContent(
BottomSheets.Handle(modifier = Modifier.padding(top = 6.dp))
}
val avatarOnClick = remember(recipient.profileAvatarFileDetails.hasFile()) {
if (recipient.profileAvatarFileDetails.hasFile()) {
val avatarOnClick = remember(model.hasAvatar) {
if (model.hasAvatar) {
onAvatarClicked
} else {
{ }
@@ -141,7 +156,7 @@ private fun AboutSheetContent(
Column(horizontalAlignment = Alignment.CenterHorizontally) {
AvatarImage(
recipient = recipient,
recipient = model.recipientForAvatar,
modifier = Modifier
.padding(top = 56.dp)
.size(240.dp)
@@ -150,7 +165,7 @@ private fun AboutSheetContent(
)
Text(
text = stringResource(id = if (recipient.isSelf) R.string.AboutSheet__you else R.string.AboutSheet__about),
text = stringResource(id = if (model.isSelf) R.string.AboutSheet__you else R.string.AboutSheet__about),
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier
.fillMaxWidth()
@@ -158,22 +173,19 @@ private fun AboutSheetContent(
.padding(top = 20.dp, bottom = 14.dp)
)
val context = LocalContext.current
val displayName = remember(recipient) { recipient.getDisplayName(context) }
AboutRow(
startIcon = painterResource(R.drawable.symbol_person_24),
text = displayName,
text = model.displayName,
modifier = Modifier.fillMaxWidth()
)
if (!recipient.about.isNullOrBlank()) {
if (model.about.isNotNullOrBlank()) {
AboutRow(
startIcon = painterResource(R.drawable.symbol_edit_24),
text = {
Row {
AndroidView(factory = ::EmojiTextView) {
it.text = recipient.combinedAboutAndEmoji
it.text = model.about
TextViewCompat.setTextAppearance(it, R.style.Signal_Text_BodyLarge)
}
@@ -183,7 +195,16 @@ private fun AboutSheetContent(
)
}
if (recipient.isProfileSharing) {
if (model.verified) {
AboutRow(
startIcon = painterResource(id = R.drawable.check),
text = stringResource(id = R.string.AboutSheet__verified),
modifier = Modifier.align(alignment = Alignment.Start),
onClick = onClickSignalConnections
)
}
if (model.profileSharing || model.systemContact) {
AboutRow(
startIcon = painterResource(id = R.drawable.symbol_connections_24),
text = stringResource(id = R.string.AboutSheet__signal_connection),
@@ -191,36 +212,38 @@ private fun AboutSheetContent(
modifier = Modifier.align(alignment = Alignment.Start),
onClick = onClickSignalConnections
)
} else {
AboutRow(
startIcon = painterResource(id = R.drawable.chat_x),
text = stringResource(id = R.string.AboutSheet__no_direct_message, model.shortName),
modifier = Modifier.align(alignment = Alignment.Start),
onClick = onClickSignalConnections
)
}
val shortName = remember(recipient) { recipient.getShortDisplayName(context) }
if (recipient.isSystemContact) {
if (model.systemContact) {
AboutRow(
startIcon = painterResource(id = R.drawable.symbol_person_circle_24),
text = stringResource(id = R.string.AboutSheet__s_is_in_your_system_contacts, shortName),
text = stringResource(id = R.string.AboutSheet__s_is_in_your_system_contacts, model.shortName),
modifier = Modifier.fillMaxWidth()
)
}
if (recipient.e164.isPresent && recipient.shouldShowE164()) {
val e164 = remember(recipient.e164.get()) {
PhoneNumberFormatter.get(context).prettyPrintFormat(recipient.e164.get())
}
if (model.formattedE164.isNotNullOrBlank()) {
AboutRow(
startIcon = painterResource(R.drawable.symbol_phone_24),
text = e164,
text = model.formattedE164,
modifier = Modifier.fillMaxWidth()
)
}
val groupsInCommonText = if (recipient.hasGroupsInCommon()) {
pluralStringResource(id = R.plurals.AboutSheet__d_groups_in, groupsInCommonCount, groupsInCommonCount)
val groupsInCommonText = if (model.groupsInCommon > 0) {
pluralStringResource(id = R.plurals.AboutSheet__d_groups_in, model.groupsInCommon, model.groupsInCommon)
} else {
stringResource(id = R.string.AboutSheet__you_have_no_groups_in_common)
}
val groupsInCommonIcon = if (!recipient.isProfileSharing && groupsInCommonCount == 0) {
val groupsInCommonIcon = if (!model.profileSharing && model.groupsInCommon == 0) {
painterResource(R.drawable.symbol_error_circle_24)
} else {
painterResource(R.drawable.symbol_group_24)
@@ -236,20 +259,6 @@ private fun AboutSheetContent(
}
}
@Preview
@Composable
private fun AboutRowPreview() {
SignalTheme {
Surface {
AboutRow(
startIcon = painterResource(R.drawable.symbol_person_24),
text = "Maya Johnson",
endIcon = painterResource(id = R.drawable.symbol_chevron_right_compact_bold_16)
)
}
}
}
@Composable
private fun AboutRow(
startIcon: Painter,
@@ -318,3 +327,126 @@ private fun AboutRow(
}
}
}
@Preview(name = "Light Theme", group = "content", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Theme", group = "content", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun ContentPreviewDefault() {
SignalTheme {
Surface {
Content(
model = AboutModel(
isSelf = false,
hasAvatar = true,
displayName = "Peter Parker",
shortName = "Peter",
about = "Photographer for the Daily Bugle.",
verified = true,
recipientForAvatar = Recipient.UNKNOWN,
formattedE164 = "(123) 456-7890",
profileSharing = true,
systemContact = true,
groupsInCommon = 0
),
onClickSignalConnections = {},
onAvatarClicked = {}
)
}
}
}
@Preview(name = "Light Theme", group = "content", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Theme", group = "content", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun ContentPreviewInContactsNotProfileSharing() {
SignalTheme {
Surface {
Content(
model = AboutModel(
isSelf = false,
hasAvatar = true,
displayName = "Peter Parker",
shortName = "Peter",
about = "Photographer for the Daily Bugle.",
verified = false,
recipientForAvatar = Recipient.UNKNOWN,
formattedE164 = null,
profileSharing = false,
systemContact = true,
groupsInCommon = 3
),
onClickSignalConnections = {},
onAvatarClicked = {}
)
}
}
}
@Preview(name = "Light Theme", group = "content", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Theme", group = "content", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun ContentPreviewGroupsInCommonNoE164() {
SignalTheme {
Surface {
Content(
model = AboutModel(
isSelf = false,
hasAvatar = true,
displayName = "Peter Parker",
shortName = "Peter",
about = "Photographer for the Daily Bugle.",
verified = false,
recipientForAvatar = Recipient.UNKNOWN,
formattedE164 = null,
profileSharing = true,
systemContact = false,
groupsInCommon = 3
),
onClickSignalConnections = {},
onAvatarClicked = {}
)
}
}
}
@Preview(name = "Light Theme", group = "content", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Theme", group = "content", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun ContentPreviewNotAConnection() {
SignalTheme {
Surface {
Content(
model = AboutModel(
isSelf = false,
hasAvatar = true,
displayName = "Peter Parker",
shortName = "Peter",
about = "Photographer for the Daily Bugle.",
verified = false,
recipientForAvatar = Recipient.UNKNOWN,
formattedE164 = null,
profileSharing = false,
systemContact = false,
groupsInCommon = 3
),
onClickSignalConnections = {},
onAvatarClicked = {}
)
}
}
}
@Preview(name = "Light Theme", group = "about row", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Theme", group = "about row", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun AboutRowPreview() {
SignalTheme {
Surface {
AboutRow(
startIcon = painterResource(R.drawable.symbol_person_24),
text = "Maya Johnson",
endIcon = painterResource(id = R.drawable.symbol_chevron_right_compact_bold_16)
)
}
}
}

View File

@@ -7,7 +7,9 @@ package org.thoughtcrime.securesms.recipients.ui.about
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import org.thoughtcrime.securesms.database.IdentityTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.recipients.RecipientId
class AboutSheetRepository {
@@ -16,4 +18,11 @@ class AboutSheetRepository {
SignalDatabase.groups.getPushGroupsContainingMember(recipientId).size
}.subscribeOn(Schedulers.io())
}
fun getVerified(recipientId: RecipientId): Single<Boolean> {
return Single.fromCallable {
val identityRecord = ApplicationDependencies.getProtocolStore().aci().identities().getIdentityRecord(recipientId)
identityRecord.isPresent && identityRecord.get().verifiedStatus == IdentityTable.VerifiedStatus.VERIFIED
}.subscribeOn(Schedulers.io())
}
}

View File

@@ -30,6 +30,9 @@ class AboutSheetViewModel(
private val _groupsInCommonCount: MutableIntState = mutableIntStateOf(0)
val groupsInCommonCount: IntState = _groupsInCommonCount
private val _verified: MutableState<Boolean> = mutableStateOf(false)
val verified: State<Boolean> = _verified
private val recipientDisposable: Disposable = Recipient
.observable(recipientId)
.observeOn(AndroidSchedulers.mainThread())
@@ -44,6 +47,13 @@ class AboutSheetViewModel(
_groupsInCommonCount.intValue = it
}
private val verifiedDisposable: Disposable = repository
.getVerified(recipientId)
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy {
_verified.value = it
}
override fun onCleared() {
recipientDisposable.dispose()
groupsInCommonDisposable.dispose()