Fix loading toots with conversation_id as String

It happens at Gleasonator but it could happen to other instances. The
ones which returns this value as an Integer should work perfectly.

Fixes: https://todo.sr.ht/~captainepoch/husky/48
This commit is contained in:
Adolfo Santiago 2022-07-07 16:48:36 +02:00
parent 06b01e5a37
commit 0bd5182b9e
No known key found for this signature in database
GPG key ID: 244D6F9A317B4A65
6 changed files with 203 additions and 150 deletions

View file

@ -1,17 +1,22 @@
/* Copyright 2017 Andrew Dawson /*
* Husky -- A Pleroma client for Android
* *
* This file is a part of Tusky. * Copyright (C) 2022 The Husky Developers
* Copyright (C) 2017 Andrew Dawson
* *
* This program is free software; you can redistribute it and/or modify it under the terms of the * This program is free software: you can redistribute it and/or modify
* GNU General Public License as published by the Free Software Foundation; either version 3 of the * it under the terms of the GNU General Public License as published by
* License, or (at your option) any later version. * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* *
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * This program is distributed in the hope that it will be useful,
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * but WITHOUT ANY WARRANTY; without even the implied warranty of
* Public License for more details. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along with Tusky; if not, * You should have received a copy of the GNU General Public License
* see <http://www.gnu.org/licenses>. */ * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.keylesspalace.tusky.entity package com.keylesspalace.tusky.entity
@ -19,7 +24,7 @@ import android.text.SpannableStringBuilder
import android.text.Spanned import android.text.Spanned
import android.text.style.URLSpan import android.text.style.URLSpan
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import java.util.* import java.util.Date
data class Status( data class Status(
var id: String, var id: String,
@ -58,12 +63,16 @@ data class Status(
enum class Visibility(val num: Int) { enum class Visibility(val num: Int) {
UNKNOWN(0), UNKNOWN(0),
@SerializedName("public") @SerializedName("public")
PUBLIC(1), PUBLIC(1),
@SerializedName("unlisted") @SerializedName("unlisted")
UNLISTED(2), UNLISTED(2),
@SerializedName("private") @SerializedName("private")
PRIVATE(3), PRIVATE(3),
@SerializedName("direct") @SerializedName("direct")
DIRECT(4); DIRECT(4);
@ -143,8 +152,8 @@ data class Status(
pleroma.threadMuted = mute pleroma.threadMuted = mute
} }
fun getConversationId(): Int { fun getConversationId(): String {
return pleroma?.conversationId ?: -1 return pleroma?.conversationId ?: ""
} }
fun getEmojiReactions(): List<EmojiReaction>? { fun getEmojiReactions(): List<EmojiReaction>? {
@ -189,7 +198,7 @@ data class Status(
data class PleromaStatus( data class PleromaStatus(
@SerializedName("thread_muted") var threadMuted: Boolean?, @SerializedName("thread_muted") var threadMuted: Boolean?,
@SerializedName("conversation_id") val conversationId: Int?, @SerializedName("conversation_id") val conversationId: String?,
@SerializedName("emoji_reactions") val emojiReactions: List<EmojiReaction>?, @SerializedName("emoji_reactions") val emojiReactions: List<EmojiReaction>?,
@SerializedName("in_reply_to_account_acct") val inReplyToAccountAcct: String?, @SerializedName("in_reply_to_account_acct") val inReplyToAccountAcct: String?,
@SerializedName("parent_visible") val parentVisible: Boolean? @SerializedName("parent_visible") val parentVisible: Boolean?

View file

@ -350,9 +350,9 @@ public class NotificationsFragment extends SFragment implements
if (posAndNotification == null) if (posAndNotification == null)
return; return;
int conversationId = posAndNotification.second.getStatus().getConversationId(); String conversationId = posAndNotification.second.getStatus().getConversationId();
if(conversationId == -1) { // invalid conversation ID if(conversationId.isEmpty()) { // invalid conversation ID
if(withMuted) { if(withMuted) {
setMutedStatusForStatus(posAndNotification.first, posAndNotification.second.getStatus(), event.getMute(), event.getMute()); setMutedStatusForStatus(posAndNotification.first, posAndNotification.second.getStatus(), event.getMute(), event.getMute());
} else { } else {
@ -1016,7 +1016,7 @@ public class NotificationsFragment extends SFragment implements
updateAdapter(); updateAdapter();
} }
private void removeAllByConversationId(int conversationId) { private void removeAllByConversationId(String conversationId) {
// using iterator to safely remove items while iterating // using iterator to safely remove items while iterating
Iterator<Either<Placeholder, Notification>> iterator = notifications.iterator(); Iterator<Either<Placeholder, Notification>> iterator = notifications.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
@ -1024,7 +1024,7 @@ public class NotificationsFragment extends SFragment implements
Notification notification = placeholderOrNotification.asRightOrNull(); Notification notification = placeholderOrNotification.asRightOrNull();
if (notification != null && notification.getStatus() != null if (notification != null && notification.getStatus() != null
&& notification.getType() == Notification.Type.MENTION && && notification.getType() == Notification.Type.MENTION &&
notification.getStatus().getConversationId() == conversationId) { notification.getStatus().getConversationId().equalsIgnoreCase(conversationId)) {
iterator.remove(); iterator.remove();
} }
} }

View file

@ -1,17 +1,23 @@
/* Copyright 2017 Andrew Dawson /*
* Husky -- A Pleroma client for Android
* *
* This file is a part of Tusky. * Copyright (C) 2022 The Husky Developers
* Copyright (C) 2017 Andrew Dawson
* *
* This program is free software; you can redistribute it and/or modify it under the terms of the * This program is free software: you can redistribute it and/or modify
* GNU General Public License as published by the Free Software Foundation; either version 3 of the * it under the terms of the GNU General Public License as published by
* License, or (at your option) any later version. * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* *
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * This program is distributed in the hope that it will be useful,
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * but WITHOUT ANY WARRANTY; without even the implied warranty of
* Public License for more details. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along with Tusky; if not, * You should have received a copy of the GNU General Public License
* see <http://www.gnu.org/licenses>. */ * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.keylesspalace.tusky.fragment; package com.keylesspalace.tusky.fragment;
@ -22,7 +28,6 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -108,13 +113,13 @@ import kotlin.jvm.functions.Function1;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;
import timber.log.Timber;
public class TimelineFragment extends SFragment implements public class TimelineFragment extends SFragment implements
SwipeRefreshLayout.OnRefreshListener, SwipeRefreshLayout.OnRefreshListener,
StatusActionListener, StatusActionListener,
Injectable, ReselectableFragment, RefreshableFragment { Injectable, ReselectableFragment, RefreshableFragment {
private static final String TAG = "TimelineF"; // logging tag
private static final String KIND_ARG = "kind"; private static final String KIND_ARG = "kind";
private static final String ID_ARG = "id"; private static final String ID_ARG = "id";
private static final String HASHTAGS_ARG = "hastags"; private static final String HASHTAGS_ARG = "hastags";
@ -475,7 +480,7 @@ public class TimelineFragment extends SFragment implements
public void onActivityCreated(@Nullable Bundle savedInstanceState) { public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(requireContext());
/* This is delayed until onActivityCreated solely because MainActivity.composeButton isn't /* This is delayed until onActivityCreated solely because MainActivity.composeButton isn't
* guaranteed to be set until then. */ * guaranteed to be set until then. */
@ -623,7 +628,7 @@ public class TimelineFragment extends SFragment implements
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY))) .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe( .subscribe(
(newStatus) -> setRebloggedForStatus(position, status, reblog), (newStatus) -> setRebloggedForStatus(position, status, reblog),
(err) -> Log.d(TAG, "Failed to reblog status " + status.getId(), err) (err) -> Timber.e("Failed to reblog status " + status.getId() + ", Error[" + err + "]")
); );
} }
@ -655,7 +660,7 @@ public class TimelineFragment extends SFragment implements
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY))) .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe( .subscribe(
(newStatus) -> setFavouriteForStatus(position, newStatus, favourite), (newStatus) -> setFavouriteForStatus(position, newStatus, favourite),
(err) -> Log.d(TAG, "Failed to favourite status " + status.getId(), err) (err) -> Timber.e("Failed to favourite status " + status.getId() + ", Error [" + err + "]")
); );
} }
@ -687,7 +692,7 @@ public class TimelineFragment extends SFragment implements
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY))) .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe( .subscribe(
(newStatus) -> setBookmarkForStatus(position, newStatus, bookmark), (newStatus) -> setBookmarkForStatus(position, newStatus, bookmark),
(err) -> Log.d(TAG, "Failed to favourite status " + status.getId(), err) (err) -> Timber.e(err, "Failed to favourite status " + status.getId())
); );
} }
@ -743,8 +748,8 @@ public class TimelineFragment extends SFragment implements
.as(autoDisposable(from(this))) .as(autoDisposable(from(this)))
.subscribe( .subscribe(
(newPoll) -> setVoteForPoll(position, status, newPoll), (newPoll) -> setVoteForPoll(position, status, newPoll),
(t) -> Log.d(TAG, (t) -> Timber.e(t,
"Failed to vote in poll: " + status.getId(), t) "Failed to vote in poll: " + status.getId())
); );
} }
@ -815,7 +820,7 @@ public class TimelineFragment extends SFragment implements
? statuses.get(position + 1).asRight().getId() ? statuses.get(position + 1).asRight().getId()
: null; : null;
if(fromStatus == null || toStatus == null) { if(fromStatus == null || toStatus == null) {
Log.e(TAG, "Failed to load more at " + position + ", wrong placeholder position"); Timber.e("Failed to load more at " + position + ", wrong placeholder position");
return; return;
} }
sendFetchTimelineRequest(fromStatus.getId(), toStatus.getId(), maxMinusOne, sendFetchTimelineRequest(fromStatus.getId(), toStatus.getId(), maxMinusOne,
@ -826,14 +831,14 @@ public class TimelineFragment extends SFragment implements
statuses.setPairedItem(position, newViewData); statuses.setPairedItem(position, newViewData);
updateAdapter(); updateAdapter();
} else { } else {
Log.e(TAG, "error loading more"); Timber.e("error loading more");
} }
} }
@Override @Override
public void onContentCollapsedChange(boolean isCollapsed, int position) { public void onContentCollapsedChange(boolean isCollapsed, int position) {
if(position < 0 || position >= statuses.size()) { if(position < 0 || position >= statuses.size()) {
Log.e(TAG, String.format("Tried to access out of bounds status position: %d of %d", position, statuses.size() - 1)); Timber.e(String.format("Tried to access out of bounds status position: %d of %d", position, statuses.size() - 1));
return; return;
} }
@ -841,7 +846,7 @@ public class TimelineFragment extends SFragment implements
if(!(status instanceof StatusViewData.Concrete)) { if(!(status instanceof StatusViewData.Concrete)) {
// Statuses PairedList contains a base type of StatusViewData.Concrete and also doesn't // Statuses PairedList contains a base type of StatusViewData.Concrete and also doesn't
// check for null values when adding values to it although this doesn't seem to be an issue. // check for null values when adding values to it although this doesn't seem to be an issue.
Log.e(TAG, String.format( Timber.e(String.format(
"Expected StatusViewData.Concrete, got %s instead at position: %d of %d", "Expected StatusViewData.Concrete, got %s instead at position: %d of %d",
status == null ? "<null>" : status.getClass().getSimpleName(), status == null ? "<null>" : status.getClass().getSimpleName(),
position, position,
@ -960,13 +965,13 @@ public class TimelineFragment extends SFragment implements
updateAdapter(); updateAdapter();
} }
private void removeAllByConversationId(int conversationId) { private void removeAllByConversationId(String conversationId) {
// using iterator to safely remove items while iterating // using iterator to safely remove items while iterating
Iterator<Either<Placeholder, Status>> iterator = statuses.iterator(); Iterator<Either<Placeholder, Status>> iterator = statuses.iterator();
while(iterator.hasNext()) { while(iterator.hasNext()) {
Status status = iterator.next().asRightOrNull(); Status status = iterator.next().asRightOrNull();
if(status != null && if(status != null &&
(status.getConversationId() == conversationId) || status.getActionableStatus().getConversationId() == conversationId) { (status.getConversationId().equalsIgnoreCase(conversationId)) || status.getActionableStatus().getConversationId().equalsIgnoreCase(conversationId)) {
iterator.remove(); iterator.remove();
} }
} }
@ -1249,7 +1254,7 @@ public class TimelineFragment extends SFragment implements
} }
} }
Log.e(TAG, "Fetch Failure: " + exception.getMessage()); Timber.e("Fetch Failure: %s", exception.getMessage());
updateBottomLoadingState(fetchEnd); updateBottomLoadingState(fetchEnd);
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
} }
@ -1453,9 +1458,9 @@ public class TimelineFragment extends SFragment implements
return; return;
Status eventStatus = statuses.get(pos).asRight(); Status eventStatus = statuses.get(pos).asRight();
int conversationId = eventStatus.getConversationId(); String conversationId = eventStatus.getConversationId();
if(conversationId == -1) { // invalid conversation ID if(conversationId.isEmpty()) { // invalid conversation ID
if(isFilteringMuted()) { if(isFilteringMuted()) {
statuses.remove(pos); statuses.remove(pos);
} else { } else {
@ -1650,13 +1655,12 @@ public class TimelineFragment extends SFragment implements
.as(autoDisposable(from(this))) .as(autoDisposable(from(this)))
.subscribe( .subscribe(
(newStatus) -> setEmojiReactionForStatus(position, newStatus), (newStatus) -> setEmojiReactionForStatus(position, newStatus),
(t) -> Log.d(TAG, (t) -> Timber.e(t,
"Failed to react with " + emoji + " on status: " + statusId, t) "Failed to react with " + emoji + " on status: " + statusId)
); );
} }
@Override @Override
public void onEmojiReactMenu(@NonNull View view, final EmojiReaction emoji, final String statusId) { public void onEmojiReactMenu(@NonNull View view, final EmojiReaction emoji, final String statusId) {
super.emojiReactMenu(statusId, emoji, view, this); super.emojiReactMenu(statusId, emoji, view, this);

View file

@ -1,3 +1,23 @@
/*
* Husky -- A Pleroma client for Android
*
* Copyright (C) 2022 The Husky Developers
* Copyright (C) 2018 Tusky Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.keylesspalace.tusky.repository package com.keylesspalace.tusky.repository
import android.text.SpannedString import android.text.SpannedString

View file

@ -53,6 +53,7 @@ object OkHttpUtils {
val builder = OkHttpClient.Builder() val builder = OkHttpClient.Builder()
.addInterceptor(getUserAgentInterceptor()) .addInterceptor(getUserAgentInterceptor())
//.addInterceptor(getDebugInformation())
.addInterceptor(BrotliInterceptor) .addInterceptor(BrotliInterceptor)
.readTimeout(60, SECONDS) .readTimeout(60, SECONDS)
.writeTimeout(60, SECONDS) .writeTimeout(60, SECONDS)

View file

@ -1,33 +1,35 @@
/* Copyright 2017 Andrew Dawson /*
* Husky -- A Pleroma client for Android
* *
* This file is a part of Tusky. * Copyright (C) 2022 The Husky Developers
* Copyright (C) 2017 Andrew Dawson
* *
* This program is free software; you can redistribute it and/or modify it under the terms of the * This program is free software: you can redistribute it and/or modify
* GNU General Public License as published by the Free Software Foundation; either version 3 of the * it under the terms of the GNU General Public License as published by
* License, or (at your option) any later version. * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* *
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * This program is distributed in the hope that it will be useful,
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * but WITHOUT ANY WARRANTY; without even the implied warranty of
* Public License for more details. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along with Tusky; if not, * You should have received a copy of the GNU General Public License
* see <http://www.gnu.org/licenses>. */ * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.keylesspalace.tusky.viewdata; package com.keylesspalace.tusky.viewdata;
import android.os.Build; import android.os.Build;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import android.text.Spanned; import android.text.Spanned;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.keylesspalace.tusky.entity.Attachment; import com.keylesspalace.tusky.entity.Attachment;
import com.keylesspalace.tusky.entity.Card; import com.keylesspalace.tusky.entity.Card;
import com.keylesspalace.tusky.entity.Emoji; import com.keylesspalace.tusky.entity.Emoji;
import com.keylesspalace.tusky.entity.EmojiReaction; import com.keylesspalace.tusky.entity.EmojiReaction;
import com.keylesspalace.tusky.entity.Poll; import com.keylesspalace.tusky.entity.Poll;
import com.keylesspalace.tusky.entity.Status; import com.keylesspalace.tusky.entity.Status;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -36,15 +38,14 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
* Created by charlag on 11/07/2017.
* <p>
* Class to represent data required to display either a notification or a placeholder. * Class to represent data required to display either a notification or a placeholder.
* It is either a {@link StatusViewData.Concrete} or a {@link StatusViewData.Placeholder}. * It is either a {@link StatusViewData.Concrete} or a {@link StatusViewData.Placeholder}.
*/ */
public abstract class StatusViewData { public abstract class StatusViewData {
private StatusViewData() { } private StatusViewData() {
}
public abstract long getViewDataId(); public abstract long getViewDataId();
@ -91,15 +92,21 @@ public abstract class StatusViewData {
private final List<Emoji> rebloggedByAccountEmojis; private final List<Emoji> rebloggedByAccountEmojis;
@Nullable @Nullable
private final Card card; private final Card card;
private final boolean isCollapsible; /** Whether the status meets the requirement to be collapse */ private final boolean isCollapsible;
final boolean isCollapsed; /** Whether the status is shown partially or fully */ /**
* Whether the status meets the requirement to be collapse
*/
final boolean isCollapsed;
/**
* Whether the status is shown partially or fully
*/
@Nullable @Nullable
private final PollViewData poll; private final PollViewData poll;
private final boolean isBot; private final boolean isBot;
private final boolean isMuted; /* user toggle */ private final boolean isMuted; /* user toggle */
private final boolean isThreadMuted; /* thread_muted state got from backend */ private final boolean isThreadMuted; /* thread_muted state got from backend */
private final boolean isUserMuted; /* muted state got from backend */ private final boolean isUserMuted; /* muted state got from backend */
private final int conversationId; private final String conversationId;
@Nullable @Nullable
private final List<EmojiReaction> emojiReactions; private final List<EmojiReaction> emojiReactions;
private final boolean parentVisible; private final boolean parentVisible;
@ -112,7 +119,7 @@ public abstract class StatusViewData {
@Nullable String inReplyToAccountAcct, @Nullable Status.Mention[] mentions, String senderId, boolean rebloggingEnabled, @Nullable String inReplyToAccountAcct, @Nullable Status.Mention[] mentions, String senderId, boolean rebloggingEnabled,
Status.Application application, List<Emoji> statusEmojis, List<Emoji> accountEmojis, List<Emoji> rebloggedByAccountEmojis, @Nullable Card card, Status.Application application, List<Emoji> statusEmojis, List<Emoji> accountEmojis, List<Emoji> rebloggedByAccountEmojis, @Nullable Card card,
boolean isCollapsible, boolean isCollapsed, @Nullable PollViewData poll, boolean isBot, boolean isMuted, boolean isThreadMuted, boolean isCollapsible, boolean isCollapsed, @Nullable PollViewData poll, boolean isBot, boolean isMuted, boolean isThreadMuted,
boolean isUserMuted, int conversationId, @Nullable List<EmojiReaction> emojiReactions, boolean parentVisible) { boolean isUserMuted, String conversationId, @Nullable List<EmojiReaction> emojiReactions, boolean parentVisible) {
this.id = id; this.id = id;
if(Build.VERSION.SDK_INT == Build.VERSION_CODES.M) { if(Build.VERSION.SDK_INT == Build.VERSION_CODES.M) {
@ -212,7 +219,9 @@ public abstract class StatusViewData {
return isShowingContent; return isShowingContent;
} }
public boolean isBot(){ return isBot; } public boolean isBot() {
return isBot;
}
@Nullable @Nullable
public String getRebloggedAvatar() { public String getRebloggedAvatar() {
@ -318,7 +327,8 @@ public abstract class StatusViewData {
return poll; return poll;
} }
@Override public long getViewDataId() { @Override
public long getViewDataId() {
// Chance of collision is super low and impact of mistake is low as well // Chance of collision is super low and impact of mistake is low as well
return id.hashCode(); return id.hashCode();
} }
@ -429,17 +439,20 @@ public abstract class StatusViewData {
return id; return id;
} }
@Override public long getViewDataId() { @Override
public long getViewDataId() {
return id.hashCode(); return id.hashCode();
} }
@Override public boolean deepEquals(StatusViewData other) { @Override
public boolean deepEquals(StatusViewData other) {
if(!(other instanceof Placeholder)) return false; if(!(other instanceof Placeholder)) return false;
Placeholder that = (Placeholder) other; Placeholder that = (Placeholder) other;
return isLoading == that.isLoading && id.equals(that.id); return isLoading == that.isLoading && id.equals(that.id);
} }
@Override public boolean equals(Object o) { @Override
public boolean equals(Object o) {
if(this == o) return true; if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false; if(o == null || getClass() != o.getClass()) return false;
@ -486,14 +499,20 @@ public abstract class StatusViewData {
private List<Emoji> accountEmojis; private List<Emoji> accountEmojis;
private List<Emoji> rebloggedByAccountEmojis; private List<Emoji> rebloggedByAccountEmojis;
private Card card; private Card card;
private boolean isCollapsible; /** Whether the status meets the requirement to be collapsed */ private boolean isCollapsible;
private boolean isCollapsed; /** Whether the status is shown partially or fully */ /**
* Whether the status meets the requirement to be collapsed
*/
private boolean isCollapsed;
/**
* Whether the status is shown partially or fully
*/
private PollViewData poll; private PollViewData poll;
private boolean isBot; private boolean isBot;
private boolean isMuted; private boolean isMuted;
private boolean isThreadMuted; private boolean isThreadMuted;
private boolean isUserMuted; private boolean isUserMuted;
private int conversationId; private String conversationId;
private List<EmojiReaction> emojiReactions; private List<EmojiReaction> emojiReactions;
private boolean parentVisible; private boolean parentVisible;
@ -739,7 +758,7 @@ public abstract class StatusViewData {
return this; return this;
} }
public Builder setConversationId(int conversationId) { public Builder setConversationId(String conversationId) {
this.conversationId = conversationId; this.conversationId = conversationId;
return this; return this;
} }