refactor pagination

This commit is contained in:
Pig Fang 2020-03-20 19:03:10 +08:00
parent d603f48bad
commit 4c52f82393
6 changed files with 61 additions and 125 deletions

View File

@ -43,42 +43,23 @@ class ClosetController extends Controller
public function getClosetData(Request $request)
{
$category = $request->input('category', 'skin');
$page = abs($request->input('page', 1));
$perPage = (int) $request->input('perPage', 6);
$q = $request->input('q', null);
$search = $request->input('q');
$perPage = $perPage > 0 ? $perPage : 6;
$user = auth()->user();
$closet = $user->closet();
$query = auth()->user()->closet();
if ($category == 'cape') {
$closet = $closet->where('type', 'cape');
$query = $query->where('type', 'cape');
} else {
$closet = $closet->where(function ($query) {
$query = $query->where(function ($query) {
return $query->where('type', 'steve')->orWhere('type', 'alex');
});
}
if ($q) {
$closet = $closet->where('item_name', 'like', "%$q%");
if ($search) {
$query = $query->where('item_name', 'like', "%$search%");
}
$total = $closet->count();
$closet->offset(($page - 1) * $perPage)->limit($perPage);
$totalPages = ceil($total / $perPage);
$items = $closet->get()->map(function ($t) {
$t->name = $t->pivot->item_name;
return $t;
});
return json('', 0, [
'category' => $category,
'items' => $items,
'total_pages' => $totalPages,
]);
return $query->paginate(6);
}
public function add(Request $request)

View File

@ -174,6 +174,7 @@ return [
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Mail\MailServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,

View File

@ -22,3 +22,12 @@ export type TextureType = 'steve' | 'alex' | 'cape'
export type ClosetItem = Texture & {
pivot: { user_uid: number; texture_tid: number; item_name: string }
}
export type Paginator<T> = {
data: T[]
current_page: number
last_page: number
from: number
to: number
total: number
}

View File

@ -4,7 +4,7 @@ import debounce from 'lodash.debounce'
import { t } from '@/scripts/i18n'
import * as fetch from '@/scripts/net'
import { showModal, toast } from '@/scripts/notify'
import { ClosetItem as Item, Texture } from '@/scripts/types'
import { ClosetItem as Item, Texture, Paginator } from '@/scripts/types'
import Loading from '@/components/Loading'
import Pagination from '@/components/Pagination'
import ClosetItem from './ClosetItem'
@ -37,23 +37,13 @@ const Closet: React.FC = () => {
useEffect(() => {
const getItems = async () => {
setIsLoading(true)
const {
data: { items, category: c, total_pages: totalPages },
} = await fetch.get<
fetch.ResponseBody<{
items: Item[]
category: Category
total_pages: number
}>
>('/user/closet/list', {
category,
q: query,
page,
})
const { data, last_page } = await fetch.get<Paginator<Item>>(
'/user/closet/list',
{ category, q: query, page },
)
setItems(items)
setCategory(c)
setTotalPages(totalPages)
setItems(data)
setTotalPages(last_page)
setIsLoading(false)
}
getItems()

View File

@ -3,7 +3,7 @@ import { render, fireEvent, wait } from '@testing-library/react'
import $ from 'jquery'
import { t } from '@/scripts/i18n'
import * as fetch from '@/scripts/net'
import { ClosetItem, Player } from '@/scripts/types'
import { ClosetItem, Player, Paginator } from '@/scripts/types'
import Closet from '@/views/user/Closet'
jest.mock('@/scripts/net')
@ -49,6 +49,17 @@ const fixturePlayer: Readonly<Player> = Object.freeze<Player>({
tid_cape: 2,
})
function createPaginator(data: ClosetItem[]): Paginator<ClosetItem> {
return {
data,
total: data.length,
from: 1,
to: data.length,
current_page: 1,
last_page: 1,
}
}
beforeEach(() => {
const container = document.createElement('div')
container.id = 'previewer'
@ -60,17 +71,13 @@ afterEach(() => {
})
test('loading indicator', () => {
fetch.get.mockResolvedValue({
data: { items: [], category: 'skin', total_pages: 1 },
})
fetch.get.mockResolvedValue(createPaginator([]))
const { queryByTitle } = render(<Closet />)
expect(queryByTitle('Loading...')).toBeInTheDocument()
})
test('empty closet', async () => {
fetch.get.mockResolvedValue({
data: { items: [], category: 'skin', total_pages: 0 },
})
fetch.get.mockResolvedValue(createPaginator([]))
const { queryByText } = render(<Closet />)
await wait()
@ -79,15 +86,9 @@ test('empty closet', async () => {
test('categories', async () => {
fetch.get
.mockResolvedValueOnce({
data: { items: [fixtureSkin], category: 'skin', total_pages: 1 },
})
.mockResolvedValueOnce({
data: { items: [fixtureCape], category: 'cape', total_pages: 1 },
})
.mockResolvedValueOnce({
data: { items: [fixtureSkin], category: 'skin', total_pages: 1 },
})
.mockResolvedValueOnce(createPaginator([fixtureSkin]))
.mockResolvedValueOnce(createPaginator([fixtureCape]))
.mockResolvedValueOnce(createPaginator([fixtureSkin]))
const { getByText, queryByText } = render(<Closet />)
await wait()
@ -107,12 +108,8 @@ test('categories', async () => {
test('search textures', async () => {
fetch.get
.mockResolvedValueOnce({
data: { items: [fixtureSkin], category: 'skin', total_pages: 1 },
})
.mockResolvedValueOnce({
data: { items: [], category: 'skin', total_pages: 0 },
})
.mockResolvedValueOnce(createPaginator([fixtureSkin]))
.mockResolvedValueOnce(createPaginator([]))
const { getByPlaceholderText, queryByText } = render(<Closet />)
await wait()
@ -127,12 +124,8 @@ test('search textures', async () => {
test('switch page', async () => {
fetch.get
.mockResolvedValueOnce({
data: { items: [], category: 'skin', total_pages: 2 },
})
.mockResolvedValueOnce({
data: { items: [fixtureSkin], category: 'skin', total_pages: 2 },
})
.mockResolvedValueOnce({ ...createPaginator([]), last_page: 2 })
.mockResolvedValueOnce({ ...createPaginator([fixtureSkin]), last_page: 2 })
const { getByText, queryByText } = render(<Closet />)
await wait()
@ -144,9 +137,7 @@ test('switch page', async () => {
describe('rename item', () => {
beforeEach(() => {
fetch.get.mockResolvedValue({
data: { items: [fixtureSkin], category: 'skin', total_pages: 1 },
})
fetch.get.mockResolvedValue(createPaginator([fixtureSkin]))
})
it('succeeded', async () => {
@ -234,9 +225,7 @@ describe('rename item', () => {
describe('remove item', () => {
beforeEach(() => {
fetch.get.mockResolvedValue({
data: { items: [fixtureSkin], category: 'skin', total_pages: 1 },
})
fetch.get.mockResolvedValue(createPaginator([fixtureSkin]))
})
it('succeeded', async () => {
@ -287,12 +276,8 @@ describe('remove item', () => {
describe('select textures', () => {
beforeEach(() => {
fetch.get
.mockResolvedValueOnce({
data: { items: [fixtureSkin], category: 'skin', total_pages: 1 },
})
.mockResolvedValueOnce({
data: { items: [fixtureCape], category: 'cape', total_pages: 1 },
})
.mockResolvedValueOnce(createPaginator([fixtureSkin]))
.mockResolvedValueOnce(createPaginator([fixtureCape]))
})
it('select skin', async () => {
@ -336,9 +321,7 @@ describe('select textures', () => {
describe('set avatar', () => {
beforeEach(() => {
fetch.get.mockResolvedValue({
data: { items: [fixtureSkin], category: 'skin', total_pages: 1 },
})
fetch.get.mockResolvedValue(createPaginator([fixtureSkin]))
const img = document.createElement('img')
img.alt = 'User Image'
@ -398,9 +381,7 @@ describe('set avatar', () => {
describe('apply textures to player', () => {
it('selected nothing', async () => {
fetch.get.mockResolvedValue({
data: { items: [], category: 'skin', total_pages: 1 },
})
fetch.get.mockResolvedValue(createPaginator([]))
const { getByText, getByRole, queryByText } = render(<Closet />)
await wait()
@ -413,9 +394,7 @@ describe('apply textures to player', () => {
it('search players', async () => {
fetch.get
.mockResolvedValueOnce({
data: { items: [fixtureSkin], category: 'skin', total_pages: 1 },
})
.mockResolvedValueOnce(createPaginator([fixtureSkin]))
.mockResolvedValueOnce({ data: [fixturePlayer] })
const {
@ -440,9 +419,7 @@ describe('apply textures to player', () => {
it('succeeded', async () => {
fetch.get
.mockResolvedValueOnce({
data: { items: [fixtureSkin], category: 'skin', total_pages: 1 },
})
.mockResolvedValueOnce(createPaginator([fixtureSkin]))
.mockResolvedValueOnce({ data: [fixturePlayer] })
fetch.post.mockResolvedValue({ code: 0, message: 'success' })
@ -470,9 +447,7 @@ describe('apply textures to player', () => {
it('failed', async () => {
fetch.get
.mockResolvedValueOnce({
data: { items: [fixtureSkin], category: 'skin', total_pages: 1 },
})
.mockResolvedValueOnce(createPaginator([fixtureSkin]))
.mockResolvedValueOnce({ data: [fixturePlayer] })
fetch.post.mockResolvedValue({ code: 1, message: 'failed' })
@ -500,9 +475,7 @@ describe('apply textures to player', () => {
it('close dialog', async () => {
fetch.get
.mockResolvedValueOnce({
data: { items: [fixtureSkin], category: 'skin', total_pages: 1 },
})
.mockResolvedValueOnce(createPaginator([fixtureSkin]))
.mockResolvedValueOnce({ data: [fixturePlayer] })
const { getByText, getByAltText } = render(<Closet />)

View File

@ -40,46 +40,28 @@ class ClosetControllerTest extends TestCase
// Use default query parameters
$this->getJson('/user/closet/list')
->assertJsonStructure([
'data' => [
'category',
'total_pages',
'items' => [['tid', 'name', 'type']],
],
'data' => [['tid', 'name', 'type']],
]);
// Responsive
$result = $this->json('get', '/user/closet/list?perPage=0')->json()['data'];
$this->assertCount(6, $result['items']);
$result = $this->json('get', '/user/closet/list?perPage=8')->json()['data'];
$this->assertCount(8, $result['items']);
$result = $this->json('get', '/user/closet/list?perPage=8&page=2')->json()['data'];
$this->assertCount(2, $result['items']);
// Get capes
$cape = factory(Texture::class)->states('cape')->create();
$this->user->closet()->attach($cape->tid, ['item_name' => 'custom_name']);
$this->getJson('/user/closet/list?category=cape')
->assertJson(['data' => [
'category' => 'cape',
'total_pages' => 1,
'items' => [[
->assertJson(['data' => [[
'tid' => $cape->tid,
'name' => 'custom_name',
'type' => 'cape',
]],
'pivot' => ['item_name' => 'custom_name'],
],
]]);
// Search by keyword
$random = $textures->random();
$this->getJson('/user/closet/list?q='.$random->name)
->assertJson(['data' => [
'category' => 'skin',
'total_pages' => 1,
'items' => [[
->assertJson(['data' => [[
'tid' => $random->tid,
'name' => $random->name,
'type' => $random->type,
]],
],
]]);
}