diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/NotificationAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/NotificationAdapter.java index 556f88b27..7a121691c 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/NotificationAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/NotificationAdapter.java @@ -143,13 +143,21 @@ public class NotificationAdapter extends RecyclerView.Adapter { + holder.bindingFetchMore.fetchMoreContainer.setEnabled(!notification.isFetchMoreHidden); + holder.bindingFetchMore.fetchMoreMin.setOnClickListener(v -> { if (position + 1 < notificationList.size()) { //We hide the button notification.isFetchMoreHidden = true; notifyItemChanged(position); - fetchMoreCallBack.onClick(notificationList.get(position + 1).id, notification.id); + fetchMoreCallBack.onClickMin(notificationList.get(position + 1).id, notification.id); + } + }); + holder.bindingFetchMore.fetchMoreMax.setOnClickListener(v -> { + if (position - 1 >= 0) { + //We hide the button + notification.isFetchMoreHidden = true; + notifyItemChanged(position); + fetchMoreCallBack.onClickMax(notificationList.get(position - 1).id, notification.id); } }); } else { @@ -264,7 +272,9 @@ public class NotificationAdapter extends RecyclerView.Adapter }); } else if (viewHolder.getItemViewType() == STATUS_FETCH_MORE) { StatusViewHolder holder = (StatusViewHolder) viewHolder; - holder.bindingFetchMore.fetchMore.setEnabled(!status.isFetchMoreHidden); - holder.bindingFetchMore.fetchMore.setOnClickListener(v -> { + holder.bindingFetchMore.fetchMoreContainer.setEnabled(!status.isFetchMoreHidden); + holder.bindingFetchMore.fetchMoreMin.setOnClickListener(v -> { if (position + 1 < statusList.size()) { //We hide the button status.isFetchMoreHidden = true; notifyItemChanged(position); - fetchMoreCallBack.onClick(statusList.get(position + 1).id, status.id); + fetchMoreCallBack.onClickMinId(statusList.get(position + 1).id, status.id); + } + }); + holder.bindingFetchMore.fetchMoreMax.setOnClickListener(v -> { + if (position - 1 >= 0) { + //We hide the button + status.isFetchMoreHidden = true; + notifyItemChanged(position); + fetchMoreCallBack.onClickMaxId(statusList.get(position - 1).id, status.id); } }); } @@ -1876,7 +1884,9 @@ public class StatusAdapter extends RecyclerView.Adapter } public interface FetchMoreCallBack { - void onClick(String min_id, String fetchmoreId); + void onClickMinId(String min_id, String fetchmoreId); + + void onClickMaxId(String max_id, String fetchmoreId); } public static class StatusViewHolder extends RecyclerView.ViewHolder { diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonNotification.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonNotification.java index 7fe933512..70005b28e 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonNotification.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonNotification.java @@ -88,7 +88,7 @@ public class FragmentMastodonNotification extends Fragment implements Notificati } } }; - private String max_id, min_id, min_id_fetch_more; + private String max_id, min_id, min_id_fetch_more, max_id_fetch_more; private LinearLayoutManager mLayoutManager; private String instance, user_id; private ArrayList idOfAddedNotifications; @@ -316,8 +316,8 @@ public class FragmentMastodonNotification extends Fragment implements Notificati notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null) .observe(getViewLifecycleOwner(), this::initializeNotificationView); } else if (direction == FragmentMastodonTimeline.DIRECTION.BOTTOM) { - notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null) - .observe(getViewLifecycleOwner(), notificationsBottom -> dealWithPagination(notificationsBottom, FragmentMastodonTimeline.DIRECTION.BOTTOM, false)); + notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null) + .observe(getViewLifecycleOwner(), notificationsBottom -> dealWithPagination(notificationsBottom, FragmentMastodonTimeline.DIRECTION.BOTTOM, fetchingMissing)); } else if (direction == FragmentMastodonTimeline.DIRECTION.TOP) { notificationsVM.getNotifications(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()), excludeType, null) .observe(getViewLifecycleOwner(), notificationsTop -> dealWithPagination(notificationsTop, FragmentMastodonTimeline.DIRECTION.TOP, fetchingMissing)); @@ -510,7 +510,7 @@ public class FragmentMastodonNotification extends Fragment implements Notificati } @Override - public void onClick(String min_id, String id) { + public void onClickMin(String min_id, String id) { //Fetch more has been pressed min_id_fetch_more = min_id; Notification notification = null; @@ -529,6 +529,25 @@ public class FragmentMastodonNotification extends Fragment implements Notificati route(FragmentMastodonTimeline.DIRECTION.TOP, true); } + @Override + public void onClickMax(String max_id, String id) { + //Fetch more has been pressed + max_id_fetch_more = max_id; + Notification notification = null; + int position = 0; + for (Notification currentNotification : this.notificationList) { + if (currentNotification.id.compareTo(id) == 0) { + notification = currentNotification; + break; + } + position++; + } + if (notification != null) { + this.notificationList.remove(position); + notificationAdapter.notifyItemRemoved(position); + } + route(FragmentMastodonTimeline.DIRECTION.BOTTOM, true); + } public enum NotificationTypeEnum { @SerializedName("ALL") diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java index 6d3f97c3a..549124093 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java @@ -77,7 +77,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. private List statuses; private String search, searchCache; private Status statusReport; - private String max_id, min_id, min_id_fetch_more; + private String max_id, min_id, min_id_fetch_more, max_id_fetch_more; private StatusAdapter statusAdapter; private Timeline.TimeLineEnum timelineType; //Handle actions that can be done in other fragments @@ -471,7 +471,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. statusFetchMore.isFetchMore = true; statusFetchMore.id = Helper.generateString(); int insertAt; - if (direction == DIRECTION.REFRESH) { + if (direction == DIRECTION.REFRESH || direction == DIRECTION.BOTTOM) { insertAt = lastInsertedPosition; } else { insertAt = initialInsertedPosition; @@ -620,8 +620,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity())) .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); } else if (direction == DIRECTION.BOTTOM) { - timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity())) - .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); + timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity())) + .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing)); } else if (direction == DIRECTION.TOP) { timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity())) .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing)); @@ -640,8 +640,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity())) .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); } else if (direction == DIRECTION.BOTTOM) { - timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity())) - .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); + timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity())) + .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing)); } else if (direction == DIRECTION.TOP) { timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity())) .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing)); @@ -726,8 +726,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity())) .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); } else if (direction == DIRECTION.BOTTOM) { - timelinesVM.getPublic(null, remoteInstance, true, false, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity())) - .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); + timelinesVM.getPublic(null, remoteInstance, true, false, false, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity())) + .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing)); } else if (direction == DIRECTION.TOP) { timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity())) .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing)); @@ -747,8 +747,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, null, null, null, MastodonHelper.statusesPerCall(requireActivity())) .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); } else if (direction == DIRECTION.BOTTOM) { - timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity())) - .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); + timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity())) + .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing)); } else if (direction == DIRECTION.TOP) { timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity())) .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing)); @@ -771,8 +771,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, null, null, null, MastodonHelper.statusesPerCall(requireActivity())) .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); } else if (direction == DIRECTION.BOTTOM) { - timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity())) - .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); + timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, fetchingMissing ? max_id_fetch_more : max_id, null, null, MastodonHelper.statusesPerCall(requireActivity())) + .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, fetchingMissing)); } else if (direction == DIRECTION.TOP) { timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity())) .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing)); @@ -907,21 +907,26 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. } } else if (direction == DIRECTION.BOTTOM) { - if (networkAvailable == BaseMainActivity.status.CONNECTED) { - //We first if we get results from cache - timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, max_id, null, null) - .observe(getViewLifecycleOwner(), statusesBottomCache -> { - if (statusesBottomCache != null && statusesBottomCache.statuses != null && statusesBottomCache.statuses.size() > 0) { - dealWithPagination(statusesBottomCache, DIRECTION.BOTTOM, false); - } else { // If not, we fetch remotely - timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), false) - .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); - } - }); + if (!fetchingMissing) { + if (networkAvailable == BaseMainActivity.status.CONNECTED) { + //We first if we get results from cache + timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, max_id, null, null) + .observe(getViewLifecycleOwner(), statusesBottomCache -> { + if (statusesBottomCache != null && statusesBottomCache.statuses != null && statusesBottomCache.statuses.size() > 0) { + dealWithPagination(statusesBottomCache, DIRECTION.BOTTOM, false); + } else { // If not, we fetch remotely + timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity()), false) + .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); + } + }); + } else { + timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, max_id, null, null) + .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); + } } else { - timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, max_id, null, null) - .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); + timelinesVM.getHomeCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, max_id_fetch_more, null, null) + .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, true)); } } else if (direction == DIRECTION.TOP) { if (!fetchingMissing) { @@ -968,7 +973,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. } @Override - public void onClick(String min_id, String id) { + public void onClickMinId(String min_id, String id) { //Fetch more has been pressed min_id_fetch_more = min_id; Status status = null; @@ -987,6 +992,25 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. route(DIRECTION.TOP, true); } + @Override + public void onClickMaxId(String max_id, String id) { + max_id_fetch_more = max_id; + Status status = null; + int position = 0; + for (Status currentStatus : this.statuses) { + if (currentStatus.id.compareTo(id) == 0) { + status = currentStatus; + break; + } + position++; + } + if (status != null) { + this.statuses.remove(position); + statusAdapter.notifyItemRemoved(position); + } + route(DIRECTION.BOTTOM, true); + } + public enum DIRECTION { TOP, BOTTOM, diff --git a/app/src/main/res/drawable/ic_baseline_arrow_circle_down_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_circle_down_24.xml new file mode 100644 index 000000000..119241672 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_arrow_circle_down_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_arrow_circle_up_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_circle_up_24.xml new file mode 100644 index 000000000..7634b238f --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_arrow_circle_up_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/drawer_fetch_more.xml b/app/src/main/res/layout/drawer_fetch_more.xml index 70736b25d..a3ba2be26 100644 --- a/app/src/main/res/layout/drawer_fetch_more.xml +++ b/app/src/main/res/layout/drawer_fetch_more.xml @@ -1,9 +1,36 @@ - \ No newline at end of file + android:orientation="horizontal" + android:padding="6dp"> + + + + + + +