ComposeActivity: preview ability for Pleroma
Based on these commits by a1ba: -c3c92fadcd
-f9f2f9aa5b
This commit is contained in:
parent
4bddbb0d50
commit
6a66056216
|
@ -16,6 +16,7 @@ data class StatusDeletedEvent(val statusId: String) : Event
|
|||
data class StatusComposedEvent(val status: Status) : Event
|
||||
data class StatusScheduledEvent(val status: Status) : Event
|
||||
data class StatusEditedEvent(val originalId: String, val status: Status) : Event
|
||||
data class StatusPreviewEvent(val status: Status) : Event
|
||||
data class ProfileEditedEvent(val newProfileData: Account) : Event
|
||||
data class PreferenceChangedEvent(val preferenceKey: String) : Event
|
||||
data class MainTabsChangedEvent(val newTabs: List<TabData>) : Event
|
||||
|
|
|
@ -78,6 +78,7 @@ import com.keylesspalace.tusky.R
|
|||
import com.keylesspalace.tusky.adapter.EmojiAdapter
|
||||
import com.keylesspalace.tusky.adapter.LocaleAdapter
|
||||
import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener
|
||||
import com.keylesspalace.tusky.appstore.*
|
||||
import com.keylesspalace.tusky.components.compose.ComposeViewModel.ConfirmationKind
|
||||
import com.keylesspalace.tusky.components.compose.dialog.CaptionDialog
|
||||
import com.keylesspalace.tusky.components.compose.dialog.makeFocusDialog
|
||||
|
@ -140,10 +141,14 @@ class ComposeActivity :
|
|||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
||||
@Inject
|
||||
lateinit var eventHub: EventHub
|
||||
|
||||
private lateinit var composeOptionsBehavior: BottomSheetBehavior<*>
|
||||
private lateinit var addMediaBehavior: BottomSheetBehavior<*>
|
||||
private lateinit var emojiBehavior: BottomSheetBehavior<*>
|
||||
private lateinit var scheduleBehavior: BottomSheetBehavior<*>
|
||||
private lateinit var previewBehavior: BottomSheetBehavior<*>
|
||||
|
||||
/** The account that is being used to compose the status */
|
||||
private lateinit var activeAccount: AccountEntity
|
||||
|
@ -427,6 +432,7 @@ class ComposeActivity :
|
|||
updateVisibleCharactersLeft()
|
||||
}
|
||||
}
|
||||
binding.composePreviewButton.visibility = View.VISIBLE
|
||||
|
||||
lifecycleScope.launch {
|
||||
viewModel.emoji.collect(::setEmojiList)
|
||||
|
@ -489,6 +495,14 @@ class ComposeActivity :
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
eventHub.events.collect { event ->
|
||||
when (event) {
|
||||
is StatusPreviewEvent -> onStatusPreviewReady(event.status)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupButtons() {
|
||||
|
@ -498,11 +512,13 @@ class ComposeActivity :
|
|||
addMediaBehavior = BottomSheetBehavior.from(binding.addMediaBottomSheet)
|
||||
scheduleBehavior = BottomSheetBehavior.from(binding.composeScheduleView)
|
||||
emojiBehavior = BottomSheetBehavior.from(binding.emojiView)
|
||||
previewBehavior = BottomSheetBehavior.from(binding.previewScroll)
|
||||
|
||||
enableButton(binding.composeEmojiButton, clickable = false, colorActive = false)
|
||||
|
||||
// Setup the interface buttons.
|
||||
binding.composeTootButton.setOnClickListener { onSendClicked() }
|
||||
binding.composeTootButton.setOnClickListener { onSendClicked(false) }
|
||||
binding.composePreviewButton.setOnClickListener { onSendClicked(true) }
|
||||
binding.composeAddMediaButton.setOnClickListener { openPickDialog() }
|
||||
binding.composeToggleVisibilityButton.setOnClickListener { showComposeOptions() }
|
||||
binding.composeContentWarningButton.setOnClickListener { onContentWarningChanged() }
|
||||
|
@ -547,6 +563,7 @@ class ComposeActivity :
|
|||
addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
previewBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -767,6 +784,7 @@ class ComposeActivity :
|
|||
addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
scheduleBehavior.setState(BottomSheetBehavior.STATE_HIDDEN)
|
||||
previewBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
} else {
|
||||
composeOptionsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN)
|
||||
}
|
||||
|
@ -786,6 +804,7 @@ class ComposeActivity :
|
|||
composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
emojiBehavior.setState(BottomSheetBehavior.STATE_HIDDEN)
|
||||
previewBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
} else {
|
||||
scheduleBehavior.setState(BottomSheetBehavior.STATE_HIDDEN)
|
||||
}
|
||||
|
@ -802,6 +821,7 @@ class ComposeActivity :
|
|||
composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
scheduleBehavior.setState(BottomSheetBehavior.STATE_HIDDEN)
|
||||
previewBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
} else {
|
||||
emojiBehavior.setState(BottomSheetBehavior.STATE_HIDDEN)
|
||||
}
|
||||
|
@ -815,6 +835,7 @@ class ComposeActivity :
|
|||
composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
scheduleBehavior.setState(BottomSheetBehavior.STATE_HIDDEN)
|
||||
previewBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
} else {
|
||||
addMediaBehavior.setState(BottomSheetBehavior.STATE_HIDDEN)
|
||||
}
|
||||
|
@ -929,9 +950,13 @@ class ComposeActivity :
|
|||
return binding.composeScheduleView.verifyScheduledTime(binding.composeScheduleView.getDateTime(viewModel.scheduledAt.value))
|
||||
}
|
||||
|
||||
private fun onSendClicked() {
|
||||
private fun onSendClicked(preview: Boolean) {
|
||||
if(preview && previewBehavior.state != BottomSheetBehavior.STATE_HIDDEN) {
|
||||
previewBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
}
|
||||
|
||||
if (verifyScheduledTime()) {
|
||||
sendStatus()
|
||||
sendStatus(preview)
|
||||
} else {
|
||||
showScheduleView()
|
||||
}
|
||||
|
@ -954,7 +979,17 @@ class ComposeActivity :
|
|||
return contentInfo
|
||||
}
|
||||
|
||||
private fun sendStatus() {
|
||||
private fun onStatusPreviewReady(status: Status) {
|
||||
enableButtons(true, viewModel.editing)
|
||||
binding.previewView.setupWithStatus(status)
|
||||
previewBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
}
|
||||
|
||||
private fun sendStatus(preview: Boolean) {
|
||||
enableButtons(false, viewModel.editing)
|
||||
val contentText = binding.composeEditField.text.toString()
|
||||
var spoilerText = ""
|
||||
|
@ -967,8 +1002,9 @@ class ComposeActivity :
|
|||
enableButtons(true, viewModel.editing)
|
||||
} else if (characterCount <= maximumTootCharacters) {
|
||||
lifecycleScope.launch {
|
||||
viewModel.sendStatus(contentText, spoilerText, activeAccount.id)
|
||||
deleteDraftAndFinish()
|
||||
viewModel.sendStatus(contentText, spoilerText, activeAccount.id, preview)
|
||||
if (!preview)
|
||||
deleteDraftAndFinish()
|
||||
}
|
||||
} else {
|
||||
binding.composeEditField.error = getString(R.string.error_compose_character_limit)
|
||||
|
@ -1114,7 +1150,7 @@ class ComposeActivity :
|
|||
if (event.isCtrlPressed) {
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER) {
|
||||
// send toot by pressing CTRL + ENTER
|
||||
this.onSendClicked()
|
||||
this.onSendClicked(false)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -310,7 +310,8 @@ class ComposeViewModel @Inject constructor(
|
|||
suspend fun sendStatus(
|
||||
content: String,
|
||||
spoilerText: String,
|
||||
accountId: Long
|
||||
accountId: Long,
|
||||
preview: Boolean
|
||||
) {
|
||||
if (!scheduledTootId.isNullOrEmpty()) {
|
||||
api.deleteScheduledStatus(scheduledTootId!!)
|
||||
|
@ -342,7 +343,8 @@ class ComposeViewModel @Inject constructor(
|
|||
idempotencyKey = randomAlphanumericString(16),
|
||||
retries = 0,
|
||||
language = postLanguage,
|
||||
statusId = originalStatusId
|
||||
statusId = originalStatusId,
|
||||
preview = preview,
|
||||
)
|
||||
|
||||
serviceClient.sendToot(tootToSend)
|
||||
|
|
|
@ -29,7 +29,8 @@ data class NewStatus(
|
|||
@SerializedName("media_attributes") val mediaAttributes: List<MediaAttribute>?,
|
||||
@SerializedName("scheduled_at") val scheduledAt: String?,
|
||||
val poll: NewPoll?,
|
||||
val language: String?
|
||||
val language: String?,
|
||||
val preview: Boolean?,
|
||||
)
|
||||
|
||||
@Parcelize
|
||||
|
|
|
@ -97,7 +97,8 @@ class SendStatusBroadcastReceiver : BroadcastReceiver() {
|
|||
idempotencyKey = randomAlphanumericString(16),
|
||||
retries = 0,
|
||||
language = null,
|
||||
statusId = null
|
||||
statusId = null,
|
||||
preview = false,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.keylesspalace.tusky.R
|
|||
import com.keylesspalace.tusky.appstore.EventHub
|
||||
import com.keylesspalace.tusky.appstore.StatusComposedEvent
|
||||
import com.keylesspalace.tusky.appstore.StatusEditedEvent
|
||||
import com.keylesspalace.tusky.appstore.StatusPreviewEvent
|
||||
import com.keylesspalace.tusky.appstore.StatusScheduledEvent
|
||||
import com.keylesspalace.tusky.components.compose.MediaUploader
|
||||
import com.keylesspalace.tusky.components.compose.UploadEvent
|
||||
|
@ -219,7 +220,8 @@ class SendStatusService : Service(), Injectable {
|
|||
focus = media.focus?.toMastodonApiString(),
|
||||
thumbnail = null
|
||||
)
|
||||
}
|
||||
},
|
||||
preview = if (statusToSend.preview) true else null,
|
||||
)
|
||||
|
||||
val sendResult = if (isNew) {
|
||||
|
@ -250,7 +252,9 @@ class SendStatusService : Service(), Injectable {
|
|||
|
||||
val scheduled = !statusToSend.scheduledAt.isNullOrEmpty()
|
||||
|
||||
if (scheduled) {
|
||||
if (statusToSend.preview) {
|
||||
eventHub.dispatch(StatusPreviewEvent(sentStatus))
|
||||
} else if (scheduled) {
|
||||
eventHub.dispatch(StatusScheduledEvent(sentStatus))
|
||||
} else if (!isNew) {
|
||||
eventHub.dispatch(StatusEditedEvent(statusToSend.statusId!!, sentStatus))
|
||||
|
@ -463,7 +467,8 @@ data class StatusToSend(
|
|||
val idempotencyKey: String,
|
||||
var retries: Int,
|
||||
val language: String?,
|
||||
val statusId: String?
|
||||
val statusId: String?,
|
||||
val preview : Boolean,
|
||||
) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package com.keylesspalace.tusky.view
|
||||
|
||||
import android.view.*
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.preference.PreferenceManager
|
||||
|
||||
import com.keylesspalace.tusky.adapter.StatusViewHolder
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
||||
import com.keylesspalace.tusky.util.CardViewMode
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.settings.PrefKeys
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
|
||||
import java.util.*;
|
||||
|
||||
class StatusView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0)
|
||||
: ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private var viewHolder : StatusViewHolder
|
||||
private var statusDisplayOptions : StatusDisplayOptions
|
||||
init {
|
||||
View.inflate(context, R.layout.item_status, this)
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
statusDisplayOptions = StatusDisplayOptions(
|
||||
animateAvatars = preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false),
|
||||
mediaPreviewEnabled = true, // FIXME: get value from AccountManager
|
||||
useAbsoluteTime = preferences.getBoolean(PrefKeys.ABSOLUTE_TIME_VIEW, false),
|
||||
showBotOverlay = preferences.getBoolean(PrefKeys.SHOW_BOT_OVERLAY, true),
|
||||
useBlurhash = preferences.getBoolean(PrefKeys.USE_BLURHASH, true),
|
||||
cardViewMode = CardViewMode.NONE,
|
||||
confirmReblogs = false,
|
||||
confirmFavourites = false,
|
||||
hideStats = false,
|
||||
animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false),
|
||||
showStatsInline = false,
|
||||
showSensitiveMedia = false, // FIXME: get value from AccountManager
|
||||
openSpoiler = false, // FIXME: get value from AccountManager
|
||||
bigEmojis = preferences.getBoolean(PrefKeys.BIG_EMOJIS, true),
|
||||
)
|
||||
viewHolder = StatusViewHolder(this)
|
||||
}
|
||||
|
||||
fun setupWithStatus(status: Status) {
|
||||
val concrete = StatusViewData.Concrete(status, false, false, false)
|
||||
viewHolder.setupWithStatus(concrete, DummyStatusActionListener(), statusDisplayOptions)
|
||||
}
|
||||
|
||||
class DummyStatusActionListener: StatusActionListener {
|
||||
override fun onReply(position: Int) { }
|
||||
override fun onReblog(reblog: Boolean, position: Int) { }
|
||||
override fun onFavourite(favourite: Boolean, position: Int) { }
|
||||
override fun onBookmark(bookmark: Boolean, position: Int) { }
|
||||
override fun onMore(view: View, position: Int) { }
|
||||
override fun onViewMedia(position: Int, attachmentIndex: Int, view: View?) { }
|
||||
override fun onViewThread(position: Int) { }
|
||||
override fun onViewReplyTo(position: Int) { }
|
||||
override fun onOpenReblog(position: Int) { }
|
||||
override fun onExpandedChange(expanded: Boolean, position: Int) { }
|
||||
override fun onContentHiddenChange(isShowing: Boolean, position: Int) { }
|
||||
override fun onLoadMore(position: Int) { }
|
||||
override fun onContentCollapsedChange(isCollapsed: Boolean, position: Int) { }
|
||||
override fun onVoteInPoll(position: Int, choices: MutableList<Int>) { }
|
||||
override fun onViewAccount(id: String) { }
|
||||
override fun onViewTag(id: String) { }
|
||||
override fun onViewUrl(id: String) { }
|
||||
override fun clearWarningAction(position: Int) { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M19,3H5C3.89,3 3,3.9 3,5v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.11,3 19,3zM19,19H5V7h14V19zM13.5,13c0,0.83 -0.67,1.5 -1.5,1.5s-1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5S13.5,12.17 13.5,13zM12,9c-2.73,0 -5.06,1.66 -6,4c0.94,2.34 3.27,4 6,4s5.06,-1.66 6,-4C17.06,10.66 14.73,9 12,9zM12,15.5c-1.38,0 -2.5,-1.12 -2.5,-2.5c0,-1.38 1.12,-2.5 2.5,-2.5c1.38,0 2.5,1.12 2.5,2.5C14.5,14.38 13.38,15.5 12,15.5z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
|
@ -278,6 +278,25 @@
|
|||
app:behavior_peekHeight="0dp"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior" />
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/previewScroll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorSurface"
|
||||
android:elevation="12dp"
|
||||
android:paddingBottom="60dp"
|
||||
app:behavior_hideable="true"
|
||||
app:behavior_peekHeight="0dp"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
||||
|
||||
<com.keylesspalace.tusky.view.StatusView
|
||||
android:id="@+id/previewView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorSurface" />
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/composeBottomBar"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -373,12 +392,25 @@
|
|||
android:textStyle="bold"
|
||||
tools:text="500" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/composePreviewButton"
|
||||
style="@style/TuskyButton"
|
||||
android:padding="4dp"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
app:icon="@drawable/ic_preview_24dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:visibility="gone"
|
||||
android:layout_toLeftOf="@+id/composeTootButton"
|
||||
android:layout_centerVertical="true"/>
|
||||
|
||||
<com.keylesspalace.tusky.components.compose.view.TootButton
|
||||
android:id="@+id/composeTootButton"
|
||||
style="@style/TuskyButton"
|
||||
android:layout_width="@dimen/toot_button_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
Loading…
Reference in New Issue