From 362e5f54d10f0776180c134ba0a94169cb7c58fb Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 28 Nov 2017 20:58:45 +0100 Subject: [PATCH 01/19] Added react-interactive package to make it easier to handle hover and focus on mobile. --- client/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/client/package.json b/client/package.json index 7d36795..f166ff8 100644 --- a/client/package.json +++ b/client/package.json @@ -19,6 +19,7 @@ "react-burger-menu": "^2.1.6", "react-dom": "^15.5.4", "react-infinite-scroller": "^1.0.15", + "react-interactive": "^0.8.1", "react-notify-toast": "^0.3.2", "react-redux": "^5.0.6", "react-responsive": "^1.3.4", From 62b6f5c8ca6935a87dc4a595434c3114e804cda9 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 28 Nov 2017 21:02:25 +0100 Subject: [PATCH 02/19] Using react-interactive we now have hover animations for our buttons. --- client/app/components/MovieObject.jsx | 42 ++++++++++++++++--- .../components/styles/movieObjectStyle.jsx | 15 ++++++- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/client/app/components/MovieObject.jsx b/client/app/components/MovieObject.jsx index 64ea04c..d78747e 100644 --- a/client/app/components/MovieObject.jsx +++ b/client/app/components/MovieObject.jsx @@ -11,6 +11,9 @@ import RequestButton from './buttons/request_button.jsx'; import { fetchJSON } from './http.jsx'; +import Interactive from 'react-interactive'; + + class MovieObject { constructor(object) { this.id = object.id; @@ -42,6 +45,13 @@ class MovieObject { notify.show(this.title + ' requested!', 'success', 3000); } + invertButtonColors(event) { + const event_type = event.type; + if (event_type) + console.log('test') + console.log(event.type) + } + getElement(index) { const element_key = index + this.id; @@ -55,11 +65,26 @@ class MovieObject { var foundInPlex; if (this.matchedInPlex) { - foundInPlex = ; + foundInPlex = {this.requestExisting(this)}} + style={movieStyle.requestButton}> + + Request Anyway + ; } else { - foundInPlex = ; + foundInPlex = {this.requestMovie()}} + onMouseOver={() => {this.invertButtonColors(event)}} + style={movieStyle.requestButton}> + + + Request + ; } if (this.type === 'movie') @@ -105,7 +130,14 @@ class MovieObject {
{foundInPlex} - + + + Info +
diff --git a/client/app/components/styles/movieObjectStyle.jsx b/client/app/components/styles/movieObjectStyle.jsx index 627d7dd..c16a89f 100644 --- a/client/app/components/styles/movieObjectStyle.jsx +++ b/client/app/components/styles/movieObjectStyle.jsx @@ -62,8 +62,9 @@ export default { requestButton: { color: '#e9a131', marginRight: '10px', - background: 'white', + backgroundColor: 'white', border: '#e9a131 2px solid', + borderColor: '#e9a131', borderRadius: '4px', textAlign: 'center', padding: '10px', @@ -74,10 +75,15 @@ export default { cursor: 'pointer' }, + requestButton_hover: { + backgroundColor: '#e9a131', + color: 'white', + }, + tmdbButton: { color: '#00d17c', marginRight: '10px', - background: 'white', + backgroundColor: 'white', border: '#00d17c 2px solid', borderRadius: '4px', textAlign: 'center', @@ -89,6 +95,11 @@ export default { cursor: 'pointer' }, + tmdbButton_hover: { + backgroundColor: '#00d17c', + color: 'white', + }, + row: { width: '100%' }, From 57658f10c148c6cddc84bdd209ea4976aeff4106 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Tue, 28 Nov 2017 22:25:06 +0100 Subject: [PATCH 03/19] Used the wrong shows.db. Now corrected. --- .gitignore | 1 + seasoned_api/.gitignore | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 53ffa91..e85cd5a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .DS_Store env +shows.db yarn.lock */yarn.lock diff --git a/seasoned_api/.gitignore b/seasoned_api/.gitignore index 922c3ee..9f7246b 100644 --- a/seasoned_api/.gitignore +++ b/seasoned_api/.gitignore @@ -60,5 +60,4 @@ typings/ # - - - - - # My own gitignore files and folders -shows.db conf From 8542da34cd4d41fd144177a38d39d1c1e2d2a067 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 10:37:31 +0100 Subject: [PATCH 04/19] Added a separate stylesheet for our buttons. --- client/app/components/styles/buttons.jsx | 80 ++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 client/app/components/styles/buttons.jsx diff --git a/client/app/components/styles/buttons.jsx b/client/app/components/styles/buttons.jsx new file mode 100644 index 0000000..a27fdba --- /dev/null +++ b/client/app/components/styles/buttons.jsx @@ -0,0 +1,80 @@ + +export default { + + submit: { + color: '#e9a131', + marginRight: '10px', + backgroundColor: 'white', + border: '#e9a131 2px solid', + borderColor: '#e9a131', + borderRadius: '4px', + textAlign: 'center', + padding: '10px', + minWidth: '100px', + float: 'left', + fontSize: '13px', + fontWeight: '800', + cursor: 'pointer', + }, + + submit_hover: { + backgroundColor: '#e9a131', + color: 'white', + }, + + info: { + color: '#00d17c', + marginRight: '10px', + backgroundColor: 'white', + border: '#00d17c 2px solid', + borderRadius: '4px', + textAlign: 'center', + padding: '10px', + minWidth: '100px', + float: 'left', + fontSize: '13px', + fontWeight: '800', + cursor: 'pointer', + }, + + info_hover: { + backgroundColor: '#00d17c', + color: 'white', + }, + + edit: { + color: '#4a95da', + marginRight: '10px', + backgroundColor: 'white', + border: '#4a95da 2px solid', + borderRadius: '4px', + textAlign: 'center', + padding: '10px', + minWidth: '100px', + float: 'left', + fontSize: '13px', + fontWeight: '800', + cursor: 'pointer', + }, + + edit_small: { + color: '#4a95da', + marginRight: '10px', + backgroundColor: 'white', + border: '#4a95da 2px solid', + borderRadius: '4px', + textAlign: 'center', + padding: '4px', + minWidth: '50px', + float: 'left', + fontSize: '13px', + fontWeight: '800', + cursor: 'pointer', + }, + + edit_hover: { + backgroundColor: '#4a95da', + color: 'white', + }, + +} \ No newline at end of file From e1fed24fc02b4a8f9b91987ac81d686024b82485 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 10:39:18 +0100 Subject: [PATCH 05/19] Added a loading image to pirateSearch. Now we get feedback when waiting for response from the api. --- client/app/components/admin/PirateSearch.jsx | 32 +++++++++++++++--- client/app/components/images/loading.jsx | 34 ++++++++++++++++++++ client/app/components/images/loading.svg | 7 ++++ 3 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 client/app/components/images/loading.jsx create mode 100644 client/app/components/images/loading.svg diff --git a/client/app/components/admin/PirateSearch.jsx b/client/app/components/admin/PirateSearch.jsx index b1d5191..25917fa 100644 --- a/client/app/components/admin/PirateSearch.jsx +++ b/client/app/components/admin/PirateSearch.jsx @@ -1,18 +1,25 @@ import React, { Component } from 'react'; import { fetchJSON } from '../http.jsx'; +// Stylesheets +import btnStylesheet from '../styles/buttons.jsx'; + +// Interactive button +import Interactive from 'react-interactive'; + +import Loading from '../images/loading.jsx' + class PirateSearch extends Component { constructor() { super(); this.state = { response: [], name: '', + loading: '', } } sendToDownload(torrent) { - console.log(torrent.magnet) - let data = {magnet: torrent.magnet} fetchJSON('https://apollo.kevinmidboe.com/api/v1/pirate/add', 'POST', data) .then((response) => { @@ -24,10 +31,15 @@ class PirateSearch extends Component { const query = this.props.name; const type = this.props.type; + this.setState({ + loading: + }) + fetchJSON('https://apollo.kevinmidboe.com/api/v1/pirate/search?query='+query+'&type='+type, 'GET') .then((response) => { console.log(response.torrents) this.setState({ + loading: '', response: response.torrents.map((torrent, index) => { return (
@@ -46,8 +58,20 @@ class PirateSearch extends Component { render() { return (
- {this.props.name} - +
+ {this.searchTheBay()}} + style={btnStylesheet.submit} + focus={btnStylesheet.submit_hover} + hover={btnStylesheet.submit_hover}> + + Search for torrents + +
+ + { this.state.loading } + {this.state.response}
) diff --git a/client/app/components/images/loading.jsx b/client/app/components/images/loading.jsx new file mode 100644 index 0000000..455e39c --- /dev/null +++ b/client/app/components/images/loading.jsx @@ -0,0 +1,34 @@ +import React from 'react'; + +function Loading() { + return ( +
+ + + + + + +
+ + ) +} + +export default Loading; \ No newline at end of file diff --git a/client/app/components/images/loading.svg b/client/app/components/images/loading.svg new file mode 100644 index 0000000..c8f7e85 --- /dev/null +++ b/client/app/components/images/loading.svg @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file From 5fb1e7ba2ed6a4e78f28c0b2437a0d724d7ff3ea Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 11:20:26 +0100 Subject: [PATCH 06/19] Sidebar is now redesigned with color indicators, filtering name by search query or filtering status by dropdown. Moved CSS to a separate stylesheet. --- client/app/components/admin/Sidebar.jsx | 248 ++++++++++++++---- client/app/components/styles/adminSidebar.jsx | 50 ++++ 2 files changed, 252 insertions(+), 46 deletions(-) create mode 100644 client/app/components/styles/adminSidebar.jsx diff --git a/client/app/components/admin/Sidebar.jsx b/client/app/components/admin/Sidebar.jsx index 5d001b0..7d48397 100644 --- a/client/app/components/admin/Sidebar.jsx +++ b/client/app/components/admin/Sidebar.jsx @@ -1,57 +1,213 @@ import React, { Component } from 'react'; import { Link } from 'react-router-dom'; +import Interactive from 'react-interactive'; + +import sidebarCSS from '../styles/adminSidebar.jsx' + class SidebarComponent extends Component { - generateListElements(index, item) { - if (index == this.props.listItemSelected) - return ( - {item.name} - ) - else - return ( - {item.name} - ) - } - - displayRequestedElementsInfo() { - if (this.props.requested_objects) { - let requestedElement = this.props.requested_objects.map((item, index) => { - return ( - - { this.generateListElements(index, item) } - {item.status} - {item.requested_date} - - ) - }) - - return ( - - - - - - - - - - {requestedElement} - -
NameStatusDate
- ) + constructor(props){ + super(props) + // Constructor with states holding the search query and the element of reponse. + this.state = { + filterValue: '', + filterQuery: '', + requestItemsToBeDisplayed: [], + listItemSelected: '', } } - render() { - console.log('sidebar: ', this.props.requested_objects) - return ( -
-

Hello from the sidebar:

- { this.displayRequestedElementsInfo() } -
- ); - } + // Where we wait for api response to be delivered from parent through props + componentWillReceiveProps(props) { + this.state.listItemSelected = props.listItemSelected; + this.displayRequestedElementsInfo(props.requested_objects); + } + + // Inputs a date and returns a text string that matches how long it was since + convertDateToDaysSince(date) { + var oneDay = 24*60*60*1000; + var firstDate = new Date(date); + var secondDate = new Date(); + + var diffDays = Math.round(Math.abs((firstDate.getTime() - secondDate.getTime()) / oneDay)); + + switch (diffDays) { + case 0: + return 'Today'; + case 1: + return '1 day ago' + default: + return diffDays + ' days ago' + } + } + + // Called from our dropdown, receives a filter string and checks it with status field + // of our request objects. + filterItems(filterValue) { + let filteredRequestElements = this.props.requested_objects.map((item, index) => { + if (item.status === filterValue || filterValue === 'all') + return this.generateListElements(index, item); + }) + + this.setState({ + requestItemsToBeDisplayed: filteredRequestElements, + filterValue: filterValue, + }) + } + + + // Updates the internal state of the query filter and updates the list to only + // display names matching the query. This is real-time filtering. + updateFilterQuery(event) { + const query = event.target.value; + + let filteredByQuery = this.props.requested_objects.map((item, index) => { + if (item.name.toLowerCase().indexOf(query.toLowerCase()) != -1) + return this.generateListElements(index, item); + }) + + this.setState({ + requestItemsToBeDisplayed: filteredByQuery, + filterQuery: query, + }); + } + + + generateFilterDropdown() { + return ( + + ) + } + + generateFilterSearchbar() { + return ( + this.updateFilterQuery(event)} + value={this.state.filterQuery}/> + ) + } + + // A colored bar indicating the status of a item by color. + generateRequestIndicator(status) { + let statusColor; + + switch (status) { + case 'requested': + // Yellow + statusColor = '#ffe14d'; + break; + case 'downloading': + // Blue + statusColor = '#3fc3f3'; + break; + case 'downloaded': + // Green + statusColor = '#6be682'; + break; + default: + statusColor = 'grey'; + } + + const indicatorCSS = { + width: '100%', + height: '4px', + marginTop: '3px', + backgroundColor: statusColor, + } + + return ( +
+ ) + } + + + generateListElements(index, item) { + if (index == this.state.listItemSelected) { + return ( +
+
+ {item.name } +
+ { this.convertDateToDaysSince(item.requested_date) } +
+
+ Status: { item.status } +
+ Matches found: 0 + { this.generateRequestIndicator(item.status) } +
+ ) + } + else + return ( + + + + {item.name } +
+ { this.convertDateToDaysSince(item.requested_date) } +
+
+ { this.generateRequestIndicator(item.status) } +
+ + ) + } + + // This is our main loader that gets called when we receive api response through props from parent + displayRequestedElementsInfo(requested_objects) { + let requestedElement = requested_objects.map((item, index) => { + if (['requested', 'downloading', 'downloaded'].indexOf(this.state.filterValue) != -1) { + if (item.status === this.state.filterValue){ + return this.generateListElements(index, item); + } + } + + else if (this.state.filterQuery !== '') { + if (item.name.toLowerCase().indexOf(this.state.filterQuery.toLowerCase()) != -1) + return this.generateListElements(index, item); + } + + else + return this.generateListElements(index, item); + }) + + this.setState({ + requestItemsToBeDisplayed: requestedElement, + }) + } + + render() { + let bodyCSS = sidebarCSS.body; + if (typeof InstallTrigger !== 'undefined') + bodyCSS.width = '-moz-min-content'; + + return ( +
+

Hello from the sidebar:

+ { this.generateFilterDropdown() } + { this.generateFilterSearchbar() } +
+ { this.state.requestItemsToBeDisplayed } +
+
+ ); + } } export default SidebarComponent; \ No newline at end of file diff --git a/client/app/components/styles/adminSidebar.jsx b/client/app/components/styles/adminSidebar.jsx new file mode 100644 index 0000000..c10fe49 --- /dev/null +++ b/client/app/components/styles/adminSidebar.jsx @@ -0,0 +1,50 @@ +export default { + body: { + backgroundColor: 'white', + width: 'min-content', + }, + + parentElement: { + display: 'inline-block', + width: '300px', + border: '1px solid black', + borderRadius: '2px', + padding: '4px', + margin: '4px', + marginLeft: '4px', + backgroundColor: 'white', + }, + + parentElement_hover: { + marginLeft: '10px', + }, + + parentElement_active: { + textDecoration: 'none', + }, + + parentElement_selected: { + display: 'inline-block', + width: '300px', + border: '1px solid black', + borderRadius: '2px', + padding: '4px', + margin: '4px 0px 4px 4px', + marginLeft: '10px', + backgroundColor: 'white', + }, + + title: { + maxWidth: '70%', + display: 'inline-flex', + }, + + link: { + color: 'black', + textDecoration: 'none', + }, + + rightContainer: { + float: 'right', + }, +} \ No newline at end of file From 3fe8f46dd49e9589daf74011e14b8bcc527dfdc7 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 11:31:25 +0100 Subject: [PATCH 07/19] Viewport changes to limit zoom in on mobile the typing in a search bar. --- client/app/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/index.html b/client/app/index.html index 3ab7f3f..3ec9c02 100644 --- a/client/app/index.html +++ b/client/app/index.html @@ -4,7 +4,7 @@ - + seasoned Shows From 8695a553d641894d2cb82f81afef9a716825372a Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 11:32:49 +0100 Subject: [PATCH 08/19] Admin page is our landing page for admin panel, now it passes our api response to sidebar and info panel when we get the response back from the api. Some renaming of stylesheet variable. --- client/app/components/admin/Admin.jsx | 30 +++++++------------ .../app/components/styles/adminComponent.jsx | 8 +++++ 2 files changed, 19 insertions(+), 19 deletions(-) create mode 100644 client/app/components/styles/adminComponent.jsx diff --git a/client/app/components/admin/Admin.jsx b/client/app/components/admin/Admin.jsx index 47d941a..50047b1 100644 --- a/client/app/components/admin/Admin.jsx +++ b/client/app/components/admin/Admin.jsx @@ -1,10 +1,5 @@ -/* - ./app/components/App.jsx - - -*/ + import React from 'react'; -import { HashRouter as Router, Route, Switch, IndexRoute } from 'react-router-dom'; import LoginForm from './LoginForm/LoginForm.jsx'; import { Provider } from 'react-redux'; @@ -16,6 +11,8 @@ import { fetchJSON } from '../http.jsx'; import Sidebar from './Sidebar.jsx'; import AdminRequestInfo from './AdminRequestInfo.jsx'; +import adminCSS from '../styles/adminComponent.jsx' + class AdminComponent extends React.Component { constructor(props) { @@ -25,6 +22,7 @@ class AdminComponent extends React.Component { } } + // Fetches all requested elements and updates the state with response componentWillMount() { fetchJSON('https://apollo.kevinmidboe.com/api/v1/plex/requests/all', 'GET') .then(result => { @@ -34,16 +32,9 @@ class AdminComponent extends React.Component { }) } + // Displays loginform if not logged in and passes response from + // api call to sidebar and infoPanel through props verifyLoggedIn() { - let adminComponentStyle = { - sidebar: { - float: 'left', - }, - selectedObjectPanel: { - float: 'left', - } - } - const logged_in = getCookie('logged_in'); if (!logged_in) { return @@ -53,20 +44,21 @@ class AdminComponent extends React.Component { let listItemSelected = undefined; const requestParam = this.props.match.params.request; + if (requestParam && this.state.requested_objects !== '') { selectedRequest = this.state.requested_objects[requestParam] - listItemSelected = requestParam + listItemSelected = requestParam; } return (
-
+
+ />
-
+
Date: Sat, 2 Dec 2017 11:35:10 +0100 Subject: [PATCH 09/19] Deleted unused svg image. --- client/app/components/images/loading.svg | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 client/app/components/images/loading.svg diff --git a/client/app/components/images/loading.svg b/client/app/components/images/loading.svg deleted file mode 100644 index c8f7e85..0000000 --- a/client/app/components/images/loading.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file From 9fcc82d7cf24fa02facc8b8dd9dab74eb6affee3 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 11:35:39 +0100 Subject: [PATCH 10/19] Put both jsx and js varaibles on same line in config file. --- client/webpack.common.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/webpack.common.js b/client/webpack.common.js index 114c775..0fd9a5f 100644 --- a/client/webpack.common.js +++ b/client/webpack.common.js @@ -21,8 +21,7 @@ module.exports = { ], module: { loaders: [ - { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, - { test: /\.jsx$/, loader: 'babel-loader', exclude: /node_modules/ }, + { test: /\.(js|jsx)$/, loader: 'babel-loader', exclude: /node_modules/ }, ] }, From 187ac6317e33a930855c9cc7bdb8798aef848672 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 12:01:49 +0100 Subject: [PATCH 11/19] Still a bit messy, but removed unsued sections and redid the naming schema for css items. --- client/app/components/MovieObject.jsx | 231 ++++++++++++-------------- 1 file changed, 106 insertions(+), 125 deletions(-) diff --git a/client/app/components/MovieObject.jsx b/client/app/components/MovieObject.jsx index d78747e..9f13a46 100644 --- a/client/app/components/MovieObject.jsx +++ b/client/app/components/MovieObject.jsx @@ -3,154 +3,135 @@ import React from 'react'; import Notifications, {notify} from 'react-notify-toast'; // StyleComponents -import movieStyle from './styles/movieObjectStyle.jsx'; +import searchResultCSS from './styles/searchResult.jsx'; +import buttonsCSS from './styles/buttons.jsx'; var MediaQuery = require('react-responsive'); -import RequestButton from './buttons/request_button.jsx'; - import { fetchJSON } from './http.jsx'; import Interactive from 'react-interactive'; class MovieObject { - constructor(object) { - this.id = object.id; - this.title = object.title; - this.year = object.year; - this.type = object.type; - // Check if object.poster != undefined - this.rating = object.rating; - this.poster = object.poster; - this.background = object.background; - this.matchedInPlex = object.matchedInPlex; - this.summary = object.summary; - } + constructor(object) { + this.id = object.id; + this.title = object.title; + this.year = object.year; + this.type = object.type; + this.rating = object.rating; + this.poster = object.poster; + this.background = object.background; + this.matchedInPlex = object.matchedInPlex; + this.summary = object.summary; + } - requestExisting(movie) { - console.log('Exists', movie); - } + requestExisting(movie) { + console.log('Exists', movie); + } - requestMovie() { - // fetch('http://localhost:31459/api/v1/plex/request/' + this.id + '?type='+this.type, { - // fetch('https://apollo.kevinmidboe.com/api/v1/plex/request/' + this.id + '?type='+this.type, { - // method: 'POST' - // }); - fetchJSON('https://apollo.kevinmidboe.com/api/v1/plex/request/' + this.id + '?type='+this.type, 'POST') - .then((response) => { - console.log(response); - }) + requestMovie() { + fetchJSON('https://apollo.kevinmidboe.com/api/v1/plex/request/' + this.id + '?type='+this.type, 'POST') + .then((response) => { + console.log(response); + notify.show(this.title + ' requested!', 'success', 3000); + }) + .catch((e) => { + console.error('Request movie fetch went wrong: '+ e); + }) - notify.show(this.title + ' requested!', 'success', 3000); - } + } - invertButtonColors(event) { - const event_type = event.type; - if (event_type) - console.log('test') - console.log(event.type) - } + getElement(index) { + const element_key = index + this.id; - getElement(index) { - const element_key = index + this.id; + if (this.poster == null || this.poster == undefined) { + var posterPath = 'https://openclipart.org/image/2400px/svg_to_png/211479/Simple-Image-Not-Found-Icon.png' + } else { + var posterPath = 'https://image.tmdb.org/t/p/w300' + this.poster; + } + var backgroundPath = 'https://image.tmdb.org/t/p/w640_and_h360_bestv2/' + this.background; - // TODO set the poster image async by updating the dom after this is returned - if (this.poster == null || this.poster == undefined) { - var posterPath = 'https://openclipart.org/image/2400px/svg_to_png/211479/Simple-Image-Not-Found-Icon.png' - } else { - var posterPath = 'https://image.tmdb.org/t/p/w300' + this.poster; - } - var backgroundPath = 'https://image.tmdb.org/t/p/w640_and_h360_bestv2/' + this.background; + var foundInPlex; + if (this.matchedInPlex) { + foundInPlex = {this.requestExisting(this)}} + style={buttonsCSS.submit} + focus={buttonsCSS.submit_hover} + hover={buttonsCSS.submit_hover}> - var foundInPlex; - if (this.matchedInPlex) { - foundInPlex = {this.requestExisting(this)}} - style={movieStyle.requestButton}> - - Request Anyway - ; - } else { - foundInPlex = {this.requestMovie()}} - onMouseOver={() => {this.invertButtonColors(event)}} - style={movieStyle.requestButton}> + Request Anyway + ; + } else { + foundInPlex = {this.requestMovie()}} + style={buttonsCSS.submit} + focus={buttonsCSS.submit_hover} + hover={buttonsCSS.submit_hover}> - + Request - ; - } + + Request + ; + } - if (this.type === 'movie') - var themoviedbLink = 'https://www.themoviedb.org/movie/' + this.id - else if (this.type === 'show') - var themoviedbLink = 'https://www.themoviedb.org/tv/' + this.id + if (this.type === 'movie') + var themoviedbLink = 'https://www.themoviedb.org/movie/' + this.id + else if (this.type === 'show') + var themoviedbLink = 'https://www.themoviedb.org/tv/' + this.id - + // TODO go away from using mediaQuery, and create custom resizer + return ( +
+ + +
+ +
+ +
+ {this.title} +

+ Released: { this.year } | Rating: {this.rating} +

+ {this.summary} +

+
- // TODO add request button class - return ( -
- -
- -
- -
-
-
- - {this.title} -

- Released: { this.year } | Rating: {this.rating} -

- {this.summary} -

-
+ + + {this.title} +

+ Released: {this.year} | Rating: {this.rating} +
+ +
+ + +
+
+ +
+
+
+
+ ) - - - - - -
- {foundInPlex} - - - - Info - - -
-
-
- -

-
-
-
-
-
) - - } + } } export default MovieObject; \ No newline at end of file From e55067025ec5fd0060b910ad4d078911665e66b0 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 12:05:17 +0100 Subject: [PATCH 12/19] Renamed movieObject to searchObject, same with css. --- .../{MovieObject.jsx => SearchObject.jsx} | 26 ++-- .../components/styles/movieObjectStyle.jsx | 112 ------------------ client/app/components/styles/searchObject.jsx | 58 +++++++++ 3 files changed, 71 insertions(+), 125 deletions(-) rename client/app/components/{MovieObject.jsx => SearchObject.jsx} (81%) delete mode 100644 client/app/components/styles/movieObjectStyle.jsx create mode 100644 client/app/components/styles/searchObject.jsx diff --git a/client/app/components/MovieObject.jsx b/client/app/components/SearchObject.jsx similarity index 81% rename from client/app/components/MovieObject.jsx rename to client/app/components/SearchObject.jsx index 9f13a46..b06208c 100644 --- a/client/app/components/MovieObject.jsx +++ b/client/app/components/SearchObject.jsx @@ -3,7 +3,7 @@ import React from 'react'; import Notifications, {notify} from 'react-notify-toast'; // StyleComponents -import searchResultCSS from './styles/searchResult.jsx'; +import searchObjectCSS from './styles/searchObject.jsx'; import buttonsCSS from './styles/buttons.jsx'; var MediaQuery = require('react-responsive'); @@ -85,27 +85,27 @@ class MovieObject {
-
+
-
- +
+
- {this.title} + {this.title}

- Released: { this.year } | Rating: {this.rating} + Released: { this.year } | Rating: {this.rating}

- {this.summary} + {this.summary}

- - {this.title} + + {this.title}

- Released: {this.year} | Rating: {this.rating} + Released: {this.year} | Rating: {this.rating}
-
+
{foundInPlex} @@ -125,8 +125,8 @@ class MovieObject {
-
-
+
+
) diff --git a/client/app/components/styles/movieObjectStyle.jsx b/client/app/components/styles/movieObjectStyle.jsx deleted file mode 100644 index c16a89f..0000000 --- a/client/app/components/styles/movieObjectStyle.jsx +++ /dev/null @@ -1,112 +0,0 @@ - -export default { - resultItem: { - maxWidth: '95%', - margin: '0 auto', - minHeight: '230px' - }, - - movie_content: { - marginLeft: '15px' - }, - - resultTitleLarge: { - color: 'black', - fontSize: '2em', - }, - - resultTitleSmall: { - color: 'black', - fontSize: '22px', - }, - - yearRatingLarge: { - fontSize: '0.8em' - }, - - resultPoster: { - float: 'left', - zIndex: '3', - position: 'relative', - marginRight: '30px' - }, - - background: { - width: '100%' - }, - - yearRatingSmall: { - marginTop: '5px', - fontSize: '0.8em' - }, - - resultPosterImg: { - border: '2px none', - borderRadius: '2px', - width: '150px' - }, - - cornerRibbon: { - position: 'absolute', - width: '450px', - }, - - summary: { - fontSize: '15px', - }, - - buttons: { - paddingTop: '20px' - }, - - requestButton: { - color: '#e9a131', - marginRight: '10px', - backgroundColor: 'white', - border: '#e9a131 2px solid', - borderColor: '#e9a131', - borderRadius: '4px', - textAlign: 'center', - padding: '10px', - minWidth: '100px', - float: 'left', - fontSize: '13px', - fontWeight: '800', - cursor: 'pointer' - }, - - requestButton_hover: { - backgroundColor: '#e9a131', - color: 'white', - }, - - tmdbButton: { - color: '#00d17c', - marginRight: '10px', - backgroundColor: 'white', - border: '#00d17c 2px solid', - borderRadius: '4px', - textAlign: 'center', - padding: '10px', - minWidth: '100px', - float: 'left', - fontSize: '13px', - fontWeight: '800', - cursor: 'pointer' - }, - - tmdbButton_hover: { - backgroundColor: '#00d17c', - color: 'white', - }, - - row: { - width: '100%' - }, - - itemDivider: { - width: '90%', - borderBottom: '1px solid grey', - margin: '2rem auto' - } -} \ No newline at end of file diff --git a/client/app/components/styles/searchObject.jsx b/client/app/components/styles/searchObject.jsx new file mode 100644 index 0000000..4d4ed72 --- /dev/null +++ b/client/app/components/styles/searchObject.jsx @@ -0,0 +1,58 @@ + +export default { + container: { + maxWidth: '95%', + margin: '0 auto', + minHeight: '230px' + }, + + title_large: { + color: 'black', + fontSize: '2em', + }, + + title_small: { + color: 'black', + fontSize: '22px', + }, + + stats_large: { + fontSize: '0.8em' + }, + + stats_small: { + marginTop: '5px', + fontSize: '0.8em' + }, + + posterContainer: { + float: 'left', + zIndex: '3', + position: 'relative', + marginRight: '30px' + }, + + posterImage: { + border: '2px none', + borderRadius: '2px', + width: '150px' + }, + + backgroundImage: { + width: '100%' + }, + + summary: { + fontSize: '15px', + }, + + dividerRow: { + width: '100%' + }, + + itemDivider: { + width: '90%', + borderBottom: '1px solid grey', + margin: '2rem auto' + } +} \ No newline at end of file From fd0a2c9d50cff4d38a170f360078ca265e4ed49c Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 12:08:47 +0100 Subject: [PATCH 13/19] Renamed the exporting class name to match the file name. --- client/app/components/SearchObject.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/app/components/SearchObject.jsx b/client/app/components/SearchObject.jsx index b06208c..2be95d3 100644 --- a/client/app/components/SearchObject.jsx +++ b/client/app/components/SearchObject.jsx @@ -13,7 +13,7 @@ import { fetchJSON } from './http.jsx'; import Interactive from 'react-interactive'; -class MovieObject { +class SearchObject { constructor(object) { this.id = object.id; this.title = object.title; @@ -134,4 +134,4 @@ class MovieObject { } } -export default MovieObject; \ No newline at end of file +export default SearchObject; \ No newline at end of file From 45db534681f77c7d5248cc9f2816cb63dc0d9918 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 12:17:00 +0100 Subject: [PATCH 14/19] When a requested element from the sidebar is selected this is where the detailed info is displayed. Now it is possible to change the status of a item by using the dropdown. This is where piratesearch also gets the query name through props. --- .../app/components/admin/AdminRequestInfo.jsx | 143 ++++++++++++------ .../components/styles/adminRequestInfo.jsx | 11 ++ 2 files changed, 105 insertions(+), 49 deletions(-) create mode 100644 client/app/components/styles/adminRequestInfo.jsx diff --git a/client/app/components/admin/AdminRequestInfo.jsx b/client/app/components/admin/AdminRequestInfo.jsx index a3a89f4..2f51113 100644 --- a/client/app/components/admin/AdminRequestInfo.jsx +++ b/client/app/components/admin/AdminRequestInfo.jsx @@ -1,10 +1,31 @@ import React, { Component } from 'react'; + +import { fetchJSON } from '../http.jsx'; + import PirateSearch from './PirateSearch.jsx' +// Stylesheets +import requestInfoCSS from '../styles/adminRequestInfo.jsx' +import buttonsCSS from '../styles/buttons.jsx'; + +// Interactive button +import Interactive from 'react-interactive'; + class AdminRequestInfo extends Component { constructor() { super(); + + this.state = { + statusValue: '', + } + + this.requestInfo = ''; + } + + componentWillReceiveProps(props) { + this.requestInfo = props.selectedRequest; + this.state.statusValue = this.requestInfo.status; } userAgent(agent) { @@ -19,6 +40,35 @@ class AdminRequestInfo extends Component { return ''; } + generateStatusDropdown() { + return ( + + ) + } + + updateRequestStatus(event) { + const eventValue = event.target.value; + const itemID = this.requestInfo.id; + + const apiData = { + type: this.requestInfo.type, + status: eventValue, + } + + fetchJSON('https://apollo.kevinmidboe.com/api/v1/plex/request/' + itemID, 'PUT', apiData) + .then((response) => { + console.log('Response, updateRequestStatus: ', response) + }) + + this.setState({ + statusValue: eventValue + }) + } + requested_by_user(request_user) { if (request_user === 'NULL') return undefined @@ -28,57 +78,52 @@ class AdminRequestInfo extends Component { ) } - displayInfo() { - let adminIndexStyle = { - wrapper: { - width: '100%', - }, - headerWrapper: { - width: '100%', - }, - poster: { - float: 'left', - minHeight: '450px', - }, - info: { - float: 'left', - minHeight: '450px', - } - } - const request = this.props.selectedRequest; + displayInfo() { + const request = this.props.selectedRequest; - if (request) { - return ( -
-
- {request.name} - {request.year} -
-
- -
-
- type: {request.type}
- status: {request.status}
- ip: {request.ip}
- user_agent: {this.userAgent(request.user_agent)}
- request_date: {request.requested_date}
- { this.requested_by_user(request.requested_by) } -
- - - -
- ) - } - } + if (request) { + return ( +
+
+ {request.name} + {request.year} +
- render() { - return ( -
{this.displayInfo()}
- ); - } +
+ type: {request.type}
+ + {this.generateStatusDropdown()}
+ + status: {request.status}
+ ip: {request.ip}
+ user_agent: {this.userAgent(request.user_agent)}
+ request_date: {request.requested_date}
+ { this.requested_by_user(request.requested_by) } +
+ +
+ {}} + style={buttonsCSS.edit} + focus={buttonsCSS.edit_hover} + hover={buttonsCSS.edit_hover}> + + Show info + + + +
+
+ ) + } + } + + render() { + return ( +
{this.displayInfo()}
+ ); + } } export default AdminRequestInfo; \ No newline at end of file diff --git a/client/app/components/styles/adminRequestInfo.jsx b/client/app/components/styles/adminRequestInfo.jsx new file mode 100644 index 0000000..e11b3d4 --- /dev/null +++ b/client/app/components/styles/adminRequestInfo.jsx @@ -0,0 +1,11 @@ +export default = { + wrapper: { + width: '100%', + }, + headerWrapper: { + width: '100%', + }, + poster: { + minHeight: '450px', + }, +} \ No newline at end of file From 7bde2821d0b1973c1324860ea1a85980c6d97acd Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 13:13:35 +0100 Subject: [PATCH 15/19] Followed with the renaming of MovieObject to now be refered to as SearchObject. Also change the loading animation for InfiniteScroll. It is the same loading animation imported as we have in torrent search. --- client/app/components/SearchRequest.jsx | 48 ++++++++++++------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/client/app/components/SearchRequest.jsx b/client/app/components/SearchRequest.jsx index dcd1cdd..b5b6554 100644 --- a/client/app/components/SearchRequest.jsx +++ b/client/app/components/SearchRequest.jsx @@ -1,14 +1,14 @@ import React from 'react'; -import MovieObject from './MovieObject.jsx'; - -// StyleComponents -import searchStyle from './styles/searchRequestStyle.jsx'; -import movieStyle from './styles/movieObjectStyle.jsx'; - import URI from 'urijs'; import InfiniteScroll from 'react-infinite-scroller'; +// StyleComponents +import searchStyle from './styles/searchRequestStyle.jsx'; + +import SearchObject from './SearchObject.jsx'; +import Loading from './images/loading.jsx' + import { fetchJSON } from './http.jsx'; import { getCookie } from './Cookie.jsx'; @@ -29,7 +29,8 @@ class SearchRequest extends React.Component { page: 1, resultHeader: '', loadResults: false, - scrollHasMore: true + scrollHasMore: true, + loading: false, } this.allowedListTypes = ['discover', 'popular', 'nowplaying', 'upcoming'] @@ -88,9 +89,9 @@ class SearchRequest extends React.Component { this.state.page = 1; } - writeLoading() { + setLoading(value) { this.setState({ - responseMovieList: 'Loading...' + loading: value }); } @@ -122,10 +123,7 @@ class SearchRequest extends React.Component { // Here we first call api for a search with the input uri, handle any errors // and fill the reponseData from api into the state of reponseMovieList as movieObjects - callSearchFillMovieList(uri) { - // Write loading animation - // this.writeLoading(); - + callSearchFillMovieList(uri) { Promise.resolve() .then(() => this.callURI(uri, 'GET')) .then(response => { @@ -152,7 +150,7 @@ class SearchRequest extends React.Component { } // Convert to json and update the state of responseMovieList with the results of the api call - // mapped as a movieObject. + // mapped as a SearchObject. response.json() .then(responseData => { if (this.state.page === 1) { @@ -180,7 +178,6 @@ class SearchRequest extends React.Component { callListFillMovieList(uri) { // Write loading animation - // this.writeLoading(); Promise.resolve() .then(() => this.callURI(uri, 'GET', undefined)) @@ -198,7 +195,7 @@ class SearchRequest extends React.Component { } // Convert to json and update the state of responseMovieList with the results of the api call - // mapped as a movieObject. + // mapped as a SearchObject. response.json() .then(responseData => { if (this.state.page === 1) { @@ -218,6 +215,7 @@ class SearchRequest extends React.Component { }) .catch((error) => { console.log('Something went wrong when fetching query.', error) + }) } @@ -287,10 +285,10 @@ class SearchRequest extends React.Component { } } - // When called passes the variable to MovieObject and calls it's interal function for + // When called passes the variable to SearchObject and calls it's interal function for // generating the wanted HTML createMovieObjects(item, index) { - let movie = new MovieObject(item); + let movie = new SearchObject(item); return movie.getElement(index); } @@ -379,7 +377,7 @@ class SearchRequest extends React.Component { pageStart={0} loadMore={this.pageForwards.bind(this)} hasMore={this.state.scrollHasMore} - loader={loader} + loader={} initialLoad={this.state.loadResults}> @@ -403,10 +401,10 @@ class SearchRequest extends React.Component {
- {this.state.resultHeader} -



- - {this.state.responseMovieList} + {this.state.resultHeader} +



+ + {this.state.responseMovieList}
@@ -435,7 +433,7 @@ class SearchRequest extends React.Component { {this.state.resultHeader}



- {this.state.responseMovieList} + {this.state.responseMovieList}
@@ -446,4 +444,4 @@ class SearchRequest extends React.Component { } -export default SearchRequest; \ No newline at end of file +export default SearchRequest; From 580cdc430f550444a248bce19d20a779bb47b9fd Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 13:15:23 +0100 Subject: [PATCH 16/19] Removed a lot of unused css classes. --- .../components/styles/searchRequestStyle.jsx | 75 ++----------------- 1 file changed, 5 insertions(+), 70 deletions(-) diff --git a/client/app/components/styles/searchRequestStyle.jsx b/client/app/components/styles/searchRequestStyle.jsx index 45d5cff..17b5b11 100644 --- a/client/app/components/styles/searchRequestStyle.jsx +++ b/client/app/components/styles/searchRequestStyle.jsx @@ -11,12 +11,13 @@ export default { backgroundLargeHeader: { width: '100%', minHeight: '400px', - backgroundColor: '#011c23', + backgroundColor: 'rgb(1, 28, 35)', + // backgroundImage: 'radial-gradient(circle, #004c67 0, #005771 120%)', zIndex: 1, marginBottom: '-100px' }, - backgroundSmallHeader: { + backgroundSmallHeader: { width: '100%', minHeight: '300px', backgroundColor: '#011c23', @@ -31,7 +32,7 @@ export default { backgroundColor: 'white', position: 'relative', zIndex: '10', - boxShadow: '0 4px 2px black' + boxShadow: '0 1px 2px grey', }, pageTitle: { @@ -43,7 +44,7 @@ export default { pageTitleLargeSpan: { color: 'white', - fontSize: '3em', + fontSize: '4em', marginTop: '4vh', marginBottom: '6vh' }, @@ -133,70 +134,4 @@ export default { color: 'black', fontSize: '1.4em', }, - - row: { - width: '100%' - }, - - itemDivider: { - width: '90%', - borderBottom: '1px solid grey', - margin: '1rem auto' - }, - - - pageNavigationBar: { - width: '100%', - display: 'flex', - alignItems: 'center', - justifyContent: 'center' - }, - - pageNavigationButton: { - margin: '0 auto', - }, - - hvrUnderlineFromCenter: { - color: 'white', - fontSize: '1em', - paddingTop: '12px', - marginBottom: '12px', - marginLeft: '10px', - cursor: 'pointer', - display: 'inline-block', - verticalAlign: 'middle', - WebkitTransform: 'perspective(1px) translateZ(0)', - transform: 'perspective(1px) translateZ(0)', - boxShadow: '0 0 1px transparent', - position: 'relative', - overflow: 'hidden', - ':before': { - content: "", - position: 'absolute', - zIndex: '-1', - left: '50%', - right: '50%', - bottom: '0', - background: '#00d17c', - height: '2px', - WebkitTransitionProperty: 'left, right', - transitionProperty: 'left, right', - WebkitTransitionDuration: '0.3s', - transitionDuration: '0.3s', - WebkitTransitionTimingFunction: 'ease-out', - transitionTimingFunction: 'ease-out' - }, - ':hover:before': { - left: 0, - right: 0 - }, - 'focus:before': { - left: 0, - right: 0 - }, - 'active:before': { - left: 0, - right: 0 - } - } } \ No newline at end of file From 246abd7020c5df5847827922495bf14034193370 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 13:16:57 +0100 Subject: [PATCH 17/19] Renamed the variable name for our stylesheet. --- client/app/components/SearchRequest.jsx | 50 ++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/client/app/components/SearchRequest.jsx b/client/app/components/SearchRequest.jsx index b5b6554..39cdce2 100644 --- a/client/app/components/SearchRequest.jsx +++ b/client/app/components/SearchRequest.jsx @@ -4,7 +4,7 @@ import URI from 'urijs'; import InfiniteScroll from 'react-infinite-scroller'; // StyleComponents -import searchStyle from './styles/searchRequestStyle.jsx'; +import searchRequestCSS from './styles/searchRequestStyle.jsx'; import SearchObject from './SearchObject.jsx'; import Loading from './images/loading.jsx' @@ -343,12 +343,12 @@ class SearchRequest extends React.Component { movieToggle() { if (this.state.movieFilter) - return {this.toggleFilter('movies')}} id="category_active">Movies else - return {this.toggleFilter('movies')}} id="category_active">Movies @@ -356,12 +356,12 @@ class SearchRequest extends React.Component { showToggle() { if (this.state.showFilter) - return {this.toggleFilter('shows')}} id="category_active">TV Shows else - return {this.toggleFilter('shows')}} id="category_active">TV Shows @@ -381,17 +381,17 @@ class SearchRequest extends React.Component { initialLoad={this.state.loadResults}> -
-
-
- Request new content +
+
+
+ Request new content
-
-
- +
+
+ - this._handleQueryKeyPress(event)} onChange={event => this.updateQueryState(event)} value={this.state.searchQuery}/> @@ -400,8 +400,8 @@ class SearchRequest extends React.Component {
-
- {this.state.resultHeader} +
+ {this.state.resultHeader}



{this.state.responseMovieList} @@ -410,17 +410,17 @@ class SearchRequest extends React.Component { -
-
-
- Request new content +
+
+
+ Request new content
-
-
- +
+
+ - this._handleQueryKeyPress(event)} onChange={event => this.updateQueryState(event)} value={this.state.searchQuery}/> @@ -429,8 +429,8 @@ class SearchRequest extends React.Component {
-
- {this.state.resultHeader} +
+ {this.state.resultHeader}



{this.state.responseMovieList} From 7849a8ce3f93d1a3a6bac880da4624d7f8aa23cc Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 13:17:17 +0100 Subject: [PATCH 18/19] Fixed syntax error. --- client/app/components/styles/adminRequestInfo.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/components/styles/adminRequestInfo.jsx b/client/app/components/styles/adminRequestInfo.jsx index e11b3d4..0d13af2 100644 --- a/client/app/components/styles/adminRequestInfo.jsx +++ b/client/app/components/styles/adminRequestInfo.jsx @@ -1,4 +1,4 @@ -export default = { +export default { wrapper: { width: '100%', }, From c65cdd84b7f185d1a3d3bf6c35c0a13a93e61d80 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Sat, 2 Dec 2017 13:18:20 +0100 Subject: [PATCH 19/19] Changed our gitignore file for the api folder. --- seasoned_api/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seasoned_api/.gitignore b/seasoned_api/.gitignore index 9f7246b..ef087ec 100644 --- a/seasoned_api/.gitignore +++ b/seasoned_api/.gitignore @@ -60,4 +60,4 @@ typings/ # - - - - - # My own gitignore files and folders -conf +conf/