1
0
mirror of https://github.com/flarum/core.git synced 2025-08-04 23:47:32 +02:00

perf(flags): add pagination to flags list (#3931)

This commit is contained in:
Tristian Kelly
2023-11-23 11:11:46 -06:00
committed by GitHub
parent 6cbdfb6aa1
commit 46357ee9a9
4 changed files with 89 additions and 60 deletions

View File

@@ -8,6 +8,8 @@ import HeaderListItem from 'flarum/forum/components/HeaderListItem';
import type Mithril from 'mithril'; import type Mithril from 'mithril';
import type Post from 'flarum/common/models/Post'; import type Post from 'flarum/common/models/Post';
import type FlagListState from '../states/FlagListState'; import type FlagListState from '../states/FlagListState';
import type Flag from '../models/Flag';
import { Page } from 'flarum/common/states/PaginatedListState';
export interface IFlagListAttrs extends ComponentAttrs { export interface IFlagListAttrs extends ComponentAttrs {
state: FlagListState; state: FlagListState;
@@ -16,49 +18,55 @@ export interface IFlagListAttrs extends ComponentAttrs {
export default class FlagList<CustomAttrs extends IFlagListAttrs = IFlagListAttrs> extends Component<CustomAttrs, FlagListState> { export default class FlagList<CustomAttrs extends IFlagListAttrs = IFlagListAttrs> extends Component<CustomAttrs, FlagListState> {
oninit(vnode: Mithril.Vnode<CustomAttrs, this>) { oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
super.oninit(vnode); super.oninit(vnode);
this.state = this.attrs.state;
} }
view() { view() {
const flags = this.state.cache || []; const state = this.attrs.state;
return ( return (
<HeaderList <HeaderList
className="FlagList" className="FlagList"
title={app.translator.trans('flarum-flags.forum.flagged_posts.title')} title={app.translator.trans('flarum-flags.forum.flagged_posts.title')}
hasItems={flags.length} hasItems={state.hasItems()}
loading={this.state.loading} loading={state.isLoading()}
emptyText={app.translator.trans('flarum-flags.forum.flagged_posts.empty_text')} emptyText={app.translator.trans('flarum-flags.forum.flagged_posts.empty_text')}
loadMore={() => state.hasNext() && !state.isLoadingNext() && state.loadNext()}
> >
<ul className="HeaderListGroup-content"> <ul className="HeaderListGroup-content">{this.content(state)}</ul>
{!this.state.loading &&
flags.map((flag) => {
const post = flag.post() as Post;
return (
<li>
<HeaderListItem
className="Flag"
avatar={<Avatar user={post.user() || null} />}
icon="fas fa-flag"
content={app.translator.trans('flarum-flags.forum.flagged_posts.item_text', {
username: username(post.user()),
em: <em />,
discussion: post.discussion().title(),
})}
excerpt={post.contentPlain()}
datetime={flag.createdAt()}
href={app.route.post(post)}
onclick={(e: MouseEvent) => {
app.flags.index = post;
e.redraw = false;
}}
/>
</li>
);
})}
</ul>
</HeaderList> </HeaderList>
); );
} }
content(state: FlagListState) {
if (!state.isLoading() && state.hasItems()) {
return state.getPages().map((page: Page<Flag>) => {
return page.items.map((flag: Flag) => {
const post = flag.post() as Post;
return (
<li>
<HeaderListItem
className="Flag"
avatar={<Avatar user={post.user() || null} />}
icon="fas fa-flag"
content={app.translator.trans('flarum-flags.forum.flagged_posts.item_text', {
username: username(post.user()),
em: <em />,
discussion: post.discussion().title(),
})}
excerpt={post.contentPlain()}
datetime={flag.createdAt()}
href={app.route.post(post)}
onclick={(e: MouseEvent) => {
e.redraw = false;
}}
/>
</li>
);
});
});
}
return null;
}
} }

View File

@@ -25,7 +25,7 @@ export default class FlagsDropdown<CustomAttrs extends IFlagsDropdownAttrs = IFl
} }
getUnreadCount() { getUnreadCount() {
return app.flags.cache ? app.flags.cache.length : app.forum.attribute<number>('flagCount'); return app.forum.attribute<number>('flagCount');
} }
getNewCount() { getNewCount() {

View File

@@ -1,39 +1,33 @@
import type ForumApplication from 'flarum/forum/ForumApplication'; import type ForumApplication from 'flarum/forum/ForumApplication';
import type Flag from '../models/Flag'; import type Flag from '../models/Flag';
import type Post from 'flarum/common/models/Post'; import PaginatedListState from 'flarum/common/states/PaginatedListState';
export default class FlagListState { export default class FlagListState extends PaginatedListState<Flag> {
public app: ForumApplication; public app: ForumApplication;
public loading = false;
public cache: Flag[] | null = null;
public index: Post | false | null = null;
constructor(app: ForumApplication) { constructor(app: ForumApplication) {
super({}, 1, null);
this.app = app; this.app = app;
} }
get type(): string {
return 'flags';
}
/** /**
* Load flags into the application's cache if they haven't already * Load flags into the application's cache if they haven't already
* been loaded. * been loaded.
*/ */
load() { load(): Promise<void> {
if (this.cache && !this.app.session.user!.attribute<number>('newFlagCount')) { if (this.app.session.user?.attribute<number>('newFlagCount')) {
return; this.pages = [];
this.location = { page: 1 };
} }
this.loading = true; if (this.pages.length > 0) {
m.redraw(); return Promise.resolve();
}
this.app.store return super.loadNext();
.find<Flag[]>('flags')
.then((flags) => {
this.app.session.user!.pushAttributes({ newFlagCount: 0 });
this.cache = flags.sort((a, b) => b.createdAt()!.getTime() - a.createdAt()!.getTime());
})
.catch(() => {})
.then(() => {
this.loading = false;
m.redraw();
});
} }
} }

View File

@@ -14,6 +14,7 @@ use Flarum\Api\Controller\AbstractListController;
use Flarum\Flags\Api\Serializer\FlagSerializer; use Flarum\Flags\Api\Serializer\FlagSerializer;
use Flarum\Flags\Flag; use Flarum\Flags\Flag;
use Flarum\Http\RequestUtil; use Flarum\Http\RequestUtil;
use Flarum\Http\UrlGenerator;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document; use Tobscure\JsonApi\Document;
@@ -28,26 +29,52 @@ class ListFlagsController extends AbstractListController
'post.discussion' 'post.discussion'
]; ];
public function __construct(
protected UrlGenerator $url
) {
}
protected function data(ServerRequestInterface $request, Document $document): iterable protected function data(ServerRequestInterface $request, Document $document): iterable
{ {
$actor = RequestUtil::getActor($request); $actor = RequestUtil::getActor($request);
$include = $this->extractInclude($request);
$actor->assertRegistered(); $actor->assertRegistered();
$actor->read_flags_at = Carbon::now(); $actor->read_flags_at = Carbon::now();
$actor->save(); $actor->save();
$flags = Flag::whereVisibleTo($actor) $limit = $this->extractLimit($request);
->latest('flags.created_at') $offset = $this->extractOffset($request);
->groupBy('post_id') $include = $this->extractInclude($request);
->get();
if (in_array('post.user', $include)) { if (in_array('post.user', $include)) {
$include[] = 'post.user.groups'; $include[] = 'post.user.groups';
} }
$this->loadRelations($flags, $include); $flags = Flag::whereVisibleTo($actor)
->latest('flags.created_at')
->groupBy('post_id')
->limit($limit + 1)
->offset($offset)
->get();
$this->loadRelations($flags, $include, $request);
$flags = $flags->all();
$areMoreResults = false;
if (count($flags) > $limit) {
array_pop($flags);
$areMoreResults = true;
}
$this->addPaginationData(
$document,
$request,
$this->url->to('api')->route('flags.index'),
$areMoreResults ? null : 0
);
return $flags; return $flags;
} }