Skip to content

Commit 10851f3

Browse files
committed
Merge remote-tracking branch 'origin' into fix-owner-checks
2 parents 7ebcfe7 + e1baf64 commit 10851f3

File tree

9 files changed

+76
-10
lines changed

9 files changed

+76
-10
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ futures = "0.1"
8282
tokio = "0.1"
8383
hyper = "0.12"
8484
ctrlc = { version = "3.0", features = ["termination"] }
85+
indexmap = "1.0.2"
8586

8687
[dev-dependencies]
8788
conduit-test = "0.8"

app/controllers/crate/version.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
55
import ArrayProxy from '@ember/array/proxy';
66
import { computed, observer } from '@ember/object';
77
import { later } from '@ember/runloop';
8-
import $ from 'jquery';
98
import moment from 'moment';
109

1110
const NUM_VERSIONS = 5;
@@ -185,6 +184,13 @@ export default Controller.extend({
185184
},
186185

187186
report: observer('crate.readme', function() {
188-
setTimeout(() => $(window).trigger('hashchange'));
187+
if (typeof document === 'undefined') {
188+
return;
189+
}
190+
setTimeout(() => {
191+
let e = document.createEvent('CustomEvent');
192+
e.initCustomEvent('hashchange', true, true);
193+
window.dispatchEvent(e);
194+
});
189195
}),
190196
});

app/initializers/hashchange.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import $ from 'jquery';
2-
31
function decodeFragmentValue(hash) {
42
try {
53
return decodeURIComponent(hash.slice(1));
@@ -29,11 +27,18 @@ function hashchange() {
2927
}
3028

3129
export function initialize() {
32-
$(window).on('hashchange', hashchange);
30+
if (typeof window === 'undefined' || typeof window.addEventListener === 'undefined') {
31+
// Don't run this initializer under FastBoot
32+
return;
33+
}
34+
window.addEventListener('hashchange', hashchange);
3335

3436
// If clicking on a link to the same fragment as currently in the address bar,
3537
// hashchange won't be fired, so we need to manually trigger rescroll.
36-
$(document).on('a[href]', 'click', function(event) {
38+
document.addEventListener('click', function(event) {
39+
if (event.target.tagName !== 'A') {
40+
return;
41+
}
3742
if (this.href === location.href && location.hash.length > 1) {
3843
setTimeout(function() {
3944
if (!event.defaultPrevented) {

src/controllers.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ mod prelude {
1414
use std::collections::HashMap;
1515
use std::io;
1616

17+
use indexmap::IndexMap;
1718
use serde::Serialize;
1819
use url;
1920

2021
pub trait RequestUtils {
2122
fn redirect(&self, url: String) -> Response;
2223

2324
fn json<T: Serialize>(&self, t: &T) -> Response;
24-
fn query(&self) -> HashMap<String, String>;
25+
fn query(&self) -> IndexMap<String, String>;
2526
fn wants_json(&self) -> bool;
2627
fn pagination(&self, default: usize, max: usize) -> CargoResult<(i64, i64)>;
2728
}
@@ -31,7 +32,7 @@ mod prelude {
3132
crate::util::json_response(t)
3233
}
3334

34-
fn query(&self) -> HashMap<String, String> {
35+
fn query(&self) -> IndexMap<String, String> {
3536
url::form_urlencoded::parse(self.query_string().unwrap_or("").as_bytes())
3637
.map(|(a, b)| (a.into_owned(), b.into_owned()))
3738
.collect()

src/controllers/krate/search.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,27 @@ pub fn search(req: &mut dyn Request) -> CargoResult<Response> {
235235
)
236236
.collect();
237237

238+
let mut next_page = None;
239+
let mut prev_page = None;
240+
let page_num = params.get("page").map(|s| s.parse()).unwrap_or(Ok(1))?;
241+
242+
let url_for_page = |num: i64| {
243+
let mut params = req.query();
244+
params.insert("page".into(), num.to_string());
245+
let query_string = url::form_urlencoded::Serializer::new(String::new())
246+
.clear()
247+
.extend_pairs(params)
248+
.finish();
249+
Some(format!("?{}", query_string))
250+
};
251+
252+
if offset + limit < total {
253+
next_page = url_for_page(page_num + 1);
254+
}
255+
if page_num != 1 {
256+
prev_page = url_for_page(page_num - 1);
257+
};
258+
238259
#[derive(Serialize)]
239260
struct R {
240261
crates: Vec<EncodableCrate>,
@@ -243,10 +264,16 @@ pub fn search(req: &mut dyn Request) -> CargoResult<Response> {
243264
#[derive(Serialize)]
244265
struct Meta {
245266
total: i64,
267+
next_page: Option<String>,
268+
prev_page: Option<String>,
246269
}
247270

248271
Ok(req.json(&R {
249272
crates,
250-
meta: Meta { total },
273+
meta: Meta {
274+
total,
275+
next_page,
276+
prev_page,
277+
},
251278
}))
252279
}

src/render.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ fn markdown_to_html(text: &str, base_url: Option<&str>) -> String {
192192

193193
/// Any readme with a filename ending in one of these extensions will be rendered as Markdown.
194194
/// Note we also render a readme as Markdown if _no_ extension is on the filename.
195-
static MARKDOWN_EXTENSIONS: [&'static str; 7] = [
195+
static MARKDOWN_EXTENSIONS: [&str; 7] = [
196196
".md",
197197
".markdown",
198198
".mdown",

src/tests/all.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ pub struct CrateList {
7474
#[derive(Deserialize)]
7575
struct CrateMeta {
7676
total: i32,
77+
next_page: Option<String>,
78+
prev_page: Option<String>,
7779
}
7880
#[derive(Deserialize)]
7981
pub struct CrateResponse {

src/tests/krate.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2215,3 +2215,26 @@ fn publish_rate_limit_doesnt_affect_existing_crates() {
22152215
token.enqueue_publish(new_version).good();
22162216
app.run_pending_background_jobs();
22172217
}
2218+
2219+
#[test]
2220+
fn pagination_links_included_if_applicable() {
2221+
let (app, anon, user) = TestApp::init().with_user();
2222+
let user = user.as_model();
2223+
2224+
app.db(|conn| {
2225+
CrateBuilder::new("pagination_links_1", user.id).expect_build(conn);
2226+
CrateBuilder::new("pagination_links_2", user.id).expect_build(conn);
2227+
CrateBuilder::new("pagination_links_3", user.id).expect_build(conn);
2228+
});
2229+
2230+
let page1 = anon.search("per_page=1");
2231+
let page2 = anon.search("page=2&per_page=1");
2232+
let page3 = anon.search("page=3&per_page=1");
2233+
2234+
assert_eq!(Some("?per_page=1&page=2".to_string()), page1.meta.next_page);
2235+
assert_eq!(None, page1.meta.prev_page);
2236+
assert_eq!(Some("?page=3&per_page=1".to_string()), page2.meta.next_page);
2237+
assert_eq!(Some("?page=1&per_page=1".to_string()), page2.meta.prev_page);
2238+
assert_eq!(None, page3.meta.next_page);
2239+
assert_eq!(Some("?page=2&per_page=1".to_string()), page3.meta.prev_page);
2240+
}

0 commit comments

Comments
 (0)