[WIP] Show quotes replies
This commit is contained in:
parent
c107aa78ef
commit
9d1159969f
|
@ -66,6 +66,7 @@ import com.keylesspalace.tusky.util.TimestampUtils;
|
||||||
import com.keylesspalace.tusky.util.TouchDelegateHelper;
|
import com.keylesspalace.tusky.util.TouchDelegateHelper;
|
||||||
import com.keylesspalace.tusky.view.MediaPreviewImageView;
|
import com.keylesspalace.tusky.view.MediaPreviewImageView;
|
||||||
import com.keylesspalace.tusky.view.MediaPreviewLayout;
|
import com.keylesspalace.tusky.view.MediaPreviewLayout;
|
||||||
|
import com.keylesspalace.tusky.view.StatusQuoteView;
|
||||||
import com.keylesspalace.tusky.viewdata.PollOptionViewData;
|
import com.keylesspalace.tusky.viewdata.PollOptionViewData;
|
||||||
import com.keylesspalace.tusky.viewdata.PollViewData;
|
import com.keylesspalace.tusky.viewdata.PollViewData;
|
||||||
import com.keylesspalace.tusky.viewdata.PollViewDataKt;
|
import com.keylesspalace.tusky.viewdata.PollViewDataKt;
|
||||||
|
@ -135,6 +136,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
private final Drawable mediaPreviewUnloaded;
|
private final Drawable mediaPreviewUnloaded;
|
||||||
|
|
||||||
private ChipGroup emojiReactionsView;
|
private ChipGroup emojiReactionsView;
|
||||||
|
private StatusQuoteView quoteView;
|
||||||
|
|
||||||
protected StatusBaseViewHolder(View itemView) {
|
protected StatusBaseViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
|
@ -151,6 +153,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
bookmarkButton = itemView.findViewById(R.id.status_bookmark);
|
bookmarkButton = itemView.findViewById(R.id.status_bookmark);
|
||||||
moreButton = itemView.findViewById(R.id.status_more);
|
moreButton = itemView.findViewById(R.id.status_more);
|
||||||
emojiReactionsView = itemView.findViewById(R.id.status_emoji_reactions);
|
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 = itemView.findViewById(R.id.status_media_preview_container);
|
||||||
mediaContainer.setClipToOutline(true);
|
mediaContainer.setClipToOutline(true);
|
||||||
|
@ -475,6 +478,18 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
bookmarkButton.setChecked(bookmarked);
|
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) {
|
private BitmapDrawable decodeBlurHash(String blurhash) {
|
||||||
return ImageLoadingHelper.decodeBlurHash(this.avatar.getContext(), blurhash);
|
return ImageLoadingHelper.decodeBlurHash(this.avatar.getContext(), blurhash);
|
||||||
}
|
}
|
||||||
|
@ -820,6 +835,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
setReblogged(actionable.getReblogged());
|
setReblogged(actionable.getReblogged());
|
||||||
setFavourited(actionable.getFavourited());
|
setFavourited(actionable.getFavourited());
|
||||||
setBookmarked(actionable.getBookmarked());
|
setBookmarked(actionable.getBookmarked());
|
||||||
|
setQuote(status, listener);
|
||||||
List<Attachment> attachments = actionable.getAttachments();
|
List<Attachment> attachments = actionable.getAttachments();
|
||||||
boolean sensitive = actionable.getSensitive();
|
boolean sensitive = actionable.getSensitive();
|
||||||
if (statusDisplayOptions.mediaPreviewEnabled() && hasPreviewableAttachment(attachments)) {
|
if (statusDisplayOptions.mediaPreviewEnabled() && hasPreviewableAttachment(attachments)) {
|
||||||
|
@ -1277,10 +1293,18 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
pollOptions.setVisibility(visibility);
|
pollOptions.setVisibility(visibility);
|
||||||
pollButton.setVisibility(visibility);
|
pollButton.setVisibility(visibility);
|
||||||
pollDescription.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);
|
replyButton.setVisibility(visibility);
|
||||||
|
replyCountLabel.setVisibility(visibility);
|
||||||
reblogButton.setVisibility(visibility);
|
reblogButton.setVisibility(visibility);
|
||||||
favouriteButton.setVisibility(visibility);
|
favouriteButton.setVisibility(visibility);
|
||||||
bookmarkButton.setVisibility(visibility);
|
bookmarkButton.setVisibility(visibility);
|
||||||
|
reactButton.setVisibility(visibility);
|
||||||
moreButton.setVisibility(visibility);
|
moreButton.setVisibility(visibility);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,6 +133,7 @@ data class ConversationStatusEntity(
|
||||||
language = language,
|
language = language,
|
||||||
filtered = null,
|
filtered = null,
|
||||||
pleroma = null, // FIXME
|
pleroma = null, // FIXME
|
||||||
|
quote = null, // FIXME??
|
||||||
),
|
),
|
||||||
isExpanded = expanded,
|
isExpanded = expanded,
|
||||||
isShowingContent = showingHiddenContent,
|
isShowingContent = showingHiddenContent,
|
||||||
|
|
|
@ -497,6 +497,11 @@ class TimelineFragment :
|
||||||
super.viewReplyTo(status.actionable.inReplyToId);
|
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) {
|
override fun onViewTag(tag: String) {
|
||||||
if (viewModel.kind == TimelineViewModel.Kind.TAG && viewModel.tags.size == 1 &&
|
if (viewModel.kind == TimelineViewModel.Kind.TAG && viewModel.tags.size == 1 &&
|
||||||
viewModel.tags.contains(tag)
|
viewModel.tags.contains(tag)
|
||||||
|
|
|
@ -204,6 +204,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson, isDetailed: Boolean = false
|
||||||
language = status.language,
|
language = status.language,
|
||||||
filtered = status.filtered,
|
filtered = status.filtered,
|
||||||
pleroma = pleroma,
|
pleroma = pleroma,
|
||||||
|
quote = null, // FIXME: cache this
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val status = if (reblog != null) {
|
val status = if (reblog != null) {
|
||||||
|
@ -238,6 +239,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson, isDetailed: Boolean = false
|
||||||
language = status.language,
|
language = status.language,
|
||||||
filtered = status.filtered,
|
filtered = status.filtered,
|
||||||
pleroma = pleroma,
|
pleroma = pleroma,
|
||||||
|
quote = null, // FIXME: cache this
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Status(
|
Status(
|
||||||
|
@ -271,6 +273,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson, isDetailed: Boolean = false
|
||||||
language = status.language,
|
language = status.language,
|
||||||
filtered = status.filtered,
|
filtered = status.filtered,
|
||||||
pleroma = pleroma,
|
pleroma = pleroma,
|
||||||
|
quote = null, // FIXME: cache this
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return StatusViewData.Concrete(
|
return StatusViewData.Concrete(
|
||||||
|
|
|
@ -366,6 +366,15 @@ class ViewThreadFragment :
|
||||||
super.viewReplyTo(id)
|
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) {
|
override fun onViewUrl(url: String) {
|
||||||
val status: StatusViewData.Concrete? = viewModel.detailedStatus()
|
val status: StatusViewData.Concrete? = viewModel.detailedStatus()
|
||||||
if (status != null && status.status.url == url) {
|
if (status != null && status.status.url == url) {
|
||||||
|
|
|
@ -52,6 +52,7 @@ data class Status(
|
||||||
val language: String?,
|
val language: String?,
|
||||||
val filtered: List<FilterResult>?,
|
val filtered: List<FilterResult>?,
|
||||||
val pleroma: PleromaStatus?,
|
val pleroma: PleromaStatus?,
|
||||||
|
val quote: Status?,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val actionableId: String
|
val actionableId: String
|
||||||
|
|
|
@ -33,6 +33,7 @@ public interface StatusActionListener extends LinkListener {
|
||||||
void onViewMedia(int position, int attachmentIndex, @Nullable View view);
|
void onViewMedia(int position, int attachmentIndex, @Nullable View view);
|
||||||
void onViewThread(int position);
|
void onViewThread(int position);
|
||||||
void onViewReplyTo(int position);
|
void onViewReplyTo(int position);
|
||||||
|
default void onViewQuote(int position) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open reblog author for the status.
|
* Open reblog author for the status.
|
||||||
|
|
|
@ -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) { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -103,6 +103,11 @@ sealed class StatusViewData {
|
||||||
this.isCollapsible = shouldTrimStatus(this.content)
|
this.isCollapsible = shouldTrimStatus(this.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Helper for Java */
|
||||||
|
fun copyWithStatus(status: Status): Concrete {
|
||||||
|
return copy(status = status)
|
||||||
|
}
|
||||||
|
|
||||||
/** Helper for Java */
|
/** Helper for Java */
|
||||||
fun copyWithCollapsed(isCollapsed: Boolean): Concrete {
|
fun copyWithCollapsed(isCollapsed: Boolean): Concrete {
|
||||||
return copy(isCollapsed = isCollapsed)
|
return copy(isCollapsed = isCollapsed)
|
||||||
|
|
|
@ -326,6 +326,18 @@
|
||||||
app:layout_constraintTop_toBottomOf="@id/status_poll_button"
|
app:layout_constraintTop_toBottomOf="@id/status_poll_button"
|
||||||
tools:text="7 votes • 7 hours remaining" />
|
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
|
<com.google.android.material.chip.ChipGroup
|
||||||
android:id="@+id/status_emoji_reactions"
|
android:id="@+id/status_emoji_reactions"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
@ -333,7 +345,7 @@
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
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" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
|
|
|
@ -281,6 +281,19 @@
|
||||||
app:layout_constraintTop_toBottomOf="@id/status_poll_button"
|
app:layout_constraintTop_toBottomOf="@id/status_poll_button"
|
||||||
tools:text="7 votes • 7 hours remaining" />
|
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
|
<com.google.android.material.chip.ChipGroup
|
||||||
android:id="@+id/status_emoji_reactions"
|
android:id="@+id/status_emoji_reactions"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
@ -289,7 +302,7 @@
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/status_poll_description" />
|
app:layout_constraintTop_toBottomOf="@id/status_quote_view" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/status_meta_info"
|
android:id="@+id/status_meta_info"
|
||||||
|
|
Loading…
Reference in New Issue