Preserve status states on updates. UI layer refactoring.
Some things were pulled out of adapters to fragments. New classes were introduced - StatusViewData and NotificationViewData. They not only have view state in them but also help decoupling. Because introducing parallel model list requires a lot of synchronisation PairedList was added. Also synchronisation between fragments and adapters is quiet tedious and error-prone and should be replaces with better solution. Oh, I also couldn’t resist and fixed bug with buttons animation in the same commit.
This commit is contained in:
parent
f68f6d7473
commit
90c1a83ba4
15 changed files with 1194 additions and 358 deletions
|
@ -0,0 +1,84 @@
|
|||
package com.keylesspalace.tusky.util;
|
||||
|
||||
import android.arch.core.util.Function;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* This list implementation can help to keep two lists in sync - like real models and view models.
|
||||
* Every operation on the main list triggers update of the supplementary list (but not vice versa).
|
||||
* This makes sure that the main list is always the source of truth.
|
||||
* Main list is projected to the supplementary list by the passed mapper function.
|
||||
* Paired list is newer actually exposed and clients are provided with {@code getPairedCopy()},
|
||||
* {@code getPairedItem()} and {@code setPairedItem()}. This prevents modifications of the
|
||||
* supplementary list size so lists are always have the same length.
|
||||
* This implementation will not try to recover from exceptional cases so lists may be out of sync
|
||||
* after the exception.
|
||||
*
|
||||
* It is most useful with immutable data because we cannot track changes inside stored objects.
|
||||
* @param <T> type of elements in the main list
|
||||
* @param <V> type of elements in supplementary list
|
||||
*/
|
||||
public final class PairedList<T, V> extends AbstractList<T> {
|
||||
private final List<T> main = new ArrayList<>();
|
||||
private final List<V> synced = new ArrayList<>();
|
||||
private final Function<T, ? extends V> mapper;
|
||||
|
||||
/**
|
||||
* Construct new paired list. Main and supplementary lists will be empty.
|
||||
* @param mapper Function, which will be used to translate items from the main list to the
|
||||
* supplementary one.
|
||||
*/
|
||||
public PairedList(Function<T, ? extends V> mapper) {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
public List<V> getPairedCopy() {
|
||||
return new ArrayList<>(synced);
|
||||
}
|
||||
|
||||
public V getPairedItem(int index) {
|
||||
return synced.get(index);
|
||||
}
|
||||
|
||||
public void setPairedItem(int index, V element) {
|
||||
synced.set(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(int index) {
|
||||
return main.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T set(int index, T element) {
|
||||
synced.set(index, mapper.apply(element));
|
||||
return main.set(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(T t) {
|
||||
synced.add(mapper.apply(t));
|
||||
return main.add(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, T element) {
|
||||
synced.add(index, mapper.apply(element));
|
||||
main.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T remove(int index) {
|
||||
synced.remove(index);
|
||||
return main.remove(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return main.size();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package com.keylesspalace.tusky.util;
|
||||
|
||||
import android.arch.core.util.Function;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.keylesspalace.tusky.entity.Notification;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.viewdata.NotificationViewData;
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by charlag on 12/07/2017.
|
||||
*/
|
||||
|
||||
public final class ViewDataUtils {
|
||||
@Nullable
|
||||
public static StatusViewData statusToViewData(@Nullable Status status) {
|
||||
if (status == null) return null;
|
||||
Status visibleStatus = status.reblog == null ? status : status.reblog;
|
||||
return new StatusViewData.Builder()
|
||||
.setId(status.id)
|
||||
.setAttachments(status.attachments)
|
||||
.setAvatar(visibleStatus.account.avatar)
|
||||
.setContent(visibleStatus.content)
|
||||
.setCreatedAt(visibleStatus.createdAt)
|
||||
.setFavourited(visibleStatus.favourited)
|
||||
.setReblogged(visibleStatus.reblogged)
|
||||
.setIsExpanded(false)
|
||||
.setIsShowingSensitiveContent(false)
|
||||
.setMentions(visibleStatus.mentions)
|
||||
.setNickname(visibleStatus.account.username)
|
||||
.setRebloggedAvatar(visibleStatus.account.avatar)
|
||||
.setSensitive(visibleStatus.sensitive)
|
||||
.setSpoilerText(visibleStatus.spoilerText)
|
||||
.setRebloggedByUsername(status.reblog == null ? null : status.account.username)
|
||||
.setUserFullName(visibleStatus.account.getDisplayName())
|
||||
.setSenderId(status.account.id)
|
||||
.setRebloggingEnabled(visibleStatus.rebloggingAllowed())
|
||||
.createStatusViewData();
|
||||
}
|
||||
|
||||
public static List<StatusViewData> statusListToViewDataList(List<Status> statuses) {
|
||||
List<StatusViewData> viewDatas = new ArrayList<>(statuses.size());
|
||||
for (Status s : statuses) {
|
||||
viewDatas.add(statusToViewData(s));
|
||||
}
|
||||
return viewDatas;
|
||||
}
|
||||
|
||||
public static Function<Status, StatusViewData> statusMapper() {
|
||||
return statusMapper;
|
||||
}
|
||||
|
||||
public static NotificationViewData notificationToViewData(Notification notification) {
|
||||
return new NotificationViewData(notification.type, notification.id, notification.account,
|
||||
statusToViewData(notification.status));
|
||||
}
|
||||
|
||||
public static List<NotificationViewData>
|
||||
notificationListToViewDataList(List<Notification> notifications) {
|
||||
List<NotificationViewData> viewDatas = new ArrayList<>(notifications.size());
|
||||
for (Notification n : notifications) {
|
||||
viewDatas.add(notificationToViewData(n));
|
||||
}
|
||||
return viewDatas;
|
||||
}
|
||||
|
||||
private static final Function<Status, StatusViewData> statusMapper =
|
||||
new Function<Status, StatusViewData>() {
|
||||
@Override
|
||||
public StatusViewData apply(Status input) {
|
||||
return ViewDataUtils.statusToViewData(input);
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue