From 65fcc05ed21093ead5c2559691b12b31d06753fc Mon Sep 17 00:00:00 2001 From: Karol Kosek Date: Mon, 21 Sep 2020 21:11:16 +0200 Subject: [PATCH 1/4] Add 'reply to' indicators in posts --- app/src/husky/res/values/strings.xml | 1 + .../tusky/adapter/StatusBaseViewHolder.java | 14 +++++++++++++ .../com/keylesspalace/tusky/entity/Status.kt | 7 ++++++- .../tusky/util/ViewDataUtils.java | 1 + .../tusky/viewdata/StatusViewData.java | 20 +++++++++++++++++-- app/src/main/res/drawable/ic_reply_18dp.xml | 10 ++++++++++ app/src/main/res/layout/item_status.xml | 20 +++++++++++++++++-- .../main/res/layout/item_status_detailed.xml | 18 ++++++++++++++++- 8 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 app/src/main/res/drawable/ic_reply_18dp.xml diff --git a/app/src/husky/res/values/strings.xml b/app/src/husky/res/values/strings.xml index 56ad4784..81d7ddcb 100644 --- a/app/src/husky/res/values/strings.xml +++ b/app/src/husky/res/values/strings.xml @@ -95,6 +95,7 @@ Share content of post Share link to post %s repeated + Reply to %s Scheduled posts Repeated by diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index 64a59bfe..d49b189b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -69,6 +69,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { private TextView displayName; private TextView username; + private TextView replyInfo; private ImageButton replyButton; private SparkButton reblogButton; private SparkButton favouriteButton; @@ -121,6 +122,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { timestampInfo = itemView.findViewById(R.id.status_timestamp_info); content = itemView.findViewById(R.id.status_content); avatar = itemView.findViewById(R.id.status_avatar); + replyInfo = itemView.findViewById(R.id.reply_info); replyButton = itemView.findViewById(R.id.status_reply); reblogButton = itemView.findViewById(R.id.status_inset); favouriteButton = itemView.findViewById(R.id.status_favourite); @@ -379,6 +381,17 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { } + protected void setReplyInfo(StatusViewData.Concrete status) { + if (status.getInReplyToId() != null) { + Context context = replyInfo.getContext(); + String replyToAccount = status.getInReplyToAccountAcct(); + replyInfo.setText(context.getString(R.string.status_replied_to_format, replyToAccount)); + replyInfo.setVisibility(View.VISIBLE); + } else { + replyInfo.setVisibility(View.GONE); + } + } + private void setReblogged(boolean reblogged) { reblogButton.setChecked(reblogged); } @@ -757,6 +770,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { setUsername(status.getNickname()); setCreatedAt(status.getCreatedAt(), statusDisplayOptions); setIsReply(status.getInReplyToId() != null); + setReplyInfo(status); setAvatar(status.getAvatar(), status.getRebloggedAvatar(), status.isBot(), statusDisplayOptions); setReblogged(status.isReblogged()); setFavourited(status.isFavourited()); diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt index 566d75bf..a6c42151 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt @@ -151,6 +151,10 @@ data class Status( return pleroma?.emojiReactions; } + fun getInReplyToAccountAcct(): String? { + return pleroma?.inReplyToAccountAcct; + } + private fun getEditableText(): String { val builder = SpannableStringBuilder(content) for (span in content.getSpans(0, content.length, URLSpan::class.java)) { @@ -182,7 +186,8 @@ data class Status( data class PleromaStatus( @SerializedName("thread_muted") var threadMuted: Boolean?, @SerializedName("conversation_id") val conversationId: Int?, - @SerializedName("emoji_reactions") val emojiReactions: List? + @SerializedName("emoji_reactions") val emojiReactions: List?, + @SerializedName("in_reply_to_account_acct") val inReplyToAccountAcct: String? ) data class Mention ( diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java index ef649496..8c3b3c4e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java @@ -46,6 +46,7 @@ public final class ViewDataUtils { .setReblogsCount(visibleStatus.getReblogsCount()) .setFavouritesCount(visibleStatus.getFavouritesCount()) .setInReplyToId(visibleStatus.getInReplyToId()) + .setInReplyToAccountAcct(visibleStatus.getInReplyToAccountAcct()) .setFavourited(visibleStatus.getFavourited()) .setBookmarked(visibleStatus.getBookmarked()) .setReblogged(visibleStatus.getReblogged()) diff --git a/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java b/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java index 4475d221..289402c1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java +++ b/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java @@ -77,6 +77,8 @@ public abstract class StatusViewData { private final int favouritesCount; @Nullable private final String inReplyToId; + @Nullable + private final String inReplyToAccountAcct; // I would rather have something else but it would be too much of a rewrite @Nullable private final Status.Mention[] mentions; @@ -104,7 +106,7 @@ public abstract class StatusViewData { @Nullable String rebloggedByUsername, @Nullable String rebloggedAvatar, boolean sensitive, boolean isExpanded, boolean isShowingContent, String userFullName, String nickname, String avatar, Date createdAt, int reblogsCount, int favouritesCount, @Nullable String inReplyToId, - @Nullable Status.Mention[] mentions, String senderId, boolean rebloggingEnabled, + @Nullable String inReplyToAccountAcct, @Nullable Status.Mention[] mentions, String senderId, boolean rebloggingEnabled, Status.Application application, List statusEmojis, List accountEmojis, @Nullable Card card, boolean isCollapsible, boolean isCollapsed, @Nullable PollViewData poll, boolean isBot, boolean isMuted, boolean isThreadMuted, boolean isUserMuted, int conversationId, @Nullable List emojiReactions) { @@ -136,6 +138,7 @@ public abstract class StatusViewData { this.reblogsCount = reblogsCount; this.favouritesCount = favouritesCount; this.inReplyToId = inReplyToId; + this.inReplyToAccountAcct = inReplyToAccountAcct; this.mentions = mentions; this.senderId = senderId; this.rebloggingEnabled = rebloggingEnabled; @@ -240,6 +243,11 @@ public abstract class StatusViewData { return inReplyToId; } + @Nullable + public String getInReplyToAccountAcct() { + return inReplyToAccountAcct; + } + public String getSenderId() { return senderId; } @@ -343,6 +351,7 @@ public abstract class StatusViewData { Objects.equals(avatar, concrete.avatar) && Objects.equals(createdAt, concrete.createdAt) && Objects.equals(inReplyToId, concrete.inReplyToId) && + Objects.equals(inReplyToAccountAcct, concrete.inReplyToAccountAcct) && Arrays.equals(mentions, concrete.mentions) && Objects.equals(senderId, concrete.senderId) && Objects.equals(application, concrete.application) && @@ -451,6 +460,7 @@ public abstract class StatusViewData { private int reblogsCount; private int favouritesCount; private String inReplyToId; + private String inReplyToAccountAcct; private Status.Mention[] mentions; private String senderId; private boolean rebloggingEnabled; @@ -492,6 +502,7 @@ public abstract class StatusViewData { reblogsCount = viewData.reblogsCount; favouritesCount = viewData.favouritesCount; inReplyToId = viewData.inReplyToId; + inReplyToAccountAcct = viewData.inReplyToAccountAcct; mentions = viewData.mentions == null ? null : viewData.mentions.clone(); senderId = viewData.senderId; rebloggingEnabled = viewData.rebloggingEnabled; @@ -614,6 +625,11 @@ public abstract class StatusViewData { return this; } + public Builder setInReplyToAccountAcct(String inReplyToAccountAcct) { + this.inReplyToAccountAcct = inReplyToAccountAcct; + return this; + } + public Builder setMentions(Status.Mention[] mentions) { this.mentions = mentions; return this; @@ -711,7 +727,7 @@ public abstract class StatusViewData { return new StatusViewData.Concrete(id, content, reblogged, favourited, bookmarked, spoilerText, visibility, attachments, rebloggedByUsername, rebloggedAvatar, isSensitive, isExpanded, isShowingContent, userFullName, nickname, avatar, createdAt, reblogsCount, - favouritesCount, inReplyToId, mentions, senderId, rebloggingEnabled, application, + favouritesCount, inReplyToId, inReplyToAccountAcct, mentions, senderId, rebloggingEnabled, application, statusEmojis, accountEmojis, card, isCollapsible, isCollapsed, poll, isBot, isMuted, isThreadMuted, isUserMuted, conversationId, emojiReactions); } diff --git a/app/src/main/res/drawable/ic_reply_18dp.xml b/app/src/main/res/drawable/ic_reply_18dp.xml new file mode 100644 index 00000000..234bc07d --- /dev/null +++ b/app/src/main/res/drawable/ic_reply_18dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/item_status.xml b/app/src/main/res/layout/item_status.xml index 82488ac7..3bc36369 100644 --- a/app/src/main/res/layout/item_status.xml +++ b/app/src/main/res/layout/item_status.xml @@ -115,6 +115,22 @@ app:layout_constraintBaseline_toBaselineOf="@id/status_display_name" tools:text="13:37" /> + + diff --git a/app/src/main/res/layout/item_status_detailed.xml b/app/src/main/res/layout/item_status_detailed.xml index 832e6133..61244772 100644 --- a/app/src/main/res/layout/item_status_detailed.xml +++ b/app/src/main/res/layout/item_status_detailed.xml @@ -79,6 +79,22 @@ app:layout_constraintTop_toBottomOf="@id/status_display_name" tools:text="\@ConnyDuck\@mastodon.social" /> + + Date: Tue, 22 Sep 2020 11:00:27 +0000 Subject: [PATCH 2/4] Add the pleroma object to the db Remember emoji reactions and the 'reply to' text. --- .../25.json | 12 +++++++++--- .../com/keylesspalace/tusky/db/AppDatabase.java | 1 + .../com/keylesspalace/tusky/db/TimelineDao.kt | 2 +- .../tusky/db/TimelineStatusEntity.kt | 3 ++- .../tusky/repository/TimelineRepository.kt | 16 +++++++++++----- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/app/schemas/com.keylesspalace.tusky.db.AppDatabase/25.json b/app/schemas/com.keylesspalace.tusky.db.AppDatabase/25.json index eb29ed08..0c57dedf 100644 --- a/app/schemas/com.keylesspalace.tusky.db.AppDatabase/25.json +++ b/app/schemas/com.keylesspalace.tusky.db.AppDatabase/25.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 25, - "identityHash": "7ab8482b8d5dcb97c4c8932f578879f2", + "identityHash": "fe234db47e8a1376fe941f10959fccca", "entities": [ { "tableName": "TootEntity", @@ -358,7 +358,7 @@ }, { "tableName": "TimelineStatusEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `url` TEXT, `timelineUserId` INTEGER NOT NULL, `authorServerId` TEXT, `inReplyToId` TEXT, `inReplyToAccountId` TEXT, `content` TEXT, `createdAt` INTEGER NOT NULL, `emojis` TEXT, `reblogsCount` INTEGER NOT NULL, `favouritesCount` INTEGER NOT NULL, `reblogged` INTEGER NOT NULL, `bookmarked` INTEGER NOT NULL, `favourited` INTEGER NOT NULL, `sensitive` INTEGER NOT NULL, `spoilerText` TEXT, `visibility` INTEGER, `attachments` TEXT, `mentions` TEXT, `application` TEXT, `reblogServerId` TEXT, `reblogAccountId` TEXT, `poll` TEXT, PRIMARY KEY(`serverId`, `timelineUserId`), FOREIGN KEY(`authorServerId`, `timelineUserId`) REFERENCES `TimelineAccountEntity`(`serverId`, `timelineUserId`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `url` TEXT, `timelineUserId` INTEGER NOT NULL, `authorServerId` TEXT, `inReplyToId` TEXT, `inReplyToAccountId` TEXT, `content` TEXT, `createdAt` INTEGER NOT NULL, `emojis` TEXT, `reblogsCount` INTEGER NOT NULL, `favouritesCount` INTEGER NOT NULL, `reblogged` INTEGER NOT NULL, `bookmarked` INTEGER NOT NULL, `favourited` INTEGER NOT NULL, `sensitive` INTEGER NOT NULL, `spoilerText` TEXT, `visibility` INTEGER, `attachments` TEXT, `mentions` TEXT, `application` TEXT, `reblogServerId` TEXT, `reblogAccountId` TEXT, `poll` TEXT, `pleroma` TEXT, PRIMARY KEY(`serverId`, `timelineUserId`), FOREIGN KEY(`authorServerId`, `timelineUserId`) REFERENCES `TimelineAccountEntity`(`serverId`, `timelineUserId`) ON UPDATE NO ACTION ON DELETE NO ACTION )", "fields": [ { "fieldPath": "serverId", @@ -497,6 +497,12 @@ "columnName": "poll", "affinity": "TEXT", "notNull": false + }, + { + "fieldPath": "pleroma", + "columnName": "pleroma", + "affinity": "TEXT", + "notNull": false } ], "primaryKey": { @@ -879,7 +885,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7ab8482b8d5dcb97c4c8932f578879f2')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'fe234db47e8a1376fe941f10959fccca')" ] } } \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java index 878cac2e..3d980e5c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java +++ b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java @@ -372,6 +372,7 @@ public abstract class AppDatabase extends RoomDatabase { "PRIMARY KEY (`localId`, `messageId`))"); database.execSQL("ALTER TABLE `InstanceEntity` ADD COLUMN `chatLimit` INTEGER"); database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `notificationsChatMessages` INTEGER NOT NULL DEFAULT 1"); + database.execSQL("ALTER TABLE `TimelineStatusEntity` ADD COLUMN `pleroma` TEXT"); } }; } diff --git a/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt b/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt index f91002d6..a9c81d5d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt @@ -26,7 +26,7 @@ SELECT s.serverId, s.url, s.timelineUserId, s.authorServerId, s.inReplyToId, s.inReplyToAccountId, s.createdAt, s.emojis, s.reblogsCount, s.favouritesCount, s.reblogged, s.favourited, s.bookmarked, s.sensitive, s.spoilerText, s.visibility, s.mentions, s.application, s.reblogServerId,s.reblogAccountId, -s.content, s.attachments, s.poll, +s.content, s.attachments, s.poll, s.pleroma, a.serverId as 'a_serverId', a.timelineUserId as 'a_timelineUserId', a.localUsername as 'a_localUsername', a.username as 'a_username', a.displayName as 'a_displayName', a.url as 'a_url', a.avatar as 'a_avatar', diff --git a/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt b/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt index 94111a95..93ff3c1c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt @@ -51,7 +51,8 @@ data class TimelineStatusEntity( val application: String?, val reblogServerId: String?, // if it has a reblogged status, it's id is stored here val reblogAccountId: String?, - val poll: String? + val poll: String?, + val pleroma: String? ) @Entity( diff --git a/app/src/main/java/com/keylesspalace/tusky/repository/TimelineRepository.kt b/app/src/main/java/com/keylesspalace/tusky/repository/TimelineRepository.kt index b8764445..058e24ee 100644 --- a/app/src/main/java/com/keylesspalace/tusky/repository/TimelineRepository.kt +++ b/app/src/main/java/com/keylesspalace/tusky/repository/TimelineRepository.kt @@ -208,6 +208,7 @@ class TimelineRepositoryImpl( val emojis: List = gson.fromJson(status.emojis, object : TypeToken>() {}.type) ?: listOf() val poll: Poll? = gson.fromJson(status.poll, Poll::class.java) + val pleroma = gson.fromJson(status.pleroma, Status.PleromaStatus::class.java) val reblog = status.reblogServerId?.let { id -> Status( @@ -233,7 +234,8 @@ class TimelineRepositoryImpl( application = application, pinned = false, poll = poll, - card = null + card = null, + pleroma = pleroma ) } val status = if (reblog != null) { @@ -260,7 +262,8 @@ class TimelineRepositoryImpl( application = null, pinned = false, poll = null, - card = null + card = null, + pleroma = null ) } else { Status( @@ -286,7 +289,8 @@ class TimelineRepositoryImpl( application = application, pinned = false, poll = poll, - card = null + card = null, + pleroma = pleroma ) } return Either.Right(status) @@ -356,7 +360,8 @@ fun Placeholder.toEntity(timelineUserId: Long): TimelineStatusEntity { application = null, reblogServerId = null, reblogAccountId = null, - poll = null + poll = null, + pleroma = null ) } @@ -386,7 +391,8 @@ fun Status.toEntity(timelineUserId: Long, application = actionable.application.let(gson::toJson), reblogServerId = reblog?.id, reblogAccountId = reblog?.let { this.account.id }, - poll = actionable.poll.let(gson::toJson) + poll = actionable.poll.let(gson::toJson), + pleroma = actionable.pleroma.let(gson::toJson) ) } From 83eee5115ce7d0cc6dc15713693c8da605f5197d Mon Sep 17 00:00:00 2001 From: Karol Kosek Date: Tue, 22 Sep 2020 18:35:50 +0200 Subject: [PATCH 3/4] Strikethrough the reply info if parent post isn't visible --- .../tusky/adapter/StatusBaseViewHolder.java | 5 +++++ .../com/keylesspalace/tusky/entity/Status.kt | 7 ++++++- .../tusky/util/ViewDataUtils.java | 1 + .../tusky/viewdata/StatusViewData.java | 20 ++++++++++++++++--- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index d49b189b..6d956860 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -17,6 +17,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import android.util.Log; +import android.graphics.Paint; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; @@ -386,6 +387,10 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { Context context = replyInfo.getContext(); String replyToAccount = status.getInReplyToAccountAcct(); replyInfo.setText(context.getString(R.string.status_replied_to_format, replyToAccount)); + if (status.getParentVisible() == false) + replyInfo.setPaintFlags(replyInfo.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + else + replyInfo.setPaintFlags(replyInfo.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); replyInfo.setVisibility(View.VISIBLE); } else { replyInfo.setVisibility(View.GONE); diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt index a6c42151..47dc44d1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt @@ -155,6 +155,10 @@ data class Status( return pleroma?.inReplyToAccountAcct; } + fun getParentVisible(): Boolean { + return pleroma?.parentVisible ?: true; + } + private fun getEditableText(): String { val builder = SpannableStringBuilder(content) for (span in content.getSpans(0, content.length, URLSpan::class.java)) { @@ -187,7 +191,8 @@ data class Status( @SerializedName("thread_muted") var threadMuted: Boolean?, @SerializedName("conversation_id") val conversationId: Int?, @SerializedName("emoji_reactions") val emojiReactions: List?, - @SerializedName("in_reply_to_account_acct") val inReplyToAccountAcct: String? + @SerializedName("in_reply_to_account_acct") val inReplyToAccountAcct: String?, + @SerializedName("parent_visible") val parentVisible: Boolean? ) data class Mention ( diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java index 8c3b3c4e..c961d746 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java @@ -76,6 +76,7 @@ public final class ViewDataUtils { .setThreadMuted(visibleStatus.isThreadMuted()) .setConversationId(visibleStatus.getConversationId()) .setEmojiReactions(visibleStatus.getEmojiReactions()) + .setParentVisible(visibleStatus.getParentVisible()) .createStatusViewData(); } diff --git a/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java b/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java index 289402c1..0f2f5c82 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java +++ b/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java @@ -100,6 +100,7 @@ public abstract class StatusViewData { private final int conversationId; @Nullable private final List emojiReactions; + private final boolean parentVisible; public Concrete(String id, Spanned content, boolean reblogged, boolean favourited, boolean bookmarked, @Nullable String spoilerText, Status.Visibility visibility, List attachments, @@ -109,7 +110,7 @@ public abstract class StatusViewData { @Nullable String inReplyToAccountAcct, @Nullable Status.Mention[] mentions, String senderId, boolean rebloggingEnabled, Status.Application application, List statusEmojis, List accountEmojis, @Nullable Card card, boolean isCollapsible, boolean isCollapsed, @Nullable PollViewData poll, boolean isBot, boolean isMuted, boolean isThreadMuted, - boolean isUserMuted, int conversationId, @Nullable List emojiReactions) { + boolean isUserMuted, int conversationId, @Nullable List emojiReactions, boolean parentVisible) { this.id = id; if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) { @@ -155,6 +156,7 @@ public abstract class StatusViewData { this.isUserMuted = isUserMuted; this.conversationId = conversationId; this.emojiReactions = emojiReactions; + this.parentVisible = parentVisible; } public String getId() { @@ -273,6 +275,10 @@ public abstract class StatusViewData { return accountEmojis; } + public boolean getParentVisible() { + return parentVisible; + } + @Nullable public Card getCard() { return card; @@ -364,7 +370,8 @@ public abstract class StatusViewData { isThreadMuted == concrete.isThreadMuted && isUserMuted == concrete.isUserMuted && conversationId == concrete.conversationId && - Objects.equals(emojiReactions, concrete.emojiReactions); + Objects.equals(emojiReactions, concrete.emojiReactions) && + parentVisible == concrete.parentVisible; } static Spanned replaceCrashingCharacters(Spanned content) { @@ -477,6 +484,7 @@ public abstract class StatusViewData { private boolean isUserMuted; private int conversationId; private List emojiReactions; + private boolean parentVisible; public Builder() { } @@ -518,6 +526,7 @@ public abstract class StatusViewData { isThreadMuted = viewData.isThreadMuted; isUserMuted = viewData.isUserMuted; emojiReactions = viewData.emojiReactions; + parentVisible = viewData.parentVisible; } public Builder setId(String id) { @@ -660,6 +669,11 @@ public abstract class StatusViewData { return this; } + public Builder setParentVisible(boolean parentVisible) { + this.parentVisible = parentVisible; + return this; + } + public Builder setCard(Card card) { this.card = card; return this; @@ -729,7 +743,7 @@ public abstract class StatusViewData { isShowingContent, userFullName, nickname, avatar, createdAt, reblogsCount, favouritesCount, inReplyToId, inReplyToAccountAcct, mentions, senderId, rebloggingEnabled, application, statusEmojis, accountEmojis, card, isCollapsible, isCollapsed, poll, isBot, isMuted, isThreadMuted, - isUserMuted, conversationId, emojiReactions); + isUserMuted, conversationId, emojiReactions, parentVisible); } } } From 98b57f33f5bbc7b39f3aa651f3b2272bbbdbf713 Mon Sep 17 00:00:00 2001 From: Karol Kosek Date: Fri, 2 Oct 2020 23:00:15 +0200 Subject: [PATCH 4/4] Implement clicking on 'Reply to' text ... and remove the Reply to button from the menu. --- .../tusky/adapter/StatusBaseViewHolder.java | 7 +++++-- .../conversation/ConversationsFragment.kt | 4 ++++ .../fragments/SearchStatusesFragment.kt | 19 +++++++------------ .../tusky/fragment/NotificationsFragment.java | 7 +++++++ .../tusky/fragment/SFragment.java | 10 ---------- .../tusky/fragment/TimelineFragment.java | 10 ++++++++++ .../tusky/fragment/ViewThreadFragment.java | 7 +++++++ .../interfaces/StatusActionListener.java | 1 + .../keylesspalace/tusky/view/StatusView.kt | 1 + app/src/main/res/menu/status_more.xml | 3 --- .../main/res/menu/status_more_for_user.xml | 3 --- 11 files changed, 42 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index 6d956860..c23265a3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -382,7 +382,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { } - protected void setReplyInfo(StatusViewData.Concrete status) { + protected void setReplyInfo(StatusViewData.Concrete status, StatusActionListener listener) { if (status.getInReplyToId() != null) { Context context = replyInfo.getContext(); String replyToAccount = status.getInReplyToAccountAcct(); @@ -391,6 +391,9 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { replyInfo.setPaintFlags(replyInfo.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); else replyInfo.setPaintFlags(replyInfo.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); + + replyInfo.setOnClickListener(v -> listener.onViewReplyTo(getAdapterPosition())); + replyInfo.setVisibility(View.VISIBLE); } else { replyInfo.setVisibility(View.GONE); @@ -775,7 +778,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { setUsername(status.getNickname()); setCreatedAt(status.getCreatedAt(), statusDisplayOptions); setIsReply(status.getInReplyToId() != null); - setReplyInfo(status); + setReplyInfo(status, listener); setAvatar(status.getAvatar(), status.getRebloggedAvatar(), status.isBot(), statusDisplayOptions); setReblogged(status.isReblogged()); setFavourited(status.isFavourited()); diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt index dc2d60c8..c75e5829 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt @@ -138,6 +138,10 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res } } + override fun onViewReplyTo(position: Int) { + // there are no Reply to labels in conversations + } + override fun onOpenReblog(position: Int) { // there are no reblogs in search results } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt index f8c8ea63..3ff348b1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt @@ -155,6 +155,13 @@ class SearchStatusesFragment : SearchFragment + val actionableStatus = status.actionableStatus + bottomSheetActivity?.viewThread(actionableStatus.inReplyToId!!, null) + } + } + override fun onOpenReblog(position: Int) { searchAdapter.getItem(position)?.first?.let { status -> bottomSheetActivity?.viewAccount(status.account.id) @@ -195,10 +202,6 @@ class SearchStatusesFragment : SearchFragment when (item.itemId) { R.id.status_share_content -> { @@ -309,10 +308,6 @@ class SearchStatusesFragment : SearchFragment { - onShowReplyTo(status.inReplyToId!!) - return@setOnMenuItemClickListener true - } R.id.status_open_as -> { showOpenAsDialog(statusUrl!!, item.title) return@setOnMenuItemClickListener true diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java index 62ad90c1..feabe041 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java @@ -591,6 +591,13 @@ public class NotificationsFragment extends SFragment implements super.viewThread(notification.getStatus()); } + @Override + public void onViewReplyTo(int position) { + Notification notification = notifications.get(position).asRightOrNull(); + if (notification == null) return; + super.onShowReplyTo(notification.getStatus().getInReplyToId()); + } + @Override public void onOpenReblog(int position) { Notification notification = notifications.get(position).asRight(); diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java index 8fd5607f..bc7dba69 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java @@ -267,12 +267,6 @@ public abstract class SFragment extends BaseFragment implements Injectable { } openAsItem.setTitle(openAsTitle); - if(status.getInReplyToId() == null) { - MenuItem replyToItem = menu.findItem(R.id.status_reply_to); - - replyToItem.setVisible(false); - } - // maybe not a best check if(status.getPleroma() != null) { boolean showMute = true; // predict state @@ -325,10 +319,6 @@ public abstract class SFragment extends BaseFragment implements Injectable { LinkHelper.openLinkInBrowser(Uri.parse(statusUrl), getContext()); return true; } - case R.id.status_reply_to: { - onShowReplyTo(status.getInReplyToId()); - return true; - } case R.id.status_open_as: { showOpenAsDialog(statusUrl, item.getTitle()); return true; diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java index e29a46f0..aa73858f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java @@ -848,6 +848,16 @@ public class TimelineFragment extends SFragment implements super.viewThread(statuses.get(position).asRight()); } + @Override + public void onViewReplyTo(int position) { + Status status = statuses.get(position).asRightOrNull(); + if (status == null) return; + + String replyToId = status.getReblog() == null ? status.getInReplyToId() : status.getReblog().getInReplyToId(); + if (replyToId == null) return; + super.onShowReplyTo(replyToId); + } + @Override public void onViewTag(String tag) { if (kind == Kind.TAG && tags.size() == 1 && tags.contains(tag)) { diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java index a6ff4a50..e7288c91 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java @@ -315,6 +315,13 @@ public final class ViewThreadFragment extends SFragment implements super.viewThread(status); } + @Override + public void onViewReplyTo(int position) { + Status status = statuses.get(position); + if (thisThreadsStatusId.equals(status.getInReplyToId())) return; + super.onShowReplyTo(status.getInReplyToId()); + } + @Override public void onOpenReblog(int position) { // there should be no reblogs in the thread but let's implement it to be sure diff --git a/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java b/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java index 16d28eac..d956cec2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java +++ b/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java @@ -31,6 +31,7 @@ public interface StatusActionListener extends LinkListener { void onMore(@NonNull View view, final int position); void onViewMedia(int position, int attachmentIndex, @Nullable View view); void onViewThread(int position); + void onViewReplyTo(int position); /** * Open reblog author for the status. diff --git a/app/src/main/java/com/keylesspalace/tusky/view/StatusView.kt b/app/src/main/java/com/keylesspalace/tusky/view/StatusView.kt index 1fc4a2e7..72793b15 100644 --- a/app/src/main/java/com/keylesspalace/tusky/view/StatusView.kt +++ b/app/src/main/java/com/keylesspalace/tusky/view/StatusView.kt @@ -58,6 +58,7 @@ class StatusView @JvmOverloads constructor( 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) { } diff --git a/app/src/main/res/menu/status_more.xml b/app/src/main/res/menu/status_more.xml index e2419d23..32eb95aa 100644 --- a/app/src/main/res/menu/status_more.xml +++ b/app/src/main/res/menu/status_more.xml @@ -12,9 +12,6 @@ android:title="@string/status_share_content" /> - diff --git a/app/src/main/res/menu/status_more_for_user.xml b/app/src/main/res/menu/status_more_for_user.xml index 13248290..fdc546fb 100644 --- a/app/src/main/res/menu/status_more_for_user.xml +++ b/app/src/main/res/menu/status_more_for_user.xml @@ -12,9 +12,6 @@ android:title="@string/status_share_content" /> -