From d08921573fa8dd31221b8ca1f5db232fe708e8c5 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Thu, 2 Jul 2020 18:45:16 +0200 Subject: [PATCH 01/26] WIP: semi-working, pre-refactor --- server/service/src/main.rs | 85 +++++++++++++++++++++++------ server/ui/src/components/navbar.tsx | 10 +++- server/ui/src/components/search.tsx | 57 ++++++++++++++++--- server/ui/src/index.tsx | 2 +- server/ui/src/interfaces.ts | 2 + 5 files changed, 131 insertions(+), 25 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 1e36640..39ea3e5 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -15,15 +15,55 @@ use actix_files::NamedFile; use actix_web::{middleware, web, App, HttpResponse, HttpServer}; use failure::Error; use r2d2_sqlite::SqliteConnectionManager; -use rusqlite::params; +use rusqlite::{params, named_params, NO_PARAMS}; use std::env; use std::ops::Deref; use std::cmp; use std::io; +use std::fmt; use serde_json::Value; const DEFAULT_SIZE: usize = 25; +enum SortDir { + Asc, + Desc, +} + +enum Sort { + Key(String), + Dir(SortDir), +} + + + +#[derive(Copy, Clone, Debug, Deserialize)] +enum SortKey { + Name, + Size, + Seeds, + Leeches, + Scraped, +} + +impl fmt::Display for SortKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +#[derive(Copy, Clone, Debug, Deserialize)] +enum SortDir { + Asc, + Desc, +} + +impl fmt::Display for SortDir { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + #[actix_rt::main] async fn main() -> io::Result<()> { println!("Access me at {}", endpoint()); @@ -69,6 +109,8 @@ struct SearchQuery { q: String, page: Option, size: Option, + sort_key: Option, + sort_dir: Option, type_: Option, } @@ -94,6 +136,8 @@ async fn search( struct NewQuery { page: Option, size: Option, + sort_key: Option, + sort_dir: Option, type_: Option, } @@ -126,20 +170,22 @@ fn search_query( } let page = query.page.unwrap_or(1); + let sort_key = query.sort_key.unwrap_or(SortKey::Name); + let sort_dir = query.sort_dir.unwrap_or(SortDir::Desc); let size = cmp::min(100, query.size.unwrap_or(DEFAULT_SIZE)); let type_ = query.type_.as_ref().map_or("torrent", String::deref); let offset = size * (page - 1); - println!( - "query = {}, type = {}, page = {}, size = {}", - q, type_, page, size + dbg!( + "query = {}, type = {}, page = {}, size = {}, sort_key = {:?}, sort_dir = {:?}", + q, type_, page, size, sort_key, sort_dir ); let res = if type_ == "file" { let results = torrent_file_search(conn, q, size, offset)?; serde_json::to_value(&results).unwrap() } else { - let results = torrent_search(conn, q, size, offset)?; + let results = torrent_search(conn, q, sort_key, sort_dir, size, offset)?; serde_json::to_value(&results).unwrap() }; @@ -153,12 +199,14 @@ fn new_query( let page = query.page.unwrap_or(1); let size = cmp::min(100, query.size.unwrap_or(DEFAULT_SIZE)); + let sort_key = query.sort_key.as_ref().unwrap_or(&SortKey::Name); + let sort_dir = query.sort_dir.as_ref().unwrap_or(&SortDir::Desc); let type_ = query.type_.as_ref().map_or("torrent", String::deref); let offset = size * (page - 1); - println!( - "new, type = {}, page = {}, size = {}", - type_, page, size + dbg!( + "new, type = {}, page = {}, size = {}, sort_key = {:?}, sort_dir = {:?}", + type_, page, size, sort_key, sort_dir ); let res = if type_ == "file" { @@ -187,18 +235,23 @@ struct Torrent { fn torrent_search( conn: r2d2::PooledConnection, query: &str, + sort_key: SortKey, + sort_dir: SortDir, size: usize, offset: usize, ) -> Result, Error> { - let stmt_str = "select * from torrents where name like '%' || ?1 || '%' limit ?2, ?3"; + let stmt_str = format!("select * from torrents where name like '%' || :query || '%' order by :sort_key {} limit :offset, :size", sort_dir.to_string().to_lowercase()); let mut stmt = conn.prepare(&stmt_str)?; - let torrent_iter = stmt.query_map( - params![ - query.replace(" ", "%"), - offset.to_string(), - size.to_string(), - ], + let torrent_iter = stmt.query_map_named( + named_params!{ + ":query": query.replace(" ", "%"), + ":sort_key": sort_key.to_string().to_lowercase(), + ":offset": offset.to_string(), + ":size": size.to_string(), + }, |row| { + let size: isize = row.get(2)?; + println!("got size {:?}", size); Ok(Torrent { infohash: row.get(0)?, name: row.get(1)?, @@ -346,7 +399,7 @@ mod tests { let manager = SqliteConnectionManager::file(super::torrents_db_file()); let pool = r2d2::Pool::builder().max_size(15).build(manager).unwrap(); let conn = pool.get().unwrap(); - let results = super::torrent_search(conn, "sherlock", 10, 0); + let results = super::torrent_search(conn, "sherlock", 10, 0, SortKey::Name, SortDir::Desc); assert!(results.unwrap().len() > 2); // println!("Query took {:?} seconds.", end - start); } diff --git a/server/ui/src/components/navbar.tsx b/server/ui/src/components/navbar.tsx index 3ee9d03..8355cbe 100644 --- a/server/ui/src/components/navbar.tsx +++ b/server/ui/src/components/navbar.tsx @@ -13,6 +13,8 @@ export class Navbar extends Component { searchParams: { page: 1, q: "", + sort_key: 'Name', + sort_dir: 'Desc', type_: "torrent" } } @@ -70,13 +72,15 @@ export class Navbar extends Component { search(i: Navbar, event) { event.preventDefault(); - i.context.router.history.push(`/search/${i.state.searchParams.type_}/${i.state.searchParams.q}/${i.state.searchParams.page}`); + i.context.router.history.push(`/search/${i.state.searchParams.type_}/${i.state.searchParams.q}/${i.state.searchParams.page}/${i.state.searchParams.sort_key}/${i.state.searchParams.sort_dir}`); } searchChange(i: Navbar, event) { let searchParams: SearchParams = { q: event.target.value, page: 1, + sort_key: 'Name', + sort_dir: 'Desc', type_: i.state.searchParams.type_ } i.setState({ searchParams: searchParams }); @@ -86,6 +90,8 @@ export class Navbar extends Component { let searchParams: SearchParams = { q: i.state.searchParams.q, page: 1, + sort_key: 'Name', + sort_dir: 'Desc', type_: event.target.value } i.setState({ searchParams: searchParams }); @@ -96,6 +102,8 @@ export class Navbar extends Component { this.state.searchParams = { page: Number(splitPath[4]), q: splitPath[3], + sort_key: splitPath[5], + sort_dir: splitPath[6], type_: splitPath[2] }; } diff --git a/server/ui/src/components/search.tsx b/server/ui/src/components/search.tsx index 821a859..a7406b9 100644 --- a/server/ui/src/components/search.tsx +++ b/server/ui/src/components/search.tsx @@ -11,6 +11,25 @@ interface State { searching: Boolean; } + +function buildSearchURL(sort_key, {state: { searchParams }) { + console.log("got search URL sort_key", sort_key, "searchParams", searchParams, "thing", searchParams); + + //let searchParams = thing.state.searchParams; + + + if (sort_key === searchParams.sort_key) { + const sort_dir = searchParams.sort_dir === "Asc" ? "Desc" : "Asc"; + searchParams.sort_dir = sort_dir; + console.log("no change in sort key from", sort_key, "so switch direction instead.") + } else { + console.log("change sort key from", searchParams.sort_key, "to", sort_key); + searchParams.sort_key = sort_key; + } + const url = `/#/search/${searchParams.type_}/${searchParams.q}/${searchParams.page}/${searchParams.sort_key}/${searchParams.sort_dir}` + return url; +} + export class Search extends Component { state: State = { @@ -20,6 +39,8 @@ export class Search extends Component { searchParams: { q: "", page: 1, + sort_key: 'Name', + sort_dir: 'Desc', type_: 'torrent' }, searching: false @@ -30,9 +51,12 @@ export class Search extends Component { } componentDidMount() { + console.log("got props", this.props); this.state.searchParams = { page: Number(this.props.match.params.page), q: this.props.match.params.q, + sort_key: this.props.match.params.sort_key, + sort_dir: this.props.match.params.sort_dir, type_: this.props.match.params.type_ } this.search(); @@ -44,6 +68,8 @@ export class Search extends Component { this.state.searchParams = { page: Number(this.props.match.params.page), q: this.props.match.params.q, + sort_key: this.props.match.params.sort_key, + sort_dir: this.props.match.params.sort_dir, type_: this.props.match.params.type_ } this.search(); @@ -52,6 +78,7 @@ export class Search extends Component { } search() { + console.log("in search, state", this.state); if (!!this.state.searchParams.q) { this.setState({ searching: true, results: { torrents: [] } }); this.fetchData(this.state.searchParams) @@ -73,7 +100,7 @@ export class Search extends Component { fetchData(searchParams: SearchParams): Promise> { let q = encodeURI(searchParams.q); - return fetch(`${endpoint}/service/search?q=${q}&page=${searchParams.page}&type_=${searchParams.type_}`) + return fetch(`${endpoint}/service/search?q=${q}&page=${searchParams.page}&sort_key=${searchParams.sort_key}&sort_dir=${searchParams.sort_dir}&type_=${searchParams.type_}`) .then(data => data.json()); } @@ -112,11 +139,27 @@ export class Search extends Component { - - - - - + + + + + @@ -207,7 +250,7 @@ export class Search extends Component { switchPage(a: { i: Search, nextPage: boolean }) { let newSearch = a.i.state.searchParams; newSearch.page += (a.nextPage) ? 1 : -1; - a.i.props.history.push(`/search/${newSearch.type_}/${newSearch.q}/${newSearch.page}`); + a.i.props.history.push(`/search/${newSearch.type_}/${newSearch.q}/${newSearch.page}/${newSearch.sort_key}/${newSearch.sort_dir}`); } copyLink(evt) { diff --git a/server/ui/src/index.tsx b/server/ui/src/index.tsx index b64dd81..dbdb31b 100644 --- a/server/ui/src/index.tsx +++ b/server/ui/src/index.tsx @@ -17,7 +17,7 @@ class Index extends Component {
- + {this.symbols()}
diff --git a/server/ui/src/interfaces.ts b/server/ui/src/interfaces.ts index 2fbf4e2..51b5332 100644 --- a/server/ui/src/interfaces.ts +++ b/server/ui/src/interfaces.ts @@ -3,6 +3,8 @@ export interface SearchParams { page: number; size?: number; type_: string; + sort_key: string; + sort_dir: string; } export interface Results { -- 2.40.1 From 276b66ed12dbfa07ee6f7b4c9a458f4de5ed9686 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 01:04:56 +0200 Subject: [PATCH 02/26] further refactoring --- server/service/src/main.rs | 108 ++++++++++++++++------------ server/ui/src/components/search.tsx | 2 +- 2 files changed, 64 insertions(+), 46 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 39ea3e5..22a5b2a 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -15,7 +15,7 @@ use actix_files::NamedFile; use actix_web::{middleware, web, App, HttpResponse, HttpServer}; use failure::Error; use r2d2_sqlite::SqliteConnectionManager; -use rusqlite::{params, named_params, NO_PARAMS}; +use rusqlite::{params, named_params, types::ToSqlOutput, Result}; use std::env; use std::ops::Deref; use std::cmp; @@ -25,42 +25,62 @@ use serde_json::Value; const DEFAULT_SIZE: usize = 25; -enum SortDir { - Asc, - Desc, -} - -enum Sort { - Key(String), - Dir(SortDir), -} - - - #[derive(Copy, Clone, Debug, Deserialize)] -enum SortKey { +enum Key { Name, Size, - Seeds, - Leeches, - Scraped, + Seeders, + Leechers, + Date, } -impl fmt::Display for SortKey { +impl Default for Key { + fn default() -> Self { + Self::Name + } +} + +impl fmt::Display for Key { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) + let s = match &self { + Self::Name => "name", + Self::Size => "size_bytes", + Self::Seeders => "seeders", + Self::Leechers => "leechers", + Self::Date => "scraped_date", + }; + write!(f, "{}", s) } } #[derive(Copy, Clone, Debug, Deserialize)] -enum SortDir { +enum Direction { Asc, Desc, } -impl fmt::Display for SortDir { +impl Default for Direction { + fn default() -> Self { + Self::Desc + } +} + +impl fmt::Display for Direction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) + let s = match &self { + Self::Asc => "asc", + Self::Desc => "desc", + }; + write!(f, "{}", s) + } +} +#[derive(Copy, Clone, Debug, Default, Deserialize)] +struct Sort(Key, Direction); + +impl fmt::Display for Sort { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = format!("{} {}", self.0, self.1); + write!(f, "{}", s) } } @@ -109,8 +129,8 @@ struct SearchQuery { q: String, page: Option, size: Option, - sort_key: Option, - sort_dir: Option, + sort_key: Option, + sort_dir: Option, type_: Option, } @@ -136,8 +156,7 @@ async fn search( struct NewQuery { page: Option, size: Option, - sort_key: Option, - sort_dir: Option, + sort: Option, type_: Option, } @@ -170,22 +189,23 @@ fn search_query( } let page = query.page.unwrap_or(1); - let sort_key = query.sort_key.unwrap_or(SortKey::Name); - let sort_dir = query.sort_dir.unwrap_or(SortDir::Desc); + let key = query.sort_key.unwrap_or_default(); + let direction = query.sort_dir.unwrap_or_default(); + let sort = Sort(key, direction); let size = cmp::min(100, query.size.unwrap_or(DEFAULT_SIZE)); let type_ = query.type_.as_ref().map_or("torrent", String::deref); let offset = size * (page - 1); dbg!( - "query = {}, type = {}, page = {}, size = {}, sort_key = {:?}, sort_dir = {:?}", - q, type_, page, size, sort_key, sort_dir + "query = {}, type = {}, page = {}, size = {}, sort = {:?}", + q, type_, page, size, sort ); let res = if type_ == "file" { let results = torrent_file_search(conn, q, size, offset)?; serde_json::to_value(&results).unwrap() } else { - let results = torrent_search(conn, q, sort_key, sort_dir, size, offset)?; + let results = torrent_search(conn, q, sort, size, offset)?; serde_json::to_value(&results).unwrap() }; @@ -199,14 +219,13 @@ fn new_query( let page = query.page.unwrap_or(1); let size = cmp::min(100, query.size.unwrap_or(DEFAULT_SIZE)); - let sort_key = query.sort_key.as_ref().unwrap_or(&SortKey::Name); - let sort_dir = query.sort_dir.as_ref().unwrap_or(&SortDir::Desc); + let sort = query.sort.unwrap_or_default(); let type_ = query.type_.as_ref().map_or("torrent", String::deref); let offset = size * (page - 1); dbg!( - "new, type = {}, page = {}, size = {}, sort_key = {:?}, sort_dir = {:?}", - type_, page, size, sort_key, sort_dir + "new, type = {}, page = {}, size = {}, sort = {:?}", + type_, page, size, sort ); let res = if type_ == "file" { @@ -235,20 +254,19 @@ struct Torrent { fn torrent_search( conn: r2d2::PooledConnection, query: &str, - sort_key: SortKey, - sort_dir: SortDir, + sort: Sort, size: usize, offset: usize, ) -> Result, Error> { - let stmt_str = format!("select * from torrents where name like '%' || :query || '%' order by :sort_key {} limit :offset, :size", sort_dir.to_string().to_lowercase()); + let stmt_str = format!("select * from torrents where name like '%' || :query || '%' order by {} limit :offset, :size", sort); let mut stmt = conn.prepare(&stmt_str)?; - let torrent_iter = stmt.query_map_named( - named_params!{ - ":query": query.replace(" ", "%"), - ":sort_key": sort_key.to_string().to_lowercase(), - ":offset": offset.to_string(), - ":size": size.to_string(), - }, + + let torrent_iter = stmt.query_map(params![ + query.replace(" ", "%"), + offset.to_string(), + size.to_string(), + ], + |row| { let size: isize = row.get(2)?; println!("got size {:?}", size); diff --git a/server/ui/src/components/search.tsx b/server/ui/src/components/search.tsx index a7406b9..60e804b 100644 --- a/server/ui/src/components/search.tsx +++ b/server/ui/src/components/search.tsx @@ -19,7 +19,7 @@ function buildSearchURL(sort_key, {state: { searchParams }) { if (sort_key === searchParams.sort_key) { - const sort_dir = searchParams.sort_dir === "Asc" ? "Desc" : "Asc"; + const sort_dir = searchParams.sort_dir === "asc" ? "desc" : "asc"; searchParams.sort_dir = sort_dir; console.log("no change in sort key from", sort_key, "so switch direction instead.") } else { -- 2.40.1 From f571ec261c4d66ef3e068f631e79db7a45db1186 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 10:38:54 +0200 Subject: [PATCH 03/26] Almost working --- server/service/src/main.rs | 59 +++++++++++++---------------- server/ui/fuse.js | 3 +- server/ui/src/components/navbar.tsx | 2 + server/ui/src/components/search.tsx | 47 ++++++++++++----------- 4 files changed, 54 insertions(+), 57 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 22a5b2a..9ca9da9 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -74,15 +74,6 @@ impl fmt::Display for Direction { write!(f, "{}", s) } } -#[derive(Copy, Clone, Debug, Default, Deserialize)] -struct Sort(Key, Direction); - -impl fmt::Display for Sort { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = format!("{} {}", self.0, self.1); - write!(f, "{}", s) - } -} #[actix_rt::main] async fn main() -> io::Result<()> { @@ -156,7 +147,8 @@ async fn search( struct NewQuery { page: Option, size: Option, - sort: Option, + sort_key: Option, + sort_dir: Option, type_: Option, } @@ -191,21 +183,20 @@ fn search_query( let page = query.page.unwrap_or(1); let key = query.sort_key.unwrap_or_default(); let direction = query.sort_dir.unwrap_or_default(); - let sort = Sort(key, direction); let size = cmp::min(100, query.size.unwrap_or(DEFAULT_SIZE)); let type_ = query.type_.as_ref().map_or("torrent", String::deref); let offset = size * (page - 1); - dbg!( - "query = {}, type = {}, page = {}, size = {}, sort = {:?}", - q, type_, page, size, sort + println!( + "query = {}, type = {}, page = {}, size = {}, sort_key = {}, sort_dir = {}", + q, type_, page, size, key, direction ); let res = if type_ == "file" { let results = torrent_file_search(conn, q, size, offset)?; serde_json::to_value(&results).unwrap() } else { - let results = torrent_search(conn, q, sort, size, offset)?; + let results = torrent_search(conn, q, key, direction, size, offset)?; serde_json::to_value(&results).unwrap() }; @@ -219,13 +210,14 @@ fn new_query( let page = query.page.unwrap_or(1); let size = cmp::min(100, query.size.unwrap_or(DEFAULT_SIZE)); - let sort = query.sort.unwrap_or_default(); + let key = query.sort_key.unwrap_or_default(); + let direction = query.sort_dir.unwrap_or_default(); let type_ = query.type_.as_ref().map_or("torrent", String::deref); let offset = size * (page - 1); dbg!( - "new, type = {}, page = {}, size = {}, sort = {:?}", - type_, page, size, sort + "new, type = {}, page = {}, size = {}, sort_key = {}, sort_dir = {}", + type_, page, size, key, direction ); let res = if type_ == "file" { @@ -254,33 +246,34 @@ struct Torrent { fn torrent_search( conn: r2d2::PooledConnection, query: &str, - sort: Sort, + key: Key, + direction: Direction, size: usize, offset: usize, ) -> Result, Error> { - let stmt_str = format!("select * from torrents where name like '%' || :query || '%' order by {} limit :offset, :size", sort); + // `key` and `direction` are already sanitized and should not be escaped: + let stmt_str = format!("select * from torrents where name like '%' || ?1 || '%' order by {} {} limit ?2, ?3", key, direction); let mut stmt = conn.prepare(&stmt_str)?; - let torrent_iter = stmt.query_map(params![ + let torrent_iter = stmt.query_map(params!{ query.replace(" ", "%"), offset.to_string(), size.to_string(), - ], - - |row| { + }, + |row| { let size: isize = row.get(2)?; println!("got size {:?}", size); Ok(Torrent { - infohash: row.get(0)?, - name: row.get(1)?, - size_bytes: row.get(2)?, - created_unix: row.get(3)?, - seeders: row.get(4)?, - leechers: row.get(5)?, - completed: row.get(6)?, - scraped_date: row.get(7)?, + infohash: row.get(0)?, + name: row.get(1)?, + size_bytes: row.get(2)?, + created_unix: row.get(3)?, + seeders: row.get(4)?, + leechers: row.get(5)?, + completed: row.get(6)?, + scraped_date: row.get(7)?, }) - }, + }, )?; let mut torrents = Vec::new(); diff --git a/server/ui/fuse.js b/server/ui/fuse.js index ff1e6d1..a78354a 100644 --- a/server/ui/fuse.js +++ b/server/ui/fuse.js @@ -41,7 +41,8 @@ Sparky.task('config', _ => { }); app = fuse.bundle('app').instructions('>index.tsx'); }); -Sparky.task('clean', _ => Sparky.src('dist/').clean('dist/')); +//Sparky.task('clean', _ => Sparky.src('dist/').clean('dist/')); +Sparky.task('clean', _ => {}); Sparky.task('env', _ => (isProduction = true)); Sparky.task('copy-assets', () => Sparky.src('assets/*.ico').dest('dist/')); Sparky.task('dev', ['clean', 'config', 'copy-assets'], _ => { diff --git a/server/ui/src/components/navbar.tsx b/server/ui/src/components/navbar.tsx index 8355cbe..a1d7cb7 100644 --- a/server/ui/src/components/navbar.tsx +++ b/server/ui/src/components/navbar.tsx @@ -76,6 +76,7 @@ export class Navbar extends Component { } searchChange(i: Navbar, event) { + console.log("in searchChange"); let searchParams: SearchParams = { q: event.target.value, page: 1, @@ -87,6 +88,7 @@ export class Navbar extends Component { } searchTypeChange(i: Navbar, event) { + console.log("in searchTypeChange"); let searchParams: SearchParams = { q: i.state.searchParams.q, page: 1, diff --git a/server/ui/src/components/search.tsx b/server/ui/src/components/search.tsx index 60e804b..9b1dfe6 100644 --- a/server/ui/src/components/search.tsx +++ b/server/ui/src/components/search.tsx @@ -11,27 +11,19 @@ interface State { searching: Boolean; } - -function buildSearchURL(sort_key, {state: { searchParams }) { - console.log("got search URL sort_key", sort_key, "searchParams", searchParams, "thing", searchParams); - - //let searchParams = thing.state.searchParams; - - - if (sort_key === searchParams.sort_key) { - const sort_dir = searchParams.sort_dir === "asc" ? "desc" : "asc"; - searchParams.sort_dir = sort_dir; - console.log("no change in sort key from", sort_key, "so switch direction instead.") +function buildSearchURL({state: { searchParams }}, key) { + let page, direction; + if (key === searchParams.sort_key) { + page = searchParams.page; + direction = searchParams.sort_dir === "Asc" ? "Desc" : "Asc"; } else { - console.log("change sort key from", searchParams.sort_key, "to", sort_key); - searchParams.sort_key = sort_key; + page = 1; + direction = "Desc"; } - const url = `/#/search/${searchParams.type_}/${searchParams.q}/${searchParams.page}/${searchParams.sort_key}/${searchParams.sort_dir}` - return url; + return `/#/search/${searchParams.type_}/${searchParams.q}/${page}/${key}/${direction}`; } export class Search extends Component { - state: State = { results: { torrents: [] @@ -51,7 +43,7 @@ export class Search extends Component { } componentDidMount() { - console.log("got props", this.props); + console.log("loading page, got match", this.props.match.params); this.state.searchParams = { page: Number(this.props.match.params.page), q: this.props.match.params.q, @@ -65,6 +57,7 @@ export class Search extends Component { // Re-do search if the props have changed componentDidUpdate(lastProps: any) { if (lastProps.match && lastProps.match.params !== this.props.match.params) { + console.log("updating component with sort_key", this.props.match.params.sort_key); this.state.searchParams = { page: Number(this.props.match.params.page), q: this.props.match.params.q, @@ -78,7 +71,6 @@ export class Search extends Component { } search() { - console.log("in search, state", this.state); if (!!this.state.searchParams.q) { this.setState({ searching: true, results: { torrents: [] } }); this.fetchData(this.state.searchParams) @@ -141,24 +133,33 @@ export class Search extends Component {
-- 2.40.1 From 2362c4eddf9c69c2c8dd74e33d9755d7deda734b Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 12:32:09 +0200 Subject: [PATCH 04/26] Fully working, needs polishing --- server/ui/src/Main.css | 4 ++ server/ui/src/components/search.tsx | 74 ++++++++++++++++------------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/server/ui/src/Main.css b/server/ui/src/Main.css index a381704..0385b6b 100644 --- a/server/ui/src/Main.css +++ b/server/ui/src/Main.css @@ -79,6 +79,10 @@ a::-moz-focus-inner { align-self: center; } +th .icon { + color: #333333; +} + .spinner { animation: spin 2s linear infinite; width: 20vw; diff --git a/server/ui/src/components/search.tsx b/server/ui/src/components/search.tsx index 9b1dfe6..c2cdfb0 100644 --- a/server/ui/src/components/search.tsx +++ b/server/ui/src/components/search.tsx @@ -1,4 +1,4 @@ -import { Component, linkEvent } from 'inferno'; +import { Component, render, linkEvent } from 'inferno'; import moment from 'moment'; import { endpoint } from '../env'; @@ -11,17 +11,42 @@ interface State { searching: Boolean; } -function buildSearchURL({state: { searchParams }}, key) { - let page, direction; - if (key === searchParams.sort_key) { - page = searchParams.page; - direction = searchParams.sort_dir === "Asc" ? "Desc" : "Asc"; - } else { - page = 1; - direction = "Desc"; +class SortableLink extends Component { + render({ sortKey, state: { searchParams } }) { + return + + {sortKey} + {this.renderIcon(sortKey, searchParams)} + + ; } - return `/#/search/${searchParams.type_}/${searchParams.q}/${page}/${key}/${direction}`; -} + + buildSearchURL(key, searchParams) { + let page, direction; + if (key === searchParams.sort_key) { + page = searchParams.page; + direction = searchParams.sort_dir === "Asc" ? "Desc" : "Asc"; + } else { + page = 1; + direction = "Desc"; + } + return `/#/search/${searchParams.type_}/${searchParams.q}/${page}/${key}/${direction}`; + } + + renderIcon(sortKey, searchParams) { + console.log("sortKey is", sortKey, "and params is", searchParams); + if (searchParams.sort_key !== sortKey) { + console.log("ret 1"); + return ""; + } + if (searchParams.sort_dir === "Asc") { + console.log("ret 2"); + return ; + } + console.log("ret 3"); + return l + } +) export class Search extends Component { state: State = { @@ -43,7 +68,6 @@ export class Search extends Component { } componentDidMount() { - console.log("loading page, got match", this.props.match.params); this.state.searchParams = { page: Number(this.props.match.params.page), q: this.props.match.params.q, @@ -57,7 +81,6 @@ export class Search extends Component { // Re-do search if the props have changed componentDidUpdate(lastProps: any) { if (lastProps.match && lastProps.match.params !== this.props.match.params) { - console.log("updating component with sort_key", this.props.match.params.sort_key); this.state.searchParams = { page: Number(this.props.match.params.page), q: this.props.match.params.q, @@ -132,34 +155,19 @@ export class Search extends Component { -- 2.40.1 From 63ed272d54177c054c1eec11ab2f80b2d63ab868 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 12:34:03 +0200 Subject: [PATCH 05/26] fix compilation warnings --- server/service/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 9ca9da9..3216214 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -15,7 +15,7 @@ use actix_files::NamedFile; use actix_web::{middleware, web, App, HttpResponse, HttpServer}; use failure::Error; use r2d2_sqlite::SqliteConnectionManager; -use rusqlite::{params, named_params, types::ToSqlOutput, Result}; +use rusqlite::{params, Result}; use std::env; use std::ops::Deref; use std::cmp; -- 2.40.1 From 91e0ee058c094647ad15f8b97e012c97dd4501fc Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 12:34:46 +0200 Subject: [PATCH 06/26] remove unnecessary import --- server/service/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 3216214..532c172 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -15,7 +15,7 @@ use actix_files::NamedFile; use actix_web::{middleware, web, App, HttpResponse, HttpServer}; use failure::Error; use r2d2_sqlite::SqliteConnectionManager; -use rusqlite::{params, Result}; +use rusqlite::params; use std::env; use std::ops::Deref; use std::cmp; -- 2.40.1 From 3ba5c60df0146eaa06b38b0813e0009d72b275a7 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 12:39:31 +0200 Subject: [PATCH 07/26] cleanup naming in rust code --- server/service/src/main.rs | 42 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 532c172..0eb5436 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -26,7 +26,7 @@ use serde_json::Value; const DEFAULT_SIZE: usize = 25; #[derive(Copy, Clone, Debug, Deserialize)] -enum Key { +enum SortKey { Name, Size, Seeders, @@ -34,13 +34,13 @@ enum Key { Date, } -impl Default for Key { +impl Default for SortKey { fn default() -> Self { Self::Name } } -impl fmt::Display for Key { +impl fmt::Display for SortKey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = match &self { Self::Name => "name", @@ -54,18 +54,18 @@ impl fmt::Display for Key { } #[derive(Copy, Clone, Debug, Deserialize)] -enum Direction { +enum SortDirection { Asc, Desc, } -impl Default for Direction { +impl Default for SortDirection { fn default() -> Self { Self::Desc } } -impl fmt::Display for Direction { +impl fmt::Display for SortDirection { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = match &self { Self::Asc => "asc", @@ -120,8 +120,8 @@ struct SearchQuery { q: String, page: Option, size: Option, - sort_key: Option, - sort_dir: Option, + sort_key: Option, + sort_dir: Option, type_: Option, } @@ -147,8 +147,8 @@ async fn search( struct NewQuery { page: Option, size: Option, - sort_key: Option, - sort_dir: Option, + sort_key: Option, + sort_dir: Option, type_: Option, } @@ -181,22 +181,22 @@ fn search_query( } let page = query.page.unwrap_or(1); - let key = query.sort_key.unwrap_or_default(); - let direction = query.sort_dir.unwrap_or_default(); + let sort_key = query.sort_key.unwrap_or_default(); + let sort_dir = query.sort_dir.unwrap_or_default(); let size = cmp::min(100, query.size.unwrap_or(DEFAULT_SIZE)); let type_ = query.type_.as_ref().map_or("torrent", String::deref); let offset = size * (page - 1); println!( "query = {}, type = {}, page = {}, size = {}, sort_key = {}, sort_dir = {}", - q, type_, page, size, key, direction + q, type_, page, size, sort_key, sort_dir ); let res = if type_ == "file" { let results = torrent_file_search(conn, q, size, offset)?; serde_json::to_value(&results).unwrap() } else { - let results = torrent_search(conn, q, key, direction, size, offset)?; + let results = torrent_search(conn, q, sort_key, sort_dir, size, offset)?; serde_json::to_value(&results).unwrap() }; @@ -210,14 +210,14 @@ fn new_query( let page = query.page.unwrap_or(1); let size = cmp::min(100, query.size.unwrap_or(DEFAULT_SIZE)); - let key = query.sort_key.unwrap_or_default(); - let direction = query.sort_dir.unwrap_or_default(); + let sort_key = query.sort_key.unwrap_or_default(); + let sort_dir = query.sort_dir.unwrap_or_default(); let type_ = query.type_.as_ref().map_or("torrent", String::deref); let offset = size * (page - 1); - dbg!( + println!( "new, type = {}, page = {}, size = {}, sort_key = {}, sort_dir = {}", - type_, page, size, key, direction + type_, page, size, sort_key, sort_dir ); let res = if type_ == "file" { @@ -246,8 +246,8 @@ struct Torrent { fn torrent_search( conn: r2d2::PooledConnection, query: &str, - key: Key, - direction: Direction, + key: SortKey, + direction: SortDirection, size: usize, offset: usize, ) -> Result, Error> { @@ -410,7 +410,7 @@ mod tests { let manager = SqliteConnectionManager::file(super::torrents_db_file()); let pool = r2d2::Pool::builder().max_size(15).build(manager).unwrap(); let conn = pool.get().unwrap(); - let results = super::torrent_search(conn, "sherlock", 10, 0, SortKey::Name, SortDir::Desc); + let results = super::torrent_search(conn, "sherlock", 10, 0, SortKey::Name, SortDirection::Desc); assert!(results.unwrap().len() > 2); // println!("Query took {:?} seconds.", end - start); } -- 2.40.1 From 7d79d3aed437a6ffa16e10560d40b62ae932e18d Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 12:40:12 +0200 Subject: [PATCH 08/26] tidy up imports --- server/service/src/main.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 0eb5436..f643661 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -16,11 +16,8 @@ use actix_web::{middleware, web, App, HttpResponse, HttpServer}; use failure::Error; use r2d2_sqlite::SqliteConnectionManager; use rusqlite::params; -use std::env; +use std::{env, cmp, io, fmt}; use std::ops::Deref; -use std::cmp; -use std::io; -use std::fmt; use serde_json::Value; const DEFAULT_SIZE: usize = 25; -- 2.40.1 From 6866215a8a1adacbce66342933709620df0cf1f1 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 12:42:01 +0200 Subject: [PATCH 09/26] remove unneeded Debug derive --- server/service/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index f643661..3316d0c 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -22,7 +22,7 @@ use serde_json::Value; const DEFAULT_SIZE: usize = 25; -#[derive(Copy, Clone, Debug, Deserialize)] +#[derive(Copy, Clone, Deserialize)] enum SortKey { Name, Size, @@ -50,7 +50,7 @@ impl fmt::Display for SortKey { } } -#[derive(Copy, Clone, Debug, Deserialize)] +#[derive(Copy, Clone, Deserialize)] enum SortDirection { Asc, Desc, -- 2.40.1 From eaa2745d4bf9a8f96ddfaabaa6a6f2b63a26d222 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 12:44:07 +0200 Subject: [PATCH 10/26] tidy up variable naming --- server/service/src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 3316d0c..4354977 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -243,13 +243,13 @@ struct Torrent { fn torrent_search( conn: r2d2::PooledConnection, query: &str, - key: SortKey, - direction: SortDirection, + sort_key: SortKey, + sort_dir: SortDirection, size: usize, offset: usize, ) -> Result, Error> { - // `key` and `direction` are already sanitized and should not be escaped: - let stmt_str = format!("select * from torrents where name like '%' || ?1 || '%' order by {} {} limit ?2, ?3", key, direction); + // `sort_key` and `sort_dir` are already sanitized and should not be escaped: + let stmt_str = format!("select * from torrents where name like '%' || ?1 || '%' order by {} {} limit ?2, ?3", sort_key, sort_dir); let mut stmt = conn.prepare(&stmt_str)?; let torrent_iter = stmt.query_map(params!{ -- 2.40.1 From 0048290868510c492f4fc3269058b8dd3841c242 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 12:45:12 +0200 Subject: [PATCH 11/26] tidy up indentation --- server/service/src/main.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 4354977..bf312e3 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -252,14 +252,13 @@ fn torrent_search( let stmt_str = format!("select * from torrents where name like '%' || ?1 || '%' order by {} {} limit ?2, ?3", sort_key, sort_dir); let mut stmt = conn.prepare(&stmt_str)?; - let torrent_iter = stmt.query_map(params!{ + let torrent_iter = stmt.query_map( + params!{ query.replace(" ", "%"), offset.to_string(), size.to_string(), - }, - |row| { - let size: isize = row.get(2)?; - println!("got size {:?}", size); + }, + |row| { Ok(Torrent { infohash: row.get(0)?, name: row.get(1)?, -- 2.40.1 From 59f292bc1e5b2d24752c1699f2659a5fc24a057c Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 12:45:53 +0200 Subject: [PATCH 12/26] tidy up indentation part 2 --- server/service/src/main.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index bf312e3..42243e1 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -253,21 +253,21 @@ fn torrent_search( let mut stmt = conn.prepare(&stmt_str)?; let torrent_iter = stmt.query_map( - params!{ + params![ query.replace(" ", "%"), offset.to_string(), size.to_string(), - }, + ], |row| { Ok(Torrent { - infohash: row.get(0)?, - name: row.get(1)?, - size_bytes: row.get(2)?, - created_unix: row.get(3)?, - seeders: row.get(4)?, - leechers: row.get(5)?, - completed: row.get(6)?, - scraped_date: row.get(7)?, + infohash: row.get(0)?, + name: row.get(1)?, + size_bytes: row.get(2)?, + created_unix: row.get(3)?, + seeders: row.get(4)?, + leechers: row.get(5)?, + completed: row.get(6)?, + scraped_date: row.get(7)?, }) }, )?; -- 2.40.1 From fec8d0969388335418f11c7bfe59fcd656d81243 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 12:46:40 +0200 Subject: [PATCH 13/26] tidy up indentation part 3 --- server/service/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 42243e1..9bbfbe6 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -251,7 +251,6 @@ fn torrent_search( // `sort_key` and `sort_dir` are already sanitized and should not be escaped: let stmt_str = format!("select * from torrents where name like '%' || ?1 || '%' order by {} {} limit ?2, ?3", sort_key, sort_dir); let mut stmt = conn.prepare(&stmt_str)?; - let torrent_iter = stmt.query_map( params![ query.replace(" ", "%"), @@ -269,7 +268,7 @@ fn torrent_search( completed: row.get(6)?, scraped_date: row.get(7)?, }) - }, + }, )?; let mut torrents = Vec::new(); -- 2.40.1 From 47dc4305d7e31358a411ff6beed5f5e52660007a Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 13:00:02 +0200 Subject: [PATCH 14/26] remove logging --- server/ui/src/components/search.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server/ui/src/components/search.tsx b/server/ui/src/components/search.tsx index c2cdfb0..57b16cf 100644 --- a/server/ui/src/components/search.tsx +++ b/server/ui/src/components/search.tsx @@ -34,16 +34,12 @@ class SortableLink extends Component { } renderIcon(sortKey, searchParams) { - console.log("sortKey is", sortKey, "and params is", searchParams); if (searchParams.sort_key !== sortKey) { - console.log("ret 1"); return ""; } if (searchParams.sort_dir === "Asc") { - console.log("ret 2"); return ; } - console.log("ret 3"); return l } ) -- 2.40.1 From 6be17c72680df616b58408257707bded251374f1 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 13:04:06 +0200 Subject: [PATCH 15/26] various cleanup of naming and tests --- server/service/src/main.rs | 13 ++++++++----- server/ui/src/components/search.tsx | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 9bbfbe6..51caf02 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -28,7 +28,7 @@ enum SortKey { Size, Seeders, Leechers, - Date, + Scraped, } impl Default for SortKey { @@ -44,7 +44,7 @@ impl fmt::Display for SortKey { Self::Size => "size_bytes", Self::Seeders => "seeders", Self::Leechers => "leechers", - Self::Date => "scraped_date", + Self::Scraped => "scraped_date", }; write!(f, "{}", s) } @@ -190,7 +190,7 @@ fn search_query( ); let res = if type_ == "file" { - let results = torrent_file_search(conn, q, size, offset)?; + let results = torrent_file_search(conn, q, sort_key, sort_dir, size, offset)?; serde_json::to_value(&results).unwrap() } else { let results = torrent_search(conn, q, sort_key, sort_dir, size, offset)?; @@ -329,10 +329,13 @@ struct File { fn torrent_file_search( conn: r2d2::PooledConnection, query: &str, + sort_key: SortKey, + sort_dir: SortDirection, size: usize, offset: usize, ) -> Result, Error> { - let stmt_str = "select * from files where path like '%' || ?1 || '%' limit ?2, ?3"; + // `sort_key` and `sort_dir` are already sanitized and should not be escaped: + let stmt_str = format!("select * from files where path like '%' || ?1 || '%' order by {} {} limit ?2, ?3", sort_key, sort_dir); let mut stmt = conn.prepare(&stmt_str).unwrap(); let file_iter = stmt.query_map( params![ @@ -405,7 +408,7 @@ mod tests { let manager = SqliteConnectionManager::file(super::torrents_db_file()); let pool = r2d2::Pool::builder().max_size(15).build(manager).unwrap(); let conn = pool.get().unwrap(); - let results = super::torrent_search(conn, "sherlock", 10, 0, SortKey::Name, SortDirection::Desc); + let results = super::torrent_search(conn, "sherlock", super::SortKey::Name, super::SortDirection::Desc, 10, 0); assert!(results.unwrap().len() > 2); // println!("Query took {:?} seconds.", end - start); } diff --git a/server/ui/src/components/search.tsx b/server/ui/src/components/search.tsx index 57b16cf..24326cd 100644 --- a/server/ui/src/components/search.tsx +++ b/server/ui/src/components/search.tsx @@ -40,7 +40,7 @@ class SortableLink extends Component { if (searchParams.sort_dir === "Asc") { return ; } - return l + return ; } ) @@ -163,7 +163,7 @@ export class Search extends Component { -- 2.40.1 From 20b9a76209e5f8f8e398c8d94a1322364186798c Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 13:04:34 +0200 Subject: [PATCH 16/26] revert change to fuse.js --- server/ui/fuse.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/ui/fuse.js b/server/ui/fuse.js index a78354a..ff1e6d1 100644 --- a/server/ui/fuse.js +++ b/server/ui/fuse.js @@ -41,8 +41,7 @@ Sparky.task('config', _ => { }); app = fuse.bundle('app').instructions('>index.tsx'); }); -//Sparky.task('clean', _ => Sparky.src('dist/').clean('dist/')); -Sparky.task('clean', _ => {}); +Sparky.task('clean', _ => Sparky.src('dist/').clean('dist/')); Sparky.task('env', _ => (isProduction = true)); Sparky.task('copy-assets', () => Sparky.src('assets/*.ico').dest('dist/')); Sparky.task('dev', ['clean', 'config', 'copy-assets'], _ => { -- 2.40.1 From 323ec46982156b46e6cc5688ab87212b24246fd1 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 18:12:28 +0200 Subject: [PATCH 17/26] refactoring --- server/service/src/main.rs | 68 +++++++++++----------- server/ui/src/components/navbar.tsx | 11 ++-- server/ui/src/components/search.tsx | 87 ++++++++++++++++------------- 3 files changed, 90 insertions(+), 76 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 51caf02..b325066 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -16,13 +16,13 @@ use actix_web::{middleware, web, App, HttpResponse, HttpServer}; use failure::Error; use r2d2_sqlite::SqliteConnectionManager; use rusqlite::params; -use std::{env, cmp, io, fmt}; +use std::{env, cmp, io}; use std::ops::Deref; use serde_json::Value; const DEFAULT_SIZE: usize = 25; -#[derive(Copy, Clone, Deserialize)] +#[derive(Copy, Clone, Debug, Deserialize)] enum SortKey { Name, Size, @@ -37,20 +37,7 @@ impl Default for SortKey { } } -impl fmt::Display for SortKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match &self { - Self::Name => "name", - Self::Size => "size_bytes", - Self::Seeders => "seeders", - Self::Leechers => "leechers", - Self::Scraped => "scraped_date", - }; - write!(f, "{}", s) - } -} - -#[derive(Copy, Clone, Deserialize)] +#[derive(Copy, Clone, Debug, Deserialize)] enum SortDirection { Asc, Desc, @@ -62,16 +49,6 @@ impl Default for SortDirection { } } -impl fmt::Display for SortDirection { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match &self { - Self::Asc => "asc", - Self::Desc => "desc", - }; - write!(f, "{}", s) - } -} - #[actix_rt::main] async fn main() -> io::Result<()> { println!("Access me at {}", endpoint()); @@ -140,6 +117,8 @@ async fn search( Ok(res) } +// TODO what is NewQuery used for? +// it is not a file search, that is done through SearchQuery #[derive(Deserialize)] struct NewQuery { page: Option, @@ -167,6 +146,29 @@ async fn new_torrents( Ok(res) } +fn build_order_clause(type_: &str, sort_key: SortKey, sort_dir: SortDirection) -> String { + let dir = match sort_dir { + SortDirection::Asc => "asc", + SortDirection::Desc => "desc", + }; + + let column = match sort_key { + SortKey::Name => { + if type_ == "file" { + "path" + } else { + "name" + } + }, + SortKey::Size => "size_bytes", + SortKey::Seeders => "seeders", + SortKey::Leechers => "leechers", + SortKey::Scraped => "scraped_date", + }; + + format!("{} {}", column, dir) +} + fn search_query( query: web::Query, conn: r2d2::PooledConnection, @@ -185,7 +187,7 @@ fn search_query( let offset = size * (page - 1); println!( - "query = {}, type = {}, page = {}, size = {}, sort_key = {}, sort_dir = {}", + "query = {}, type = {}, page = {}, size = {}, sort_key = {:?}, sort_dir = {:?}", q, type_, page, size, sort_key, sort_dir ); @@ -213,7 +215,7 @@ fn new_query( let offset = size * (page - 1); println!( - "new, type = {}, page = {}, size = {}, sort_key = {}, sort_dir = {}", + "new, type = {}, page = {}, size = {}, sort_key = {:?}, sort_dir = {:?}", type_, page, size, sort_key, sort_dir ); @@ -248,8 +250,9 @@ fn torrent_search( size: usize, offset: usize, ) -> Result, Error> { - // `sort_key` and `sort_dir` are already sanitized and should not be escaped: - let stmt_str = format!("select * from torrents where name like '%' || ?1 || '%' order by {} {} limit ?2, ?3", sort_key, sort_dir); + let order_clause = build_order_clause("torrent", sort_key, sort_dir); + // `order_clause` is already sanitized and should not be escaped: + let stmt_str = format!("select * from torrents where name like '%' || ?1 || '%' order by {} limit ?2, ?3", order_clause); let mut stmt = conn.prepare(&stmt_str)?; let torrent_iter = stmt.query_map( params![ @@ -334,8 +337,9 @@ fn torrent_file_search( size: usize, offset: usize, ) -> Result, Error> { - // `sort_key` and `sort_dir` are already sanitized and should not be escaped: - let stmt_str = format!("select * from files where path like '%' || ?1 || '%' order by {} {} limit ?2, ?3", sort_key, sort_dir); + let order_clause = build_order_clause("file", sort_key, sort_dir); + // `order_clause` is already sanitized and should not be escaped: + let stmt_str = format!("select * from files where path like '%' || ?1 || '%' order by {} limit ?2, ?3", order_clause); let mut stmt = conn.prepare(&stmt_str).unwrap(); let file_iter = stmt.query_map( params![ diff --git a/server/ui/src/components/navbar.tsx b/server/ui/src/components/navbar.tsx index a1d7cb7..9f200b0 100644 --- a/server/ui/src/components/navbar.tsx +++ b/server/ui/src/components/navbar.tsx @@ -8,7 +8,6 @@ interface State { } export class Navbar extends Component { - state: State = { searchParams: { page: 1, @@ -80,20 +79,20 @@ export class Navbar extends Component { let searchParams: SearchParams = { q: event.target.value, page: 1, - sort_key: 'Name', - sort_dir: 'Desc', + sort_key: i.state.searchParams.sort_key, + sort_dir: i.state.searchParams.sort_dir, type_: i.state.searchParams.type_ } i.setState({ searchParams: searchParams }); } + // TODO why does switching from torrent to file search clear the sort_key? searchTypeChange(i: Navbar, event) { - console.log("in searchTypeChange"); let searchParams: SearchParams = { q: i.state.searchParams.q, page: 1, - sort_key: 'Name', - sort_dir: 'Desc', + sort_key: i.state.searchParams.sort_key, + sort_dir: i.state.searchParams.sort_dir, type_: event.target.value } i.setState({ searchParams: searchParams }); diff --git a/server/ui/src/components/search.tsx b/server/ui/src/components/search.tsx index 24326cd..39c7864 100644 --- a/server/ui/src/components/search.tsx +++ b/server/ui/src/components/search.tsx @@ -11,39 +11,6 @@ interface State { searching: Boolean; } -class SortableLink extends Component { - render({ sortKey, state: { searchParams } }) { - return - - {sortKey} - {this.renderIcon(sortKey, searchParams)} - - ; - } - - buildSearchURL(key, searchParams) { - let page, direction; - if (key === searchParams.sort_key) { - page = searchParams.page; - direction = searchParams.sort_dir === "Asc" ? "Desc" : "Asc"; - } else { - page = 1; - direction = "Desc"; - } - return `/#/search/${searchParams.type_}/${searchParams.q}/${page}/${key}/${direction}`; - } - - renderIcon(sortKey, searchParams) { - if (searchParams.sort_key !== sortKey) { - return ""; - } - if (searchParams.sort_dir === "Asc") { - return ; - } - return ; - } -) - export class Search extends Component { state: State = { results: { @@ -64,6 +31,7 @@ export class Search extends Component { } componentDidMount() { + console.log("comp did mount"); this.state.searchParams = { page: Number(this.props.match.params.page), q: this.props.match.params.q, @@ -76,6 +44,7 @@ export class Search extends Component { // Re-do search if the props have changed componentDidUpdate(lastProps: any) { + console.log("comp did update"); if (lastProps.match && lastProps.match.params !== this.props.match.params) { this.state.searchParams = { page: Number(this.props.match.params.page), @@ -144,6 +113,48 @@ export class Search extends Component { ) } + sortLink(sortKey) { + return ( + + + {sortKey} + {this.sortLinkIcon(sortKey)} + + + ) + } + + buildSortURL(sortKey) { + const searchParams = this.state.searchParams; + let page, sortDir; + if (sortKey === searchParams.sort_key) { + page = searchParams.page; + sortDir = searchParams.sort_dir === "Asc" ? "Desc" : "Asc"; + } else { + page = 1; + sortDir = "Desc"; + } + return `/#/search/${searchParams.type_}/${searchParams.q}/${page}/${sortKey}/${sortDir}`; + } + + sortLinkIcon(sortKey) { + const searchParams = this.state.searchParams; + + if (searchParams.sort_key !== sortKey) { + return ""; + } + + if (searchParams.sort_dir === "Asc") { + return ( + + ) + } else { + return ( + + ) + } + } + torrentsTable() { return (
@@ -151,19 +162,19 @@ export class Search extends Component {
-- 2.40.1 From 91073da873545387277d9c852c6d7f358facf043 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 18:17:00 +0200 Subject: [PATCH 18/26] tidy up imports --- server/service/src/main.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index b325066..038f811 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -16,8 +16,10 @@ use actix_web::{middleware, web, App, HttpResponse, HttpServer}; use failure::Error; use r2d2_sqlite::SqliteConnectionManager; use rusqlite::params; -use std::{env, cmp, io}; +use std::env; use std::ops::Deref; +use std::cmp; +use std::io; use serde_json::Value; const DEFAULT_SIZE: usize = 25; -- 2.40.1 From 300a53548157fb85e6ca4510d035e3ea64cb62cf Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 18:27:46 +0200 Subject: [PATCH 19/26] further cleanup --- server/ui/src/components/navbar.tsx | 1 - server/ui/src/components/search.tsx | 22 +++++----------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/server/ui/src/components/navbar.tsx b/server/ui/src/components/navbar.tsx index 9f200b0..08cad6d 100644 --- a/server/ui/src/components/navbar.tsx +++ b/server/ui/src/components/navbar.tsx @@ -75,7 +75,6 @@ export class Navbar extends Component { } searchChange(i: Navbar, event) { - console.log("in searchChange"); let searchParams: SearchParams = { q: event.target.value, page: 1, diff --git a/server/ui/src/components/search.tsx b/server/ui/src/components/search.tsx index 39c7864..fa18350 100644 --- a/server/ui/src/components/search.tsx +++ b/server/ui/src/components/search.tsx @@ -31,7 +31,6 @@ export class Search extends Component { } componentDidMount() { - console.log("comp did mount"); this.state.searchParams = { page: Number(this.props.match.params.page), q: this.props.match.params.q, @@ -44,7 +43,6 @@ export class Search extends Component { // Re-do search if the props have changed componentDidUpdate(lastProps: any) { - console.log("comp did update"); if (lastProps.match && lastProps.match.params !== this.props.match.params) { this.state.searchParams = { page: Number(this.props.match.params.page), @@ -161,21 +159,11 @@ export class Search extends Component {
NameSizeSeedsLeechesScraped + + Name + + + + Size + + + Seeds + + Leeches + + Scraped +
+ href={buildSearchURL(this, 'Name')}> Name + href={buildSearchURL(this, 'Size')}> Size - Seeds + + Seeds + - Leeches + + Leechers + - Scraped + + Scraped +
- - Name - + - - Size - + - - Seeds - + - - Leechers - + - - Scraped - +
- +
- + {this.sortLink("Name")} - + {this.sortLink("Size")} - + {this.sortLink("Seeders")} - + {this.sortLink("Leechers")} - + {this.sortLink("Scraped")}
- - - - - + + + + + -- 2.40.1 From 5f7dede514ad3862e63dd2072232690e94d61d2e Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 3 Jul 2020 18:35:19 +0200 Subject: [PATCH 20/26] clean up import --- server/ui/src/components/search.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/ui/src/components/search.tsx b/server/ui/src/components/search.tsx index fa18350..7102249 100644 --- a/server/ui/src/components/search.tsx +++ b/server/ui/src/components/search.tsx @@ -1,4 +1,4 @@ -import { Component, render, linkEvent } from 'inferno'; +import { Component, linkEvent } from 'inferno'; import moment from 'moment'; import { endpoint } from '../env'; -- 2.40.1 From e0483329fab7454fdbf768d13251847425970f8c Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Sat, 4 Jul 2020 08:40:43 +0200 Subject: [PATCH 21/26] Revert to old column naming in UI --- server/service/src/main.rs | 8 ++++---- server/ui/src/components/search.tsx | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 038f811..4debadc 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -28,8 +28,8 @@ const DEFAULT_SIZE: usize = 25; enum SortKey { Name, Size, - Seeders, - Leechers, + Seeds, + Leeches, Scraped, } @@ -163,8 +163,8 @@ fn build_order_clause(type_: &str, sort_key: SortKey, sort_dir: SortDirection) - } }, SortKey::Size => "size_bytes", - SortKey::Seeders => "seeders", - SortKey::Leechers => "leechers", + SortKey::Seeds => "seeders", + SortKey::Leeches => "leechers", SortKey::Scraped => "scraped_date", }; diff --git a/server/ui/src/components/search.tsx b/server/ui/src/components/search.tsx index 7102249..c6357ee 100644 --- a/server/ui/src/components/search.tsx +++ b/server/ui/src/components/search.tsx @@ -161,8 +161,8 @@ export class Search extends Component { - - + + -- 2.40.1 From efd0338a33f7622102c37c298887e7e07c168339 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Sat, 4 Jul 2020 08:40:58 +0200 Subject: [PATCH 22/26] Add TypeScript annotations --- server/ui/src/components/search.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/ui/src/components/search.tsx b/server/ui/src/components/search.tsx index c6357ee..47471f4 100644 --- a/server/ui/src/components/search.tsx +++ b/server/ui/src/components/search.tsx @@ -111,7 +111,7 @@ export class Search extends Component { ) } - sortLink(sortKey) { + sortLink(sortKey: string) { return ( @@ -122,7 +122,7 @@ export class Search extends Component { ) } - buildSortURL(sortKey) { + buildSortURL(sortKey: string) { const searchParams = this.state.searchParams; let page, sortDir; if (sortKey === searchParams.sort_key) { @@ -135,7 +135,7 @@ export class Search extends Component { return `/#/search/${searchParams.type_}/${searchParams.q}/${page}/${sortKey}/${sortDir}`; } - sortLinkIcon(sortKey) { + sortLinkIcon(sortKey: string) { const searchParams = this.state.searchParams; if (searchParams.sort_key !== sortKey) { -- 2.40.1 From 16b1ffbe45118726e8854ca6d83a08679a3498f5 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Sat, 4 Jul 2020 08:46:43 +0200 Subject: [PATCH 23/26] Fix fillSearchField bug --- server/ui/src/components/navbar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/ui/src/components/navbar.tsx b/server/ui/src/components/navbar.tsx index 08cad6d..fde4884 100644 --- a/server/ui/src/components/navbar.tsx +++ b/server/ui/src/components/navbar.tsx @@ -98,7 +98,7 @@ export class Navbar extends Component { } fillSearchField() { let splitPath: Array = this.context.router.route.location.pathname.split("/"); - if (splitPath.length == 5 && splitPath[1] == 'search') + if (splitPath.length == 7 && splitPath[1] == 'search') this.state.searchParams = { page: Number(splitPath[4]), q: splitPath[3], -- 2.40.1 From 7b880e58198dd7ae1d94cf89aeb7f1982caea628 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Sat, 4 Jul 2020 09:11:41 +0200 Subject: [PATCH 24/26] improve icons --- server/ui/src/Main.css | 5 ++++- server/ui/src/components/search.tsx | 6 ++---- server/ui/src/index.tsx | 6 ++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/server/ui/src/Main.css b/server/ui/src/Main.css index 0385b6b..8e0fc7f 100644 --- a/server/ui/src/Main.css +++ b/server/ui/src/Main.css @@ -80,7 +80,10 @@ a::-moz-focus-inner { } th .icon { - color: #333333; + color: #666666; + position: relative; + top: 0.125em; + left: 0.125em; } .spinner { diff --git a/server/ui/src/components/search.tsx b/server/ui/src/components/search.tsx index 47471f4..80781dd 100644 --- a/server/ui/src/components/search.tsx +++ b/server/ui/src/components/search.tsx @@ -137,18 +137,16 @@ export class Search extends Component { sortLinkIcon(sortKey: string) { const searchParams = this.state.searchParams; - if (searchParams.sort_key !== sortKey) { return ""; } - if (searchParams.sort_dir === "Asc") { return ( - + ) } else { return ( - + ) } } diff --git a/server/ui/src/index.tsx b/server/ui/src/index.tsx index dbdb31b..0585508 100644 --- a/server/ui/src/index.tsx +++ b/server/ui/src/index.tsx @@ -49,6 +49,12 @@ class Index extends Component { arrow-up + + + + + + search -- 2.40.1 From ad738296a0e70289f1fdba56abede7436565597f Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Sat, 4 Jul 2020 09:30:50 +0200 Subject: [PATCH 25/26] Remove changes to NewQuery --- server/service/src/main.rs | 59 +++++++++++++---------------- server/ui/src/components/navbar.tsx | 4 +- 2 files changed, 28 insertions(+), 35 deletions(-) diff --git a/server/service/src/main.rs b/server/service/src/main.rs index 4debadc..1767da9 100644 --- a/server/service/src/main.rs +++ b/server/service/src/main.rs @@ -35,7 +35,7 @@ enum SortKey { impl Default for SortKey { fn default() -> Self { - Self::Name + Self::Scraped } } @@ -119,14 +119,10 @@ async fn search( Ok(res) } -// TODO what is NewQuery used for? -// it is not a file search, that is done through SearchQuery #[derive(Deserialize)] struct NewQuery { page: Option, size: Option, - sort_key: Option, - sort_dir: Option, type_: Option, } @@ -134,7 +130,6 @@ async fn new_torrents( db: web::Data>, query: web::Query, ) -> Result { - let res = web::block(move || { let conn = db.get().unwrap(); new_query(query, conn) @@ -148,29 +143,6 @@ async fn new_torrents( Ok(res) } -fn build_order_clause(type_: &str, sort_key: SortKey, sort_dir: SortDirection) -> String { - let dir = match sort_dir { - SortDirection::Asc => "asc", - SortDirection::Desc => "desc", - }; - - let column = match sort_key { - SortKey::Name => { - if type_ == "file" { - "path" - } else { - "name" - } - }, - SortKey::Size => "size_bytes", - SortKey::Seeds => "seeders", - SortKey::Leeches => "leechers", - SortKey::Scraped => "scraped_date", - }; - - format!("{} {}", column, dir) -} - fn search_query( query: web::Query, conn: r2d2::PooledConnection, @@ -211,14 +183,12 @@ fn new_query( let page = query.page.unwrap_or(1); let size = cmp::min(100, query.size.unwrap_or(DEFAULT_SIZE)); - let sort_key = query.sort_key.unwrap_or_default(); - let sort_dir = query.sort_dir.unwrap_or_default(); let type_ = query.type_.as_ref().map_or("torrent", String::deref); let offset = size * (page - 1); println!( - "new, type = {}, page = {}, size = {}, sort_key = {:?}, sort_dir = {:?}", - type_, page, size, sort_key, sort_dir + "new, type = {}, page = {}, size = {}", + type_, page, size ); let res = if type_ == "file" { @@ -405,6 +375,29 @@ fn torrent_file_new( Ok(files) } +fn build_order_clause(type_: &str, sort_key: SortKey, sort_dir: SortDirection) -> String { + let dir = match sort_dir { + SortDirection::Asc => "asc", + SortDirection::Desc => "desc", + }; + + let column = match sort_key { + SortKey::Name => { + if type_ == "file" { + "path" + } else { + "name" + } + }, + SortKey::Size => "size_bytes", + SortKey::Seeds => "seeders", + SortKey::Leeches => "leechers", + SortKey::Scraped => "scraped_date", + }; + + format!("{} {}", column, dir) +} + #[cfg(test)] mod tests { use r2d2_sqlite::SqliteConnectionManager; diff --git a/server/ui/src/components/navbar.tsx b/server/ui/src/components/navbar.tsx index fde4884..95139ba 100644 --- a/server/ui/src/components/navbar.tsx +++ b/server/ui/src/components/navbar.tsx @@ -53,7 +53,7 @@ export class Navbar extends Component { value={this.state.searchParams.q} onInput={linkEvent(this, this.searchChange)}>
-
- {this.sortLink("Name")} - - {this.sortLink("Size")} - - {this.sortLink("Seeders")} - - {this.sortLink("Leechers")} - - {this.sortLink("Scraped")} - {this.sortLink("Name")}{this.sortLink("Size")}{this.sortLink("Seeders")}{this.sortLink("Leechers")}{this.sortLink("Scraped")}
{this.sortLink("Name")} {this.sortLink("Size")}{this.sortLink("Seeders")}{this.sortLink("Leechers")}{this.sortLink("Seeds")}{this.sortLink("Leeches")} {this.sortLink("Scraped")}