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

Create abstract PaginatedListState for DiscussionList and others #2781

Merged
merged 33 commits into from
May 11, 2021

Conversation

dsevillamartin
Copy link
Member

@dsevillamartin dsevillamartin commented Apr 11, 2021

Refs flarum/issue-archive#162

Reviewers should focus on:

Look at // TODO comments in code

  • What does the current page track?
  • Do we want the abstract state tied to store models?
  • How do we want to treat this.pages?
  • Logic to set when loading/done loading needs to be revised
  • Calling refresh sets page location to pg 1, this needs to be changed (refresh is called on page load, will replace params or whatever)
  • The params implementation in general needs clarification - do we want like DiscussionListState or something else?

Confirmed

  • Frontend changes: tested on a local Flarum installation.

Required changes:

  • Related documentation PR: (Remove if irrelevant)

Copy link
Sponsor Member

@askvortsov1 askvortsov1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The params implementation in general needs clarification - do we want like DiscussionListState or something else?

I think DiscussionListState's implementation makes sense, with one exception: in FriendsOfFlarum/user-directory@bc4ffe8, Ian and I added a "qBuilder", which makes it easier to assemble the q query string. I think that's something we might want to move over into the official implementation.

On a side note, to keep things simple, we might want to do this in 2 phases:

  1. Swap out DiscussionListState for PaginatedListState, implement the url pagination
  2. Possibly in a separate release (since Improve discussion page near canonical url #2397 is still on the milestone), swap out DiscussionList for an abstract PaginatedList

// - what does this page track? last page request? last page that is loaded
// (e.g. pg 7 if u have 3-7 loaded)
// - When do we set it?
protected location!: PaginationLocation;
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pages is a contiguous structure that expands from both ends. So it could either be an array or a doubly linked list. If it's a doubly linked list, location's page prop should point to the active element. If it's an array, location's page prop should be the index of the active page (although we'd need to remember to increment by 1 when adding new pages.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are we defining with "active" page though? If the user loads the previous page, does the active page still remain the last number in ascending order (e.g. loaded pg 2 but pg 5 is still active) or does it change to page 2?

Copy link
Sponsor Member

@askvortsov1 askvortsov1 Apr 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the previous page be the page that the user is currently on?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are you referring to with previous page? I meant as load next page, load prev page, for loading a page that hasn't been loaded.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Active page, sorry.

js/src/common/states/PaginatedListState.ts Outdated Show resolved Hide resolved
js/src/common/states/PaginatedListState.ts Outdated Show resolved Hide resolved
js/src/common/states/PaginatedListState.ts Show resolved Hide resolved
js/src/common/states/PaginatedListState.ts Outdated Show resolved Hide resolved
js/src/common/states/PaginatedListState.ts Show resolved Hide resolved
@dsevillamartin
Copy link
Member Author

Regarding the handling of query parameters, some thoughts from Sasha:

  • A default params method is good
  • As is a method to assemble params before sending (since we'd like to add in a qBuilder field)
  • e.g. requestParams in DiscussionListState.js
  • So the params object will be in a "construction" form, not fully in the format it'd be sent in, for some of the methods and for internal storage

@askvortsov1
Copy link
Sponsor Member

Looked at this again. DiscussionListState only has 2 methods: one for getting the underlying params object, the other for converting that into the format expected by JSON:API. I think having these 2 methods is sensible for PaginatedList. It shouldn't need to worry about global / URL query params IMO.

Have you tried plugging this into the NotificationList? Does it work as well as with DiscussionList?

@dsevillamartin
Copy link
Member Author

Have you tried plugging this into the NotificationList?

I have not. I will try to do so today.

@dsevillamartin
Copy link
Member Author

dsevillamartin commented Apr 25, 2021

  • How do we feel about the getAllItems method in c7d129b?
  • How do we like the DiscussionListState#removeDiscussion implementation in e01ab57?
  • How do we want to handle DiscussionListState#addDiscussion
    • Do we want an extra field for the added discussions that is cleared on refresh?
      That way we don't do weird stuff with pagination.
      We can have an extra method that adds the extra field to the array of discussions returned.
  • Do we want getter methods for hasPrev, hasNext? (I assume yes)
  • Do we want to keep methods like hasDiscussions and empty that call to the actual methods for BC?

Copy link
Sponsor Member

@askvortsov1 askvortsov1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want getter methods for hasPrev, hasNext? (I assume yes)

I think so, if only for the consistency with other methods

Do we want to keep methods like hasDiscussions and empty that call to the actual methods for BC?

Yes, but let's deprecate them. Before the release, we can do a sweep and decide which deprecated stuff to drop (since the next time we'll get the chance to do so will be v2)

Do we want an extra field for the added discussions that is cleared on refresh?

That way we don't do weird stuff with pagination.
We can have an extra method that adds the extra field to the array of discussions returned.

So when the next page comes in, we'd check if the added discussion is present, and if so, override it in the returned results? I like this idea.

How do we feel about the getAllItems method in c7d129b?

Makes sense to me. Let's keep it protected for now though.

How do we like the DiscussionListState#removeDiscussion implementation in e01ab57?

Also looks good to me!

I'm not seeing any code for navigating to the previous page in the component. Will this be part of the followup component PR?

js/src/common/states/PaginatedListState.ts Outdated Show resolved Hide resolved
js/src/forum/components/DiscussionList.js Show resolved Hide resolved
js/src/forum/components/IndexPage.js Show resolved Hide resolved
js/src/common/states/PaginatedListState.ts Outdated Show resolved Hide resolved
js/src/common/states/PaginatedListState.ts Outdated Show resolved Hide resolved
js/src/common/states/PaginatedListState.ts Outdated Show resolved Hide resolved
js/src/forum/components/IndexPage.js Show resolved Hide resolved
js/src/forum/states/DiscussionListState.ts Show resolved Hide resolved
*/
load(): Promise<void> {
if (app.session.user.newNotificationCount()) {
this.pages = [];
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this reset location too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want a method to reset location perhaps? Not sure. Don't like having this.location = { page: 1 } a lot tbh.

js/src/common/states/PaginatedListState.ts Outdated Show resolved Hide resolved
Copy link
Sponsor Member

@askvortsov1 askvortsov1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've given it some more thought, and I'm no longer sure that qBuilder should go here. I think it might make sense to put it in GlobalSearchState instead (which is a refactor for another day).

this.loadingPrev = false;
this.loadingNext = false;

m.redraw();
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We currently clear it in DiscussionList here so that the loading indicator is shown while refreshing, IIRC

js/src/common/states/PaginatedListState.ts Outdated Show resolved Hide resolved
js/src/common/states/PaginatedListState.ts Outdated Show resolved Hide resolved
Comment on lines +107 to +115
if (this.extraDiscussions.length) {
return [
{
number: -1,
items: this.extraDiscussions,
},
...pages,
];
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love this, but I don't know how else we could do this.

This is so that DiscussionList can do .getPages() and add a page separator (as an example) that forum owners can then style.

Not sure whether the state or the component should check whether the page is empty, it's currently on the state (checks that extraDiscussions isn't empty before adding the page).

@dsevillamartin
Copy link
Member Author

I found a logic error - hasNext and hasPrev are calculated using the current page, and not the earliest or latest pages loaded.

So, if there are 3 pages of discussions, and we begin by loading the 3rd, then load the 2nd, the "load more" button appears because there are more after the 2nd page, even though the 3rd is already loaded.

I think the fix for this would be to move the hasPrev and hasNext values to the Page interface, and then use the methods to check the first & last items in this.pages. Thoughts?

@askvortsov1
Copy link
Sponsor Member

I found a logic error - hasNext and hasPrev are calculated using the current page, and not the earliest or latest pages loaded.

So, if there are 3 pages of discussions, and we begin by loading the 3rd, then load the 2nd, the "load more" button appears because there are more after the 2nd page, even though the 3rd is already loaded.

I think the fix for this would be to move the hasPrev and hasNext values to the Page interface, and then use the methods to check the first & last items in this.pages. Thoughts?

That makes sense to me!

@askvortsov1 askvortsov1 added this to the 1.0 milestone May 9, 2021
@dsevillamartin
Copy link
Member Author

dsevillamartin commented May 10, 2021

This is ready to review now. I'll try moving the unnecessary UI changes for DiscussionList to another PR so that can be discussed there (e.g. the <hr> marking page & load prev button).

After I do that I'll mark as ready for review.

@dsevillamartin dsevillamartin marked this pull request as ready for review May 10, 2021 20:49
@dsevillamartin dsevillamartin changed the title [WIP] Create abstract PaginatedListState for DiscussionList and others Create abstract PaginatedListState for DiscussionList and others May 10, 2021
This is needed for visual confirmation with a loading indicator
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

Successfully merging this pull request may close these issues.

3 participants