-
Notifications
You must be signed in to change notification settings - Fork 603
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
Team page #790
Team page #790
Changes from all commits
850f693
1a9d58a
2a33f2b
5b4ca02
d0e0282
f2dd45a
60ac320
4a72903
e2c1a3c
fe02763
7b0661e
e1794d3
500f9af
ca637bc
6da41f9
f8e595a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import Ember from 'ember'; | ||
import PaginationMixin from '../mixins/pagination'; | ||
|
||
const { computed } = Ember; | ||
|
||
export default Ember.Controller.extend(PaginationMixin, { | ||
queryParams: ['page', 'per_page', 'sort'], | ||
page: '1', | ||
per_page: 10, | ||
sort: 'alpha', | ||
|
||
totalItems: computed.readOnly('model.crates.meta.total'), | ||
|
||
currentSortBy: computed('sort', function() { | ||
return (this.get('sort') === 'downloads') ? 'Downloads' : 'Alphabetical'; | ||
}), | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import DS from 'ember-data'; | ||
import Ember from 'ember'; | ||
|
||
export default DS.Model.extend({ | ||
email: DS.attr('string'), | ||
name: DS.attr('string'), | ||
login: DS.attr('string'), | ||
api_token: DS.attr('string'), | ||
avatar: DS.attr('string'), | ||
url: DS.attr('string'), | ||
kind: DS.attr('string'), | ||
org_name: Ember.computed('login', function() { | ||
let login = this.get('login'); | ||
let login_split = login.split(':'); | ||
return login_split[1]; | ||
}) | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import Ember from 'ember'; | ||
|
||
export default Ember.Route.extend({ | ||
queryParams: { | ||
page: { refreshedModel: true }, | ||
sort: { refreshedModel: true }, | ||
}, | ||
data: {}, | ||
|
||
setupController(controller, model) { | ||
this._super(controller, model); | ||
|
||
controller.set('fetchingFeed', true); | ||
controller.set('crates', this.get('data.crates')); | ||
}, | ||
|
||
model(params) { | ||
const { team_id } = params; | ||
return this.store.find('team', team_id).then( | ||
(team) => { | ||
params.team_id = team.get('id'); | ||
return Ember.RSVP.hash({ | ||
crates: this.store.query('crate', params), | ||
team | ||
}); | ||
}, | ||
(e) => { | ||
if (e.errors.any(e => e.detail === 'Not Found')) { | ||
this | ||
.controllerFor('application') | ||
.set('nextFlashError', `User '${params.team_id}' does not exist`); | ||
return this.replaceWith('index'); | ||
} | ||
} | ||
); | ||
}, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
<div id='crates-heading'> | ||
<div class="wide"> | ||
<div class='info'> | ||
{{user-avatar user=model.team size='medium'}} | ||
<div class='team-info'> | ||
<h1> | ||
{{ model.team.org_name }} | ||
</h1> | ||
<h2> | ||
{{ model.team.name }} | ||
</h2> | ||
</div> | ||
{{#user-link user=model.team}} | ||
<img alt="GitHub profile" title="GitHub profile" src="/assets/GitHub-Mark-32px.png"/> | ||
{{/user-link}} | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div id='user-profile'> | ||
<div class='info'> | ||
{{! TODO: reduce duplication with templates/crates.hbs }} | ||
|
||
<div id='results'> | ||
<div class='nav'> | ||
<span class='amt small'> | ||
Displaying | ||
<span class='cur'>{{ currentPageStart }}-{{ currentPageEnd }}</span> | ||
of <span class='total'>{{ totalItems }}</span> total results | ||
</span> | ||
</div> | ||
|
||
<div class='sort'> | ||
<span class='small'>Sort by</span> | ||
{{#rl-dropdown-container class="dropdown-container"}} | ||
{{#rl-dropdown-toggle tagName="a" class="dropdown"}} | ||
<img class="sort" src="/assets/sort.png"/> | ||
{{ currentSortBy }} | ||
<span class='arrow'></span> | ||
{{/rl-dropdown-toggle}} | ||
|
||
{{#rl-dropdown tagName="ul" class="dropdown" closeOnChildClick="a:link"}} | ||
<li> | ||
{{#link-to (query-params sort="alpha")}} | ||
Alphabetical | ||
{{/link-to}} | ||
</li> | ||
<li> | ||
{{#link-to (query-params sort="downloads")}} | ||
Downloads | ||
{{/link-to}} | ||
</li> | ||
{{/rl-dropdown}} | ||
{{/rl-dropdown-container}} | ||
</div> | ||
</div> | ||
|
||
<div id='crates' class='white-rows'> | ||
{{#each model.crates as |crate|}} | ||
{{crate-row crate=crate}} | ||
{{/each}} | ||
</div> | ||
|
||
<div class='pagination'> | ||
{{#link-to (query-params page=prevPage) class="prev" rel="prev" title="previous page"}} | ||
<img class="left-pag" src="/assets/left-pag.png"/> | ||
{{/link-to}} | ||
{{#each pages as |page|}} | ||
{{#link-to (query-params page=page)}}{{ page }}{{/link-to}} | ||
{{/each}} | ||
{{#link-to (query-params page=nextPage) class="next" rel="next" title="next page"}} | ||
<img class="right-pag" src="/assets/right-pag.png"/> | ||
{{/link-to}} | ||
</div> | ||
</div> | ||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// jscs:disable validateQuoteMarks | ||
export default { | ||
"teams": [ | ||
{ | ||
"avatar": "https://avatars.githubusercontent.com/u/565790?v=3", | ||
"email": "someone@example.com", | ||
"id": 2, | ||
"kind": "team", | ||
"login": "github:org:thehydroimpulse", | ||
"name": "Team Daniel Fagnan", | ||
"url": "https://github.com/thehydroimpulse" | ||
}, | ||
{ | ||
"avatar": "https://avatars.githubusercontent.com/u/9447137?v=3", | ||
"email": null, | ||
"id": 303, | ||
"kind": "team", | ||
"login": "github:org:blabaere", | ||
"name": "Team Benoît Labaere", | ||
"url": "https://github.com/blabaere" | ||
} | ||
] | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// jscs:disable validateQuoteMarks | ||
export default { | ||
"team": { | ||
"avatar": "https://avatars.githubusercontent.com/u/565790?v=3", | ||
"id": 1, | ||
"login": "github:org_test:thehydroimpulseteam", | ||
"name": "thehydroimpulseteam", | ||
"url": "https://github.com/thehydroimpulse", | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// jscs:disable validateQuoteMarks | ||
export default { | ||
"user": { | ||
"avatar": "https://avatars.githubusercontent.com/u/565790?v=3", | ||
"email": "someone@example.com", | ||
"id": 1, | ||
"login": "thehydroimpulse", | ||
"name": "Daniel Fagnan", | ||
"url": "https://github.com/thehydroimpulse" | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -114,6 +114,8 @@ pub struct CrateLinks { | |
pub version_downloads: String, | ||
pub versions: Option<String>, | ||
pub owners: Option<String>, | ||
pub owner_team: Option<String>, | ||
pub owner_user: Option<String>, | ||
pub reverse_dependencies: String, | ||
} | ||
|
||
|
@@ -535,6 +537,8 @@ impl Crate { | |
version_downloads: format!("/api/v1/crates/{}/downloads", name), | ||
versions: versions_link, | ||
owners: Some(format!("/api/v1/crates/{}/owners", name)), | ||
owner_team: Some(format!("/api/v1/crates/{}/owner_team", name)), | ||
owner_user: Some(format!("/api/v1/crates/{}/owner_user", name)), | ||
reverse_dependencies: format!("/api/v1/crates/{}/reverse_dependencies", name), | ||
}, | ||
} | ||
|
@@ -864,6 +868,15 @@ pub fn index(req: &mut Request) -> CargoResult<Response> { | |
.filter(crate_owners::owner_kind.eq(OwnerKind::User as i32)), | ||
), | ||
); | ||
} else if let Some(team_id) = params.get("team_id").and_then(|s| s.parse::<i32>().ok()) { | ||
query = query.filter( | ||
crates::id.eq_any( | ||
crate_owners::table | ||
.select(crate_owners::crate_id) | ||
.filter(crate_owners::owner_id.eq(team_id)) | ||
.filter(crate_owners::owner_kind.eq(OwnerKind::Team as i32)), | ||
), | ||
); | ||
} else if params.get("following").is_some() { | ||
query = query.filter(crates::id.eq_any( | ||
follows::table.select(follows::crate_id).filter( | ||
|
@@ -1423,6 +1436,40 @@ pub fn owners(req: &mut Request) -> CargoResult<Response> { | |
Ok(req.json(&R { users: owners })) | ||
} | ||
|
||
/// Handles the `GET /crates/:crate_id/owner_team` route. | ||
pub fn owner_team(req: &mut Request) -> CargoResult<Response> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think about making this a single route and adding a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We tried, but the structure of the response has I wouldn't be opposed to refactoring this in the future, it would just take me more time in ember than we'd like to spend right now :P |
||
let crate_name = &req.params()["crate_id"]; | ||
let conn = req.db_conn()?; | ||
let krate = Crate::by_name(crate_name).first::<Crate>(&*conn)?; | ||
let owners = Team::owning(&krate, &conn)? | ||
.into_iter() | ||
.map(Owner::encodable) | ||
.collect(); | ||
|
||
#[derive(RustcEncodable)] | ||
struct R { | ||
teams: Vec<EncodableOwner>, | ||
} | ||
Ok(req.json(&R { teams: owners })) | ||
} | ||
|
||
/// Handles the `GET /crates/:crate_id/owner_user` route. | ||
pub fn owner_user(req: &mut Request) -> CargoResult<Response> { | ||
let crate_name = &req.params()["crate_id"]; | ||
let conn = req.db_conn()?; | ||
let krate = Crate::by_name(crate_name).first::<Crate>(&*conn)?; | ||
let owners = User::owning(&krate, &conn)? | ||
.into_iter() | ||
.map(Owner::encodable) | ||
.collect(); | ||
|
||
#[derive(RustcEncodable)] | ||
struct R { | ||
users: Vec<EncodableOwner>, | ||
} | ||
Ok(req.json(&R { users: owners })) | ||
} | ||
|
||
/// Handles the `PUT /crates/:crate_id/owners` route. | ||
pub fn add_owners(req: &mut Request) -> CargoResult<Response> { | ||
modify_owners(req, true) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason to put this on
index
instead of adding a new route?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency with how getting a user's crates works, mostly. If we're going to pull this out into its own route, then I think users should be pulled out too, which imo should be a separate PR. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that users should be pulled out as well, but I don't think that should block this PR being a separate route, if it's not significantly more work for this PR.