mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-21 18:26:57 +00:00
Add entry points for adding to a group story.
This commit is contained in:
committed by
Cody Henthorne
parent
7949996c5c
commit
7b13550086
@@ -77,6 +77,7 @@ import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheet
|
||||
import org.thoughtcrime.securesms.stories.Stories
|
||||
import org.thoughtcrime.securesms.stories.StoryViewerArgs
|
||||
import org.thoughtcrime.securesms.stories.dialogs.StoryDialogs
|
||||
import org.thoughtcrime.securesms.stories.viewer.AddToGroupStoryDelegate
|
||||
import org.thoughtcrime.securesms.stories.viewer.StoryViewerActivity
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions
|
||||
import org.thoughtcrime.securesms.util.ContextUtil
|
||||
@@ -137,6 +138,7 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
private lateinit var toolbarBadge: BadgeImageView
|
||||
private lateinit var toolbarTitle: TextView
|
||||
private lateinit var toolbarBackground: View
|
||||
private lateinit var addToGroupStoryDelegate: AddToGroupStoryDelegate
|
||||
|
||||
private val navController get() = Navigation.findNavController(requireView())
|
||||
|
||||
@@ -221,6 +223,7 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
}
|
||||
}
|
||||
|
||||
addToGroupStoryDelegate = AddToGroupStoryDelegate(this)
|
||||
viewModel.state.observe(viewLifecycleOwner) { state ->
|
||||
|
||||
if (state.recipient != Recipient.UNKNOWN) {
|
||||
@@ -368,6 +371,17 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
customPref(
|
||||
ButtonStripPreference.Model(
|
||||
state = state.buttonStripState,
|
||||
onAddToStoryClick = {
|
||||
if (state.recipient.isPushV2Group && state.requireGroupSettingsState().isAnnouncementGroup && !state.requireGroupSettingsState().isSelfAdmin) {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.ConversationSettingsFragment__cant_add_to_group_story)
|
||||
.setMessage(R.string.ConversationSettingsFragment__only_admins_of_this_group_can_add_to_its_story)
|
||||
.setPositiveButton(android.R.string.ok) { d, _ -> d.dismiss() }
|
||||
.show()
|
||||
} else {
|
||||
addToGroupStoryDelegate.addToStory(state.recipient.id)
|
||||
}
|
||||
},
|
||||
onVideoClick = {
|
||||
if (state.recipient.isPushV2Group && state.requireGroupSettingsState().isAnnouncementGroup && !state.requireGroupSettingsState().isSelfAdmin) {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
|
||||
@@ -272,7 +272,8 @@ sealed class ConversationSettingsViewModel(
|
||||
isAudioSecure = recipient.isPushV2Group,
|
||||
isMuted = recipient.isMuted,
|
||||
isMuteAvailable = true,
|
||||
isSearchAvailable = true
|
||||
isSearchAvailable = true,
|
||||
isAddToStoryAvailable = recipient.isPushV2Group && !recipient.isBlocked && isActive
|
||||
),
|
||||
canModifyBlockedState = RecipientUtil.isBlockable(recipient),
|
||||
specificSettingsState = state.requireGroupSettingsState().copy(
|
||||
|
||||
@@ -24,6 +24,7 @@ object ButtonStripPreference {
|
||||
class Model(
|
||||
val state: State,
|
||||
val background: DSLSettingsIcon? = null,
|
||||
val onAddToStoryClick: () -> Unit = {},
|
||||
val onMessageClick: () -> Unit = {},
|
||||
val onVideoClick: () -> Unit = {},
|
||||
val onAudioClick: () -> Unit = {},
|
||||
@@ -41,6 +42,8 @@ object ButtonStripPreference {
|
||||
|
||||
class ViewHolder(itemView: View) : MappingViewHolder<Model>(itemView) {
|
||||
|
||||
private val addToStory: View = itemView.findViewById(R.id.add_to_story)
|
||||
private val addToStoryContainer: View = itemView.findViewById(R.id.button_strip_add_to_story_container)
|
||||
private val message: View = itemView.findViewById(R.id.message)
|
||||
private val messageContainer: View = itemView.findViewById(R.id.button_strip_message_container)
|
||||
private val videoCall: View = itemView.findViewById(R.id.start_video)
|
||||
@@ -60,6 +63,7 @@ object ButtonStripPreference {
|
||||
audioContainer.visible = model.state.isAudioAvailable
|
||||
muteContainer.visible = model.state.isMuteAvailable
|
||||
searchContainer.visible = model.state.isSearchAvailable
|
||||
addToStoryContainer.visible = model.state.isAddToStoryAvailable
|
||||
|
||||
if (model.state.isAudioSecure) {
|
||||
audioLabel.setText(R.string.ConversationSettingsFragment__audio)
|
||||
@@ -88,6 +92,7 @@ object ButtonStripPreference {
|
||||
audioCall.setOnClickListener { model.onAudioClick() }
|
||||
mute.setOnClickListener { model.onMuteClick() }
|
||||
search.setOnClickListener { model.onSearchClick() }
|
||||
addToStory.setOnClickListener { model.onAddToStoryClick() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,5 +104,6 @@ object ButtonStripPreference {
|
||||
val isSearchAvailable: Boolean = false,
|
||||
val isAudioSecure: Boolean = false,
|
||||
val isMuted: Boolean = false,
|
||||
val isAddToStoryAvailable: Boolean = false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -93,8 +93,9 @@ class MediaSelectionActivity :
|
||||
val initialMedia: List<Media> = intent.getParcelableArrayListExtra(MEDIA) ?: listOf()
|
||||
val message: CharSequence? = if (shareToTextStory) null else draftText
|
||||
val isReply: Boolean = intent.getBooleanExtra(IS_REPLY, false)
|
||||
val isAddToGroupStoryFlow: Boolean = intent.getBooleanExtra(IS_ADD_TO_GROUP_STORY_FLOW, false)
|
||||
|
||||
val factory = MediaSelectionViewModel.Factory(destination, sendType, initialMedia, message, isReply, isStory, MediaSelectionRepository(this))
|
||||
val factory = MediaSelectionViewModel.Factory(destination, sendType, initialMedia, message, isReply, isStory, isAddToGroupStoryFlow, MediaSelectionRepository(this))
|
||||
viewModel = ViewModelProvider(this, factory)[MediaSelectionViewModel::class.java]
|
||||
|
||||
val textStoryToggle: ConstraintLayout = findViewById(R.id.switch_widget)
|
||||
@@ -221,7 +222,7 @@ class MediaSelectionActivity :
|
||||
return Stories.isFeatureEnabled() &&
|
||||
isCameraFirst() &&
|
||||
!viewModel.hasSelectedMedia() &&
|
||||
destination == MediaSelectionDestination.ChooseAfterMediaSelection
|
||||
(destination == MediaSelectionDestination.ChooseAfterMediaSelection || destination is MediaSelectionDestination.SingleStory)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
@@ -348,6 +349,7 @@ class MediaSelectionActivity :
|
||||
private const val IS_REPLY = "is_reply"
|
||||
private const val IS_STORY = "is_story"
|
||||
private const val AS_TEXT_STORY = "as_text_story"
|
||||
private const val IS_ADD_TO_GROUP_STORY_FLOW = "is_add_to_group_story_flow"
|
||||
|
||||
@JvmStatic
|
||||
fun camera(context: Context): Intent {
|
||||
@@ -363,6 +365,19 @@ class MediaSelectionActivity :
|
||||
)
|
||||
}
|
||||
|
||||
fun addToGroupStory(
|
||||
context: Context,
|
||||
recipientId: RecipientId
|
||||
): Intent {
|
||||
return buildIntent(
|
||||
context = context,
|
||||
startAction = R.id.action_directly_to_mediaCaptureFragment,
|
||||
isStory = true,
|
||||
isAddToGroupStoryFlow = true,
|
||||
destination = MediaSelectionDestination.SingleStory(recipientId)
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun camera(
|
||||
context: Context,
|
||||
@@ -457,7 +472,8 @@ class MediaSelectionActivity :
|
||||
message: CharSequence? = null,
|
||||
isReply: Boolean = false,
|
||||
isStory: Boolean = false,
|
||||
asTextStory: Boolean = false
|
||||
asTextStory: Boolean = false,
|
||||
isAddToGroupStoryFlow: Boolean = false
|
||||
): Intent {
|
||||
return Intent(context, MediaSelectionActivity::class.java).apply {
|
||||
putExtra(START_ACTION, startAction)
|
||||
@@ -468,6 +484,7 @@ class MediaSelectionActivity :
|
||||
putExtra(IS_REPLY, isReply)
|
||||
putExtra(IS_STORY, isStory)
|
||||
putExtra(AS_TEXT_STORY, asTextStory)
|
||||
putExtra(IS_ADD_TO_GROUP_STORY_FLOW, isAddToGroupStoryFlow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,16 @@ sealed class MediaSelectionDestination {
|
||||
}
|
||||
}
|
||||
|
||||
class SingleStory(private val id: RecipientId) : MediaSelectionDestination() {
|
||||
override fun getRecipientSearchKey(): ContactSearchKey.RecipientSearchKey = ContactSearchKey.RecipientSearchKey.Story(id)
|
||||
|
||||
override fun toBundle(): Bundle {
|
||||
return Bundle().apply {
|
||||
putParcelable(STORY, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MultipleRecipients(val recipientSearchKeys: List<ContactSearchKey.RecipientSearchKey>) : MediaSelectionDestination() {
|
||||
|
||||
companion object {
|
||||
@@ -72,6 +82,7 @@ sealed class MediaSelectionDestination {
|
||||
private const val WALLPAPER = "wallpaper"
|
||||
private const val AVATAR = "avatar"
|
||||
private const val RECIPIENT = "recipient"
|
||||
private const val STORY = "story"
|
||||
private const val RECIPIENT_LIST = "recipient_list"
|
||||
|
||||
fun fromBundle(bundle: Bundle): MediaSelectionDestination {
|
||||
@@ -79,6 +90,7 @@ sealed class MediaSelectionDestination {
|
||||
bundle.containsKey(WALLPAPER) -> Wallpaper
|
||||
bundle.containsKey(AVATAR) -> Avatar
|
||||
bundle.containsKey(RECIPIENT) -> SingleRecipient(requireNotNull(bundle.getParcelable(RECIPIENT)))
|
||||
bundle.containsKey(STORY) -> SingleStory(requireNotNull(bundle.getParcelable(STORY)))
|
||||
bundle.containsKey(RECIPIENT_LIST) -> MultipleRecipients.fromParcel(requireNotNull(bundle.getParcelableArrayList(RECIPIENT_LIST)))
|
||||
else -> ChooseAfterMediaSelection
|
||||
}
|
||||
|
||||
@@ -105,6 +105,8 @@ class MediaSelectionRepository(context: Context) {
|
||||
val singleRecipient: Recipient? = singleContact?.let { Recipient.resolved(it.recipientId) }
|
||||
val storyType: StoryType = if (singleRecipient?.isDistributionList == true) {
|
||||
SignalDatabase.distributionLists.getStoryType(singleRecipient.requireDistributionListId())
|
||||
} else if (singleRecipient?.isGroup == true && singleContact.isStory) {
|
||||
StoryType.STORY_WITH_REPLIES
|
||||
} else {
|
||||
StoryType.NONE
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ class MediaSelectionViewModel(
|
||||
initialMessage: CharSequence?,
|
||||
val isReply: Boolean,
|
||||
isStory: Boolean,
|
||||
val isAddToGroupStoryFlow: Boolean,
|
||||
private val repository: MediaSelectionRepository,
|
||||
private val identityChangesSince: Long = System.currentTimeMillis()
|
||||
) : ViewModel() {
|
||||
@@ -360,10 +361,10 @@ class MediaSelectionViewModel(
|
||||
return
|
||||
}
|
||||
|
||||
val filteredPreUploadMedia = if (Stories.isFeatureEnabled()) {
|
||||
media.filter { Stories.MediaTransform.canPreUploadMedia(it) }
|
||||
} else {
|
||||
val filteredPreUploadMedia = if (destination is MediaSelectionDestination.SingleRecipient || !Stories.isFeatureEnabled()) {
|
||||
media
|
||||
} else {
|
||||
media.filter { Stories.MediaTransform.canPreUploadMedia(it) }
|
||||
}
|
||||
|
||||
repository.uploadRepository.startUpload(filteredPreUploadMedia, store.state.recipient)
|
||||
@@ -482,10 +483,11 @@ class MediaSelectionViewModel(
|
||||
private val initialMessage: CharSequence?,
|
||||
private val isReply: Boolean,
|
||||
private val isStory: Boolean,
|
||||
private val isAddToGroupStoryFlow: Boolean,
|
||||
private val repository: MediaSelectionRepository
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return requireNotNull(modelClass.cast(MediaSelectionViewModel(destination, sendType, initialMedia, initialMessage, isReply, isStory, repository)))
|
||||
return requireNotNull(modelClass.cast(MediaSelectionViewModel(destination, sendType, initialMedia, initialMessage, isReply, isStory, isAddToGroupStoryFlow, repository)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import app.cash.exhaustive.Exhaustive
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import org.signal.core.util.concurrent.SimpleTask
|
||||
import org.thoughtcrime.securesms.R
|
||||
@@ -200,6 +201,12 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment) {
|
||||
} else {
|
||||
multiselectLauncher.launch(args)
|
||||
}
|
||||
} else if (sharedViewModel.isAddToGroupStoryFlow) {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setMessage(getString(R.string.MediaReviewFragment__add_to_the_group_story, sharedViewModel.state.value!!.recipient!!.getDisplayName(requireContext())))
|
||||
.setPositiveButton(R.string.MediaReviewFragment__add_to_story) { _, _ -> performSend() }
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
.show()
|
||||
} else {
|
||||
performSend()
|
||||
}
|
||||
@@ -317,7 +324,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment) {
|
||||
.setInterpolator(MediaAnimations.interpolator)
|
||||
.alpha(1f)
|
||||
|
||||
sharedViewModel
|
||||
disposables += sharedViewModel
|
||||
.send(selection.filterIsInstance(ContactSearchKey.RecipientSearchKey::class.java))
|
||||
.subscribe(
|
||||
{ result -> callback.onSentWithResult(result) },
|
||||
|
||||
@@ -12,6 +12,7 @@ import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||
@@ -157,6 +158,12 @@ class TextStoryPostCreationFragment : Fragment(R.layout.stories_text_post_creati
|
||||
)
|
||||
)
|
||||
}
|
||||
} else if (sharedViewModel.isAddToGroupStoryFlow) {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setMessage(getString(R.string.MediaReviewFragment__add_to_the_group_story, sharedViewModel.state.value!!.recipient!!.getDisplayName(requireContext())))
|
||||
.setPositiveButton(R.string.MediaReviewFragment__add_to_story) { _, _ -> performSend(contacts) }
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
.show()
|
||||
} else {
|
||||
performSend(contacts)
|
||||
}
|
||||
|
||||
@@ -231,18 +231,20 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
||||
!recipient.isReleaseNotes();
|
||||
|
||||
ButtonStripPreference.State buttonStripState = new ButtonStripPreference.State(
|
||||
/* isMessageAvailable = */ !recipient.isBlocked() && !recipient.isSelf() && !recipient.isReleaseNotes(),
|
||||
/* isVideoAvailable = */ !recipient.isBlocked() && !recipient.isSelf() && recipient.isRegistered(),
|
||||
/* isAudioAvailable = */ isAudioAvailable,
|
||||
/* isMuteAvailable = */ false,
|
||||
/* isSearchAvailable = */ false,
|
||||
/* isAudioSecure = */ recipient.isRegistered(),
|
||||
/* isMuted = */ false
|
||||
/* isMessageAvailable = */ !recipient.isBlocked() && !recipient.isSelf() && !recipient.isReleaseNotes(),
|
||||
/* isVideoAvailable = */ !recipient.isBlocked() && !recipient.isSelf() && recipient.isRegistered(),
|
||||
/* isAudioAvailable = */ isAudioAvailable,
|
||||
/* isMuteAvailable = */ false,
|
||||
/* isSearchAvailable = */ false,
|
||||
/* isAudioSecure = */ recipient.isRegistered(),
|
||||
/* isMuted = */ false,
|
||||
/* isAddToStoryAvailable = */ false
|
||||
);
|
||||
|
||||
ButtonStripPreference.Model buttonStripModel = new ButtonStripPreference.Model(
|
||||
buttonStripState,
|
||||
DSLSettingsIcon.from(ContextUtil.requireDrawable(requireContext(), R.drawable.selectable_recipient_bottom_sheet_icon_button)),
|
||||
() -> Unit.INSTANCE,
|
||||
() -> {
|
||||
dismiss();
|
||||
viewModel.onMessageClicked(requireActivity());
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
package org.thoughtcrime.securesms.stories.viewer
|
||||
|
||||
import android.content.Intent
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.CheckResult
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.fragment.app.Fragment
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||
import io.reactivex.rxjava3.subjects.CompletableSubject
|
||||
import org.signal.core.util.concurrent.SignalExecutors
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.ThreadTable
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult
|
||||
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage
|
||||
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage
|
||||
import org.thoughtcrime.securesms.mms.SlideDeck
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.sharing.MultiShareArgs
|
||||
import org.thoughtcrime.securesms.sharing.MultiShareSender
|
||||
import org.thoughtcrime.securesms.sms.MessageSender
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||
|
||||
/**
|
||||
* Delegate for dealing with sending stories directly to a group.
|
||||
*/
|
||||
class AddToGroupStoryDelegate(
|
||||
private val fragment: Fragment
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(AddToGroupStoryDelegate::class.java)
|
||||
}
|
||||
|
||||
private val lifecycleDisposable = LifecycleDisposable().apply {
|
||||
bindTo(fragment.viewLifecycleOwner)
|
||||
}
|
||||
|
||||
private val addToStoryLauncher: ActivityResultLauncher<Intent> = fragment.registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
val data = result.data
|
||||
if (data == null) {
|
||||
Log.d(TAG, "No result data.")
|
||||
} else {
|
||||
Log.d(TAG, "Processing result...")
|
||||
val mediaSelectionResult: MediaSendActivityResult = MediaSendActivityResult.fromData(data)
|
||||
handleResult(mediaSelectionResult)
|
||||
}
|
||||
}
|
||||
|
||||
fun addToStory(recipientId: RecipientId) {
|
||||
val addToStoryIntent = MediaSelectionActivity.addToGroupStory(
|
||||
fragment.requireContext(),
|
||||
recipientId
|
||||
)
|
||||
|
||||
addToStoryLauncher.launch(addToStoryIntent)
|
||||
}
|
||||
|
||||
private fun handleResult(result: MediaSendActivityResult) {
|
||||
lifecycleDisposable += ResultHandler.handleResult(result)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeBy {
|
||||
Toast.makeText(fragment.requireContext(), R.string.TextStoryPostCreationFragment__sent_story, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the send result on a background thread, isolated from the fragment.
|
||||
*/
|
||||
private object ResultHandler {
|
||||
|
||||
/**
|
||||
* Handles the result, completing after sending the message.
|
||||
*/
|
||||
@CheckResult
|
||||
fun handleResult(result: MediaSendActivityResult): Completable {
|
||||
Log.d(TAG, "Dispatching result handler.")
|
||||
val subject = CompletableSubject.create()
|
||||
SignalExecutors.BOUNDED_IO.execute {
|
||||
if (result.isPushPreUpload) {
|
||||
sendPreUploadedMedia(result)
|
||||
} else {
|
||||
sendNonPreUploadedMedia(result)
|
||||
}
|
||||
|
||||
subject.onComplete()
|
||||
}
|
||||
|
||||
return subject
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun sendPreUploadedMedia(result: MediaSendActivityResult) {
|
||||
Log.d(TAG, "Sending preupload media.")
|
||||
|
||||
val recipient = Recipient.resolved(result.recipientId)
|
||||
val secureMessage = OutgoingSecureMediaMessage(
|
||||
OutgoingMediaMessage(
|
||||
Recipient.resolved(result.recipientId),
|
||||
SlideDeck(),
|
||||
"",
|
||||
System.currentTimeMillis(),
|
||||
-1,
|
||||
0,
|
||||
false,
|
||||
ThreadTable.DistributionTypes.DEFAULT,
|
||||
result.storyType,
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
result.mentions.toList(),
|
||||
null
|
||||
)
|
||||
)
|
||||
|
||||
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
|
||||
if (result.body.isNotEmpty()) {
|
||||
result.preUploadResults.forEach {
|
||||
SignalDatabase.attachments.updateAttachmentCaption(it.attachmentId, result.body)
|
||||
}
|
||||
}
|
||||
|
||||
MessageSender.sendPushWithPreUploadedMedia(
|
||||
ApplicationDependencies.getApplication(),
|
||||
secureMessage,
|
||||
result.preUploadResults,
|
||||
threadId
|
||||
) {
|
||||
Log.d(TAG, "Sent.")
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun sendNonPreUploadedMedia(result: MediaSendActivityResult) {
|
||||
Log.d(TAG, "Sending non-preupload media.")
|
||||
|
||||
val multiShareArgs = MultiShareArgs.Builder(setOf(ContactSearchKey.RecipientSearchKey.Story(result.recipientId)))
|
||||
.withMedia(result.nonUploadedMedia.toList())
|
||||
.withDraftText(result.body)
|
||||
.withMentions(result.mentions.toList())
|
||||
.build()
|
||||
|
||||
val results = MultiShareSender.sendSync(multiShareArgs)
|
||||
|
||||
Log.d(TAG, "Sent. Failures? ${results.containsFailures()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,7 @@ import org.thoughtcrime.securesms.stories.StorySlateView
|
||||
import org.thoughtcrime.securesms.stories.StoryVolumeOverlayView
|
||||
import org.thoughtcrime.securesms.stories.dialogs.StoryContextMenu
|
||||
import org.thoughtcrime.securesms.stories.dialogs.StoryDialogs
|
||||
import org.thoughtcrime.securesms.stories.viewer.AddToGroupStoryDelegate
|
||||
import org.thoughtcrime.securesms.stories.viewer.StoryViewerViewModel
|
||||
import org.thoughtcrime.securesms.stories.viewer.StoryVolumeViewModel
|
||||
import org.thoughtcrime.securesms.stories.viewer.info.StoryInfoBottomSheetDialogFragment
|
||||
@@ -109,6 +110,7 @@ class StoryViewerPageFragment :
|
||||
private lateinit var sendingBar: View
|
||||
private lateinit var storyNormalBottomGradient: View
|
||||
private lateinit var storyCaptionBottomGradient: View
|
||||
private lateinit var addToGroupStoryButton: MaterialButton
|
||||
|
||||
private lateinit var callback: Callback
|
||||
|
||||
@@ -176,6 +178,7 @@ class StoryViewerPageFragment :
|
||||
val storyGradientTop: View = view.findViewById(R.id.story_gradient_top)
|
||||
val storyGradientBottom: View = view.findViewById(R.id.story_bottom_gradient_container)
|
||||
val storyVolumeOverlayView: StoryVolumeOverlayView = view.findViewById(R.id.story_volume_overlay)
|
||||
val addToGroupStoryButtonWrapper: View = view.findViewById(R.id.add_wrapper)
|
||||
|
||||
storyNormalBottomGradient = view.findViewById(R.id.story_gradient_bottom)
|
||||
storyCaptionBottomGradient = view.findViewById(R.id.story_caption_gradient)
|
||||
@@ -187,6 +190,7 @@ class StoryViewerPageFragment :
|
||||
viewsAndReplies = view.findViewById(R.id.views_and_replies_bar)
|
||||
sendingBarTextView = view.findViewById(R.id.sending_text_view)
|
||||
sendingBar = view.findViewById(R.id.sending_bar)
|
||||
addToGroupStoryButton = view.findViewById(R.id.add)
|
||||
|
||||
storySlate.callback = this
|
||||
|
||||
@@ -202,7 +206,8 @@ class StoryViewerPageFragment :
|
||||
progressBar,
|
||||
storyGradientTop,
|
||||
storyGradientBottom,
|
||||
storyCaptionContainer
|
||||
storyCaptionContainer,
|
||||
addToGroupStoryButtonWrapper
|
||||
)
|
||||
|
||||
senderAvatar.setFallbackPhotoProvider(FallbackPhotoProvider())
|
||||
@@ -212,6 +217,11 @@ class StoryViewerPageFragment :
|
||||
requireActivity().onBackPressed()
|
||||
}
|
||||
|
||||
val addToGroupStoryDelegate = AddToGroupStoryDelegate(this)
|
||||
addToGroupStoryButton.setOnClickListener {
|
||||
addToGroupStoryDelegate.addToStory(storyViewerPageArgs.recipientId)
|
||||
}
|
||||
|
||||
val singleTapHandler = SingleTapHandler(
|
||||
cardWrapper,
|
||||
viewModel::goToNextPost,
|
||||
@@ -384,6 +394,8 @@ class StoryViewerPageFragment :
|
||||
if (state.posts.isNotEmpty() && state.selectedPostIndex in state.posts.indices) {
|
||||
val post = state.posts[state.selectedPostIndex]
|
||||
|
||||
addToGroupStoryButton.visible = post.group != null
|
||||
|
||||
presentBottomBar(post, state.replyState, state.isReceiptsEnabled)
|
||||
presentSenderAvatar(senderAvatar, post)
|
||||
presentGroupAvatar(groupAvatar, post)
|
||||
|
||||
Reference in New Issue
Block a user