Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Endless Scroll Feature Request #749

Open
akm0012 opened this issue Nov 9, 2019 · 2 comments
Open

Endless Scroll Feature Request #749

akm0012 opened this issue Nov 9, 2019 · 2 comments

Comments

@akm0012
Copy link

akm0012 commented Nov 9, 2019

I am creating a messaging screen and am using the Endless Scroll Listener to lazy load old messages. Here is my problem. Currently the callback for onLoadMore is only called when you hit the last item minus the threshold. This works great in most cases but imagine this scenario:

Message API Limit = 10 messages

I am saving my messages to disk. I have a conversation with 50 messages in it. Then I don't use the app for months. Let's say 50 more messages have been sent to me since I last opened the app. I now have 100 total messages in the server. I open the app again and go to this conversation. The 50 messages that were saved to disk will show. Then I will pull down the latest 10 messages, but I won't try loading more messages until I reach the end of the list. So I have in effect missed out on 40 messages. (10 messages from initial load, plus the 50 stored in the database)

Is there a way to be notified every time I scroll 10 messages so I can download the next 10 messages, whether they are needed to be downloaded or not? Is there a better way to solve this problem that I am not seeing?

@VaslD
Copy link

VaslD commented Nov 13, 2019

I have used the library for a similar project. I don't have the exact code with me anymore but for your particular scenario, there are 2 ways to solve this:

  1. You should change your message server API to accept a cursor/timestamp/page of the last request made and to identify that there is a gap between this request and last request that's larger than the API paging limit. When this gap is detected, you should return next 10 messages on the server that immediately follow the last request, rather than returning 10 most recent messages. Messages are not "What's New" contents, they are contextual and should always be presented as a timeline; in other words, your API can't just return 10 latest messages for every request, that doesn't make sense in the real world.

OR

  1. Suppose you're using RecyclerView, it has an OnScrollListener (can't remember the full name) you can implement to get notified every time the view port (of the recycler/list) changes. OnScrollListener isn't managed by this library so you may need to do some extra work. Because your API doesn't really work the same way as traditional endless scrolling does, get rid of this library's endless scroll feature and implement the listener on your own. You can have it as simple as (pseudo-code):
// Outside your listener:
let lastIndex = adapter.layoutManager.firstVisibleItemIndex;

...

// Inside your listener:
let currentIndex = adapter.layoutManager.lastVisibleItemIndex;

if (currentIndex  - lastIndex  > 10) {
    loadSomeData();
    lastIndex = currentIndex;
}

@akm0012
Copy link
Author

akm0012 commented Nov 14, 2019

Thanks @VaslD! Timestamps is a good idea. I ended up taking out the Endless listener as you suggested and did something like this in my FlexibleItem

@Override
    public void bindViewHolder(FlexibleAdapter adapter, SpeechBubbleViewHolder holder, int position,
                               List payloads) {

        if ((position + NetworkConstants.MESSAGE_THRESHOLD) % (NetworkConstants.MESSAGES_LIMIT) == 0) {
            int offset = position + NetworkConstants.MESSAGE_THRESHOLD;
            Timber.d("Load more items... at position %s. Offset to load: %s", position, offset);
            messageRepository.syncMessages(message.getGroupId(), offset);
        }

        holder.speechBubbleView.setViewModel(new SpeechBubbleViewModel(message));
    }�

That got me super close, but it will be called more than once as you are scrolling, so just added a check where I only load the offset for that thread once per session. Not ideal, but good enough for now.

private HashMap<String, HashSet<Integer>> pagedGroups = new HashMap<>();

    public void syncMessages(@NonNull String groupId, int offset) {
        Timber.v("Syncing Messages for Group %s with offset %s", groupId, offset);

        if (pagedGroups.get(groupId) == null) {
            pagedGroups.put(groupId, new HashSet<>());
        }

        if (!pagedGroups.get(groupId).contains(offset)) {
            Timber.i("We have not loaded the offset %s for group %s yet. Starting Job Now.", offset, groupId);
            pagedGroups.get(groupId).add(offset);
            jobManager.addJobInBackground(new GetMessagesForGroupJob(groupId, offset));
        } else {
            Timber.d("We have already loaded the offset %s for group %s.", offset, groupId);
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants