[WIP] Show quotes replies

This commit is contained in:
fruye 2022-11-09 20:10:32 +01:00
parent c107aa78ef
commit 9d1159969f
11 changed files with 167 additions and 2 deletions

View File

@ -66,6 +66,7 @@ import com.keylesspalace.tusky.util.TimestampUtils;
import com.keylesspalace.tusky.util.TouchDelegateHelper;
import com.keylesspalace.tusky.view.MediaPreviewImageView;
import com.keylesspalace.tusky.view.MediaPreviewLayout;
import com.keylesspalace.tusky.view.StatusQuoteView;
import com.keylesspalace.tusky.viewdata.PollOptionViewData;
import com.keylesspalace.tusky.viewdata.PollViewData;
import com.keylesspalace.tusky.viewdata.PollViewDataKt;
@ -135,6 +136,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
private final Drawable mediaPreviewUnloaded;
private ChipGroup emojiReactionsView;
private StatusQuoteView quoteView;
protected StatusBaseViewHolder(View itemView) {
super(itemView);
@ -151,6 +153,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
bookmarkButton = itemView.findViewById(R.id.status_bookmark);
moreButton = itemView.findViewById(R.id.status_more);
emojiReactionsView = itemView.findViewById(R.id.status_emoji_reactions);
quoteView = itemView.findViewById(R.id.status_quote_view);
mediaContainer = itemView.findViewById(R.id.status_media_preview_container);
mediaContainer.setClipToOutline(true);
@ -475,6 +478,18 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
bookmarkButton.setChecked(bookmarked);
}
protected void setQuote(StatusViewData.Concrete status, final StatusActionListener listener) {
Status quotedStatus = status.getActionable().getQuote();
if (quotedStatus == null) {
quoteView.setVisibility(View.GONE);
return;
}
StatusViewData.Concrete statusViewData = status.copyWithStatus(quotedStatus);
quoteView.setupWithStatus(statusViewData, listener, getBindingAdapterPosition());
quoteView.setVisibility(View.VISIBLE);
}
private BitmapDrawable decodeBlurHash(String blurhash) {
return ImageLoadingHelper.decodeBlurHash(this.avatar.getContext(), blurhash);
}
@ -820,6 +835,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
setReblogged(actionable.getReblogged());
setFavourited(actionable.getFavourited());
setBookmarked(actionable.getBookmarked());
setQuote(status, listener);
List<Attachment> attachments = actionable.getAttachments();
boolean sensitive = actionable.getSensitive();
if (statusDisplayOptions.mediaPreviewEnabled() && hasPreviewableAttachment(attachments)) {
@ -1277,10 +1293,18 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
pollOptions.setVisibility(visibility);
pollButton.setVisibility(visibility);
pollDescription.setVisibility(visibility);
showStatusButtons(show);
}
public void showStatusButtons(boolean show) {
int visibility = show ? View.VISIBLE : View.GONE;
emojiReactionsView.setVisibility(visibility);
replyButton.setVisibility(visibility);
replyCountLabel.setVisibility(visibility);
reblogButton.setVisibility(visibility);
favouriteButton.setVisibility(visibility);
bookmarkButton.setVisibility(visibility);
reactButton.setVisibility(visibility);
moreButton.setVisibility(visibility);
}

View File

@ -133,6 +133,7 @@ data class ConversationStatusEntity(
language = language,
filtered = null,
pleroma = null, // FIXME
quote = null, // FIXME??
),
isExpanded = expanded,
isShowingContent = showingHiddenContent,

View File

@ -497,6 +497,11 @@ class TimelineFragment :
super.viewReplyTo(status.actionable.inReplyToId);
}
override fun onViewQuote(position: Int) {
val status = adapter.peek(position)?.asStatusOrNull()?.actionable?.quote ?: return
super.viewThread(status.id, status.url)
}
override fun onViewTag(tag: String) {
if (viewModel.kind == TimelineViewModel.Kind.TAG && viewModel.tags.size == 1 &&
viewModel.tags.contains(tag)

View File

@ -204,6 +204,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson, isDetailed: Boolean = false
language = status.language,
filtered = status.filtered,
pleroma = pleroma,
quote = null, // FIXME: cache this
)
}
val status = if (reblog != null) {
@ -238,6 +239,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson, isDetailed: Boolean = false
language = status.language,
filtered = status.filtered,
pleroma = pleroma,
quote = null, // FIXME: cache this
)
} else {
Status(
@ -271,6 +273,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson, isDetailed: Boolean = false
language = status.language,
filtered = status.filtered,
pleroma = pleroma,
quote = null, // FIXME: cache this
)
}
return StatusViewData.Concrete(

View File

@ -366,6 +366,15 @@ class ViewThreadFragment :
super.viewReplyTo(id)
}
override fun onViewQuote(position: Int) {
val status = adapter.currentList[position].actionable.quote ?: return
if (thisThreadsStatusId == status.id) {
// If already viewing this thread, don't reopen it.
return
}
super.viewThread(status.id, status.url)
}
override fun onViewUrl(url: String) {
val status: StatusViewData.Concrete? = viewModel.detailedStatus()
if (status != null && status.status.url == url) {

View File

@ -52,6 +52,7 @@ data class Status(
val language: String?,
val filtered: List<FilterResult>?,
val pleroma: PleromaStatus?,
val quote: Status?,
) {
val actionableId: String

View File

@ -33,6 +33,7 @@ public interface StatusActionListener extends LinkListener {
void onViewMedia(int position, int attachmentIndex, @Nullable View view);
void onViewThread(int position);
void onViewReplyTo(int position);
default void onViewQuote(int position) {}
/**
* Open reblog author for the status.

View File

@ -0,0 +1,91 @@
package com.keylesspalace.tusky.view
import android.content.*
import android.util.*
import android.view.*
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.preference.PreferenceManager
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.adapter.StatusViewHolder
import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.interfaces.StatusActionListener
import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.util.CardViewMode
import com.keylesspalace.tusky.util.StatusDisplayOptions
import com.keylesspalace.tusky.viewdata.StatusViewData
class StatusQuoteView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0)
: ConstraintLayout(context, attrs, defStyleAttr) {
private lateinit var viewHolder : StatusViewHolder
private var statusDisplayOptions : StatusDisplayOptions
init {
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),
)
}
fun setupWithStatus(status: StatusViewData.Concrete, statusActionListener: StatusActionListener, position: Int) {
removeAllViews()
View.inflate(context, R.layout.item_status, this)
viewHolder = StatusViewHolder(this)
viewHolder.showStatusButtons(false)
viewHolder.setupWithStatus(status, ProxyQuoteStatusActionListener(statusActionListener, position), statusDisplayOptions)
}
class ProxyQuoteStatusActionListener constructor(
private val higherLevelStatusListener: StatusActionListener,
val position: Int
): 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) {
higherLevelStatusListener.onViewQuote(this.position)
}
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) {
higherLevelStatusListener.onViewAccount(id)
}
override fun onViewTag(id: String) {
higherLevelStatusListener.onViewTag(id)
}
override fun onViewUrl(id: String) {
higherLevelStatusListener.onViewUrl(id)
}
override fun clearWarningAction(position: Int) { }
}
}

View File

@ -103,6 +103,11 @@ sealed class StatusViewData {
this.isCollapsible = shouldTrimStatus(this.content)
}
/** Helper for Java */
fun copyWithStatus(status: Status): Concrete {
return copy(status = status)
}
/** Helper for Java */
fun copyWithCollapsed(isCollapsed: Boolean): Concrete {
return copy(isCollapsed = isCollapsed)

View File

@ -326,6 +326,18 @@
app:layout_constraintTop_toBottomOf="@id/status_poll_button"
tools:text="7 votes • 7 hours remaining" />
<com.keylesspalace.tusky.view.StatusQuoteView
android:id="@+id/status_quote_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/status_media_preview_margin_top"
android:layout_marginEnd="14dp"
android:background="@drawable/card_frame"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_description" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/status_emoji_reactions"
android:layout_width="0dp"
@ -333,7 +345,7 @@
android:layout_marginTop="4dp"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_description"
app:layout_constraintTop_toBottomOf="@id/status_quote_view"
app:layout_constraintEnd_toEndOf="parent" />
<ImageButton

View File

@ -281,6 +281,19 @@
app:layout_constraintTop_toBottomOf="@id/status_poll_button"
tools:text="7 votes • 7 hours remaining" />
<com.keylesspalace.tusky.view.StatusQuoteView
android:id="@+id/status_quote_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="14dp"
android:layout_marginBottom="4dp"
android:background="@drawable/card_frame"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_description" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/status_emoji_reactions"
android:layout_width="0dp"
@ -289,7 +302,7 @@
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_description" />
app:layout_constraintTop_toBottomOf="@id/status_quote_view" />
<TextView
android:id="@+id/status_meta_info"