mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-02 00:17:41 +01:00
Add member labels education sheet.
This commit is contained in:
committed by
Cody Henthorne
parent
955bcde062
commit
0b2d3edcce
@@ -933,6 +933,11 @@
|
||||
android:exported="false"
|
||||
android:theme="@style/Signal.DayNight.NoActionBar" />
|
||||
|
||||
<activity
|
||||
android:name=".groups.memberlabel.MemberLabelActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Signal.DayNight.NoActionBar" />
|
||||
|
||||
<!-- ======================================= -->
|
||||
<!-- Activity Aliases -->
|
||||
<!-- ======================================= -->
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.signal.core.util.concurrent.LifecycleDisposable
|
||||
import org.signal.core.util.concurrent.addTo
|
||||
import org.signal.core.util.getParcelableArrayListExtraCompat
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.core.util.requireParcelableCompat
|
||||
import org.signal.donations.InAppPaymentType
|
||||
import org.thoughtcrime.securesms.AvatarPreviewActivity
|
||||
import org.thoughtcrime.securesms.BlockUnblockDialog
|
||||
@@ -71,6 +72,7 @@ import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||
import org.thoughtcrime.securesms.conversation.colors.ColorizerV2
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabelEducationSheet
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.StyledMemberLabel
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupErrors
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupLimitDialog
|
||||
@@ -189,6 +191,16 @@ class ConversationSettingsFragment :
|
||||
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
parentFragmentManager.setFragmentResultListener(MemberLabelEducationSheet.RESULT_EDIT_MEMBER_LABEL, viewLifecycleOwner) { _, bundle ->
|
||||
val groupId = bundle.requireParcelableCompat(MemberLabelEducationSheet.KEY_GROUP_ID, GroupId.V2::class.java)
|
||||
navController.safeNavigate(ConversationSettingsFragmentDirections.actionConversationSettingsFragmentToMemberLabelFragment(groupId))
|
||||
}
|
||||
|
||||
parentFragmentManager.setFragmentResultListener(AboutSheet.RESULT_EDIT_MEMBER_LABEL, viewLifecycleOwner) { _, bundle ->
|
||||
val groupId = bundle.requireParcelableCompat(AboutSheet.RESULT_GROUP_ID, GroupId.V2::class.java)
|
||||
navController.safeNavigate(ConversationSettingsFragmentDirections.actionConversationSettingsFragmentToMemberLabelFragment(groupId))
|
||||
}
|
||||
|
||||
recyclerView?.addOnScrollListener(ConversationSettingsOnUserScrolledAnimationHelper(toolbarAvatarContainer, toolbarTitle, toolbarBackground))
|
||||
}
|
||||
|
||||
|
||||
@@ -120,6 +120,7 @@ import org.signal.core.util.concurrent.addTo
|
||||
import org.signal.core.util.dp
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.core.util.requireParcelableCompat
|
||||
import org.signal.core.util.setActionItemTint
|
||||
import org.signal.donations.InAppPaymentType
|
||||
import org.signal.ringrtc.CallLinkRootKey
|
||||
@@ -158,7 +159,6 @@ import org.thoughtcrime.securesms.components.mention.MentionAnnotation
|
||||
import org.thoughtcrime.securesms.components.menu.ActionItem
|
||||
import org.thoughtcrime.securesms.components.menu.SignalBottomActionBar
|
||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
|
||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity.Companion.remoteBackups
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.CheckoutFlowActivity
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.DonateToSignalFragment
|
||||
import org.thoughtcrime.securesms.components.settings.conversation.ConversationSettingsActivity
|
||||
@@ -257,6 +257,8 @@ import org.thoughtcrime.securesms.giph.mp4.GiphyMp4ProjectionPlayerHolder
|
||||
import org.thoughtcrime.securesms.giph.mp4.GiphyMp4ProjectionRecycler
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabelActivity
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabelEducationSheet
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupErrors
|
||||
import org.thoughtcrime.securesms.groups.ui.LeaveGroupDialog
|
||||
@@ -319,6 +321,7 @@ import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDial
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientExporter
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.recipients.ui.about.AboutSheet
|
||||
import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment
|
||||
import org.thoughtcrime.securesms.recipients.ui.disappearingmessages.RecipientDisappearingMessagesActivity
|
||||
import org.thoughtcrime.securesms.registration.ui.RegistrationActivity
|
||||
@@ -686,6 +689,16 @@ class ConversationFragment :
|
||||
|
||||
container.fragmentManager = childFragmentManager
|
||||
|
||||
childFragmentManager.setFragmentResultListener(MemberLabelEducationSheet.RESULT_EDIT_MEMBER_LABEL, viewLifecycleOwner) { _, bundle ->
|
||||
val groupId = bundle.requireParcelableCompat(MemberLabelEducationSheet.KEY_GROUP_ID, GroupId.V2::class.java)
|
||||
startActivity(MemberLabelActivity.createIntent(requireContext(), groupId))
|
||||
}
|
||||
|
||||
childFragmentManager.setFragmentResultListener(AboutSheet.RESULT_EDIT_MEMBER_LABEL, viewLifecycleOwner) { _, bundle ->
|
||||
val groupId = bundle.requireParcelableCompat(AboutSheet.RESULT_GROUP_ID, GroupId.V2::class.java)
|
||||
startActivity(MemberLabelActivity.createIntent(requireContext(), groupId))
|
||||
}
|
||||
|
||||
ToolbarDependentMarginListener(binding.toolbar)
|
||||
initializeMediaKeyboard()
|
||||
|
||||
@@ -1004,9 +1017,13 @@ class ConversationFragment :
|
||||
|
||||
when {
|
||||
state.isReactionDelegateShowing -> reactionDelegate.hide()
|
||||
|
||||
state.isSearchRequested -> searchMenuItem?.collapseActionView()
|
||||
|
||||
state.isInActionMode -> finishActionMode()
|
||||
|
||||
state.isMediaKeyboardShowing -> container.hideInput()
|
||||
|
||||
else -> {
|
||||
// State has changed since the back handler was enabled. Let the back press proceed
|
||||
// to the next handler by triggering onBackPressed again after setting a skip flag
|
||||
@@ -1886,13 +1903,16 @@ class ConversationFragment :
|
||||
|
||||
when (data) {
|
||||
is ShareOrDraftData.SendKeyboardImage -> sendMessageWithoutComposeInput(slide = data.slide, clearCompose = false)
|
||||
|
||||
is ShareOrDraftData.SendSticker -> sendMessageWithoutComposeInput(slide = data.slide, clearCompose = true)
|
||||
|
||||
is ShareOrDraftData.SetText -> {
|
||||
composeText.setDraftText(data.text)
|
||||
inputPanel.clickOnComposeInput()
|
||||
}
|
||||
|
||||
is ShareOrDraftData.SetLocation -> attachmentManager.setLocation(data.location, MediaConstraints.getPushMediaConstraints())
|
||||
|
||||
is ShareOrDraftData.SetEditMessage -> {
|
||||
composeText.setDraftText(data.draftText)
|
||||
inputPanel.enterEditMessageMode(Glide.with(this), data.messageEdit, true, data.clearQuote)
|
||||
@@ -2689,6 +2709,7 @@ class ConversationFragment :
|
||||
.subscribeBy { result ->
|
||||
when (result) {
|
||||
is Result.Success -> Log.d(TAG, "$logMessage complete")
|
||||
|
||||
is Result.Failure -> {
|
||||
Log.d(TAG, "$logMessage failed ${result.failure}")
|
||||
toast(GroupErrors.getUserDisplayMessage(result.failure))
|
||||
@@ -4156,8 +4177,11 @@ class ConversationFragment :
|
||||
val slides: List<Slide> = result.nonUploadedMedia.mapNotNull {
|
||||
when {
|
||||
MediaUtil.isVideoType(it.contentType) -> VideoSlide(requireContext(), it.uri, it.size, it.isVideoGif, it.width, it.height, it.caption, it.transformProperties)
|
||||
|
||||
MediaUtil.isGif(it.contentType) -> GifSlide(requireContext(), it.uri, it.size, it.width, it.height, it.isBorderless, it.caption)
|
||||
|
||||
MediaUtil.isImageType(it.contentType) -> ImageSlide(requireContext(), it.uri, it.contentType, it.size, it.width, it.height, it.isBorderless, it.caption, null, it.transformProperties)
|
||||
|
||||
MediaUtil.isDocumentType(it.contentType) -> {
|
||||
DocumentSlide(requireContext(), it.uri, it.contentType!!, it.size, it.fileName)
|
||||
}
|
||||
@@ -4359,6 +4383,7 @@ class ConversationFragment :
|
||||
.subscribeBy { result ->
|
||||
when (result) {
|
||||
is Result.Success -> Log.d(TAG, "Cancel request complete")
|
||||
|
||||
is Result.Failure -> {
|
||||
Log.d(TAG, "Cancel join request failed ${result.failure}")
|
||||
toast(GroupErrors.getUserDisplayMessage(result.failure))
|
||||
@@ -4737,9 +4762,13 @@ class ConversationFragment :
|
||||
if (button != null) {
|
||||
when (button) {
|
||||
AttachmentKeyboardButton.GALLERY -> conversationActivityResultContracts.launchGallery(recipient.id, composeText.textTrimmed, inputPanel.quote.isPresent)
|
||||
|
||||
AttachmentKeyboardButton.CONTACT -> conversationActivityResultContracts.launchSelectContact()
|
||||
|
||||
AttachmentKeyboardButton.LOCATION -> conversationActivityResultContracts.launchSelectLocation(recipient.chatColors)
|
||||
|
||||
AttachmentKeyboardButton.PAYMENT -> AttachmentManager.selectPayment(this@ConversationFragment, recipient)
|
||||
|
||||
AttachmentKeyboardButton.FILE -> {
|
||||
if (!conversationActivityResultContracts.launchSelectFile()) {
|
||||
toast(R.string.AttachmentManager_cant_open_media_selection, Toast.LENGTH_LONG)
|
||||
|
||||
@@ -244,6 +244,10 @@ sealed class GroupId(private val encodedId: String) : DatabaseId, Parcelable {
|
||||
return this as V2
|
||||
}
|
||||
|
||||
fun v2OrNull(): V2? {
|
||||
return if (isV2) (this as V2) else null
|
||||
}
|
||||
|
||||
fun requirePush(): Push {
|
||||
assert(this is Push)
|
||||
return this as Push
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.groups.memberlabel
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import org.signal.core.util.getParcelableExtraCompat
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActivity
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
|
||||
/**
|
||||
* Hosts [MemberLabelFragment], allowing navigation to the member label editor from any context.
|
||||
*/
|
||||
class MemberLabelActivity : PassphraseRequiredActivity() {
|
||||
companion object {
|
||||
private const val EXTRA_GROUP_ID = "group_id"
|
||||
|
||||
fun createIntent(context: Context, groupId: GroupId.V2): Intent {
|
||||
return Intent(context, MemberLabelActivity::class.java).apply {
|
||||
putExtra(EXTRA_GROUP_ID, groupId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||
super.onCreate(savedInstanceState, ready)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
val groupId = intent.getParcelableExtraCompat(EXTRA_GROUP_ID, GroupId.V2::class.java)!!
|
||||
val fragment = MemberLabelFragment.newInstance(groupId)
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(android.R.id.content, fragment)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.groups.memberlabel
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import org.signal.core.ui.compose.AllDevicePreviews
|
||||
import org.signal.core.ui.compose.BottomSheets
|
||||
import org.signal.core.ui.compose.Buttons
|
||||
import org.signal.core.ui.compose.ComposeBottomSheetDialogFragment
|
||||
import org.signal.core.ui.compose.DayNightPreviews
|
||||
import org.signal.core.ui.compose.Previews
|
||||
import org.signal.core.util.requireParcelableCompat
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.util.viewModel
|
||||
|
||||
/**
|
||||
* Explains what member labels are and provides options to edit the current user's label.
|
||||
*/
|
||||
class MemberLabelEducationSheet : ComposeBottomSheetDialogFragment() {
|
||||
companion object {
|
||||
const val RESULT_EDIT_MEMBER_LABEL = "edit_member_label"
|
||||
const val KEY_GROUP_ID = "group_id"
|
||||
|
||||
private const val FRAGMENT_TAG = "MemberLabelEducationSheet"
|
||||
private const val ARGS_GROUP_ID = "group_id"
|
||||
|
||||
fun show(fragmentManager: FragmentManager, groupId: GroupId.V2) {
|
||||
val fragment = MemberLabelEducationSheet().apply {
|
||||
arguments = bundleOf(ARGS_GROUP_ID to groupId)
|
||||
}
|
||||
fragment.show(fragmentManager, FRAGMENT_TAG)
|
||||
}
|
||||
}
|
||||
|
||||
private val groupId: GroupId.V2 by lazy {
|
||||
requireArguments().requireParcelableCompat(ARGS_GROUP_ID, GroupId.V2::class.java)
|
||||
}
|
||||
|
||||
private val viewModel: MemberLabelEducationViewModel by viewModel {
|
||||
MemberLabelEducationViewModel(groupId)
|
||||
}
|
||||
|
||||
override val peekHeightPercentage: Float = 1f
|
||||
|
||||
@Composable
|
||||
override fun SheetContent() {
|
||||
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
val callbacks = remember {
|
||||
object : MemberLabelEducationUiCallbacks {
|
||||
override fun onSetLabelClicked() {
|
||||
setFragmentResult(RESULT_EDIT_MEMBER_LABEL, bundleOf(KEY_GROUP_ID to groupId))
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun onDismiss() = dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
MemberLabelEducationSheetContent(
|
||||
state = state,
|
||||
callbacks = callbacks
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MemberLabelEducationSheetContent(
|
||||
state: MemberLabelEducationViewModel.UiState,
|
||||
callbacks: MemberLabelEducationUiCallbacks = MemberLabelEducationUiCallbacks.Empty
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.padding(top = 4.dp, bottom = 28.dp, start = 28.dp, end = 28.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
BottomSheets.Handle()
|
||||
|
||||
Image(
|
||||
painter = painterResource(R.drawable.symbol_tag_filled_64),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(top = 24.dp)
|
||||
.size(64.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.MemberLabelsEducation__title),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.MemberLabelsEducation__body),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 12.dp)
|
||||
)
|
||||
|
||||
if (state.selfCanSetLabel) {
|
||||
TextButton(
|
||||
onClick = callbacks::onSetLabelClicked,
|
||||
modifier = Modifier.padding(top = 56.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(
|
||||
if (state.selfHasLabel) R.string.MemberLabelsEducation__edit_label
|
||||
else R.string.MemberLabelsEducation__set_label
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(56.dp))
|
||||
}
|
||||
|
||||
Buttons.LargeTonal(
|
||||
onClick = callbacks::onDismiss,
|
||||
modifier = Modifier
|
||||
.padding(top = 16.dp)
|
||||
.defaultMinSize(minWidth = 220.dp)
|
||||
) {
|
||||
Text(text = stringResource(android.R.string.ok))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface MemberLabelEducationUiCallbacks {
|
||||
fun onSetLabelClicked()
|
||||
fun onDismiss()
|
||||
|
||||
object Empty : MemberLabelEducationUiCallbacks {
|
||||
override fun onSetLabelClicked() = Unit
|
||||
override fun onDismiss() = Unit
|
||||
}
|
||||
}
|
||||
|
||||
@AllDevicePreviews
|
||||
@Composable
|
||||
private fun MemberLabelEducationSheetPreviewCanSetNoLabel() = Previews.Preview {
|
||||
MemberLabelEducationSheetContent(
|
||||
state = MemberLabelEducationViewModel.UiState(
|
||||
selfHasLabel = false,
|
||||
selfCanSetLabel = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
private fun MemberLabelEducationSheetPreviewCanSetHasLabel() = Previews.Preview {
|
||||
MemberLabelEducationSheetContent(
|
||||
state = MemberLabelEducationViewModel.UiState(
|
||||
selfHasLabel = true,
|
||||
selfCanSetLabel = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
private fun MemberLabelEducationSheetPreviewCannotSet() = Previews.Preview {
|
||||
MemberLabelEducationSheetContent(
|
||||
state = MemberLabelEducationViewModel.UiState(
|
||||
selfHasLabel = false,
|
||||
selfCanSetLabel = false
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.groups.memberlabel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import org.signal.core.util.concurrent.SignalDispatchers
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
|
||||
class MemberLabelEducationViewModel(
|
||||
private val groupId: GroupId.V2,
|
||||
private val repository: MemberLabelRepository = MemberLabelRepository.instance
|
||||
) : ViewModel() {
|
||||
|
||||
data class UiState(
|
||||
val selfHasLabel: Boolean = false,
|
||||
val selfCanSetLabel: Boolean = false
|
||||
)
|
||||
|
||||
private val _uiState = MutableStateFlow(UiState())
|
||||
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch(SignalDispatchers.IO) {
|
||||
val self = Recipient.self()
|
||||
val selfMemberLabel = repository.getLabel(groupId, self.id)
|
||||
val selfCanSetLabel = repository.canSetLabel(groupId, self)
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
selfHasLabel = selfMemberLabel != null,
|
||||
selfCanSetLabel = selfCanSetLabel
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,8 +38,8 @@ import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import org.signal.core.ui.compose.AllDevicePreviews
|
||||
import org.signal.core.ui.compose.Buttons
|
||||
import org.signal.core.ui.compose.ClearableTextField
|
||||
@@ -48,6 +48,7 @@ import org.signal.core.ui.compose.Previews
|
||||
import org.signal.core.ui.compose.Scaffolds
|
||||
import org.signal.core.ui.compose.SignalIcons
|
||||
import org.signal.core.util.isNotNullOrBlank
|
||||
import org.signal.core.util.requireParcelableCompat
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabelUiState.SaveState
|
||||
@@ -62,12 +63,22 @@ import org.thoughtcrime.securesms.util.viewModel
|
||||
class MemberLabelFragment : ComposeFragment(), ReactWithAnyEmojiBottomSheetDialogFragment.Callback {
|
||||
companion object {
|
||||
private const val EMOJI_PICKER_DIALOG_TAG = "emoji_picker_dialog"
|
||||
private const val ARG_GROUP_ID = "group_id"
|
||||
|
||||
fun newInstance(groupId: GroupId.V2): MemberLabelFragment {
|
||||
return MemberLabelFragment().apply {
|
||||
arguments = bundleOf(ARG_GROUP_ID to groupId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val groupId: GroupId.V2 by lazy {
|
||||
requireArguments().requireParcelableCompat(ARG_GROUP_ID, GroupId.V2::class.java)
|
||||
}
|
||||
|
||||
private val args: MemberLabelFragmentArgs by navArgs()
|
||||
private val viewModel: MemberLabelViewModel by viewModel {
|
||||
MemberLabelViewModel(
|
||||
groupId = (args.groupId as GroupId).requireV2(),
|
||||
groupId = groupId,
|
||||
recipientId = Recipient.self().id
|
||||
)
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.Navigation
|
||||
import org.signal.core.ui.compose.BottomSheets
|
||||
import org.signal.core.ui.compose.ComposeBottomSheetDialogFragment
|
||||
import org.signal.core.ui.compose.DayNightPreviews
|
||||
@@ -53,7 +53,6 @@ import org.thoughtcrime.securesms.AvatarPreviewActivity
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.avatar.AvatarImage
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
import org.thoughtcrime.securesms.components.settings.conversation.ConversationSettingsFragmentDirections
|
||||
import org.thoughtcrime.securesms.conversation.v2.UnverifiedProfileNameBottomSheet
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabel
|
||||
@@ -72,6 +71,9 @@ import org.signal.core.ui.R as CoreUiR
|
||||
class AboutSheet : ComposeBottomSheetDialogFragment() {
|
||||
|
||||
companion object {
|
||||
const val RESULT_EDIT_MEMBER_LABEL = "edit_member_label"
|
||||
const val RESULT_GROUP_ID = "group_id"
|
||||
|
||||
private const val RECIPIENT_ID = "recipient_id"
|
||||
private const val VIEWING_FROM_GROUP_ID = "viewing_from_group_id"
|
||||
|
||||
@@ -157,8 +159,7 @@ class AboutSheet : ComposeBottomSheetDialogFragment() {
|
||||
|
||||
private fun openMemberLabelScreen() {
|
||||
viewingFromGroupId?.let { groupId ->
|
||||
val navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment)
|
||||
navController.navigate(ConversationSettingsFragmentDirections.actionConversationSettingsFragmentToMemberLabelFragment(groupId))
|
||||
setFragmentResult(RESULT_EDIT_MEMBER_LABEL, bundleOf(RESULT_GROUP_ID to groupId))
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.thoughtcrime.securesms.components.settings.conversation.preferences.B
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.AvatarDownloadStateCache
|
||||
import org.thoughtcrime.securesms.fonts.SignalSymbols
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabelEducationSheet
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabelPillView
|
||||
import org.thoughtcrime.securesms.nicknames.NicknameActivity
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
@@ -334,7 +335,7 @@ class RecipientBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDialogFr
|
||||
}
|
||||
|
||||
viewModel.recipientDetails.observe(viewLifecycleOwner) { state ->
|
||||
updateRecipientDetails(state, memberLabelView, aboutView)
|
||||
updateRecipientDetails(state, memberLabelView, aboutView, groupId?.v2OrNull())
|
||||
}
|
||||
|
||||
viewModel.canAddToAGroup.observe(getViewLifecycleOwner()) { canAdd: Boolean ->
|
||||
@@ -450,13 +451,23 @@ class RecipientBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDialogFr
|
||||
private fun updateRecipientDetails(
|
||||
state: RecipientDetailsState,
|
||||
memberLabelView: MemberLabelPillView,
|
||||
aboutView: TextView
|
||||
aboutView: TextView,
|
||||
groupId: GroupId.V2?
|
||||
) {
|
||||
when {
|
||||
state.memberLabel != null -> {
|
||||
memberLabelView.setLabel(state.memberLabel.label, state.memberLabel.tintColor)
|
||||
memberLabelView.visible = true
|
||||
aboutView.visible = false
|
||||
|
||||
if (groupId != null) {
|
||||
memberLabelView.setOnClickListener {
|
||||
dismiss()
|
||||
MemberLabelEducationSheet.show(parentFragmentManager, groupId)
|
||||
}
|
||||
} else {
|
||||
memberLabelView.setOnClickListener(null)
|
||||
}
|
||||
}
|
||||
|
||||
!state.aboutText.isNullOrBlank() -> {
|
||||
|
||||
9
app/src/main/res/drawable/symbol_tag_filled_64.xml
Normal file
9
app/src/main/res/drawable/symbol_tag_filled_64.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="64dp" android:viewportHeight="64" android:viewportWidth="64" android:width="64dp">
|
||||
|
||||
<path android:fillColor="#D2D8FE" android:pathData="M45.5,8H33L19,20L7.5,32V40L24,56.5H30L54.5,33L56,17.5L45.5,8Z"/>
|
||||
|
||||
<path android:fillColor="#3B45FD" android:pathData="M42,18.333C44.025,18.333 45.667,19.975 45.667,22C45.667,24.025 44.025,25.667 42,25.667C39.975,25.667 38.334,24.025 38.334,22C38.334,19.975 39.975,18.333 42,18.333Z"/>
|
||||
|
||||
<path android:fillColor="#3B45FD" android:fillType="evenOdd" android:pathData="M41.582,6.667C43.118,6.667 44.211,6.654 45.262,6.906C46.146,7.119 46.992,7.469 47.767,7.944C48.689,8.509 49.453,9.291 50.538,10.376L53.624,13.462C54.709,14.547 55.492,15.312 56.056,16.233C56.532,17.008 56.882,17.854 57.094,18.738C57.346,19.789 57.334,20.883 57.334,22.418V27.582C57.334,29.117 57.346,30.211 57.094,31.262C56.882,32.146 56.532,32.992 56.056,33.767C55.492,34.688 54.709,35.452 53.624,36.538L37.132,53.03C35.571,54.59 34.317,55.847 33.22,56.779C32.105,57.725 31.021,58.452 29.757,58.863C27.749,59.516 25.585,59.516 23.577,58.863C22.313,58.452 21.229,57.725 20.114,56.779C19.017,55.847 17.763,54.59 16.202,53.03L10.97,47.798C9.41,46.238 8.153,44.984 7.222,43.887C6.275,42.772 5.548,41.688 5.137,40.423C4.485,38.415 4.485,36.252 5.137,34.243C5.548,32.979 6.275,31.895 7.222,30.78C8.153,29.683 9.41,28.429 10.97,26.868L27.463,10.376C28.548,9.291 29.312,8.509 30.233,7.944C31.009,7.469 31.854,7.118 32.739,6.906C33.789,6.654 34.883,6.667 36.418,6.667H41.582ZM36.418,10.667C34.693,10.667 34.156,10.679 33.672,10.795C33.196,10.91 32.741,11.098 32.323,11.354C31.899,11.614 31.51,11.985 30.291,13.204L13.799,29.697C12.191,31.304 11.072,32.426 10.271,33.37C9.485,34.295 9.123,34.917 8.94,35.479C8.549,36.684 8.549,37.982 8.94,39.187C9.123,39.75 9.485,40.371 10.271,41.297C11.072,42.24 12.191,43.363 13.799,44.97L19.03,50.202C20.638,51.809 21.76,52.928 22.704,53.729C23.629,54.515 24.251,54.877 24.813,55.06C26.018,55.451 27.316,55.451 28.521,55.06C29.083,54.877 29.705,54.515 30.631,53.729C31.574,52.928 32.696,51.809 34.304,50.202L50.796,33.71C52.016,32.49 52.386,32.101 52.646,31.677C52.902,31.26 53.09,30.804 53.205,30.328C53.321,29.845 53.334,29.307 53.334,27.582V22.418C53.334,20.692 53.321,20.155 53.205,19.672C53.09,19.196 52.902,18.74 52.646,18.323C52.386,17.899 52.016,17.51 50.796,16.29L47.71,13.204C46.49,11.985 46.101,11.614 45.677,11.354C45.26,11.098 44.805,10.91 44.328,10.795C43.845,10.679 43.308,10.667 41.582,10.667H36.418Z"/>
|
||||
|
||||
</vector>
|
||||
@@ -9385,5 +9385,14 @@
|
||||
<string name="GroupMemberLabel__accessibility_clear_label">Clear label</string>
|
||||
<string name="GroupMemberLabel__description">Add a member label to describe yourself or your role in this group. Labels are only visible within this group.</string>
|
||||
|
||||
<!-- Title for the member labels education sheet. -->
|
||||
<string name="MemberLabelsEducation__title">Member labels</string>
|
||||
<!-- Body for the member labels education sheet. -->
|
||||
<string name="MemberLabelsEducation__body">Use a member label to describe yourself or your role in this group. Member labels are only visible within this group.</string>
|
||||
<!-- Button to set a new member label. -->
|
||||
<string name="MemberLabelsEducation__set_label">Set a member label</string>
|
||||
<!-- Button to edit an existing member label. -->
|
||||
<string name="MemberLabelsEducation__edit_label">Edit your label</string>
|
||||
|
||||
<!-- EOF -->
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user