diff --git a/app/components/seek-pagination.hbs b/app/components/seek-pagination.hbs new file mode 100644 index 00000000000..4ae6e0a1650 --- /dev/null +++ b/app/components/seek-pagination.hbs @@ -0,0 +1,7 @@ +{{#if @pagination.nextPage}} + +{{/if}} diff --git a/app/components/seek-pagination.module.css b/app/components/seek-pagination.module.css new file mode 100644 index 00000000000..76cebb05f16 --- /dev/null +++ b/app/components/seek-pagination.module.css @@ -0,0 +1,5 @@ +.seek-pagination { + text-align: center; + font-size: 90%; + margin-bottom: 20px; +} diff --git a/app/controllers/me/pending-invites.js b/app/controllers/me/pending-invites.js new file mode 100644 index 00000000000..19efd238c7d --- /dev/null +++ b/app/controllers/me/pending-invites.js @@ -0,0 +1,10 @@ +import Controller from '@ember/controller'; + +import { reads } from 'macro-decorators'; + +import { pagination } from '../../utils/seek-pagination'; + +export default class PendingInvitesController extends Controller { + @reads('model.meta.next_page') nextPage; + @pagination() pagination; +} diff --git a/app/routes/me/pending-invites.js b/app/routes/me/pending-invites.js index af51d27432c..1cb57350c0d 100644 --- a/app/routes/me/pending-invites.js +++ b/app/routes/me/pending-invites.js @@ -5,7 +5,11 @@ import AuthenticatedRoute from '../-authenticated-route'; export default class PendingInvitesRoute extends AuthenticatedRoute { @service store; - model() { - return this.store.findAll('crate-owner-invite'); + queryParams = { + seek: { refreshModel: true }, + }; + + model(params) { + return this.store.query('crate-owner-invite', params); } } diff --git a/app/styles/me/pending-invites.module.css b/app/styles/me/pending-invites.module.css index 19813a7a861..9c5d3d4379c 100644 --- a/app/styles/me/pending-invites.module.css +++ b/app/styles/me/pending-invites.module.css @@ -1,5 +1,6 @@ .list { background: white; + margin-bottom: 2rem; } .row { diff --git a/app/templates/me/pending-invites.hbs b/app/templates/me/pending-invites.hbs index 7fcd2d84d50..b1c9c762841 100644 --- a/app/templates/me/pending-invites.hbs +++ b/app/templates/me/pending-invites.hbs @@ -9,3 +9,5 @@

You don't seem to have any pending invitations.

{{/each}} + + diff --git a/app/utils/seek-pagination.js b/app/utils/seek-pagination.js new file mode 100644 index 00000000000..23423dcccba --- /dev/null +++ b/app/utils/seek-pagination.js @@ -0,0 +1,12 @@ +import macro from 'macro-decorators'; + +export function pagination() { + return macro(function () { + const { nextPage } = this; + const nextPageParams = new URLSearchParams(nextPage); + + return { + nextPage: nextPageParams.get('seek'), + }; + }); +} diff --git a/mirage/route-handlers/me.js b/mirage/route-handlers/me.js index 2f130022034..3749a8ad551 100644 --- a/mirage/route-handlers/me.js +++ b/mirage/route-handlers/me.js @@ -1,6 +1,7 @@ import { Response } from 'miragejs'; import { getSession } from '../utils/session'; +import { pageParams } from './-utils'; export function register(server) { server.get('/api/v1/me', function (schema) { @@ -98,13 +99,20 @@ export function register(server) { return { ok: true }; }); - server.get('/api/v1/me/crate_owner_invitations', function (schema) { + server.get('/api/v1/me/crate_owner_invitations', function (schema, request) { let { user } = getSession(schema); if (!user) { return new Response(403, {}, { errors: [{ detail: 'must be logged in to perform that action' }] }); } - return schema.crateOwnerInvitations.where({ inviteeId: user.id }); + const { start, end } = pageParams(request); + const invites = schema.crateOwnerInvitations.where({ inviteeId: user.id }); + + let response = this.serialize(invites.slice(start, end)); + + response.meta = { total: invites.length }; + + return response; }); server.put('/api/v1/me/crate_owner_invitations/:crate_id', (schema, request) => { diff --git a/mirage/serializers/crate-owner-invitation.js b/mirage/serializers/crate-owner-invitation.js index 85ec23bef00..3e356b0207f 100644 --- a/mirage/serializers/crate-owner-invitation.js +++ b/mirage/serializers/crate-owner-invitation.js @@ -24,7 +24,9 @@ export default BaseSerializer.extend({ delete hash.id; delete hash.token; - hash.crate_id = Number(hash.crate_id); + // TODO: Check this further, `crate_id` are strings in the current fixtures + // when attempting to parse into number we will get `NaN`s here. + // hash.crate_id = Number(hash.crate_id); let crate = this.schema.crates.find(hash.crate_id); hash.crate_name = crate.name; diff --git a/src/controllers/crate_owner_invitation.rs b/src/controllers/crate_owner_invitation.rs index 35ec3642fc0..b2d7c93a741 100644 --- a/src/controllers/crate_owner_invitation.rs +++ b/src/controllers/crate_owner_invitation.rs @@ -20,7 +20,9 @@ pub fn list(req: &mut dyn RequestExt) -> EndpointResult { let user_id = auth.user_id(); let PrivateListResponse { - invitations, users, .. + invitations, + users, + meta, } = prepare_list(req, auth, ListFilter::InviteeId(user_id))?; // The schema for the private endpoints is converted to the schema used by v1 endpoints. @@ -47,6 +49,7 @@ pub fn list(req: &mut dyn RequestExt) -> EndpointResult { Ok(req.json(&json!({ "crate_owner_invitations": crate_owner_invitations, "users": users, + "meta": meta, }))) } diff --git a/tests/mirage/me/crate-owner-invitations/list-test.js b/tests/mirage/me/crate-owner-invitations/list-test.js index 6e4116819f2..84c8f84c7c8 100644 --- a/tests/mirage/me/crate-owner-invitations/list-test.js +++ b/tests/mirage/me/crate-owner-invitations/list-test.js @@ -15,7 +15,7 @@ module('Mirage | GET /api/v1/me/crate_owner_invitations', function (hooks) { let response = await fetch('/api/v1/me/crate_owner_invitations'); assert.equal(response.status, 200); - assert.deepEqual(await response.json(), { crate_owner_invitations: [] }); + assert.deepEqual(await response.json(), { crate_owner_invitations: [], meta: { total: 0 } }); }); test('returns the list of invitations for the authenticated user', async function (assert) { @@ -47,6 +47,7 @@ module('Mirage | GET /api/v1/me/crate_owner_invitations', function (hooks) { let response = await fetch('/api/v1/me/crate_owner_invitations'); assert.equal(response.status, 200); assert.deepEqual(await response.json(), { + meta: { total: 0 }, crate_owner_invitations: [ { crate_id: Number(nanomsg.id),