Skip to content

Commit 616a41d

Browse files
committedSep 18, 2017
Merge #1057
1057: Frontend page for listing pending owner invites r=carols10cents This implements the last checkbox in Deployable chunk 1: Listing invitations of issue #924. The frontend route created, `/me/pending-invites`, calls the API route previously created and will list all current invitations associated with the user's account. Although the buttons for accepting/denying invitations have been added, they do not currently do anything as that chunk has not been implemented yet (Deployable chunks 2 & 3). It was easier to add them now for styling purposes. The page isn't linked to anywhere but can be accessed via `crates.io/me/pending-invites` if logged in.
2 parents 78e23d7 + 563e039 commit 616a41d

File tree

9 files changed

+103
-10
lines changed

9 files changed

+103
-10
lines changed
 

‎app/adapters/crate-owner-invite.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import DS from 'ember-data';
2+
3+
export default DS.RESTAdapter.extend({
4+
namespace: 'api/v1/me',
5+
pathForType() {
6+
return 'crate_owner_invitations';
7+
}
8+
});

‎app/models/crate-owner-invite.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import DS from 'ember-data';
2+
3+
export default DS.Model.extend({
4+
invited_by_username: DS.attr('string'),
5+
crate_name: DS.attr('string'),
6+
crate_id: DS.attr('number'),
7+
created_at: DS.attr('date')
8+
});

‎app/router.js

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Router.map(function() {
2828
this.route('me', function() {
2929
this.route('crates');
3030
this.route('following');
31+
this.route('pending-invites');
3132
});
3233
this.route('user', { path: '/users/:user_id' });
3334
this.route('install');

‎app/routes/me/pending-invites.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Ember from 'ember';
2+
3+
import AuthenticatedRoute from '../../mixins/authenticated-route';
4+
5+
export default Ember.Route.extend(AuthenticatedRoute, {
6+
model() {
7+
return this.get('store').findAll('crate-owner-invite');
8+
}
9+
});

‎app/serializers/crate-owner-invite.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import DS from 'ember-data';
2+
3+
export default DS.RESTSerializer.extend({
4+
primaryKey: 'crate_id',
5+
modelNameFromPayloadKey() {
6+
return 'crate-owner-invite';
7+
}
8+
});

‎app/styles/me.scss

+18
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@
138138
#my-crates, #my-following { margin: 0; }
139139
}
140140
}
141+
141142
#my-feed {
142143
@include flex-grow(5);
143144
h2 { font-size: 105%; }
@@ -170,6 +171,23 @@
170171
}
171172
}
172173

174+
#my-invites {
175+
@include flex-grow(5);
176+
177+
.row {
178+
@include display-flex;
179+
@include flex-direction(column);
180+
}
181+
182+
.info {
183+
@include display-flex;
184+
@include align-items(baseline);
185+
@include justify-content(space-between);
186+
187+
.date { @include flex-grow(2); text-align: right; }
188+
}
189+
}
190+
173191
#stats {
174192
margin-left: auto;
175193
padding: 10px;

‎app/templates/me/pending-invites.hbs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{{ title 'Pending Invites' }}
2+
3+
<div id='crates-heading'>
4+
{{svg-jar "gear"}}
5+
<h1>Pending Owner Invites</h1>
6+
</div>
7+
8+
<div id='my-invites'>
9+
<div class='white-rows'>
10+
{{#each model as |invite|}}
11+
<div class='row'>
12+
<div class='info'>
13+
<div class='name'>
14+
<h3>
15+
{{#link-to 'crate' invite.crate_name}}
16+
{{invite.crate_name}}
17+
{{/link-to}}
18+
</h3>
19+
</div>
20+
<div class='invite'>
21+
<p>Invited by:
22+
{{#link-to 'user' invite.invited_by_username}}
23+
{{invite.invited_by_username}}
24+
{{/link-to}}
25+
</p>
26+
</div>
27+
<div class='sent'>
28+
<span class='small'>{{moment-from-now invite.created_at}}</span>
29+
</div>
30+
<div class='actions'>
31+
<button class='small yellow-button'>Accept</button>
32+
<button class='small yellow-button'>Deny</button>
33+
</div>
34+
</div>
35+
</div>
36+
{{/each}}
37+
</div>
38+
</div>

‎src/crate_owner_invitation.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ pub fn list(req: &mut Request) -> CargoResult<Response> {
6767
let conn = &*req.db_conn()?;
6868
let user_id = req.user()?.id;
6969

70-
let invitations = crate_owner_invitations::table
70+
let crate_owner_invitations = crate_owner_invitations::table
7171
.filter(crate_owner_invitations::invited_user_id.eq(user_id))
7272
.load::<CrateOwnerInvitation>(&*conn)?
7373
.into_iter()
@@ -76,7 +76,7 @@ pub fn list(req: &mut Request) -> CargoResult<Response> {
7676

7777
#[derive(Serialize)]
7878
struct R {
79-
invitations: Vec<EncodableCrateOwnerInvitation>,
79+
crate_owner_invitations: Vec<EncodableCrateOwnerInvitation>,
8080
}
81-
Ok(req.json(&R { invitations }))
81+
Ok(req.json(&R { crate_owner_invitations }))
8282
}

‎src/tests/owners.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ fn check_ownership_one_crate() {
301301
fn invitations_are_empty_by_default() {
302302
#[derive(Deserialize)]
303303
struct R {
304-
invitations: Vec<EncodableCrateOwnerInvitation>,
304+
crate_owner_invitations: Vec<EncodableCrateOwnerInvitation>,
305305
}
306306

307307
let (_b, app, middle) = ::app();
@@ -322,14 +322,14 @@ fn invitations_are_empty_by_default() {
322322
let mut response = ok_resp!(middle.call(&mut req));
323323
let json: R = ::json(&mut response);
324324

325-
assert_eq!(json.invitations.len(), 0);
325+
assert_eq!(json.crate_owner_invitations.len(), 0);
326326
}
327327

328328
#[test]
329329
fn invitations_list() {
330330
#[derive(Deserialize)]
331331
struct R {
332-
invitations: Vec<EncodableCrateOwnerInvitation>,
332+
crate_owner_invitations: Vec<EncodableCrateOwnerInvitation>,
333333
}
334334

335335
let (_b, app, middle) = ::app();
@@ -362,8 +362,11 @@ fn invitations_list() {
362362
let mut response = ok_resp!(middle.call(&mut req));
363363
let json: R = ::json(&mut response);
364364

365-
assert_eq!(json.invitations.len(), 1);
366-
assert_eq!(json.invitations[0].invited_by_username, "inviting_user");
367-
assert_eq!(json.invitations[0].crate_name, "invited_crate");
368-
assert_eq!(json.invitations[0].crate_id, krate.id);
365+
assert_eq!(json.crate_owner_invitations.len(), 1);
366+
assert_eq!(
367+
json.crate_owner_invitations[0].invited_by_username,
368+
"inviting_user"
369+
);
370+
assert_eq!(json.crate_owner_invitations[0].crate_name, "invited_crate");
371+
assert_eq!(json.crate_owner_invitations[0].crate_id, krate.id);
369372
}

0 commit comments

Comments
 (0)
Please sign in to comment.