Make more clear representation of placeholder in notifications
This commit is contained in:
parent
80a10c1ac1
commit
0dede1ba7d
7 changed files with 348 additions and 127 deletions
|
@ -101,11 +101,19 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
|
||||
if (position < notifications.size()) {
|
||||
NotificationViewData notification = notifications.get(position);
|
||||
Notification.Type type = notification.getType();
|
||||
if (notification instanceof NotificationViewData.Placeholder) {
|
||||
NotificationViewData.Placeholder placeholder = ((NotificationViewData.Placeholder) notification);
|
||||
PlaceholderViewHolder holder = (PlaceholderViewHolder) viewHolder;
|
||||
holder.setup(!placeholder.isLoading(), statusListener);
|
||||
return;
|
||||
}
|
||||
NotificationViewData.Concrete concreteNotificaton =
|
||||
(NotificationViewData.Concrete) notification;
|
||||
Notification.Type type = concreteNotificaton.getType();
|
||||
switch (type) {
|
||||
case MENTION: {
|
||||
StatusViewHolder holder = (StatusViewHolder) viewHolder;
|
||||
StatusViewData status = notification.getStatusViewData();
|
||||
StatusViewData status = concreteNotificaton.getStatusViewData();
|
||||
holder.setupWithStatus(status,
|
||||
statusListener, mediaPreviewEnabled);
|
||||
break;
|
||||
|
@ -113,23 +121,18 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
case FAVOURITE:
|
||||
case REBLOG: {
|
||||
StatusNotificationViewHolder holder = (StatusNotificationViewHolder) viewHolder;
|
||||
holder.setMessage(type, notification.getAccount().getDisplayName(),
|
||||
notification.getStatusViewData());
|
||||
holder.setupButtons(notificationActionListener, notification.getAccount().id);
|
||||
holder.setAvatars(notification.getStatusViewData().getAvatar(),
|
||||
notification.getAccount().avatar);
|
||||
holder.setMessage(type, concreteNotificaton.getAccount().getDisplayName(),
|
||||
concreteNotificaton.getStatusViewData());
|
||||
holder.setupButtons(notificationActionListener, concreteNotificaton.getAccount().id);
|
||||
holder.setAvatars(concreteNotificaton.getStatusViewData().getAvatar(),
|
||||
concreteNotificaton.getAccount().avatar);
|
||||
break;
|
||||
}
|
||||
case FOLLOW: {
|
||||
FollowViewHolder holder = (FollowViewHolder) viewHolder;
|
||||
holder.setMessage(notification.getAccount().getDisplayName(),
|
||||
notification.getAccount().username, notification.getAccount().avatar);
|
||||
holder.setupButtons(notificationActionListener, notification.getAccount().id);
|
||||
break;
|
||||
}
|
||||
case PLACEHOLDER: {
|
||||
PlaceholderViewHolder holder = (PlaceholderViewHolder) viewHolder;
|
||||
holder.setup(!notification.isPlaceholderLoading(), statusListener);
|
||||
holder.setMessage(concreteNotificaton.getAccount().getDisplayName(),
|
||||
concreteNotificaton.getAccount().username, concreteNotificaton.getAccount().avatar);
|
||||
holder.setupButtons(notificationActionListener, concreteNotificaton.getAccount().id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +153,9 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
return VIEW_TYPE_FOOTER;
|
||||
} else {
|
||||
NotificationViewData notification = notifications.get(position);
|
||||
switch (notification.getType()) {
|
||||
if (notification instanceof NotificationViewData.Concrete) {
|
||||
NotificationViewData.Concrete concrete = ((NotificationViewData.Concrete) notification);
|
||||
switch (concrete.getType()) {
|
||||
default:
|
||||
case MENTION: {
|
||||
return VIEW_TYPE_MENTION;
|
||||
|
@ -162,9 +167,11 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
case FOLLOW: {
|
||||
return VIEW_TYPE_FOLLOW;
|
||||
}
|
||||
case PLACEHOLDER: {
|
||||
return VIEW_TYPE_PLACEHOLDER;
|
||||
}
|
||||
} else if (notification instanceof NotificationViewData.Placeholder) {
|
||||
return VIEW_TYPE_PLACEHOLDER;
|
||||
} else {
|
||||
throw new AssertionError("Unknown notification type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ public class Notification {
|
|||
FAVOURITE,
|
||||
@SerializedName("follow")
|
||||
FOLLOW,
|
||||
PLACEHOLDER
|
||||
}
|
||||
|
||||
public Type type;
|
||||
|
|
|
@ -44,6 +44,8 @@ import com.keylesspalace.tusky.entity.Status;
|
|||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
||||
import com.keylesspalace.tusky.util.CollectionUtil;
|
||||
import com.keylesspalace.tusky.util.Either;
|
||||
import com.keylesspalace.tusky.util.HttpHeaderLink;
|
||||
import com.keylesspalace.tusky.util.ListUtils;
|
||||
import com.keylesspalace.tusky.util.PairedList;
|
||||
|
@ -74,6 +76,21 @@ public class NotificationsFragment extends SFragment implements
|
|||
MIDDLE
|
||||
}
|
||||
|
||||
/**
|
||||
* Placeholder for the notifications. Consider moving to the separate class to hide constructor
|
||||
* and reuse in different places as needed.
|
||||
*/
|
||||
private static final class Placeholder {
|
||||
private static final Placeholder INSTANCE = new Placeholder();
|
||||
|
||||
public static Placeholder getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private Placeholder() {
|
||||
}
|
||||
}
|
||||
|
||||
private SwipeRefreshLayout swipeRefreshLayout;
|
||||
private LinearLayoutManager layoutManager;
|
||||
private RecyclerView recyclerView;
|
||||
|
@ -89,11 +106,17 @@ public class NotificationsFragment extends SFragment implements
|
|||
private String bottomId;
|
||||
private String topId;
|
||||
|
||||
private final PairedList<Notification, NotificationViewData> notifications
|
||||
= new PairedList<>(new Function<Notification, NotificationViewData>() {
|
||||
// Each element is either a Notification for loading data or a Placeholder
|
||||
private final PairedList<Either<Placeholder, Notification>, NotificationViewData> notifications
|
||||
= new PairedList<>(new Function<Either<Placeholder, Notification>, NotificationViewData>() {
|
||||
@Override
|
||||
public NotificationViewData apply(Notification input) {
|
||||
return ViewDataUtils.notificationToViewData(input);
|
||||
public NotificationViewData apply(Either<Placeholder, Notification> input) {
|
||||
if (input.isRight()) {
|
||||
Notification notification = input.getAsRight();
|
||||
return ViewDataUtils.notificationToViewData(notification);
|
||||
} else {
|
||||
return new NotificationViewData.Placeholder(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -225,13 +248,12 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
@Override
|
||||
public void onReply(int position) {
|
||||
Notification notification = notifications.get(position);
|
||||
super.reply(notification.status);
|
||||
super.reply(notifications.get(position).getAsRight().status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReblog(final boolean reblog, final int position) {
|
||||
final Notification notification = notifications.get(position);
|
||||
final Notification notification = notifications.get(position).getAsRight();
|
||||
final Status status = notification.status;
|
||||
reblogWithCallback(status, reblog, new Callback<Status>() {
|
||||
@Override
|
||||
|
@ -242,7 +264,9 @@ public class NotificationsFragment extends SFragment implements
|
|||
if (status.reblog != null) {
|
||||
status.reblog.reblogged = reblog;
|
||||
}
|
||||
notifications.set(position, notification);
|
||||
// Java's type inference *eyeroll*
|
||||
notifications.set(position,
|
||||
Either.<Placeholder, Notification>right(notification));
|
||||
|
||||
adapter.updateItemWithNotify(position, notifications.getPairedItem(position), true);
|
||||
|
||||
|
@ -260,7 +284,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
@Override
|
||||
public void onFavourite(final boolean favourite, final int position) {
|
||||
final Notification notification = notifications.get(position);
|
||||
final Notification notification = notifications.get(position).getAsRight();
|
||||
final Status status = notification.status;
|
||||
favouriteWithCallback(status, favourite, new Callback<Status>() {
|
||||
@Override
|
||||
|
@ -272,7 +296,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
status.reblog.favourited = favourite;
|
||||
}
|
||||
|
||||
notifications.set(position, notification);
|
||||
notifications.set(position,
|
||||
Either.<Placeholder, Notification>right(notification));
|
||||
|
||||
adapter.updateItemWithNotify(position, notifications.getPairedItem(position), true);
|
||||
|
||||
|
@ -289,7 +314,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
@Override
|
||||
public void onMore(View view, int position) {
|
||||
Notification notification = notifications.get(position);
|
||||
Notification notification = notifications.get(position).getAsRight();
|
||||
super.more(notification.status, view, position);
|
||||
}
|
||||
|
||||
|
@ -301,38 +326,40 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
@Override
|
||||
public void onViewThread(int position) {
|
||||
Notification notification = notifications.get(position);
|
||||
Notification notification = notifications.get(position).getAsRight();
|
||||
super.viewThread(notification.status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpenReblog(int position) {
|
||||
Notification notification = notifications.get(position);
|
||||
if (notification != null) onViewAccount(notification.account.id);
|
||||
Notification notification = notifications.get(position).getAsRight();
|
||||
onViewAccount(notification.account.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExpandedChange(boolean expanded, int position) {
|
||||
NotificationViewData old = notifications.getPairedItem(position);
|
||||
NotificationViewData.Concrete old =
|
||||
(NotificationViewData.Concrete) notifications.getPairedItem(position);
|
||||
StatusViewData statusViewData =
|
||||
new StatusViewData.Builder(old.getStatusViewData())
|
||||
.setIsExpanded(expanded)
|
||||
.createStatusViewData();
|
||||
NotificationViewData notificationViewData = new NotificationViewData(old.getType(),
|
||||
old.getId(), old.getAccount(), statusViewData, false);
|
||||
NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(),
|
||||
old.getId(), old.getAccount(), statusViewData);
|
||||
notifications.setPairedItem(position, notificationViewData);
|
||||
adapter.updateItemWithNotify(position, notificationViewData, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContentHiddenChange(boolean isShowing, int position) {
|
||||
NotificationViewData old = notifications.getPairedItem(position);
|
||||
NotificationViewData.Concrete old =
|
||||
(NotificationViewData.Concrete) notifications.getPairedItem(position);
|
||||
StatusViewData statusViewData =
|
||||
new StatusViewData.Builder(old.getStatusViewData())
|
||||
.setIsShowingSensitiveContent(isShowing)
|
||||
.createStatusViewData();
|
||||
NotificationViewData notificationViewData = new NotificationViewData(old.getType(),
|
||||
old.getId(), old.getAccount(), statusViewData, false);
|
||||
NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(),
|
||||
old.getId(), old.getAccount(), statusViewData);
|
||||
notifications.setPairedItem(position, notificationViewData);
|
||||
adapter.updateItemWithNotify(position, notificationViewData, false);
|
||||
}
|
||||
|
@ -341,13 +368,12 @@ public class NotificationsFragment extends SFragment implements
|
|||
public void onLoadMore(int position) {
|
||||
//check bounds before accessing list,
|
||||
if (notifications.size() >= position && position > 0) {
|
||||
String fromId = notifications.get(position - 1).id;
|
||||
String toId = notifications.get(position + 1).id;
|
||||
// is it safe?
|
||||
String fromId = notifications.get(position - 1).getAsRight().id;
|
||||
String toId = notifications.get(position + 1).getAsRight().id;
|
||||
sendFetchNotificationsRequest(fromId, toId, FetchEnd.MIDDLE, position);
|
||||
|
||||
NotificationViewData old = notifications.getPairedItem(position);
|
||||
NotificationViewData notificationViewData = new NotificationViewData(old.getType(),
|
||||
old.getId(), old.getAccount(), old.getStatusViewData(), true);
|
||||
NotificationViewData notificationViewData =
|
||||
new NotificationViewData.Placeholder(true);
|
||||
notifications.setPairedItem(position, notificationViewData);
|
||||
adapter.updateItemWithNotify(position, notificationViewData, false);
|
||||
} else {
|
||||
|
@ -390,10 +416,11 @@ public class NotificationsFragment extends SFragment implements
|
|||
@Override
|
||||
public void removeAllByAccountId(String accountId) {
|
||||
// using iterator to safely remove items while iterating
|
||||
Iterator<Notification> iterator = notifications.iterator();
|
||||
Iterator<Either<Placeholder, Notification>> iterator = notifications.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Notification notification = iterator.next();
|
||||
if (notification.account.id.equals(accountId)) {
|
||||
Either<Placeholder, Notification> notification = iterator.next();
|
||||
Notification maybeNotification = notification.getAsRightOrNull();
|
||||
if (maybeNotification != null && maybeNotification.account.id.equals(accountId)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
@ -470,7 +497,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
break;
|
||||
}
|
||||
case MIDDLE: {
|
||||
insert(notifications, pos);
|
||||
replacePlaceholderWithNotifications(notifications, pos);
|
||||
break;
|
||||
}
|
||||
case BOTTOM: {
|
||||
|
@ -520,24 +547,25 @@ public class NotificationsFragment extends SFragment implements
|
|||
if (uptoId != null) {
|
||||
topId = uptoId;
|
||||
}
|
||||
List<Either<Placeholder, Notification>> liftedNew =
|
||||
liftNotificationList(newNotifications);
|
||||
if (notifications.isEmpty()) {
|
||||
notifications.addAll(newNotifications);
|
||||
notifications.addAll(liftedNew);
|
||||
} else {
|
||||
int index = notifications.indexOf(newNotifications.get(newNotifications.size() - 1));
|
||||
int index = notifications.indexOf(liftedNew.get(newNotifications.size() - 1));
|
||||
for (int i = 0; i < index; i++) {
|
||||
notifications.remove(0);
|
||||
}
|
||||
int newIndex = newNotifications.indexOf(notifications.get(0));
|
||||
|
||||
|
||||
int newIndex = liftedNew.indexOf(notifications.get(0));
|
||||
if (newIndex == -1) {
|
||||
if(index == -1 && newNotifications.size() >= LOAD_AT_ONCE) {
|
||||
Notification placeholder = new Notification();
|
||||
placeholder.type = Notification.Type.PLACEHOLDER;
|
||||
newNotifications.add(placeholder);
|
||||
if (index == -1 && liftedNew.size() >= LOAD_AT_ONCE) {
|
||||
liftedNew.add(Either.<Placeholder, Notification>left(Placeholder.getInstance()));
|
||||
}
|
||||
notifications.addAll(0, newNotifications);
|
||||
notifications.addAll(0, liftedNew);
|
||||
} else {
|
||||
List<Notification> sublist = newNotifications.subList(0, newIndex);
|
||||
notifications.addAll(0, sublist);
|
||||
notifications.addAll(0, liftedNew.subList(0, newIndex));
|
||||
}
|
||||
}
|
||||
adapter.update(notifications.getPairedCopy());
|
||||
|
@ -551,9 +579,10 @@ public class NotificationsFragment extends SFragment implements
|
|||
bottomId = fromId;
|
||||
}
|
||||
int end = notifications.size();
|
||||
Notification last = notifications.get(end - 1);
|
||||
if (last != null && !findNotification(newNotifications, last.id)) {
|
||||
notifications.addAll(newNotifications);
|
||||
List<Either<Placeholder, Notification>> liftedNew = liftNotificationList(newNotifications);
|
||||
Either<Placeholder, Notification> last = notifications.get(end - 1);
|
||||
if (last != null && liftedNew.indexOf(last) == -1) {
|
||||
notifications.addAll(liftedNew);
|
||||
List<NotificationViewData> newViewDatas = notifications.getPairedCopy()
|
||||
.subList(notifications.size() - newNotifications.size(),
|
||||
notifications.size());
|
||||
|
@ -561,23 +590,14 @@ public class NotificationsFragment extends SFragment implements
|
|||
}
|
||||
}
|
||||
|
||||
private static boolean findNotification(List<Notification> notifications, String id) {
|
||||
for (Notification notification : notifications) {
|
||||
if (notification.id.equals(id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void onFetchNotificationsFailure(Exception exception, FetchEnd fetchEnd, int position) {
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
if(fetchEnd == FetchEnd.MIDDLE && notifications.getPairedItem(position).getType() == Notification.Type.PLACEHOLDER) {
|
||||
NotificationViewData old = notifications.getPairedItem(position);
|
||||
NotificationViewData notificationViewData = new NotificationViewData(old.getType(),
|
||||
old.getId(), old.getAccount(), old.getStatusViewData(), false);
|
||||
notifications.setPairedItem(position, notificationViewData);
|
||||
adapter.updateItemWithNotify(position, notificationViewData, true);
|
||||
if (fetchEnd == FetchEnd.MIDDLE && !notifications.get(position).isRight()) {
|
||||
NotificationViewData placeholderVD =
|
||||
new NotificationViewData.Placeholder(false);
|
||||
notifications.setPairedItem(position, placeholderVD);
|
||||
adapter.updateItemWithNotify(position, placeholderVD, true);
|
||||
}
|
||||
Log.e(TAG, "Fetch failure: " + exception.getMessage());
|
||||
fulfillAnyQueuedFetches(fetchEnd);
|
||||
|
@ -604,8 +624,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
}
|
||||
}
|
||||
|
||||
private void insert(List<Notification> newNotifications, int pos) {
|
||||
|
||||
private void replacePlaceholderWithNotifications(List<Notification> newNotifications, int pos) {
|
||||
// Remove placeholder
|
||||
notifications.remove(pos);
|
||||
|
||||
if (ListUtils.isEmpty(newNotifications)) {
|
||||
|
@ -613,15 +633,29 @@ public class NotificationsFragment extends SFragment implements
|
|||
return;
|
||||
}
|
||||
|
||||
List<Either<Placeholder, Notification>> liftedNew = liftNotificationList(newNotifications);
|
||||
|
||||
// If we fetched less posts than in the limit, it means that the hole is not filled
|
||||
// If we fetched at least as much it means that there are more posts to load and we should
|
||||
// insert new placeholder
|
||||
if (newNotifications.size() >= LOAD_AT_ONCE) {
|
||||
Notification placeholder = new Notification();
|
||||
placeholder.type = Notification.Type.PLACEHOLDER;
|
||||
newNotifications.add(placeholder);
|
||||
liftedNew.add(Either.<Placeholder, Notification>left(Placeholder.getInstance()));
|
||||
}
|
||||
|
||||
notifications.addAll(pos, newNotifications);
|
||||
notifications.addAll(pos, liftedNew);
|
||||
adapter.update(notifications.getPairedCopy());
|
||||
}
|
||||
|
||||
private final Function<Notification, Either<Placeholder, Notification>> notificationLifter =
|
||||
new Function<Notification, Either<Placeholder, Notification>>() {
|
||||
@Override
|
||||
public Either<Placeholder, Notification> apply(Notification input) {
|
||||
return Either.right(input);
|
||||
}
|
||||
};
|
||||
|
||||
private List<Either<Placeholder, Notification>> liftNotificationList(List<Notification> list) {
|
||||
return CollectionUtil.map(list, notificationLifter);
|
||||
}
|
||||
|
||||
private void fullyRefresh() {
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/* Copyright 2017 Andrew Dawson
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Tusky 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 Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
package com.keylesspalace.tusky.util;
|
||||
|
||||
import android.arch.core.util.Function;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by charlag on 05/11/17.
|
||||
*/
|
||||
|
||||
public final class CollectionUtil {
|
||||
private CollectionUtil() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static <E, R> List<R> map(List<E> list, Function<E, R> mapper) {
|
||||
final List<R> newList = new ArrayList<>(list.size());
|
||||
for (E el : list) {
|
||||
newList.add(mapper.apply(el));
|
||||
}
|
||||
return newList;
|
||||
}
|
||||
}
|
125
app/src/main/java/com/keylesspalace/tusky/util/Either.java
Normal file
125
app/src/main/java/com/keylesspalace/tusky/util/Either.java
Normal file
|
@ -0,0 +1,125 @@
|
|||
/* Copyright 2017 Andrew Dawson
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Tusky 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 Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
package com.keylesspalace.tusky.util;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Created by charlag on 05/11/17.
|
||||
*
|
||||
* Class to represent sum type/tagged union/variant/ADT e.t.c.
|
||||
* It is either Left or Right.
|
||||
*/
|
||||
public final class Either<L, R> {
|
||||
|
||||
/**
|
||||
* Constructs Left instance of either
|
||||
* @param left Object to be considered Left
|
||||
* @param <L> Left type
|
||||
* @param <R> Right type
|
||||
* @return new instance of Either which contains left.
|
||||
*/
|
||||
public static <L, R> Either<L, R> left(L left) {
|
||||
return new Either<>(left, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs Right instance of either
|
||||
* @param right Object to be considered Right
|
||||
* @param <L> Left type
|
||||
* @param <R> Right type
|
||||
* @return new instance of Either which contains right.
|
||||
*/
|
||||
public static <L, R> Either<L, R> right(R right) {
|
||||
return new Either<>(right, true);
|
||||
}
|
||||
|
||||
private final Object value;
|
||||
// we need it because of the types erasure
|
||||
private boolean isRight;
|
||||
|
||||
private Either(Object value, boolean isRight) {
|
||||
this.value = value;
|
||||
this.isRight = isRight;
|
||||
}
|
||||
|
||||
public boolean isRight() {
|
||||
return isRight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get contained object as a Left or throw an exception.
|
||||
* @throws AssertionError If contained value is Right
|
||||
* @return contained value as Right
|
||||
*/
|
||||
public @NonNull L getAsLeft() {
|
||||
if (isRight) {
|
||||
throw new AssertionError("Tried to get the Either as Left while it is Right");
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (L) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get contained object as a Right or throw an exception.
|
||||
* @throws AssertionError If contained value is Left
|
||||
* @return contained value as Right
|
||||
*/
|
||||
public @NonNull R getAsRight() {
|
||||
if (!isRight) {
|
||||
throw new AssertionError("Tried to get the Either as Right while it is Left");
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (R) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #getAsLeft()} but returns {@code null} is the value if Right instead of
|
||||
* throwing an exception.
|
||||
* @return contained value as Left or null
|
||||
*/
|
||||
public @Nullable L getAsLeftOrNull() {
|
||||
if (isRight) {
|
||||
return null;
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (L) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #getAsRightOrNull()} but returns {@code null} is the value if Left instead of
|
||||
* throwing an exception.
|
||||
* @return contained value as Right or null
|
||||
*/
|
||||
public @Nullable R getAsRightOrNull() {
|
||||
if (!isRight) {
|
||||
return null;
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (R) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (!(obj instanceof Either)) return false;
|
||||
Either that = (Either) obj;
|
||||
return this.isRight == that.isRight &&
|
||||
(this.value == that.value ||
|
||||
this.value != null && this.value.equals(that.value));
|
||||
}
|
||||
}
|
|
@ -80,8 +80,8 @@ public final class ViewDataUtils {
|
|||
}
|
||||
|
||||
public static NotificationViewData notificationToViewData(Notification notification) {
|
||||
return new NotificationViewData(notification.type, notification.id, notification.account,
|
||||
statusToViewData(notification.status), false);
|
||||
return new NotificationViewData.Concrete(notification.type, notification.id, notification.account,
|
||||
statusToViewData(notification.status));
|
||||
}
|
||||
|
||||
public static List<NotificationViewData> notificationListToViewDataList(
|
||||
|
|
|
@ -20,22 +20,31 @@ import com.keylesspalace.tusky.entity.Notification;
|
|||
|
||||
/**
|
||||
* Created by charlag on 12/07/2017.
|
||||
*
|
||||
* Class to represent data required to display either a notification or a placeholder.
|
||||
* It is either a {@link Placeholder} or a {@link Concrete}.
|
||||
* It is modelled this way because close relationship between placeholder and concrete notification
|
||||
* is fine in this case. Placeholder case is not modelled as a type of notification because
|
||||
* invariants would be violated and because it would model domain incorrectly. It is prefereable to
|
||||
* {@link com.keylesspalace.tusky.util.Either} because class hierarchy is cheaper, faster and
|
||||
* more native.
|
||||
*/
|
||||
public abstract class NotificationViewData {
|
||||
private NotificationViewData() {
|
||||
}
|
||||
|
||||
public final class NotificationViewData {
|
||||
public static final class Concrete extends NotificationViewData {
|
||||
private final Notification.Type type;
|
||||
private final String id;
|
||||
private final Account account;
|
||||
private final StatusViewData statusViewData;
|
||||
private final boolean placeholderLoading;
|
||||
|
||||
public NotificationViewData(Notification.Type type, String id, Account account,
|
||||
StatusViewData statusViewData, boolean placeholderLoading) {
|
||||
public Concrete(Notification.Type type, String id, Account account,
|
||||
StatusViewData statusViewData) {
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.account = account;
|
||||
this.statusViewData = statusViewData;
|
||||
this.placeholderLoading = placeholderLoading;
|
||||
}
|
||||
|
||||
public Notification.Type getType() {
|
||||
|
@ -53,8 +62,17 @@ public final class NotificationViewData {
|
|||
public StatusViewData getStatusViewData() {
|
||||
return statusViewData;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPlaceholderLoading() {
|
||||
return placeholderLoading;
|
||||
public static final class Placeholder extends NotificationViewData {
|
||||
private final boolean isLoading;
|
||||
|
||||
public Placeholder(boolean isLoading) {
|
||||
this.isLoading = isLoading;
|
||||
}
|
||||
|
||||
public boolean isLoading() {
|
||||
return isLoading;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue