reimplement thread ancestry

This commit is contained in:
sk
2023-06-02 19:05:18 +02:00
parent 02e3421f98
commit 56a93288c4
5 changed files with 182 additions and 72 deletions

View File

@@ -19,41 +19,40 @@ public class ThreadFragmentTest {
return status; return status;
} }
private ThreadFragment.NeighborAncestryInfo fakeInfo(Status s, Status d, Status a) {
ThreadFragment.NeighborAncestryInfo info = new ThreadFragment.NeighborAncestryInfo(s);
info.descendantNeighbor = d;
info.ancestoringNeighbor = a;
return info;
}
@Test @Test
public void countAncestryLevels() { public void mapNeighborhoodAncestry() {
StatusContext context = new StatusContext(); StatusContext context = new StatusContext();
context.ancestors = List.of( context.ancestors = List.of(
fakeStatus("oldest ancestor", null), fakeStatus("oldest ancestor", null),
fakeStatus("younger ancestor", "oldest ancestor") fakeStatus("younger ancestor", "oldest ancestor")
); );
Status mainStatus = fakeStatus("main status", "younger ancestor");
context.descendants = List.of( context.descendants = List.of(
fakeStatus("first reply", "main status"), fakeStatus("first reply", "main status"),
fakeStatus("reply to first reply", "first reply"), fakeStatus("reply to first reply", "first reply"),
fakeStatus("third level reply", "reply to first reply"), fakeStatus("third level reply", "reply to first reply"),
fakeStatus("another reply", "main status") fakeStatus("another reply", "main status")
); );
List<Pair<String, Integer>> actual =
ThreadFragment.countAncestryLevels("main status", context);
List<Pair<String, Integer>> expected = List.of( List<ThreadFragment.NeighborAncestryInfo> neighbors =
Pair.create("oldest ancestor", -2), ThreadFragment.mapNeighborhoodAncestry(mainStatus, context);
Pair.create("younger ancestor", -1),
Pair.create("main status", 0), assertEquals(List.of(
Pair.create("first reply", 1), fakeInfo(context.ancestors.get(0), context.ancestors.get(1), null),
Pair.create("reply to first reply", 2), fakeInfo(context.ancestors.get(1), mainStatus, context.ancestors.get(0)),
Pair.create("third level reply", 3), fakeInfo(mainStatus, context.descendants.get(0), context.ancestors.get(1)),
Pair.create("another reply", 1) fakeInfo(context.descendants.get(0), context.descendants.get(1), mainStatus),
); fakeInfo(context.descendants.get(1), context.descendants.get(2), context.descendants.get(0)),
assertEquals( fakeInfo(context.descendants.get(2), null, context.descendants.get(1)),
"status ids are in the right order", fakeInfo(context.descendants.get(3), null, null)
expected.stream().map(p -> p.first).collect(Collectors.toList()), ), neighbors);
actual.stream().map(p -> p.first).collect(Collectors.toList())
);
assertEquals(
"counted levels match",
expected.stream().map(p -> p.second).collect(Collectors.toList()),
actual.stream().map(p -> p.second).collect(Collectors.toList())
);
} }
@Test @Test

View File

@@ -16,6 +16,7 @@ import android.text.TextPaint;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowInsets; import android.view.WindowInsets;
import android.view.animation.TranslateAnimation; import android.view.animation.TranslateAnimation;
import android.widget.ImageButton; import android.widget.ImageButton;
@@ -26,7 +27,6 @@ import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R; import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships; import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
import org.joinmastodon.android.api.requests.polls.SubmitPollVote; import org.joinmastodon.android.api.requests.polls.SubmitPollVote;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.PollUpdatedEvent; import org.joinmastodon.android.events.PollUpdatedEvent;
import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.DisplayItemsParent; import org.joinmastodon.android.model.DisplayItemsParent;
@@ -35,7 +35,6 @@ import org.joinmastodon.android.model.Relationship;
import org.joinmastodon.android.model.Status; import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.BetterItemAnimator; import org.joinmastodon.android.ui.BetterItemAnimator;
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem;
@@ -207,7 +206,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
@Override @Override
public boolean startPhotoViewTransition(int index, @NonNull Rect outRect, @NonNull int[] outCornerRadius){ public boolean startPhotoViewTransition(int index, @NonNull Rect outRect, @NonNull int[] outCornerRadius){
MediaAttachmentViewController holder=findPhotoViewHolder(index); MediaAttachmentViewController holder=findPhotoViewHolder(index);
if(holder!=null){ if(holder!=null && list!=null){
transitioningHolder=holder; transitioningHolder=holder;
View view=transitioningHolder.photo; View view=transitioningHolder.photo;
int[] pos={0, 0}; int[] pos={0, 0};
@@ -339,6 +338,8 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
private Rect tmpRect=new Rect(); private Rect tmpRect=new Rect();
@Override @Override
public void getSelectorBounds(View view, Rect outRect){ public void getSelectorBounds(View view, Rect outRect){
boolean hasDescendant = false, hasAncestor = false, isWarning = false;
int lastIndex = -1, firstIndex = -1;
list.getDecoratedBoundsWithMargins(view, outRect); list.getDecoratedBoundsWithMargins(view, outRect);
RecyclerView.ViewHolder holder=list.getChildViewHolder(view); RecyclerView.ViewHolder holder=list.getChildViewHolder(view);
if(holder instanceof StatusDisplayItem.Holder){ if(holder instanceof StatusDisplayItem.Holder){
@@ -350,23 +351,40 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
for(int i=0;i<list.getChildCount();i++){ for(int i=0;i<list.getChildCount();i++){
View child=list.getChildAt(i); View child=list.getChildAt(i);
holder=list.getChildViewHolder(child); holder=list.getChildViewHolder(child);
if(holder instanceof StatusDisplayItem.Holder){ if(holder instanceof StatusDisplayItem.Holder<?> h){
String otherID=((StatusDisplayItem.Holder<?>) holder).getItemID(); String otherID=((StatusDisplayItem.Holder<?>) holder).getItemID();
if(otherID.equals(id)){ if(otherID.equals(id)){
if (firstIndex < 0) firstIndex = i;
lastIndex = i;
StatusDisplayItem item = h.getItem();
hasDescendant = item.hasDescendantNeighbor();
// no for direct descendants because main status (right above) is
// being displayed with an extended footer - no connected layout
hasAncestor = item.hasAncestoringNeighbor() && !item.isDirectDescendant;
list.getDecoratedBoundsWithMargins(child, tmpRect); list.getDecoratedBoundsWithMargins(child, tmpRect);
outRect.left=Math.min(outRect.left, tmpRect.left); outRect.left=Math.min(outRect.left, tmpRect.left);
outRect.top=Math.min(outRect.top, tmpRect.top); outRect.top=Math.min(outRect.top, tmpRect.top);
outRect.right=Math.max(outRect.right, tmpRect.right); outRect.right=Math.max(outRect.right, tmpRect.right);
int bottom = tmpRect.bottom; outRect.bottom=Math.max(outRect.bottom, tmpRect.bottom);
if (holder instanceof FooterStatusDisplayItem.Holder fh if (holder instanceof WarningFilteredStatusDisplayItem.Holder) {
&& fh.getItem().hasDescendantSibling) { isWarning = true;
bottom += V.dp(8);
} }
outRect.bottom=Math.max(outRect.bottom, bottom);
} }
} }
} }
} }
// shifting the selection box down
// see also: FooterStatusDisplayItem#onBind (setMargins)
if (isWarning || firstIndex < 0 || lastIndex < 0) return;
int prevIndex = firstIndex - 1, nextIndex = lastIndex + 1;
boolean prevIsWarning = prevIndex > 0 && prevIndex < list.getChildCount() &&
list.getChildViewHolder(list.getChildAt(prevIndex))
instanceof WarningFilteredStatusDisplayItem.Holder;
boolean nextIsWarning = nextIndex > 0 && nextIndex < list.getChildCount() &&
list.getChildViewHolder(list.getChildAt(nextIndex))
instanceof WarningFilteredStatusDisplayItem.Holder;
if (!prevIsWarning && hasAncestor) outRect.top += V.dp(4);
if (!nextIsWarning && hasDescendant) outRect.bottom += V.dp(4);
} }
}); });
list.setItemAnimator(new BetterItemAnimator()); list.setItemAnimator(new BetterItemAnimator());
@@ -779,7 +797,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
RecyclerView.ViewHolder siblingHolder=parent.getChildViewHolder(bottomSibling); RecyclerView.ViewHolder siblingHolder=parent.getChildViewHolder(bottomSibling);
if(holder instanceof StatusDisplayItem.Holder<?> ih && siblingHolder instanceof StatusDisplayItem.Holder<?> sh if(holder instanceof StatusDisplayItem.Holder<?> ih && siblingHolder instanceof StatusDisplayItem.Holder<?> sh
&& (!ih.getItemID().equals(sh.getItemID()) || sh instanceof ExtendedFooterStatusDisplayItem.Holder) && ih.getItem().getType()!=StatusDisplayItem.Type.GAP){ && (!ih.getItemID().equals(sh.getItemID()) || sh instanceof ExtendedFooterStatusDisplayItem.Holder) && ih.getItem().getType()!=StatusDisplayItem.Type.GAP){
if (ih.getItem().descendantLevel != 0 && ih.getItem().hasDescendantSibling) continue; if (!ih.getItem().isMainStatus && ih.getItem().hasDescendantNeighbor()) continue;
drawDivider(child, bottomSibling, holder, siblingHolder, parent, c, dividerPaint); drawDivider(child, bottomSibling, holder, siblingHolder, parent, c, dividerPaint);
} }
} }

View File

@@ -5,6 +5,8 @@ import android.os.Bundle;
import android.util.Pair; import android.util.Pair;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull;
import org.joinmastodon.android.R; import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.statuses.GetStatusContext; import org.joinmastodon.android.api.requests.statuses.GetStatusContext;
import org.joinmastodon.android.events.StatusCreatedEvent; import org.joinmastodon.android.events.StatusCreatedEvent;
@@ -17,6 +19,7 @@ import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem; import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.WarningFilteredStatusDisplayItem;
import org.joinmastodon.android.ui.text.HtmlParser; import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.UiUtils; import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.utils.ProvidesAssistContent; import org.joinmastodon.android.utils.ProvidesAssistContent;
@@ -30,8 +33,10 @@ import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import me.grishka.appkit.api.SimpleCallback; import me.grishka.appkit.api.SimpleCallback;
@@ -55,6 +60,7 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
* confused? good. /j * confused? good. /j
*/ */
private final List<Pair<String, Integer>> levels = new ArrayList<>(); private final List<Pair<String, Integer>> levels = new ArrayList<>();
private final HashMap<String, NeighborAncestryInfo> ancestryMap = new HashMap<>();
@Override @Override
public void onCreate(Bundle savedInstanceState){ public void onCreate(Bundle savedInstanceState){
@@ -74,23 +80,27 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
// "what the fuck is a deque"? yes // "what the fuck is a deque"? yes
// (it's just so the last-added item automatically comes first when looping over it) // (it's just so the last-added item automatically comes first when looping over it)
Deque<Integer> deleteTheseItems = new ArrayDeque<>(); Deque<Integer> deleteTheseItems = new ArrayDeque<>();
for(int i = 0; i < items.size(); i++){
StatusDisplayItem item = items.get(i);
Optional<Pair<String, Integer>> levelForStatus = // modifying hidden filtered items if status is displayed as a warning
levels.stream().filter(p -> p.first.equals(s.id)).findAny(); List<StatusDisplayItem> itemsToModify =
item.descendantLevel = levelForStatus.map(p -> p.second).orElse(0); (items.get(0) instanceof WarningFilteredStatusDisplayItem warning)
if (levelForStatus.isPresent()) { ? warning.filteredItems
int idx = levels.indexOf(levelForStatus.get()); : items;
item.hasDescendantSibling = (levels.size() > idx + 1)
&& levels.get(idx + 1).second > levelForStatus.get().second; for(int i = 0; i < itemsToModify.size(); i++){
item.isDescendantSibling = (idx - 1 >= 0) StatusDisplayItem item = itemsToModify.get(i);
&& levels.get(idx - 1).second < levelForStatus.get().second; NeighborAncestryInfo ancestryInfo = ancestryMap.get(s.id);
if (ancestryInfo != null) {
item.setAncestryInfo(
ancestryInfo,
s.id.equals(mainStatus.id),
ancestryInfo.getAncestoringNeighbor()
.map(ancestor -> ancestor.id.equals(mainStatus.id))
.orElse(false)
);
} }
if (item instanceof ReblogOrReplyLineStatusDisplayItem if (item instanceof ReblogOrReplyLineStatusDisplayItem && !item.isDirectDescendant) {
&& item.isDescendantSibling
&& item.descendantLevel != 1) {
deleteTheseItems.add(i); deleteTheseItems.add(i);
} }
@@ -101,7 +111,7 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
footer.hideCounts=true; footer.hideCounts=true;
} }
} }
for (int deleteThisItem : deleteTheseItems) items.remove(deleteThisItem); for (int deleteThisItem : deleteTheseItems) itemsToModify.remove(deleteThisItem);
if(s.id.equals(mainStatus.id)) { if(s.id.equals(mainStatus.id)) {
items.add(new ExtendedFooterStatusDisplayItem(s.id, this, s.getContentStatus())); items.add(new ExtendedFooterStatusDisplayItem(s.id, this, s.getContentStatus()));
} }
@@ -117,7 +127,7 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
if (getActivity() == null) return; if (getActivity() == null) return;
if(refreshing){ if(refreshing){
data.clear(); data.clear();
levels.clear(); ancestryMap.clear();
displayItems.clear(); displayItems.clear();
data.add(mainStatus); data.add(mainStatus);
onAppendItems(Collections.singletonList(mainStatus)); onAppendItems(Collections.singletonList(mainStatus));
@@ -129,7 +139,9 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
result.descendants=filterStatuses(result.descendants); result.descendants=filterStatuses(result.descendants);
result.ancestors=filterStatuses(result.ancestors); result.ancestors=filterStatuses(result.ancestors);
levels.addAll(countAncestryLevels(mainStatus.id, result)); for (NeighborAncestryInfo i : mapNeighborhoodAncestry(mainStatus, result)) {
ancestryMap.put(i.status.id, i);
}
if(footerProgress!=null) if(footerProgress!=null)
footerProgress.setVisibility(View.GONE); footerProgress.setVisibility(View.GONE);
@@ -156,26 +168,35 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
.exec(accountID); .exec(accountID);
} }
public static List<Pair<String, Integer>> countAncestryLevels(String mainStatusID, StatusContext context) { public static List<NeighborAncestryInfo> mapNeighborhoodAncestry(Status mainStatus, StatusContext context) {
List<Pair<String, Integer>> levels = new ArrayList<>(); List<NeighborAncestryInfo> ancestry = new ArrayList<>();
for (int i = 0; i < context.ancestors.size(); i++) { List<Status> statuses = new ArrayList<>(context.ancestors);
levels.add(Pair.create( statuses.add(mainStatus);
context.ancestors.get(i).id, statuses.addAll(context.descendants);
-context.ancestors.size() + i // -3, -2, -1
)); int count = statuses.size();
for (int index = 0; index < count; index++) {
Status current = statuses.get(index);
NeighborAncestryInfo item = new NeighborAncestryInfo(current);
item.descendantNeighbor = Optional
.ofNullable(count > index + 1 ? statuses.get(index + 1) : null)
.filter(s -> s.inReplyToId.equals(current.id))
.orElse(null);
item.ancestoringNeighbor = Optional.ofNullable(index > 0 ? ancestry.get(index - 1) : null)
.filter(ancestor -> ancestor
.getDescendantNeighbor()
.map(ancestorsDescendant -> ancestorsDescendant.id.equals(current.id))
.orElse(false))
.flatMap(NeighborAncestryInfo::getStatus)
.orElse(null);
ancestry.add(item);
} }
levels.add(Pair.create(mainStatusID, 0)); return ancestry;
Map<String, Integer> levelPerStatus = new HashMap<>();
// sum up the amounts of descendants per status
context.descendants.forEach(s -> levelPerStatus.put(s.id,
levelPerStatus.getOrDefault(s.inReplyToId, 0) + 1));
context.descendants.forEach(s ->
levels.add(Pair.create(s.id, levelPerStatus.get(s.id))));
return levels;
} }
public static void sortStatusContext(Status mainStatus, StatusContext context) { public static void sortStatusContext(Status mainStatus, StatusContext context) {
@@ -275,4 +296,47 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
public Uri getWebUri(Uri.Builder base) { public Uri getWebUri(Uri.Builder base) {
return Uri.parse(mainStatus.url); return Uri.parse(mainStatus.url);
} }
public static class NeighborAncestryInfo {
protected Status status, descendantNeighbor, ancestoringNeighbor;
public NeighborAncestryInfo(@NonNull Status status) {
this.status = status;
}
public Optional<Status> getStatus() {
return Optional.ofNullable(status);
}
public Optional<Status> getDescendantNeighbor() {
return Optional.ofNullable(descendantNeighbor);
}
public Optional<Status> getAncestoringNeighbor() {
return Optional.ofNullable(ancestoringNeighbor);
}
public boolean hasDescendantNeighbor() {
return getDescendantNeighbor().isPresent();
}
public boolean hasAncestoringNeighbor() {
return getAncestoringNeighbor().isPresent();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NeighborAncestryInfo that = (NeighborAncestryInfo) o;
return status.equals(that.status)
&& Objects.equals(descendantNeighbor, that.descendantNeighbor)
&& Objects.equals(ancestoringNeighbor, that.ancestoringNeighbor);
}
@Override
public int hashCode() {
return Objects.hash(status, descendantNeighbor, ancestoringNeighbor);
}
}
} }

View File

@@ -136,16 +136,23 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
bindButton(favorite, item.status.favouritesCount); bindButton(favorite, item.status.favouritesCount);
// in thread view, direct descendant posts display one direct reply to themselves, // in thread view, direct descendant posts display one direct reply to themselves,
// hence in that case displaying whether there is another reply // hence in that case displaying whether there is another reply
reply.setSelected(item.status.repliesCount > (item.descendantLevel > 0 ? 1 : 0)); int compareTo = item.isMainStatus || !item.hasDescendantNeighbor() ? 0 : 1;
reply.setSelected(item.status.repliesCount > compareTo);
boost.setSelected(item.status.reblogged); boost.setSelected(item.status.reblogged);
favorite.setSelected(item.status.favourited); favorite.setSelected(item.status.favourited);
bookmark.setSelected(item.status.bookmarked); bookmark.setSelected(item.status.bookmarked);
boost.setEnabled(item.status.visibility==StatusPrivacy.PUBLIC || item.status.visibility==StatusPrivacy.UNLISTED || item.status.visibility==StatusPrivacy.LOCAL boost.setEnabled(item.status.visibility==StatusPrivacy.PUBLIC || item.status.visibility==StatusPrivacy.UNLISTED || item.status.visibility==StatusPrivacy.LOCAL
|| (item.status.visibility==StatusPrivacy.PRIVATE && item.status.account.id.equals(AccountSessionManager.getInstance().getAccount(item.accountID).self.id))); || (item.status.visibility==StatusPrivacy.PRIVATE && item.status.account.id.equals(AccountSessionManager.getInstance().getAccount(item.accountID).self.id)));
int nextPos = getAbsoluteAdapterPosition() + 1;
boolean nextIsWarning = item.parentFragment.getDisplayItems().size() > nextPos &&
item.parentFragment.getDisplayItems().get(nextPos) instanceof WarningFilteredStatusDisplayItem;
boolean condenseBottom = !item.isMainStatus && item.hasDescendantNeighbor() &&
!nextIsWarning;
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) itemView.getLayoutParams(); ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) itemView.getLayoutParams();
params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, params.setMargins(params.leftMargin, params.topMargin, params.rightMargin,
item.descendantLevel != 0 && item.hasDescendantSibling ? V.dp(-6) : 0); condenseBottom ? V.dp(-8) : 0);
itemView.requestLayout(); itemView.requestLayout();
} }

View File

@@ -10,7 +10,6 @@ import android.view.ViewGroup;
import org.joinmastodon.android.GlobalUserPreferences; import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R; import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.BaseStatusListFragment; import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.HashtagTimelineFragment; import org.joinmastodon.android.fragments.HashtagTimelineFragment;
@@ -22,7 +21,6 @@ import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Attachment; import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.DisplayItemsParent; import org.joinmastodon.android.model.DisplayItemsParent;
import org.joinmastodon.android.model.Filter; import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.Notification; import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.Poll; import org.joinmastodon.android.model.Poll;
import org.joinmastodon.android.model.ScheduledStatus; import org.joinmastodon.android.model.ScheduledStatus;
@@ -49,8 +47,32 @@ public abstract class StatusDisplayItem{
public final BaseStatusListFragment parentFragment; public final BaseStatusListFragment parentFragment;
public boolean inset; public boolean inset;
public int index; public int index;
public int descendantLevel; private ThreadFragment.NeighborAncestryInfo ancestryInfo;
public boolean hasDescendantSibling, isDescendantSibling; public boolean
isMainStatus = true,
isDirectDescendant = false;
public boolean hasDescendantNeighbor() {
return Optional.ofNullable(ancestryInfo)
.map(ThreadFragment.NeighborAncestryInfo::hasDescendantNeighbor)
.orElse(false);
}
public boolean hasAncestoringNeighbor() {
return Optional.ofNullable(ancestryInfo)
.map(ThreadFragment.NeighborAncestryInfo::hasAncestoringNeighbor)
.orElse(false);
}
public void setAncestryInfo(
ThreadFragment.NeighborAncestryInfo ancestryInfo,
boolean isMainStatus,
boolean isDirectDescendant
) {
this.ancestryInfo = ancestryInfo;
this.isMainStatus = isMainStatus;
this.isDirectDescendant = isDirectDescendant;
}
public StatusDisplayItem(String parentID, BaseStatusListFragment parentFragment){ public StatusDisplayItem(String parentID, BaseStatusListFragment parentFragment){
this.parentID=parentID; this.parentID=parentID;