Compare commits
	
		
			59 Commits
		
	
	
		
			drone-test
			...
			feat/botto
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2665a27803 | |||
| 74b96225c6 | |||
| f180b7f39b | |||
| a2fbfcb13c | |||
| d640f7f882 | |||
| d43c12b103 | |||
| 38c3792675 | |||
| ac2785abd5 | |||
| 1ff6a0e831 | |||
| 7a3b709404 | |||
|  | d63cb4ac52 | ||
| b6ee1cf906 | |||
| 60201b1b67 | |||
| a8b8603649 | |||
| e193528fe9 | |||
| 73afb34964 | |||
| 65bbc453e6 | |||
|  | 188477ab64 | ||
|  | a31bfb6b39 | ||
| 681ed69ef0 | |||
| b771428b4d | |||
| fc0103ee5d | |||
| 55067b81b8 | |||
| dfe2b5df09 | |||
| dc0c435163 | |||
| 9d1ac56b9a | |||
| fc2c3664d9 | |||
| 0bd45ed777 | |||
| 3912766982 | |||
| 3becce2a6c | |||
| 20b8692c91 | |||
| 14ac780aa5 | |||
| d836870612 | |||
| bc6f706e4a | |||
| 6ac6a9b039 | |||
| 85be80d712 | |||
| 105be1e411 | |||
| 010830243e | |||
| 923dc46dc7 | |||
| f2ef5366f5 | |||
| 20380a4587 | |||
| 069ef2c458 | |||
| 2f430b2d8f | |||
| f7a579a438 | |||
| b9ddd998bc | |||
| ae59d02df2 | |||
| ec205bab0c | |||
| ed49d825b8 | |||
| a9db8be46a | |||
| 1caa3c7fae | |||
| 2ea4bffd49 | |||
| 5ae52f59fc | |||
| a7e6d25d3f | |||
| 83751a4e3e | |||
| 0e9daab187 | |||
| 4390491873 | |||
| d620a4cc2e | |||
| 1fd48edd42 | |||
| 68e45303c6 | 
							
								
								
									
										33
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								.drone.yml
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | |||||||
| --- | --- | ||||||
| kind: pipeline | kind: pipeline | ||||||
| type: exec | type: docker | ||||||
| name: default | name: seasoned build | ||||||
|  |  | ||||||
| platform: | platform: | ||||||
|   os: linux |   os: linux | ||||||
| @@ -9,11 +9,30 @@ platform: | |||||||
|  |  | ||||||
| steps: | steps: | ||||||
| - name: frontend_install | - name: frontend_install | ||||||
|  |   image: node:13.6.0 | ||||||
|   commands: |   commands: | ||||||
|     - yarn |     - node -v | ||||||
| - name: frontend_build |     - yarn --version | ||||||
|   commands: | - name: deploy | ||||||
|     - yarn build |   image: appleboy/drone-ssh | ||||||
|  |   pull: true | ||||||
|  |   secrets: | ||||||
|  |     - ssh_key | ||||||
|  |   when: | ||||||
|  |     event: | ||||||
|  |       - push | ||||||
|  |     branch: | ||||||
|  |       - master | ||||||
|  |       - drone-test | ||||||
|  |     status: success | ||||||
|  |   settings: | ||||||
|  |     host: 10.0.0.114 | ||||||
|  |     username: root | ||||||
|  |     key: | ||||||
|  |       from_secret: ssh_key | ||||||
|  |     command_timeout: 600s | ||||||
|  |     script: | ||||||
|  |       - /home/kevin/deploy/seasoned.sh | ||||||
|  |  | ||||||
| trigger: | trigger: | ||||||
|   branch: |   branch: | ||||||
| @@ -21,3 +40,5 @@ trigger: | |||||||
|   event: |   event: | ||||||
|     include: |     include: | ||||||
|       - pull_request |       - pull_request | ||||||
|  |       - push | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | { | ||||||
|  |   "tabWidth": 2, | ||||||
|  |   "useTabs": false, | ||||||
|  |   "semi": true, | ||||||
|  |   "singleQuote": false, | ||||||
|  |   "bracketSpacing": true, | ||||||
|  |   "arrowParens": "avoid", | ||||||
|  |   "vueIndentScriptAndStyle": false, | ||||||
|  |   "trailingComma": "none" | ||||||
|  | } | ||||||
| @@ -21,10 +21,6 @@ | |||||||
|       <symbol id="icon_now_playing" viewBox="0 0 30 30"> |       <symbol id="icon_now_playing" viewBox="0 0 30 30"> | ||||||
|         <title>Now Playing</title> |         <title>Now Playing</title> | ||||||
|         <path d="M27.9847266,7.50322266 C25.9822852,4.03494141 22.749082,1.55390625 18.8806055,0.517382812 C15.0121875,-0.519257812 10.9716797,0.0127148437 7.50322266,2.01527344 C4.03482422,4.01777344 1.55390625,7.25097656 0.517382812,11.1194531 C-0.519140625,14.9878711 0.0128320312,19.0284961 2.01527344,22.4967773 C4.01765625,25.9650586 7.25097656,28.4460937 11.1193945,29.4826172 C12.4111523,29.8287891 13.7219531,30 15.0244336,30 C17.6224219,30 20.1866016,29.3186133 22.4968359,27.9847852 C25.9651172,25.9823437 28.4461523,22.7491406 29.4826758,18.8806641 C30.5192578,15.0121289 29.987168,10.9716211 27.9847266,7.50322266 Z M27.9743555,18.476543 C27.0457617,21.9421289 24.8231836,24.8387109 21.715957,26.6326172 C18.6088477,28.426582 14.989043,28.9030664 11.523457,27.9745898 C8.0578125,27.0459961 5.16128906,24.823418 3.36732422,21.7161914 C1.57341797,18.609082 1.096875,14.9892188 2.02552734,11.5235742 C2.95417969,8.05798828 5.17675781,5.16152344 8.28392578,3.3675 C10.35375,2.17248047 12.6505664,1.56210937 14.9782031,1.56210937 C16.1448047,1.56210937 17.3195508,1.71550781 18.4763672,2.02552734 C21.9419531,2.95412109 24.8385352,5.17669922 26.6324414,8.28392578 C28.4264063,11.3910937 28.9030078,15.0108984 27.9743555,18.476543 Z M22.1940234,13.5850781 L12.5538281,8.01925781 C12.0422461,7.72388672 11.4314648,7.72400391 10.9198828,8.01919922 C10.4083008,8.31451172 10.1028516,8.84355469 10.1028516,9.43423828 L10.1028516,20.5658789 C10.1028516,21.1565625 10.4082422,21.6855469 10.9198828,21.980918 C11.1756445,22.1286328 11.4561328,22.2024023 11.7367383,22.2024023 C12.0174023,22.2024023 12.2980078,22.128457 12.5537695,21.9808594 L22.194082,16.4150977 C22.7056055,16.119668 23.0109375,15.5906836 23.0109375,15 C23.0109375,14.409375 22.7055469,13.8803906 22.1940234,13.5850781 Z M21.4132031,15.0629297 L11.7729492,20.6286914 C11.7611719,20.6355469 11.7366211,20.649668 11.7005273,20.6286914 C11.6643164,20.6077734 11.6643164,20.5795312 11.6643164,20.5659375 L11.6643164,9.43429687 C11.6643164,9.42070312 11.6643164,9.39246094 11.7005273,9.37154297 C11.714707,9.36333984 11.7270703,9.36052734 11.7376172,9.36052734 C11.7540234,9.36052734 11.7658594,9.36738281 11.7730664,9.37154297 L21.4132617,14.9373633 C21.4250391,14.9441602 21.4494727,14.9582812 21.4494727,15.0001172 C21.4494727,15.0419531 21.4249219,15.0561328 21.4132031,15.0629297 Z M24.2169727,7.87734375 C22.3601953,5.47863281 19.5689648,3.86707031 16.5588867,3.45580078 C16.1321484,3.39738281 15.7380469,3.69638672 15.6796289,4.12371094 C15.6213281,4.55091797 15.920332,4.94455078 16.3475391,5.00296875 C18.9556641,5.35927734 21.3738867,6.75544922 22.9822266,8.83318359 C23.1360937,9.03193359 23.3668945,9.13599609 23.6001562,9.13599609 C23.7670898,9.13599609 23.9353125,9.08273437 24.0774609,8.97257813 C24.418418,8.70867187 24.4808789,8.21830078 24.2169727,7.87734375 Z" fill-rule="nonzero"></path> |         <path d="M27.9847266,7.50322266 C25.9822852,4.03494141 22.749082,1.55390625 18.8806055,0.517382812 C15.0121875,-0.519257812 10.9716797,0.0127148437 7.50322266,2.01527344 C4.03482422,4.01777344 1.55390625,7.25097656 0.517382812,11.1194531 C-0.519140625,14.9878711 0.0128320312,19.0284961 2.01527344,22.4967773 C4.01765625,25.9650586 7.25097656,28.4460937 11.1193945,29.4826172 C12.4111523,29.8287891 13.7219531,30 15.0244336,30 C17.6224219,30 20.1866016,29.3186133 22.4968359,27.9847852 C25.9651172,25.9823437 28.4461523,22.7491406 29.4826758,18.8806641 C30.5192578,15.0121289 29.987168,10.9716211 27.9847266,7.50322266 Z M27.9743555,18.476543 C27.0457617,21.9421289 24.8231836,24.8387109 21.715957,26.6326172 C18.6088477,28.426582 14.989043,28.9030664 11.523457,27.9745898 C8.0578125,27.0459961 5.16128906,24.823418 3.36732422,21.7161914 C1.57341797,18.609082 1.096875,14.9892188 2.02552734,11.5235742 C2.95417969,8.05798828 5.17675781,5.16152344 8.28392578,3.3675 C10.35375,2.17248047 12.6505664,1.56210937 14.9782031,1.56210937 C16.1448047,1.56210937 17.3195508,1.71550781 18.4763672,2.02552734 C21.9419531,2.95412109 24.8385352,5.17669922 26.6324414,8.28392578 C28.4264063,11.3910937 28.9030078,15.0108984 27.9743555,18.476543 Z M22.1940234,13.5850781 L12.5538281,8.01925781 C12.0422461,7.72388672 11.4314648,7.72400391 10.9198828,8.01919922 C10.4083008,8.31451172 10.1028516,8.84355469 10.1028516,9.43423828 L10.1028516,20.5658789 C10.1028516,21.1565625 10.4082422,21.6855469 10.9198828,21.980918 C11.1756445,22.1286328 11.4561328,22.2024023 11.7367383,22.2024023 C12.0174023,22.2024023 12.2980078,22.128457 12.5537695,21.9808594 L22.194082,16.4150977 C22.7056055,16.119668 23.0109375,15.5906836 23.0109375,15 C23.0109375,14.409375 22.7055469,13.8803906 22.1940234,13.5850781 Z M21.4132031,15.0629297 L11.7729492,20.6286914 C11.7611719,20.6355469 11.7366211,20.649668 11.7005273,20.6286914 C11.6643164,20.6077734 11.6643164,20.5795312 11.6643164,20.5659375 L11.6643164,9.43429687 C11.6643164,9.42070312 11.6643164,9.39246094 11.7005273,9.37154297 C11.714707,9.36333984 11.7270703,9.36052734 11.7376172,9.36052734 C11.7540234,9.36052734 11.7658594,9.36738281 11.7730664,9.37154297 L21.4132617,14.9373633 C21.4250391,14.9441602 21.4494727,14.9582812 21.4494727,15.0001172 C21.4494727,15.0419531 21.4249219,15.0561328 21.4132031,15.0629297 Z M24.2169727,7.87734375 C22.3601953,5.47863281 19.5689648,3.86707031 16.5588867,3.45580078 C16.1321484,3.39738281 15.7380469,3.69638672 15.6796289,4.12371094 C15.6213281,4.55091797 15.920332,4.94455078 16.3475391,5.00296875 C18.9556641,5.35927734 21.3738867,6.75544922 22.9822266,8.83318359 C23.1360937,9.03193359 23.3668945,9.13599609 23.6001562,9.13599609 C23.7670898,9.13599609 23.9353125,9.08273437 24.0774609,8.97257813 C24.418418,8.70867187 24.4808789,8.21830078 24.2169727,7.87734375 Z" fill-rule="nonzero"></path> | ||||||
|       </symbol> |  | ||||||
|             <symbol id="icon_top_rated" viewBox="0 0 30 30"> |  | ||||||
|         <title>Top Rated</title> |  | ||||||
|         <path d="M24.7750847,5.22491532 C24.7021599,5.15199056 24.6169531,5.09595364 24.52407,5.05757218 C24.4304192,5.01919073 24.3313951,5 24.2323709,5 L8.84447835,5.00076763 C8.41997947,5.00076763 8.07684927,5.34466546 8.07684927,5.76839671 C8.07684927,6.19289559 8.4207471,6.53602579 8.84447835,6.53602579 L22.3785467,6.53525816 L5.22510723,23.6894653 C4.92496426,23.9896082 4.92496426,24.4747498 5.22510723,24.7748928 C5.3747949,24.9245804 5.57130794,24.9998081 5.76782099,24.9998081 C5.96433403,24.9998081 6.16084708,24.9245804 6.31053475,24.7748928 L23.4647418,7.62068568 L23.4647418,21.1539864 C23.4647418,21.5784853 23.807872,21.9216155 24.2323709,21.9216155 C24.6568698,21.9216155 25,21.5784853 25,21.1539864 L25,5.76762908 C25,5.66860493 24.9808093,5.56958078 24.9424278,5.47593003 C24.9040464,5.38304691 24.8480094,5.29784008 24.7750847,5.22491532 Z"></path> |  | ||||||
|       </symbol> |       </symbol> | ||||||
|             <symbol id="icon_popular" viewBox="0 0 30 30"> |             <symbol id="icon_popular" viewBox="0 0 30 30"> | ||||||
|         <title>Popular</title> |         <title>Popular</title> | ||||||
| @@ -125,9 +121,8 @@ | |||||||
|       l246.17-246.175C512.959,136.021,512.959,129.804,509.121,125.966z" fill-rule="nozero" transform="scale(1.4)"></path> |       l246.17-246.175C512.959,136.021,512.959,129.804,509.121,125.966z" fill-rule="nozero" transform="scale(1.4)"></path> | ||||||
|     </symbol> |     </symbol> | ||||||
|     <div id="app"></div> |     <div id="app"></div> | ||||||
|     <script type="text/javascript" src="dist/build.js"></script> |     <script type="text/javascript" src="/build.js"></script> | ||||||
|   </body> |   </body> | ||||||
|  |  | ||||||
|   <script src="https://cdn.ravenjs.com/3.23.1/vue/raven.min.js" crossorigin="anonymous"></script> |   <script src="https://cdn.ravenjs.com/3.23.1/vue/raven.min.js" crossorigin="anonymous"></script> | ||||||
|   <!-- <script>Raven.config('https://c1fa1a17de3d4b24abcd05161648fe4d@sentry.io/300063').install();</script> --> |  | ||||||
| </html> | </html> | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ | |||||||
|     "@babel/runtime": "^7.4.5", |     "@babel/runtime": "^7.4.5", | ||||||
|     "babel-loader": "^8.0.6", |     "babel-loader": "^8.0.6", | ||||||
|     "cross-env": "^3.0.0", |     "cross-env": "^3.0.0", | ||||||
|     "css-loader": "^0.25.0", |     "css-loader": "^3.4.2", | ||||||
|     "documentation": "^11.0.0", |     "documentation": "^11.0.0", | ||||||
|     "file-loader": "^0.9.0", |     "file-loader": "^0.9.0", | ||||||
|     "node-sass": "^4.5.0", |     "node-sass": "^4.5.0", | ||||||
|   | |||||||
							
								
								
									
										102
									
								
								src/App.vue
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								src/App.vue
									
									
									
									
									
								
							| @@ -1,37 +1,32 @@ | |||||||
| <template> | <template> | ||||||
|   <div id="app"> |   <div id="app"> | ||||||
|  |  | ||||||
|     <!-- Header and hamburger navigation --> |     <!-- Header and hamburger navigation --> | ||||||
|     <navigation></navigation> |     <navigation></navigation> | ||||||
|  |  | ||||||
|     <!-- Header with search field --> |  | ||||||
|  |  | ||||||
|     <!-- TODO move this to the navigation component --> |  | ||||||
|     <header class="header"> |  | ||||||
|     <search-input v-model="query"></search-input> |     <search-input v-model="query"></search-input> | ||||||
|     </header> |  | ||||||
|  |  | ||||||
|     <!-- Movie popup that will show above existing rendered content --> |     <!-- Movie popup that will show above existing rendered content --> | ||||||
|     <movie-popup v-if="moviePopupIsVisible" :id="popupID" :type="popupType"></movie-popup> |     <movie-popup | ||||||
|  |       v-if="moviePopupIsVisible" | ||||||
|  |       :id="popupID" | ||||||
|  |       :type="popupType" | ||||||
|  |     ></movie-popup> | ||||||
|  |  | ||||||
|     <darkmode-toggle /> |     <darkmode-toggle /> | ||||||
|  |  | ||||||
|     <!-- Display the component assigned to the given route (default: home) --> |     <!-- Display the component assigned to the given route (default: home) --> | ||||||
|     <router-view class="content" :key="$route.fullPath"></router-view> |     <router-view class="content" :key="$route.fullPath"></router-view> | ||||||
|  |  | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| import Vue from 'vue' | import Vue from "vue"; | ||||||
| import Navigation from '@/components/Navigation' | import Navigation from "@/components/Navigation"; | ||||||
| import MoviePopup from '@/components/MoviePopup' | import MoviePopup from "@/components/MoviePopup"; | ||||||
| import SearchInput from '@/components/SearchInput' | import SearchInput from "@/components/SearchInput"; | ||||||
| import DarkmodeToggle from '@/components/ui/darkmodeToggle' | import DarkmodeToggle from "@/components/ui/darkmodeToggle"; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   name: 'app', |   name: "app", | ||||||
|   components: { |   components: { | ||||||
|     Navigation, |     Navigation, | ||||||
|     MoviePopup, |     MoviePopup, | ||||||
| @@ -40,39 +35,42 @@ export default { | |||||||
|   }, |   }, | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       query: '', |       query: "", | ||||||
|       moviePopupIsVisible: false, |       moviePopupIsVisible: false, | ||||||
|       popupID: 0, |       popupID: 0, | ||||||
|       popupType: 'movie' |       popupType: "movie" | ||||||
|     } |     }; | ||||||
|   }, |   }, | ||||||
|   created(){ |   created() { | ||||||
|     let that = this |     let that = this; | ||||||
|     Vue.prototype.$popup = { |     Vue.prototype.$popup = { | ||||||
|       get isOpen() { |       get isOpen() { | ||||||
|         return that.moviePopupIsVisible |         return that.moviePopupIsVisible; | ||||||
|       }, |       }, | ||||||
|       open: (id, type) => { |       open: (id, type) => { | ||||||
|         this.popupID = id || this.popupID |         this.popupID = id || this.popupID; | ||||||
|         this.popupType = type || this.popupType |         this.popupType = type || this.popupType; | ||||||
|         this.moviePopupIsVisible = true |         this.moviePopupIsVisible = true; | ||||||
|         console.log('opened') |         console.log("opened"); | ||||||
|       }, |       }, | ||||||
|       close: () => { |       close: () => { | ||||||
|         this.moviePopupIsVisible = false |         this.moviePopupIsVisible = false; | ||||||
|         console.log('closed') |         console.log("closed"); | ||||||
|       } |       } | ||||||
|  |     }; | ||||||
|  |     console.log( | ||||||
|  |       "MoviePopup registered at this.$popup and has state: ", | ||||||
|  |       this.$popup.isOpen | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
|     console.log('MoviePopup registered at this.$popup and has state: ', this.$popup.isOpen) | }; | ||||||
|   } |  | ||||||
| } |  | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
| @import "./src/scss/media-queries"; | @import "./src/scss/media-queries"; | ||||||
| @import "./src/scss/variables"; | @import "./src/scss/variables"; | ||||||
| .content { | .content { | ||||||
|     @include tablet-min{ |   @include tablet-min { | ||||||
|     width: calc(100% - 95px); |     width: calc(100% - 95px); | ||||||
|     margin-top: $header-size; |     margin-top: $header-size; | ||||||
|     margin-left: 95px; |     margin-left: 95px; | ||||||
| @@ -86,38 +84,42 @@ export default { | |||||||
| @import "./src/scss/variables"; | @import "./src/scss/variables"; | ||||||
| @import "./src/scss/media-queries"; | @import "./src/scss/media-queries"; | ||||||
|  |  | ||||||
| *{ | * { | ||||||
|   box-sizing: border-box; |   box-sizing: border-box; | ||||||
| } | } | ||||||
| html { | html { | ||||||
|   height: 100%; |   height: 100%; | ||||||
| } | } | ||||||
| body{ | body { | ||||||
|   margin: 0; |   margin: 0; | ||||||
|   padding: 0; |   padding: 0; | ||||||
|   font-family: 'Roboto', sans-serif; |   font-family: "Roboto", sans-serif; | ||||||
|   line-height: 1.6; |   line-height: 1.6; | ||||||
|   background: $background-color; |   background: $background-color; | ||||||
|   color: $text-color; |   color: $text-color; | ||||||
|   transition: background-color .5s ease, color .5s ease; |   transition: background-color 0.5s ease, color 0.5s ease; | ||||||
|   &.hidden{ |   &.hidden { | ||||||
|     overflow: hidden; |     overflow: hidden; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| h1,h2,h3 { | h1, | ||||||
|   transition: color .5s ease; | h2, | ||||||
|  | h3 { | ||||||
|  |   transition: color 0.5s ease; | ||||||
| } | } | ||||||
| a:any-link { | a:any-link { | ||||||
|   color: inherit; |   color: inherit; | ||||||
| } | } | ||||||
| input, textarea, button{ | input, | ||||||
|   font-family: 'Roboto', sans-serif; | textarea, | ||||||
|  | button { | ||||||
|  |   font-family: "Roboto", sans-serif; | ||||||
| } | } | ||||||
| figure{ | figure { | ||||||
|   padding: 0; |   padding: 0; | ||||||
|   margin: 0; |   margin: 0; | ||||||
| } | } | ||||||
| img{ | img { | ||||||
|   display: block; |   display: block; | ||||||
|   // max-width: 100%; |   // max-width: 100%; | ||||||
|   height: auto; |   height: auto; | ||||||
| @@ -127,16 +129,16 @@ img{ | |||||||
|   overflow: hidden; |   overflow: hidden; | ||||||
| } | } | ||||||
|  |  | ||||||
| .wrapper{ | .wrapper { | ||||||
|   position: relative; |   position: relative; | ||||||
| } | } | ||||||
| .header{ | .header { | ||||||
|   position: fixed; |   position: fixed; | ||||||
|   z-index: 15; |   z-index: 15; | ||||||
|   display: flex; |   display: flex; | ||||||
|   flex-direction: column; |   flex-direction: column; | ||||||
|  |  | ||||||
|   @include tablet-min{ |   @include tablet-min { | ||||||
|     width: calc(100% - 170px); |     width: calc(100% - 170px); | ||||||
|     margin-left: 95px; |     margin-left: 95px; | ||||||
|     border-top: 0; |     border-top: 0; | ||||||
| @@ -146,14 +148,16 @@ img{ | |||||||
| } | } | ||||||
|  |  | ||||||
| // router view transition | // router view transition | ||||||
| .fade-enter-active, .fade-leave-active { | .fade-enter-active, | ||||||
|  | .fade-leave-active { | ||||||
|   transition-property: opacity; |   transition-property: opacity; | ||||||
|   transition-duration: 0.25s; |   transition-duration: 0.25s; | ||||||
| } | } | ||||||
| .fade-enter-active { | .fade-enter-active { | ||||||
|   transition-delay: 0.25s; |   transition-delay: 0.25s; | ||||||
| } | } | ||||||
| .fade-enter, .fade-leave-active { | .fade-enter, | ||||||
|   opacity: 0 | .fade-leave-active { | ||||||
|  |   opacity: 0; | ||||||
| } | } | ||||||
| </style> | </style> | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								src/api.js
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/api.js
									
									
									
									
									
								
							| @@ -252,6 +252,21 @@ const getRequestStatus = (id, type, authorization_token=undefined) => { | |||||||
|     .catch(err => Promise.reject(err)) |     .catch(err => Promise.reject(err)) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const watchLink = (title, year, authorization_token=undefined) => { | ||||||
|  |   const url = new URL('v1/plex/watch-link', SEASONED_URL) | ||||||
|  |   url.searchParams.append('title', title) | ||||||
|  |   url.searchParams.append('year', year) | ||||||
|  |  | ||||||
|  |   const headers = { | ||||||
|  |     'Authorization': authorization_token, | ||||||
|  |     'Content-Type': 'application/json' | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return fetch(url.href, { headers }) | ||||||
|  |     .then(resp => resp.json()) | ||||||
|  |     .then(response => response.link) | ||||||
|  | } | ||||||
|  |  | ||||||
| // - - - Seasoned user endpoints - - - | // - - - Seasoned user endpoints - - - | ||||||
|  |  | ||||||
| const register = (username, password) => { | const register = (username, password) => { | ||||||
| @@ -271,7 +286,7 @@ const register = (username, password) => { | |||||||
|     }) |     }) | ||||||
| } | } | ||||||
|  |  | ||||||
| const login = (username, password) => { | const login = (username, password, throwError=false) => { | ||||||
|   const url = new URL('v1/user/login', SEASONED_URL) |   const url = new URL('v1/user/login', SEASONED_URL) | ||||||
|   const options = { |   const options = { | ||||||
|     method: 'POST', |     method: 'POST', | ||||||
| @@ -280,11 +295,14 @@ const login = (username, password) => { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   return fetch(url.href, options) |   return fetch(url.href, options) | ||||||
|     .then(resp => resp.json()) |     .then(resp => { | ||||||
|     .catch(error => { |       if (resp.status == 200) | ||||||
|       console.error('Unexpected error occured before receiving response. Error:', error) |         return resp.json(); | ||||||
|       // TODO log to sentry the issue here |  | ||||||
|       throw error |       if (throwError) | ||||||
|  |         throw resp; | ||||||
|  |       else | ||||||
|  |         console.error("Error occured when trying to sign in.\nError:", resp); | ||||||
|     }) |     }) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -458,6 +476,7 @@ export { | |||||||
|   searchTorrents, |   searchTorrents, | ||||||
|   addMagnet, |   addMagnet, | ||||||
|   request, |   request, | ||||||
|  |   watchLink, | ||||||
|   getRequestStatus, |   getRequestStatus, | ||||||
|   linkPlexAccount, |   linkPlexAccount, | ||||||
|   unlinkPlexAccount, |   unlinkPlexAccount, | ||||||
|   | |||||||
| @@ -1,38 +1,74 @@ | |||||||
| <template> | <template> | ||||||
|  |   <div> | ||||||
|     <section class="not-found"> |     <section class="not-found"> | ||||||
|       <h1 class="not-found__title">Page Not Found</h1> |       <h1 class="not-found__title">Page Not Found</h1> | ||||||
|     </section> |     </section> | ||||||
|  |     <seasoned-button class="button" @click="goBack">go back to previous page</seasoned-button> | ||||||
|  |   </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  | import store from '@/store' | ||||||
|  | import SeasonedButton from '@/components/ui/SeasonedButton' | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   components: { SeasonedButton }, | ||||||
|  |   methods: { | ||||||
|  |     goBack() { | ||||||
|  |       this.$router.go(-1) | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     if (this.$popup.isOpen == true) | ||||||
|  |       this.$popup.close() | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
| @import "./src/scss/variables"; | @import "./src/scss/variables"; | ||||||
| @import "./src/scss/media-queries"; | @import "./src/scss/media-queries"; | ||||||
|  |  | ||||||
|  | .button { | ||||||
|  |   font-size: 1.2rem; | ||||||
|  |   position: fixed; | ||||||
|  |   top: 50%; | ||||||
|  |   left: calc(50% + 46px); | ||||||
|  |   transform: translate(-50%, -50%); | ||||||
|  |  | ||||||
|  |   @include mobile { | ||||||
|  |     top: 60%; | ||||||
|  |     left: 50%; | ||||||
|  |     font-size: 1rem; | ||||||
|  |     width: content; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| .not-found { | .not-found { | ||||||
|   display: flex; |   display: flex; | ||||||
|   height: calc(100vh - var(--header-size)); |   height: calc(100vh - var(--header-size)); | ||||||
|   width: 100%; |  | ||||||
|   background: url('~assets/pulp-fiction.jpg') no-repeat 50% 50%; |   background: url('~assets/pulp-fiction.jpg') no-repeat 50% 50%; | ||||||
|   background-size: cover; |   background-size: cover; | ||||||
|   justify-content: center; |   align-items: center; | ||||||
|  |   flex-direction: column; | ||||||
|  |  | ||||||
|   &:before { |   &::before { | ||||||
|     content: ""; |     content: ""; | ||||||
|     position: absolute; |     position: absolute; | ||||||
|     height: calc(100vh - var(--header-size)); |     height: calc(100vh - var(--header-size)); | ||||||
|     width: 100%; |     width: 100%; | ||||||
|  |     pointer-events: none; | ||||||
|     background: $background-40; |     background: $background-40; | ||||||
|   } |   } | ||||||
|   &__title { |   &__title { | ||||||
|    padding-top: 40vh; |     margin-top: 30vh; | ||||||
|     font-size: 2rem; |     font-size: 2.5rem; | ||||||
|     font-weight: 500; |     font-weight: 500; | ||||||
|     color: $text-color; |     color: $text-color; | ||||||
|     position: relative; |     position: relative; | ||||||
|     margin: 0; |  | ||||||
|  |  | ||||||
|     @include tablet-min { |     @include tablet-min { | ||||||
|       font-size: 2.3rem; |       font-size: 3.5rem; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,74 +12,76 @@ | |||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| import LandingBanner from '@/components/LandingBanner' | import LandingBanner from "@/components/LandingBanner"; | ||||||
| import ListHeader from '@/components/ListHeader' | import ListHeader from "@/components/ListHeader"; | ||||||
| import ResultsList from '@/components/ResultsList' | import ResultsList from "@/components/ResultsList"; | ||||||
| import Loader from '@/components/ui/Loader' | import Loader from "@/components/ui/Loader"; | ||||||
|  |  | ||||||
| import { getTmdbMovieListByName, getRequests } from '@/api' | import { getTmdbMovieListByName, getRequests } from "@/api"; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   name: 'home', |   name: "home", | ||||||
|   components: { LandingBanner, ResultsList, ListHeader, Loader }, |   components: { LandingBanner, ResultsList, ListHeader, Loader }, | ||||||
|   data(){ |   data() { | ||||||
|     return { |     return { | ||||||
|       imageFile: 'dist/pulp-fiction.jpg', |       imageFile: "/pulp-fiction.jpg", | ||||||
|       requests: [], |       requests: [], | ||||||
|       nowplaying: [], |       nowplaying: [], | ||||||
|       upcoming: [], |       upcoming: [], | ||||||
|       popular: [] |       popular: [] | ||||||
|     } |     }; | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|     lists() { |     lists() { | ||||||
|       return [ |       return [ | ||||||
|         { |         { | ||||||
|           title: 'Requests', |           title: "Requests", | ||||||
|           route: 'request', |           route: "request", | ||||||
|           data: this.requests |           data: this.requests | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|           title: 'Now playing', |           title: "Now playing", | ||||||
|           route: 'now_playing', |           route: "now_playing", | ||||||
|           data: this.nowplaying |           data: this.nowplaying | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|           title: 'Upcoming', |           title: "Upcoming", | ||||||
|           route: 'upcoming', |           route: "upcoming", | ||||||
|           data: this.upcoming |           data: this.upcoming | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|           title: 'Popular', |           title: "Popular", | ||||||
|           route: 'popular', |           route: "popular", | ||||||
|           data: this.popular |           data: this.popular | ||||||
|         } |         } | ||||||
|       ] |       ]; | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     fetchRequests() { |     fetchRequests() { | ||||||
|       getRequests() |       getRequests().then(results => (this.requests = results.results)); | ||||||
|         .then(results => this.requests = results.results) |  | ||||||
|     }, |     }, | ||||||
|     fetchNowPlaying() { |     fetchNowPlaying() { | ||||||
|       getTmdbMovieListByName('now_playing') |       getTmdbMovieListByName("now_playing").then( | ||||||
|         .then(results => this.nowplaying = results.results) |         results => (this.nowplaying = results.results) | ||||||
|  |       ); | ||||||
|     }, |     }, | ||||||
|     fetchUpcoming() { |     fetchUpcoming() { | ||||||
|       getTmdbMovieListByName('upcoming') |       getTmdbMovieListByName("upcoming").then( | ||||||
|         .then(results => this.upcoming = results.results) |         results => (this.upcoming = results.results) | ||||||
|  |       ); | ||||||
|     }, |     }, | ||||||
|     fetchPopular() { |     fetchPopular() { | ||||||
|       getTmdbMovieListByName('popular') |       getTmdbMovieListByName("popular").then( | ||||||
|         .then(results => this.popular = results.results) |         results => (this.popular = results.results) | ||||||
|  |       ); | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   created(){ |   created() { | ||||||
|     this.fetchRequests() |     this.fetchRequests(); | ||||||
|     this.fetchNowPlaying() |     this.fetchNowPlaying(); | ||||||
|     this.fetchUpcoming() |     this.fetchUpcoming(); | ||||||
|     this.fetchPopular() |     this.fetchPopular(); | ||||||
|   } |   } | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|   | |||||||
| @@ -1,8 +1,10 @@ | |||||||
| <template> | <template> | ||||||
|   <header v-bind:style="{ 'background-image': 'url(' + imageFile + ')' }"> |   <header v-bind:style="{ 'background-image': 'url(' + imageFile + ')' }"> | ||||||
|     <div class="container"> |     <div class="container"> | ||||||
|       <h1 class="title">Request new movies or tv shows for plex</h1> |       <h1 class="title">Request movies or tv shows</h1> | ||||||
|       <strong class="subtitle">Made with Vue.js</strong> |       <strong class="subtitle" | ||||||
|  |         >Create a profile to track and view requests</strong | ||||||
|  |       > | ||||||
|     </div> |     </div> | ||||||
|   </header> |   </header> | ||||||
| </template> | </template> | ||||||
| @@ -17,15 +19,15 @@ export default { | |||||||
|   }, |   }, | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       imageFile: 'dist/pulp-fiction.jpg' |       imageFile: "/pulp-fiction.jpg" | ||||||
|     } |     }; | ||||||
|   }, |   }, | ||||||
|   beforeMount() { |   beforeMount() { | ||||||
|     if (this.image && this.image.length > 0) { |     if (this.image && this.image.length > 0) { | ||||||
|       this.imageFile = this.image |       this.imageFile = this.image; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
| @@ -55,13 +57,13 @@ header { | |||||||
|     width: 100%; |     width: 100%; | ||||||
|     height: 100%; |     height: 100%; | ||||||
|     background-color: $background-70; |     background-color: $background-70; | ||||||
|     transition: background-color .5s ease; |     transition: background-color 0.5s ease; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   .container { |   .container { | ||||||
|     text-align: center; |     text-align: center; | ||||||
|     position: relative; |     position: relative; | ||||||
|     transition: color .5s ease; |     transition: color 0.5s ease; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   .title { |   .title { | ||||||
| @@ -72,8 +74,8 @@ header { | |||||||
|     color: $text-color; |     color: $text-color; | ||||||
|     margin: 0; |     margin: 0; | ||||||
|  |  | ||||||
|     @include tablet-min{ |     @include tablet-min { | ||||||
|       font-size: 28px; |       font-size: 2.5rem; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -84,8 +86,8 @@ header { | |||||||
|     color: $text-color-70; |     color: $text-color-70; | ||||||
|     margin: 5px 0; |     margin: 5px 0; | ||||||
|  |  | ||||||
|     @include tablet-min{ |     @include tablet-min { | ||||||
|       font-size: 16px; |       font-size: 1.3rem; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,12 +1,17 @@ | |||||||
| <template> | <template> | ||||||
|   <header :class="{ 'sticky': sticky }"> |   <header :class="{ sticky: sticky }"> | ||||||
|     <h2>{{ title }}</h2> |     <h2>{{ title }}</h2> | ||||||
|  |  | ||||||
|     <div v-if="info instanceof Array" class="flex flex-direction-column"> |     <div v-if="info instanceof Array" class="flex flex-direction-column"> | ||||||
|       <span v-for="item in info" class="info">{{ item }}</span> |       <span v-for="item in info" class="info">{{ item }}</span> | ||||||
|     </div> |     </div> | ||||||
|     <span v-else class="info">{{ info }}</span> |     <span v-else class="info">{{ info }}</span> | ||||||
|     <router-link v-if="link" :to="link" class='view-more' :aria-label="`View all ${title}`"> |     <router-link | ||||||
|  |       v-if="link" | ||||||
|  |       :to="link" | ||||||
|  |       class="view-more" | ||||||
|  |       :aria-label="`View all ${title}`" | ||||||
|  |     > | ||||||
|       View All |       View All | ||||||
|     </router-link> |     </router-link> | ||||||
|   </header> |   </header> | ||||||
| @@ -33,31 +38,38 @@ export default { | |||||||
|       required: false |       required: false | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
| @import './src/scss/variables'; | @import "./src/scss/variables"; | ||||||
| @import './src/scss/media-queries'; | @import "./src/scss/media-queries"; | ||||||
| @import './src/scss/main'; | @import "./src/scss/main"; | ||||||
|  |  | ||||||
| header { | header { | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   min-height: 80px; |   min-height: 45px; | ||||||
|   display: flex; |   display: flex; | ||||||
|   justify-content: space-between; |   justify-content: space-between; | ||||||
|   align-items: center; |   align-items: center; | ||||||
|   padding-left: 0.75rem; |   padding-left: 0.75rem; | ||||||
|   padding-right: 0.75rem; |   padding-right: 0.75rem; | ||||||
|  |  | ||||||
|  |   @include tablet-min { | ||||||
|  |     min-height: 65px; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   &.sticky { |   &.sticky { | ||||||
|     background-color: $background-color; |     background-color: $background-color; | ||||||
|  |  | ||||||
|     position: sticky; |     position: sticky; | ||||||
|     position: -webkit-sticky; |     position: -webkit-sticky; | ||||||
|     top: $header-size; |     top: 0; | ||||||
|     z-index: 4; |     z-index: 4; | ||||||
|  |  | ||||||
|  |     @include tablet-min { | ||||||
|  |       top: $header-size; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   h2 { |   h2 { | ||||||
| @@ -72,16 +84,16 @@ header { | |||||||
|   .view-more { |   .view-more { | ||||||
|     font-size: 0.9rem; |     font-size: 0.9rem; | ||||||
|     font-weight: 300; |     font-weight: 300; | ||||||
|     letter-spacing: .5px; |     letter-spacing: 0.5px; | ||||||
|     color: $text-color-70; |     color: $text-color-70; | ||||||
|     text-decoration: none; |     text-decoration: none; | ||||||
|     transition: color .5s ease; |     transition: color 0.5s ease; | ||||||
|     cursor: pointer; |     cursor: pointer; | ||||||
|  |  | ||||||
|     &:after{ |     &:after { | ||||||
|       content: " →"; |       content: " →"; | ||||||
|     } |     } | ||||||
|     &:hover{ |     &:hover { | ||||||
|       color: $text-color; |       color: $text-color; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -89,18 +101,17 @@ header { | |||||||
|   .info { |   .info { | ||||||
|     font-size: 13px; |     font-size: 13px; | ||||||
|     font-weight: 300; |     font-weight: 300; | ||||||
|     letter-spacing: .5px; |     letter-spacing: 0.5px; | ||||||
|     color: $text-color; |     color: $text-color; | ||||||
|     text-decoration: none; |     text-decoration: none; | ||||||
|     text-align: right; |     text-align: right; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @include tablet-min { |   @include tablet-min { | ||||||
|     padding-left: 1.25rem;; |     padding-left: 1.25rem; | ||||||
|   } |   } | ||||||
|   @include desktop-lg-min { |   @include desktop-lg-min { | ||||||
|     padding-left: 1.75rem; |     padding-left: 1.75rem; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| </style> | </style> | ||||||
| @@ -1,6 +1,5 @@ | |||||||
|  |  | ||||||
| <template> | <template> | ||||||
|   <div> |   <div class="page-container"> | ||||||
|     <list-header :title="listTitle" :info="info" :sticky="true" /> |     <list-header :title="listTitle" :info="info" :sticky="true" /> | ||||||
|  |  | ||||||
|     <results-list :results="results" v-if="results" /> |     <results-list :results="results" v-if="results" /> | ||||||
| @@ -13,96 +12,107 @@ | |||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| import ListHeader from '@/components/ListHeader' | import ListHeader from "@/components/ListHeader"; | ||||||
| import ResultsList from '@/components/ResultsList' | import ResultsList from "@/components/ResultsList"; | ||||||
| import SeasonedButton from '@/components/ui/SeasonedButton' | import SeasonedButton from "@/components/ui/SeasonedButton"; | ||||||
| import Loader from '@/components/ui/Loader' | import Loader from "@/components/ui/Loader"; | ||||||
| import { getTmdbMovieListByName, getRequests } from '@/api' | import { getTmdbMovieListByName, getRequests } from "@/api"; | ||||||
| import store from '@/store' | import store from "@/store"; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   components: { ListHeader, ResultsList, SeasonedButton, Loader }, |   components: { ListHeader, ResultsList, SeasonedButton, Loader }, | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       legalTmdbLists: [ 'now_playing', 'upcoming', 'popular' ], |       legalTmdbLists: ["now_playing", "upcoming", "popular"], | ||||||
|       results: [], |       results: [], | ||||||
|       page: 1, |       page: 1, | ||||||
|       totalPages: 0, |       totalPages: 0, | ||||||
|       totalResults: 0, |       totalResults: 0, | ||||||
|       loading: true |       loading: true | ||||||
|     } |     }; | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|     listTitle() { |     listTitle() { | ||||||
|       if (this.results.length === 0) |       if (this.results.length === 0) return ""; | ||||||
|         return '' |  | ||||||
|  |  | ||||||
|       const routeListName = this.$route.params.name |       const routeListName = this.$route.params.name; | ||||||
|       console.log('routelistname', routeListName) |       console.log("routelistname", routeListName); | ||||||
|       return routeListName.includes('_') ? routeListName.split('_').join(' ') : routeListName |       return routeListName.includes("_") | ||||||
|  |         ? routeListName.split("_").join(" ") | ||||||
|  |         : routeListName; | ||||||
|     }, |     }, | ||||||
|     info() { |     info() { | ||||||
|       if (this.results.length === 0) |       if (this.results.length === 0) return [null, null]; | ||||||
|         return [null, null] |       return [this.pageCount, this.resultCount]; | ||||||
|       return [this.pageCount, this.resultCount] |  | ||||||
|     }, |     }, | ||||||
|     resultCount() { |     resultCount() { | ||||||
|       const loadedResults = this.results.length |       const loadedResults = this.results.length; | ||||||
|       const totalResults = this.totalResults < 10000 ? this.totalResults : '∞' |       const totalResults = this.totalResults < 10000 ? this.totalResults : "∞"; | ||||||
|       return `${loadedResults} of ${totalResults} results` |       return `${loadedResults} of ${totalResults} results`; | ||||||
|     }, |     }, | ||||||
|     pageCount() { |     pageCount() { | ||||||
|       return `Page ${this.page} of ${this.totalPages}` |       return `Page ${this.page} of ${this.totalPages}`; | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     loadMore() { |     loadMore() { | ||||||
|       console.log(this.$route) |       console.log(this.$route); | ||||||
|       this.loading = true; |       this.loading = true; | ||||||
|       this.page++ |       this.page++; | ||||||
|  |  | ||||||
|       window.history.replaceState({}, 'search', `/#/${this.$route.fullPath}?page=${this.page}`) |       window.history.replaceState( | ||||||
|       this.init() |         {}, | ||||||
|  |         "search", | ||||||
|  |         `/#/${this.$route.fullPath}?page=${this.page}` | ||||||
|  |       ); | ||||||
|  |       this.init(); | ||||||
|     }, |     }, | ||||||
|     init() { |     init() { | ||||||
|       const routeListName = this.$route.params.name |       const routeListName = this.$route.params.name; | ||||||
|  |  | ||||||
|       if (routeListName === 'request') { |       if (routeListName === "request") { | ||||||
|         getRequests(this.page) |         getRequests(this.page).then(results => { | ||||||
|           .then(results => { |           this.results = this.results.concat(...results.results); | ||||||
|             this.results = this.results.concat(...results.results) |           this.page = results.page; | ||||||
|             this.page = results.page |           this.totalPages = results.total_pages; | ||||||
|             this.totalPages = results.total_pages |           this.totalResults = results.total_results; | ||||||
|             this.totalResults = results.total_results |         }); | ||||||
|           }) |  | ||||||
|       } else if (this.legalTmdbLists.includes(routeListName)) { |       } else if (this.legalTmdbLists.includes(routeListName)) { | ||||||
|         getTmdbMovieListByName(routeListName, this.page) |         getTmdbMovieListByName(routeListName, this.page).then(results => { | ||||||
|           .then(results => { |           this.results = this.results.concat(...results.results); | ||||||
|             this.results = this.results.concat(...results.results) |           this.page = results.page; | ||||||
|             this.page = results.page |           this.totalPages = results.total_pages; | ||||||
|             this.totalPages = results.total_pages |           this.totalResults = results.total_results; | ||||||
|             this.totalResults = results.total_results |         }); | ||||||
|           }) |  | ||||||
|       } else { |       } else { | ||||||
|         // TODO handle if list is not found |         // TODO handle if list is not found | ||||||
|         console.log('404 this is not a tmdb list') |         console.log("404 this is not a tmdb list"); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       this.loading = false |       this.loading = false; | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   created() { |   created() { | ||||||
|     if (this.results.length === 0) |     if (this.results.length === 0) this.init(); | ||||||
|       this.init() |  | ||||||
|  |  | ||||||
|     store.dispatch('documentTitle/updateTitle', `${this.$router.history.current.name} ${this.$route.params.name}`) |     store.dispatch( | ||||||
|  |       "documentTitle/updateTitle", | ||||||
|  |       `${this.$router.history.current.name} ${this.$route.params.name}` | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
|  | @import "./src/scss/media-queries"; | ||||||
|  |  | ||||||
|  | @include mobile-only { | ||||||
|  |   .page-container { | ||||||
|  |     margin-top: 1rem; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| .fullwidth-button { | .fullwidth-button { | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   margin: 1rem 0; |   margin: 1rem 0; | ||||||
|   | |||||||
| @@ -1,52 +1,60 @@ | |||||||
| <template> | <template> | ||||||
|   <section class="movie"> |   <section class="movie"> | ||||||
|  |  | ||||||
|     <!-- HEADER w/ POSTER --> |     <!-- HEADER w/ POSTER --> | ||||||
|     <header class="movie__header" :style="{ 'background-image': movie && backdrop !== null ? 'url(' + ASSET_URL + ASSET_SIZES[1] + backdrop + ')' : '' }" :class="compact ? 'compact' : ''" @click="compact=!compact"> |     <header | ||||||
|       <div class="movie__wrap movie__wrap--header"> |       ref="header" | ||||||
|  |       :class="compact ? 'compact' : ''" | ||||||
|  |       @click="compact = !compact" | ||||||
|  |     > | ||||||
|       <figure class="movie__poster"> |       <figure class="movie__poster"> | ||||||
|           <img v-if="movie && poster === null" |         <img | ||||||
|             class="movies-item__img is-loaded" |           class="movie-item__img is-loaded" | ||||||
|             alt="movie poster image" |           ref="poster-image" | ||||||
|             src="~assets/no-image.png"> |           src="~assets/placeholder.png" | ||||||
|           <img v-else-if="poster === undefined" |         /> | ||||||
|             class="movies-item__img grey" |  | ||||||
|             alt="movie poster image"> |  | ||||||
|             <!-- src="~assets/placeholder.png"> --> |  | ||||||
|           <img v-else |  | ||||||
|             class="movies-item__img is-loaded" |  | ||||||
|             alt="movie poster image" |  | ||||||
|             :src="ASSET_URL + ASSET_SIZES[0] + poster"> |  | ||||||
|       </figure> |       </figure> | ||||||
|  |  | ||||||
|         <div class="movie__title"> |       <h1 class="movie__title" v-if="movie">{{ movie.title }}</h1> | ||||||
|           <h1 v-if="movie">{{ movie.title }}</h1> |  | ||||||
|       <loading-placeholder v-else :count="1" /> |       <loading-placeholder v-else :count="1" /> | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     </header> |     </header> | ||||||
|  |  | ||||||
|     <!-- Siderbar and movie info --> |     <!-- Siderbar and movie info --> | ||||||
|     <div class="movie__main"> |     <div class="movie__main"> | ||||||
|       <div class="movie__wrap movie__wrap--main"> |       <div class="movie__wrap movie__wrap--main"> | ||||||
|  |  | ||||||
|         <!-- SIDEBAR ACTIONS --> |         <!-- SIDEBAR ACTIONS --> | ||||||
|         <div class="movie__actions" v-if="movie"> |         <div class="movie__actions" v-if="movie"> | ||||||
|  |           <sidebar-list-element | ||||||
|           <sidebar-list-element :iconRef="'#iconNot_exsits'" :active="matched" |             :iconRef="'#iconNot_exsits'" | ||||||
|             :iconRefActive="'#iconExists'" :textActive="'Already in plex 🎉'"> |             :active="matched" | ||||||
|  |             :iconRefActive="'#iconExists'" | ||||||
|  |             :textActive="'Already in plex 🎉'" | ||||||
|  |           > | ||||||
|             Not yet in plex |             Not yet in plex | ||||||
|           </sidebar-list-element> |           </sidebar-list-element> | ||||||
|           <sidebar-list-element @click="sendRequest" :iconRef="'#iconSent'" |           <sidebar-list-element | ||||||
|             :active="requested" :textActive="'Requested to be downloaded'"> |             @click="sendRequest" | ||||||
|  |             :iconRef="'#iconSent'" | ||||||
|  |             :active="requested" | ||||||
|  |             :textActive="'Requested to be downloaded'" | ||||||
|  |           > | ||||||
|             Request to be downloaded? |             Request to be downloaded? | ||||||
|           </sidebar-list-element> |           </sidebar-list-element> | ||||||
|           <sidebar-list-element v-if="admin" @click="showTorrents=!showTorrents" |  | ||||||
|             :iconRef="'#icon_torrents'" :active="showTorrents" |  | ||||||
|             :supplementaryText="numberOfTorrentResults"> |  | ||||||
|  |  | ||||||
|  |           <sidebar-list-element | ||||||
|  |             v-if="isPlexAuthenticated && matched" | ||||||
|  |             @click="openInPlex" | ||||||
|  |             :iconString="'⏯ '" | ||||||
|  |           > | ||||||
|  |             Watch in plex now! | ||||||
|  |           </sidebar-list-element> | ||||||
|  |  | ||||||
|  |           <sidebar-list-element | ||||||
|  |             v-if="admin" | ||||||
|  |             @click="showTorrents = !showTorrents" | ||||||
|  |             :iconRef="'#icon_torrents'" | ||||||
|  |             :active="showTorrents" | ||||||
|  |             :supplementaryText="numberOfTorrentResults" | ||||||
|  |           > | ||||||
|             Search for torrents |             Search for torrents | ||||||
|           </sidebar-list-element> |           </sidebar-list-element> | ||||||
|           <sidebar-list-element @click="openTmdb" :iconRef="'#icon_info'"> |           <sidebar-list-element @click="openTmdb" :iconRef="'#icon_info'"> | ||||||
| @@ -56,83 +64,129 @@ | |||||||
|  |  | ||||||
|         <!-- Loading placeholder --> |         <!-- Loading placeholder --> | ||||||
|         <div class="movie__actions text-input__loading" v-else> |         <div class="movie__actions text-input__loading" v-else> | ||||||
|           <div class="movie__actions-link" v-for="_ in admin ? Array(4) : Array(3)"> |           <div | ||||||
|             <div class="movie__actions-text text-input__loading--line" style="margin:9px; margin-left: -3px;"></div> |             class="movie__actions-link" | ||||||
|  |             v-for="_ in admin ? Array(4) : Array(3)" | ||||||
|  |           > | ||||||
|  |             <div | ||||||
|  |               class="movie__actions-text text-input__loading--line" | ||||||
|  |               style="margin: 9px; margin-left: -3px" | ||||||
|  |             ></div> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|  |  | ||||||
|         <!-- MOVIE INFO --> |         <!-- MOVIE INFO --> | ||||||
|         <div class="movie__info"> |         <div class="movie__info"> | ||||||
|           <div class="movie__description" v-if="movie"> {{ movie.overview }}</div> |  | ||||||
|  |  | ||||||
|           <!-- Loading placeholder --> |           <!-- Loading placeholder --> | ||||||
|  |           <div | ||||||
|  |             class="movie__description noselect" | ||||||
|  |             @click="truncatedDescription = !truncatedDescription" | ||||||
|  |             v-if="!loading" | ||||||
|  |           > | ||||||
|  |             <span :class="truncatedDescription ? 'truncated' : null">{{ | ||||||
|  |               movie.overview | ||||||
|  |             }}</span> | ||||||
|  |             <button class="truncate-toggle"><i>⬆</i></button> | ||||||
|  |           </div> | ||||||
|           <div v-else class="movie__description"> |           <div v-else class="movie__description"> | ||||||
|             <loading-placeholder :count="12" /> |             <loading-placeholder :count="5" /> | ||||||
|           </div> |           </div> | ||||||
|  |  | ||||||
|           <div class="movie__details" v-if="movie"> |           <div class="movie__details" v-if="movie"> | ||||||
|             <div v-if="movie.year" class="movie__details-block"> |             <div v-if="movie.year"> | ||||||
|               <h2 class="movie__details-title">Release Date</h2> |               <h2 class="title">Release Date</h2> | ||||||
|               <div class="movie__details-text">{{ movie.year }}</div> |               <div class="text">{{ movie.year }}</div> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|              <div v-if="movie.rank" class="movie__details-block"> |             <div v-if="movie.rating"> | ||||||
|               <h2 class="movie__details-title">Rating</h2> |               <h2 class="title">Rating</h2> | ||||||
|               <div class="movie__details-text">{{ movie.rank }}</div> |               <div class="text">{{ movie.rating }}</div> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|             <div v-if="movie.type == 'show'" class="movie__details-block"> |             <div v-if="movie.type == 'show'"> | ||||||
|               <h2 class="movie__details-title">Seasons</h2> |               <h2 class="title">Seasons</h2> | ||||||
|               <div class="movie__details-text">{{ movie.seasons }}</div> |               <div class="text">{{ movie.seasons }}</div> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|             <div v-if="movie.genres" class="movie__details-block"> |             <div v-if="movie.genres"> | ||||||
|               <h2 class="movie__details-title">Genres</h2> |               <h2 class="title">Genres</h2> | ||||||
|               <div class="movie__details-text">{{ nestedDataToString(movie.genres) }}</div> |               <div class="text">{{ movie.genres.join(", ") }}</div> | ||||||
|             </div> |  | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|  |             <div v-if="movie.type == 'show'"> | ||||||
|  |               <h2 class="title">Production status</h2> | ||||||
|  |               <div class="text">{{ movie.production_status }}</div> | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|  |             <div v-if="movie.type == 'show'"> | ||||||
|  |               <h2 class="title">Runtime</h2> | ||||||
|  |               <div class="text">{{ movie.runtime[0] }} minutes</div> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|         <!-- TODO: change this classname, this is general  --> |         <!-- TODO: change this classname, this is general  --> | ||||||
|  |  | ||||||
|         <div class="movie__admin" v-if="movie && movie.credits"> |         <div class="movie__admin" v-if="movie && movie.credits"> | ||||||
|           <h2 class="movie__details-title">Cast</h2> |           <h2 class="movie__details-title">Cast</h2> | ||||||
|           <div style="display: flex; flex-wrap: wrap;"> |           <div style="display: flex; flex-wrap: wrap"> | ||||||
|             <person v-for="cast in movie.credits.cast" :info="cast" |             <person | ||||||
|               style="flex-basis: 0;"></person> |               v-for="cast in movie.credits.cast" | ||||||
|  |               :info="cast" | ||||||
|  |               style="flex-basis: 0" | ||||||
|  |             ></person> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|       <!-- TORRENT LIST --> |       <!-- TORRENT LIST --> | ||||||
|       <TorrentList v-if="movie" :show="showTorrents" :query="title" :tmdb_id="id" |       <TorrentList | ||||||
|                    :admin="admin"></TorrentList> |         v-if="movie" | ||||||
|  |         :show="showTorrents" | ||||||
|  |         :query="title" | ||||||
|  |         :tmdb_id="id" | ||||||
|  |         :admin="admin" | ||||||
|  |       ></TorrentList> | ||||||
|     </div> |     </div> | ||||||
|   </section> |   </section> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| import storage from '@/storage' | import storage from "@/storage"; | ||||||
| import img from '@/directives/v-image' | import img from "@/directives/v-image"; | ||||||
| import TorrentList from './TorrentList' | import TorrentList from "./TorrentList"; | ||||||
| import Person from './Person' | import Person from "./Person"; | ||||||
| import SidebarListElement from './ui/sidebarListElem' | import SidebarListElement from "./ui/sidebarListElem"; | ||||||
| import store from '@/store' | import store from "@/store"; | ||||||
| import LoadingPlaceholder from './ui/LoadingPlaceholder' | import LoadingPlaceholder from "./ui/LoadingPlaceholder"; | ||||||
|  |  | ||||||
| import { getMovie, getPerson, getShow, request, getRequestStatus } from '@/api' | import { | ||||||
|  |   getMovie, | ||||||
|  |   getPerson, | ||||||
|  |   getShow, | ||||||
|  |   request, | ||||||
|  |   getRequestStatus, | ||||||
|  |   watchLink | ||||||
|  | } from "@/api"; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   props: ['id', 'type'], |   // props: ['id', 'type'], | ||||||
|  |   props: { | ||||||
|  |     id: { | ||||||
|  |       required: true, | ||||||
|  |       type: Number | ||||||
|  |     }, | ||||||
|  |     type: { | ||||||
|  |       required: false, | ||||||
|  |       type: String | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|   components: { TorrentList, Person, LoadingPlaceholder, SidebarListElement }, |   components: { TorrentList, Person, LoadingPlaceholder, SidebarListElement }, | ||||||
|   directives: { img: img }, // TODO decide to remove or use |   directives: { img: img }, // TODO decide to remove or use | ||||||
|   data(){ |   data() { | ||||||
|     return{ |     return { | ||||||
|       ASSET_URL: 'https://image.tmdb.org/t/p/', |       ASSET_URL: "https://image.tmdb.org/t/p/", | ||||||
|       ASSET_SIZES: ['w500', 'w780', 'original'], |       ASSET_SIZES: ["w500", "w780", "original"], | ||||||
|       movie: undefined, |       movie: undefined, | ||||||
|       title: undefined, |       title: undefined, | ||||||
|       poster: undefined, |       poster: undefined, | ||||||
| @@ -140,113 +194,120 @@ export default { | |||||||
|       matched: false, |       matched: false, | ||||||
|       userLoggedIn: storage.sessionId ? true : false, |       userLoggedIn: storage.sessionId ? true : false, | ||||||
|       requested: false, |       requested: false, | ||||||
|       admin: localStorage.getItem('admin') == "true" ? true : false, |       admin: localStorage.getItem("admin") == "true" ? true : false, | ||||||
|       showTorrents: false, |       showTorrents: false, | ||||||
|       compact: false |       compact: false, | ||||||
|     } |       loading: true, | ||||||
|   }, |       truncatedDescription: true | ||||||
|   methods: { |     }; | ||||||
|     parseResponse(movie) { |  | ||||||
|       this.movie = { ...movie } |  | ||||||
|       this.title = movie.title |  | ||||||
|       this.poster = movie.poster |  | ||||||
|       this.backdrop = movie.backdrop |  | ||||||
|       this.matched = movie.exists_in_plex || false |  | ||||||
|       this.checkIfRequested(movie) |  | ||||||
|         .then(status => this.requested = status) |  | ||||||
|  |  | ||||||
|       store.dispatch('documentTitle/updateTitle', movie.title) |  | ||||||
|     }, |  | ||||||
|     async checkIfRequested(movie) { |  | ||||||
|       return await getRequestStatus(movie.id, movie.type) |  | ||||||
|     }, |  | ||||||
|     nestedDataToString(data) { |  | ||||||
|       return data.join(', ') |  | ||||||
|     }, |  | ||||||
|     sendRequest(){ |  | ||||||
|       request(this.id, this.type, storage.token) |  | ||||||
|         .then(resp => { |  | ||||||
|           if (resp.success) { |  | ||||||
|             this.requested = true |  | ||||||
|           } |  | ||||||
|         }) |  | ||||||
|     }, |  | ||||||
|     openTmdb(){ |  | ||||||
|       const tmdbType = this.type === 'show' ? 'tv' : this.type |  | ||||||
|       window.location.href = 'https://www.themoviedb.org/' + tmdbType + '/' + this.id |  | ||||||
|     }, |  | ||||||
|   }, |   }, | ||||||
|   watch: { |   watch: { | ||||||
|     id: function(val){ |     id: function (val) { | ||||||
|       if (this.type === 'movie') { |       if (this.type === "movie") { | ||||||
|         this.fetchMovie(val); |         this.fetchMovie(val); | ||||||
|       } else { |       } else { | ||||||
|         this.fetchShow(val) |         this.fetchShow(val); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     backdrop: function (backdrop) { | ||||||
|  |       if (backdrop != null) { | ||||||
|  |         const style = { | ||||||
|  |           backgroundImage: | ||||||
|  |             "url(" + this.ASSET_URL + this.ASSET_SIZES[1] + backdrop + ")" | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         Object.assign(this.$refs.header.style, style); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|     numberOfTorrentResults: () => { |     numberOfTorrentResults: () => { | ||||||
|       let numTorrents = store.getters['torrentModule/resultCount'] |       let numTorrents = store.getters["torrentModule/resultCount"]; | ||||||
|       return numTorrents !== null ? numTorrents + ' results' : null |       return numTorrents !== null ? numTorrents + " results" : null; | ||||||
|  |     }, | ||||||
|  |     isPlexAuthenticated: () => { | ||||||
|  |       return store.getters["userModule/isPlexAuthenticated"]; | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   beforeDestroy() { |   methods: { | ||||||
|     store.dispatch('documentTitle/updateTitle', this.prevDocumentTitle) |     parseResponse(movie) { | ||||||
|   }, |       this.loading = false; | ||||||
|   created(){ |       this.movie = { ...movie }; | ||||||
|     this.prevDocumentTitle = store.getters['documentTitle/title'] |       this.title = movie.title; | ||||||
|  |       this.poster = movie.poster; | ||||||
|  |       this.backdrop = movie.backdrop; | ||||||
|  |       this.matched = movie.exists_in_plex || false; | ||||||
|  |       this.checkIfRequested(movie).then(status => (this.requested = status)); | ||||||
|  |  | ||||||
|     if (this.type === 'movie') { |       store.dispatch("documentTitle/updateTitle", movie.title); | ||||||
|  |       this.setPosterSrc(); | ||||||
|  |     }, | ||||||
|  |     async checkIfRequested(movie) { | ||||||
|  |       return await getRequestStatus(movie.id, movie.type); | ||||||
|  |     }, | ||||||
|  |     setPosterSrc() { | ||||||
|  |       const poster = this.$refs["poster-image"]; | ||||||
|  |       if (this.poster == null) { | ||||||
|  |         poster.src = "/no-image.png"; | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       poster.src = `${this.ASSET_URL}${this.ASSET_SIZES[0]}${this.poster}`; | ||||||
|  |     }, | ||||||
|  |     sendRequest() { | ||||||
|  |       request(this.id, this.type, storage.token).then(resp => { | ||||||
|  |         if (resp.success) { | ||||||
|  |           this.requested = true; | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  |     openInPlex() { | ||||||
|  |       watchLink(this.title, this.movie.year, storage.token).then( | ||||||
|  |         watchLink => (window.location = watchLink) | ||||||
|  |       ); | ||||||
|  |     }, | ||||||
|  |     openTmdb() { | ||||||
|  |       const tmdbType = this.type === "show" ? "tv" : this.type; | ||||||
|  |       window.location.href = | ||||||
|  |         "https://www.themoviedb.org/" + tmdbType + "/" + this.id; | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.prevDocumentTitle = store.getters["documentTitle/title"]; | ||||||
|  |  | ||||||
|  |     if (this.type === "movie") { | ||||||
|       getMovie(this.id, true) |       getMovie(this.id, true) | ||||||
|         .then(this.parseResponse) |         .then(this.parseResponse) | ||||||
|         .catch(error => { |         .catch(error => { | ||||||
|           this.$router.push({ name: '404' }); |           this.$router.push({ name: "404" }); | ||||||
|         }) |         }); | ||||||
|     } else if (this.type == 'person') { |     } else if (this.type == "person") { | ||||||
|       getPerson(this.id, true) |       getPerson(this.id, true) | ||||||
|         .then(this.parseResponse) |         .then(this.parseResponse) | ||||||
|         .catch(error => { |         .catch(error => { | ||||||
|           this.$router.push({ name: '404' }); |           this.$router.push({ name: "404" }); | ||||||
|         }) |         }); | ||||||
|     } else { |     } else { | ||||||
|       getShow(this.id, true) |       getShow(this.id, true) | ||||||
|         .then(this.parseResponse) |         .then(this.parseResponse) | ||||||
|         .catch(error => { |         .catch(error => { | ||||||
|           this.$router.push({ name: '404' }); |           this.$router.push({ name: "404" }); | ||||||
|         }) |         }); | ||||||
|     } |     } | ||||||
|  |   }, | ||||||
|     console.log('admin: ', this.admin) |   beforeDestroy() { | ||||||
|  |     store.dispatch("documentTitle/updateTitle", this.prevDocumentTitle); | ||||||
|   } |   } | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
| @import "./src/scss/loading-placeholder"; | @import "./src/scss/loading-placeholder"; | ||||||
| @import "./src/scss/variables"; | @import "./src/scss/variables"; | ||||||
| @import "./src/scss/media-queries"; | @import "./src/scss/media-queries"; | ||||||
|  | @import "./src/scss/main"; | ||||||
|  |  | ||||||
| .movie { | header { | ||||||
|   &__wrap { |  | ||||||
|     display: flex; |  | ||||||
|     &--header { |  | ||||||
|       align-items: center; |  | ||||||
|       height: 100%; |  | ||||||
|     } |  | ||||||
|     &--main { |  | ||||||
|       display: flex; |  | ||||||
|       flex-wrap: wrap; |  | ||||||
|       flex-direction: column; |  | ||||||
|       @include tablet-min{ |  | ||||||
|         flex-direction: row; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       background-color: $background-color; |  | ||||||
|       color: $text-color; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   &__header { |  | ||||||
|   $duration: 0.2s; |   $duration: 0.2s; | ||||||
|   height: 250px; |   height: 250px; | ||||||
|   transform: scaleY(1); |   transform: scaleY(1); | ||||||
| @@ -257,6 +318,9 @@ export default { | |||||||
|   background-repeat: no-repeat; |   background-repeat: no-repeat; | ||||||
|   background-position: 50% 50%; |   background-position: 50% 50%; | ||||||
|   background-color: $background-color; |   background-color: $background-color; | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |  | ||||||
|   @include tablet-min { |   @include tablet-min { | ||||||
|     height: 350px; |     height: 350px; | ||||||
|   } |   } | ||||||
| @@ -271,14 +335,17 @@ export default { | |||||||
|     height: 100%; |     height: 100%; | ||||||
|     background: $background-dark-85; |     background: $background-dark-85; | ||||||
|   } |   } | ||||||
|  |   @include mobile { | ||||||
|     &.compact { |     &.compact { | ||||||
|       height: 100px; |       height: 100px; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|   &__poster { | .movie__poster { | ||||||
|   display: none; |   display: none; | ||||||
|     @include tablet-min { |  | ||||||
|  |   @include desktop { | ||||||
|     background: $background-color; |     background: $background-color; | ||||||
|     height: 0; |     height: 0; | ||||||
|     display: block; |     display: block; | ||||||
| @@ -287,6 +354,59 @@ export default { | |||||||
|     top: 40px; |     top: 40px; | ||||||
|     left: 40px; |     left: 40px; | ||||||
|  |  | ||||||
|  |     > img { | ||||||
|  |       width: 100%; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .truncate-toggle { | ||||||
|  |   border: none; | ||||||
|  |   background: none; | ||||||
|  |   width: 100%; | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |   text-align: center; | ||||||
|  |   color: $text-color; | ||||||
|  |  | ||||||
|  |   > i { | ||||||
|  |     font-style: unset; | ||||||
|  |     font-size: 0.7rem; | ||||||
|  |     transition: 0.3s ease all; | ||||||
|  |     transform: rotateY(180deg); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &::before, | ||||||
|  |   &::after { | ||||||
|  |     content: ""; | ||||||
|  |     flex: 1; | ||||||
|  |     border-bottom: 1px solid $text-color-50; | ||||||
|  |   } | ||||||
|  |   &::before { | ||||||
|  |     margin-right: 1rem; | ||||||
|  |   } | ||||||
|  |   &::after { | ||||||
|  |     margin-left: 1rem; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .movie { | ||||||
|  |   &__wrap { | ||||||
|  |     display: flex; | ||||||
|  |     &--header { | ||||||
|  |       align-items: center; | ||||||
|  |       height: 100%; | ||||||
|  |     } | ||||||
|  |     &--main { | ||||||
|  |       display: flex; | ||||||
|  |       flex-wrap: wrap; | ||||||
|  |       flex-direction: column; | ||||||
|  |       @include tablet-min { | ||||||
|  |         flex-direction: row; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       background-color: $background-color; | ||||||
|  |       color: $text-color; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -364,24 +484,35 @@ export default { | |||||||
|     font-size: 13px; |     font-size: 13px; | ||||||
|     line-height: 1.8; |     line-height: 1.8; | ||||||
|     margin-bottom: 20px; |     margin-bottom: 20px; | ||||||
|  |  | ||||||
|  |     & .truncated { | ||||||
|  |       display: -webkit-box; | ||||||
|  |       overflow: hidden; | ||||||
|  |       -webkit-line-clamp: 4; | ||||||
|  |       -webkit-box-orient: vertical; | ||||||
|  |  | ||||||
|  |       & + .truncate-toggle > i { | ||||||
|  |         transform: rotateY(0deg) rotateZ(180deg); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @include tablet-min { |     @include tablet-min { | ||||||
|       margin-bottom: 30px; |       margin-bottom: 30px; | ||||||
|       font-size: 14px; |       font-size: 14px; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   &__details { |   &__details { | ||||||
|       &-block { |     display: flex; | ||||||
|         float: left; |     flex-wrap: wrap; | ||||||
|       } |  | ||||||
|       &-block:not(:last-child) { |     > div { | ||||||
|       margin-bottom: 20px; |       margin-bottom: 20px; | ||||||
|       margin-right: 20px; |       margin-right: 20px; | ||||||
|       @include tablet-min { |       @include tablet-min { | ||||||
|         margin-bottom: 30px; |         margin-bottom: 30px; | ||||||
|         margin-right: 30px; |         margin-right: 30px; | ||||||
|       } |       } | ||||||
|       } |       & .title { | ||||||
|       &-title { |  | ||||||
|         margin: 0; |         margin: 0; | ||||||
|         font-weight: 400; |         font-weight: 400; | ||||||
|         text-transform: uppercase; |         text-transform: uppercase; | ||||||
| @@ -391,12 +522,13 @@ export default { | |||||||
|           font-size: 16px; |           font-size: 16px; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       &-text { |       & .text { | ||||||
|         font-weight: 300; |         font-weight: 300; | ||||||
|         font-size: 14px; |         font-size: 14px; | ||||||
|         margin-top: 5px; |         margin-top: 5px; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|   &__admin { |   &__admin { | ||||||
|     width: 100%; |     width: 100%; | ||||||
|     padding: 20px; |     padding: 20px; | ||||||
|   | |||||||
| @@ -1,122 +1,207 @@ | |||||||
| <template> | <template> | ||||||
|   <li class="movies-item" :class="{'shortList': shortList}"> |   <li class="movie-item" :class="{ shortList: shortList }"> | ||||||
|     <a class="movies-item__link" :class="{'no-image': noImage}" @click.prevent="openMoviePopup(movie.id, movie.type)"> |     <figure class="movie-item__poster"> | ||||||
|  |       <img | ||||||
|       <!-- TODO change to picture element --> |         class="movie-item__img" | ||||||
|       <figure class="movies-item__poster"> |         ref="poster-image" | ||||||
|         <img v-if="!noImage" class="movies-item__img" src="~assets/placeholder.png" v-img="poster()" alt=""> |         @click="openMoviePopup(movie.id, movie.type)" | ||||||
|         <img v-if="noImage" class="movies-item__img is-loaded" src="~assets/no-image.png" alt=""> |         :alt="posterAltText" | ||||||
|  |         :data-src="poster" | ||||||
|  |         src="~assets/placeholder.png" | ||||||
|  |       /> | ||||||
|  |  | ||||||
|       <div v-if="movie.download" class="progress"> |       <div v-if="movie.download" class="progress"> | ||||||
|         <progress :value="movie.download.progress" max="100"></progress> |         <progress :value="movie.download.progress" max="100"></progress> | ||||||
|         <span>{{ movie.download.state }}: {{ movie.download.progress }}%</span> |         <span>{{ movie.download.state }}: {{ movie.download.progress }}%</span> | ||||||
|       </div> |       </div> | ||||||
|     </figure> |     </figure> | ||||||
|       <div class="movies-item__content"> |  | ||||||
|         <p class="movies-item__title">{{ movie.title || movie.name }}</p> |     <div class="movie-item__info"> | ||||||
|         <p class="movies-item__title">{{ movie.year }}</p> |       <p v-if="movie.title || movie.name">{{ movie.title || movie.name }}</p> | ||||||
|  |       <p v-if="movie.year">{{ movie.year }}</p> | ||||||
|  |       <p v-if="movie.type == 'person'"> | ||||||
|  |         Known for: {{ movie.known_for_department }} | ||||||
|  |       </p> | ||||||
|     </div> |     </div> | ||||||
|     </a> |  | ||||||
|   </li> |   </li> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| import img from '../directives/v-image' | import img from "../directives/v-image"; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   props: ['movie', 'shortList'], |   props: { | ||||||
|  |     movie: { | ||||||
|  |       type: Object, | ||||||
|  |       required: true | ||||||
|  |     }, | ||||||
|  |     shortList: { | ||||||
|  |       type: Boolean, | ||||||
|  |       required: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|   directives: { |   directives: { | ||||||
|     img: img |     img: img | ||||||
|   }, |   }, | ||||||
|   data(){ |   data() { | ||||||
|     return { |     return { | ||||||
|       noImage: false |       poster: undefined, | ||||||
|  |       observed: false, | ||||||
|  |       posterSizes: [ | ||||||
|  |         { | ||||||
|  |           id: "w500", | ||||||
|  |           minWidth: 500 | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           id: "w342", | ||||||
|  |           minWidth: 342 | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           id: "w185", | ||||||
|  |           minWidth: 185 | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           id: "w154", | ||||||
|  |           minWidth: 0 | ||||||
|         } |         } | ||||||
|  |       ] | ||||||
|  |     }; | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     posterAltText: function () { | ||||||
|  |       const type = this.movie.type || ""; | ||||||
|  |       const title = this.movie.title || this.movie.name; | ||||||
|  |       return this.movie.poster | ||||||
|  |         ? `Poster for ${type} ${title}` | ||||||
|  |         : `Missing image for ${type} ${title}`; | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   beforeMount() { | ||||||
|  |     if (this.movie.poster != null) { | ||||||
|  |       this.poster = "https://image.tmdb.org/t/p/w500" + this.movie.poster; | ||||||
|  |     } else { | ||||||
|  |       this.poster = "/no-image.png"; | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     const poster = this.$refs["poster-image"]; | ||||||
|  |     if (poster == null) return; | ||||||
|  |  | ||||||
|  |     const imageObserver = new IntersectionObserver((entries, imgObserver) => { | ||||||
|  |       entries.forEach(entry => { | ||||||
|  |         if (entry.isIntersecting && this.observed == false) { | ||||||
|  |           const lazyImage = entry.target; | ||||||
|  |           lazyImage.src = lazyImage.dataset.src; | ||||||
|  |           lazyImage.className = lazyImage.className + " is-loaded"; | ||||||
|  |           this.observed = true; | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     imageObserver.observe(poster); | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     // TODO handle missing images better and load diff sizes based on screen size |  | ||||||
|     poster() { |  | ||||||
|       if (this.movie.poster) { |  | ||||||
|         return 'https://image.tmdb.org/t/p/w500' + this.movie.poster |  | ||||||
|       } else { |  | ||||||
|         this.noImage = true |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     openMoviePopup(id, type) { |     openMoviePopup(id, type) { | ||||||
|       this.$popup.open(id, type) |       this.$popup.open(id, type); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style lang="scss"> | <style lang="scss" scoped> | ||||||
| @import "./src/scss/variables"; | @import "./src/scss/variables"; | ||||||
| @import "./src/scss/media-queries"; | @import "./src/scss/media-queries"; | ||||||
|  | @import "./src/scss/main"; | ||||||
|  |  | ||||||
| .movies-item { | .movie-item { | ||||||
|   padding: 10px; |   padding: 10px; | ||||||
|   width: 50%; |   width: 50%; | ||||||
|   background-color: $background-color; |   background-color: $background-color; | ||||||
|   transition: background-color 0.5s ease; |   transition: background-color 0.5s ease; | ||||||
|  |  | ||||||
|   @include tablet-min{ |   @include tablet-min { | ||||||
|     padding: 15px; |     padding: 15px; | ||||||
|  |     width: 33%; | ||||||
|   } |   } | ||||||
|   @include tablet-landscape-min{ |   @include tablet-landscape-min { | ||||||
|     padding: 15px; |     padding: 15px; | ||||||
|     width: 25%; |     width: 25%; | ||||||
|   } |   } | ||||||
|   @include desktop-min{ |   @include desktop-min { | ||||||
|     padding: 15px; |     padding: 15px; | ||||||
|     width: 20%; |     width: 20%; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @include desktop-lg-min{ |   @include desktop-lg-min { | ||||||
|     padding: 15px; |     padding: 15px; | ||||||
|     width: 12.5%; |     width: 12.5%; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   &__link{ |   &:hover &__info > p { | ||||||
|  |     color: $text-color; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &__poster { | ||||||
|     text-decoration: none; |     text-decoration: none; | ||||||
|     color: $text-color-70; |     color: $text-color-70; | ||||||
|     font-weight: 300; |     font-weight: 300; | ||||||
|   } |  | ||||||
|   &__content{ |     > img { | ||||||
|     padding-top: 15px; |  | ||||||
|   } |  | ||||||
|   &__poster{ |  | ||||||
|     transition: transform 0.5s ease, box-shadow 0.3s ease; |  | ||||||
|     transform: translateZ(0); |  | ||||||
|   } |  | ||||||
|   &__img{ |  | ||||||
|       width: 100%; |       width: 100%; | ||||||
|       opacity: 0; |       opacity: 0; | ||||||
|       transform: scale(0.97) translateZ(0); |       transform: scale(0.97) translateZ(0); | ||||||
|     transition: opacity 0.5s ease, transform 0.5s ease; |       transition: opacity 1s ease, transform 0.5s ease; | ||||||
|     &.is-loaded{ |       &.is-loaded { | ||||||
|         opacity: 1; |         opacity: 1; | ||||||
|         transform: scale(1); |         transform: scale(1); | ||||||
|       } |       } | ||||||
|   } |  | ||||||
|   &__link:not(.no-image):hover &__poster{ |       &:hover { | ||||||
|         transform: scale(1.03); |         transform: scale(1.03); | ||||||
|         box-shadow: 0 0 10px rgba($dark, 0.1); |         box-shadow: 0 0 10px rgba($dark, 0.1); | ||||||
|       } |       } | ||||||
|   &__title{ |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &__info { | ||||||
|  |     padding-top: 15px; | ||||||
|  |     font-weight: 300; | ||||||
|  |  | ||||||
|  |     > p { | ||||||
|  |       color: $text-color-70; | ||||||
|       margin: 0; |       margin: 0; | ||||||
|       font-size: 11px; |       font-size: 11px; | ||||||
|       letter-spacing: 0.5px; |       letter-spacing: 0.5px; | ||||||
|       transition: color 0.5s ease; |       transition: color 0.5s ease; | ||||||
|       cursor: pointer; |       cursor: pointer; | ||||||
|     @include mobile-ls-min{ |       @include mobile-ls-min { | ||||||
|         font-size: 12px; |         font-size: 12px; | ||||||
|       } |       } | ||||||
|     @include tablet-min{ |       @include tablet-min { | ||||||
|         font-size: 14px; |         font-size: 14px; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   &__link:hover &__title{ |   } | ||||||
|     color: $text-color; | } | ||||||
|  |  | ||||||
|  | .no-image { | ||||||
|  |   background-color: var(--text-color); | ||||||
|  |   color: var(--background-color); | ||||||
|  |   width: 100%; | ||||||
|  |   height: 383px; | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: center; | ||||||
|  |  | ||||||
|  |   span { | ||||||
|  |     font-size: 1.5rem; | ||||||
|  |     width: 70%; | ||||||
|  |     text-align: center; | ||||||
|  |     text-transform: uppercase; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &:hover { | ||||||
|  |     transform: scale(1); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| </style> | </style> | ||||||
| @@ -155,7 +240,6 @@ export default { | |||||||
|   progress::-webkit-progress-value { |   progress::-webkit-progress-value { | ||||||
|     background-color: $green-70; |     background-color: $green-70; | ||||||
|     border-radius: 4px; |     border-radius: 4px; | ||||||
|  |  | ||||||
|   } |   } | ||||||
|   progress::-moz-progress-bar { |   progress::-moz-progress-bar { | ||||||
|     /* style rules */ |     /* style rules */ | ||||||
|   | |||||||
| @@ -1,7 +1,11 @@ | |||||||
| <template> | <template> | ||||||
|   <div> |  | ||||||
|   <nav class="nav"> |   <nav class="nav"> | ||||||
|       <router-link class="nav__logo" :to="{name: 'home'}" exact title="Vue.js — TMDb App"> |     <router-link | ||||||
|  |       class="nav__logo" | ||||||
|  |       :to="{ name: 'home' }" | ||||||
|  |       exact | ||||||
|  |       title="Vue.js — TMDb App" | ||||||
|  |     > | ||||||
|       <svg class="nav__logo-image"> |       <svg class="nav__logo-image"> | ||||||
|         <use xlink:href="#svgLogo"></use> |         <use xlink:href="#svgLogo"></use> | ||||||
|       </svg> |       </svg> | ||||||
| @@ -23,8 +27,14 @@ | |||||||
|         </router-link> |         </router-link> | ||||||
|       </li> |       </li> | ||||||
|  |  | ||||||
|  |       <li class="nav__item mobile-only"></li> | ||||||
|  |  | ||||||
|       <li class="nav__item nav__item--profile"> |       <li class="nav__item nav__item--profile"> | ||||||
|           <router-link class="nav__link nav__link--profile" :to="{name: 'signin'}" v-if="!userLoggedIn"> |         <router-link | ||||||
|  |           class="nav__link nav__link--profile" | ||||||
|  |           :to="{ name: 'signin' }" | ||||||
|  |           v-if="!userLoggedIn" | ||||||
|  |         > | ||||||
|           <div class="nav__link-wrap"> |           <div class="nav__link-wrap"> | ||||||
|             <svg class="nav__link-icon"> |             <svg class="nav__link-icon"> | ||||||
|               <use xlink:href="#iconLogin"></use> |               <use xlink:href="#iconLogin"></use> | ||||||
| @@ -33,7 +43,11 @@ | |||||||
|           </div> |           </div> | ||||||
|         </router-link> |         </router-link> | ||||||
|  |  | ||||||
|           <router-link class="nav__link nav__link--profile" :to="{name: 'profile'}" v-if="userLoggedIn"> |         <router-link | ||||||
|  |           class="nav__link nav__link--profile" | ||||||
|  |           :to="{ name: 'profile' }" | ||||||
|  |           v-if="userLoggedIn" | ||||||
|  |         > | ||||||
|           <div class="nav__link-wrap"> |           <div class="nav__link-wrap"> | ||||||
|             <svg class="nav__link-icon"> |             <svg class="nav__link-icon"> | ||||||
|               <use xlink:href="#iconLogin"></use> |               <use xlink:href="#iconLogin"></use> | ||||||
| @@ -44,35 +58,36 @@ | |||||||
|       </li> |       </li> | ||||||
|     </ul> |     </ul> | ||||||
|   </nav> |   </nav> | ||||||
|  |  | ||||||
|     <div class="spacer"></div> |  | ||||||
|   </div> |  | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| import storage from '@/storage' | import storage from "@/storage"; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   data(){ |   data() { | ||||||
|     return { |     return { | ||||||
|       listTypes: storage.homepageLists, |       listTypes: storage.homepageLists, | ||||||
|       userLoggedIn: localStorage.getItem('token') ? true : false |       userLoggedIn: localStorage.getItem("token") ? true : false | ||||||
|     } |     }; | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     setUserStatus(){ |     setUserStatus() { | ||||||
|       this.userLoggedIn = localStorage.getItem('token') ? true : false; |       this.userLoggedIn = localStorage.getItem("token") ? true : false; | ||||||
|     }, |     }, | ||||||
|     toggleNav(){ |     toggleNav() { | ||||||
|       document.querySelector('.nav__hamburger').classList.toggle('nav__hamburger--active'); |       document | ||||||
|       document.querySelector('.nav__list').classList.toggle('nav__list--active'); |         .querySelector(".nav__hamburger") | ||||||
|  |         .classList.toggle("nav__hamburger--active"); | ||||||
|  |       document | ||||||
|  |         .querySelector(".nav__list") | ||||||
|  |         .classList.toggle("nav__list--active"); | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   created(){ |   created() { | ||||||
|     // TODO move this to state manager |     // TODO move this to state manager | ||||||
|     eventHub.$on('setUserStatus', this.setUserStatus); |     eventHub.$on("setUserStatus", this.setUserStatus); | ||||||
|   } |   } | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
| @@ -83,45 +98,45 @@ export default { | |||||||
|   width: 30px; |   width: 30px; | ||||||
| } | } | ||||||
|  |  | ||||||
| .spacer { |  | ||||||
|   @include mobile-only { |  | ||||||
|     width: 100%; |  | ||||||
|     height: $header-size; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .nav { | .nav { | ||||||
|   transition: background .5s ease; |   transition: background 0.5s ease; | ||||||
|   position: fixed; |   position: fixed; | ||||||
|   top: 0; |   bottom: 0; | ||||||
|   left: 0; |   left: 0; | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   height: 50px; |   height: var(--header-size); | ||||||
|   z-index: 10; |   z-index: 10; | ||||||
|   display: block; |   display: block; | ||||||
|   color: $text-color; |   color: $text-color; | ||||||
|   background-color: $background-color-secondary; |   background-color: $background-color-secondary; | ||||||
|  |  | ||||||
|   @include tablet-min{ |   @include tablet-min { | ||||||
|  |     top: 0; | ||||||
|  |     bottom: unset; | ||||||
|     width: 95px; |     width: 95px; | ||||||
|     height: 100vh; |     height: 100vh; | ||||||
|   } |   } | ||||||
|   &__logo { |   &__logo { | ||||||
|     width: 55px; |     width: 95px; | ||||||
|     height: $header-size; |     height: $header-size; | ||||||
|     display: flex; |     display: flex; | ||||||
|     align-items: center; |     align-items: center; | ||||||
|     justify-content: center; |     justify-content: center; | ||||||
|     background: $background-nav-logo; |     background: $background-nav-logo; | ||||||
|     @include tablet-min{ |  | ||||||
|       width: 95px; |     @include mobile-only { | ||||||
|  |       align-items: flex-start; | ||||||
|  |       padding-top: 0.5rem; | ||||||
|  |       width: 55px; | ||||||
|     } |     } | ||||||
|     &-image{ |  | ||||||
|  |     &-image { | ||||||
|       width: 35px; |       width: 35px; | ||||||
|       height: 31px; |       height: 31px; | ||||||
|       fill: $green; |       fill: $green; | ||||||
|       transition: transform 0.5s ease; |       transition: transform 0.5s ease; | ||||||
|       @include tablet-min{ |  | ||||||
|  |       @include tablet-min { | ||||||
|         width: 45px; |         width: 45px; | ||||||
|         height: 40px; |         height: 40px; | ||||||
|       } |       } | ||||||
| @@ -135,12 +150,12 @@ export default { | |||||||
|     position: fixed; |     position: fixed; | ||||||
|     width: 55px; |     width: 55px; | ||||||
|     height: 50px; |     height: 50px; | ||||||
|     top: 0; |     bottom: 1.5rem; | ||||||
|     right: 0; |     right: 0; | ||||||
|     cursor: pointer; |     cursor: pointer; | ||||||
|     z-index: 10; |     z-index: 10; | ||||||
|     border-left: 1px solid $background-color; |     border-left: 1px solid $background-color; | ||||||
|     @include tablet-min{ |     @include tablet-min { | ||||||
|       display: none; |       display: none; | ||||||
|     } |     } | ||||||
|     .bar { |     .bar { | ||||||
| @@ -172,9 +187,9 @@ export default { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     &--active { |     &--active { | ||||||
|       .bar{ |       .bar { | ||||||
|         &:nth-child(1), |         &:nth-child(1), | ||||||
|         &:nth-child(3){ |         &:nth-child(3) { | ||||||
|           width: 0; |           width: 0; | ||||||
|         } |         } | ||||||
|         &:nth-child(2) { |         &:nth-child(2) { | ||||||
| @@ -198,15 +213,21 @@ export default { | |||||||
|     left: 0; |     left: 0; | ||||||
|     top: 50px; |     top: 50px; | ||||||
|     border-top: 1px solid $background-color; |     border-top: 1px solid $background-color; | ||||||
|  |  | ||||||
|     @include mobile-only { |     @include mobile-only { | ||||||
|       display: flex; |       display: flex; | ||||||
|  |       position: absolute; | ||||||
|  |       top: unset; | ||||||
|  |       bottom: var(--header-size); | ||||||
|  |       height: min-content; | ||||||
|       flex-wrap: wrap; |       flex-wrap: wrap; | ||||||
|       font-size: 0; |       font-size: 0; | ||||||
|       opacity: 0; |       opacity: 0; | ||||||
|       visibility: hidden; |       visibility: hidden; | ||||||
|       background-color: $background-95; |       background-color: $background-95; | ||||||
|       text-align: left; |       text-align: left; | ||||||
|       &--active{ |  | ||||||
|  |       &--active { | ||||||
|         opacity: 1; |         opacity: 1; | ||||||
|         visibility: visible; |         visibility: visible; | ||||||
|       } |       } | ||||||
| @@ -221,15 +242,15 @@ export default { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   &__item { |   &__item { | ||||||
|     transition: background .5s ease, color .5s ease, border .5s ease; |     transition: background 0.5s ease, color 0.5s ease, border 0.5s ease; | ||||||
|     background-color: $background-color-secondary; |     background-color: $background-color-secondary; | ||||||
|     color: $text-color-70; |     color: $text-color-70; | ||||||
|  |  | ||||||
|     @include mobile-only { |     @include mobile-only { | ||||||
|       flex: 0 0 50%; |       flex: 0 0 33.3%; | ||||||
|       text-align: center; |       text-align: center; | ||||||
|       border-bottom: 1px solid $background-color; |       border-bottom: 1px solid $background-color; | ||||||
|       &:nth-child(odd){ |       &:nth-child(odd) { | ||||||
|         border-right: 1px solid $background-color; |         border-right: 1px solid $background-color; | ||||||
|  |  | ||||||
|         &:last-child { |         &:last-child { | ||||||
| @@ -251,7 +272,8 @@ export default { | |||||||
|         border-left: 1px solid $background-color; |         border-left: 1px solid $background-color; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     &:hover, .is-active { |     &:hover, | ||||||
|  |     .is-active { | ||||||
|       color: $text-color; |       color: $text-color; | ||||||
|       background-color: $background-color; |       background-color: $background-color; | ||||||
|     } |     } | ||||||
| @@ -299,14 +321,14 @@ export default { | |||||||
|         height: 20px; |         height: 20px; | ||||||
|         margin-bottom: 5px; |         margin-bottom: 5px; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|     } |     } | ||||||
|     &-title { |     &-title { | ||||||
|       margin-top: 5px; |       margin-top: 5px; | ||||||
|       display: block; |       display: block; | ||||||
|       width: 100%; |       width: 100%; | ||||||
|     } |     } | ||||||
|     &:hover &-icon, &.is-active &-icon { |     &:hover &-icon, | ||||||
|  |     &.is-active &-icon { | ||||||
|       fill: $text-color; |       fill: $text-color; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
|         <h2 class="profile__title">{{ emoji }} Welcome {{ username }}</h2> |         <h2 class="profile__title">{{ emoji }} Welcome {{ username }}</h2> | ||||||
|  |  | ||||||
|         <div class="button--group"> |         <div class="button--group"> | ||||||
|           <seasoned-button @click="showSettings = !showSettings">{{ showSettings ? 'hide settings' : 'show settings' }}</seasoned-button> |           <seasoned-button @click="toggleSettings">{{ showSettings ? 'hide settings' : 'show settings' }}</seasoned-button> | ||||||
|  |  | ||||||
|           <seasoned-button @click="logOut">Log out</seasoned-button> |           <seasoned-button @click="logOut">Log out</seasoned-button> | ||||||
|         </div> |         </div> | ||||||
| @@ -63,11 +63,15 @@ export default { | |||||||
|   methods: { |   methods: { | ||||||
|     toggleSettings() { |     toggleSettings() { | ||||||
|       this.showSettings = this.showSettings ? false : true; |       this.showSettings = this.showSettings ? false : true; | ||||||
|  |  | ||||||
|  |       if (this.showSettings) { | ||||||
|  |         this.$router.replace({ query: { settings: true} }) | ||||||
|  |       } else { | ||||||
|  |         this.$router.replace({ name: 'profile' }) | ||||||
|  |       } | ||||||
|     }, |     }, | ||||||
|     logOut(){ |     logOut(){ | ||||||
|       localStorage.clear(); |       this.$router.push('logout') | ||||||
|       eventHub.$emit('setUserStatus'); |  | ||||||
|       this.$router.push({ name: 'home' }); |  | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   created(){ |   created(){ | ||||||
| @@ -76,6 +80,8 @@ export default { | |||||||
|     } else { |     } else { | ||||||
|       this.userLoggedIn = true; |       this.userLoggedIn = true; | ||||||
|  |  | ||||||
|  |       this.showSettings = window.location.toString().includes('settings=true') | ||||||
|  |  | ||||||
|       getUserRequests() |       getUserRequests() | ||||||
|         .then(results => { |         .then(results => { | ||||||
|           this.results = results.results |           this.results = results.results | ||||||
|   | |||||||
| @@ -2,17 +2,14 @@ | |||||||
|   <section> |   <section> | ||||||
|     <h1>Register new user</h1> |     <h1>Register new user</h1> | ||||||
|  |  | ||||||
|     <seasoned-input placeholder="username" icon="Email" type="username" :value.sync="username" /> |     <seasoned-input placeholder="username" icon="Email" type="username" :value.sync="username" @enter="submit"/> | ||||||
|  |  | ||||||
|     <seasoned-input placeholder="password" icon="Keyhole" type="password" |     <seasoned-input placeholder="password" icon="Keyhole" type="password" :value.sync="password" @enter="submit"/> | ||||||
|       :value.sync="password" @enter="requestNewUser"/> |     <seasoned-input placeholder="repeat password" icon="Keyhole" type="password" :value.sync="passwordRepeat" @enter="submit"/> | ||||||
|  |  | ||||||
|     <seasoned-input placeholder="repeat password" icon="Keyhole" type="password" |  | ||||||
|       :value.sync="passwordRepeat" @enter="requestNewUser"/> |  | ||||||
|  |  | ||||||
|     <seasoned-button @click="requestNewUser">Register</seasoned-button> |  | ||||||
|  |  | ||||||
|  |     <seasoned-button @click="submit">Register</seasoned-button> | ||||||
|     <router-link class="link" to="/signin">Have a user? Sign in here</router-link> |     <router-link class="link" to="/signin">Have a user? Sign in here</router-link> | ||||||
|  |  | ||||||
|     <seasoned-messages :messages.sync="messages"></seasoned-messages> |     <seasoned-messages :messages.sync="messages"></seasoned-messages> | ||||||
|   </section> |   </section> | ||||||
| </template> | </template> | ||||||
| @@ -34,57 +31,47 @@ export default { | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     requestNewUser(){ |     submit() { | ||||||
|       let { username, password, passwordRepeat } = this |       this.messages = []; | ||||||
|  |       let { username, password, passwordRepeat } = this; | ||||||
|  |  | ||||||
|       let verifyCredentials = this.checkCredentials(username, password, passwordRepeat); |       if (username == null || username.length == 0) { | ||||||
|  |         this.messages.push({ type: 'error', title: 'Missing username' }) | ||||||
|  |         return | ||||||
|  |       } else if (password == null || password.length == 0) { | ||||||
|  |         this.messages.push({ type: 'error', title: 'Missing password' }) | ||||||
|  |         return | ||||||
|  |       } else if (passwordRepeat == null || passwordRepeat.length == 0) { | ||||||
|  |         this.messages.push({ type: 'error', title: 'Missing repeat password' }) | ||||||
|  |         return | ||||||
|  |       } else if (passwordRepeat != password) { | ||||||
|  |         this.messages.push({ type: 'error', title: 'Passwords do not match' }) | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |  | ||||||
|       if (verifyCredentials.verified) { |       this.registerUser(username, password) | ||||||
|  |     }, | ||||||
|         register(username, password) |     registerUser(username, password) { | ||||||
|  |       register(username, password, true) | ||||||
|         .then(data => { |         .then(data => { | ||||||
|           if (data.success){ |           if (data.success){ | ||||||
|             localStorage.setItem('token', data.token); |             localStorage.setItem('token', data.token); | ||||||
|               localStorage.setItem('username', username); |             const jwtData = parseJwt(data.token) | ||||||
|               localStorage.setItem('admin', data.admin) |             localStorage.setItem('username', jwtData['username']); | ||||||
|  |             localStorage.setItem('admin', jwtData['admin'] || false); | ||||||
|  |  | ||||||
|             eventHub.$emit('setUserStatus'); |             eventHub.$emit('setUserStatus'); | ||||||
|             this.$router.push({ name: 'profile' }) |             this.$router.push({ name: 'profile' }) | ||||||
|           } |           } | ||||||
|         }) |         }) | ||||||
|         .catch(error => { |         .catch(error => { | ||||||
|  |           if (error.status === 401) { | ||||||
|  |             this.messages.push({ type: 'error', title: 'Access denied', message: 'Incorrect username or password' }) | ||||||
|  |           } | ||||||
|  |           else { | ||||||
|             this.messages.push({ type: 'error', title: 'Unexpected error', message: error.message }) |             this.messages.push({ type: 'error', title: 'Unexpected error', message: error.message }) | ||||||
|  |           } | ||||||
|         }); |         }); | ||||||
|       }  |  | ||||||
|       else { |  | ||||||
|         this.messages.push({ type: 'warning', title: 'Parse error', message: verifyCredentials.reason }) |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     checkCredentials(username, password, passwordRepeat) { |  | ||||||
|       if (!username || username.length === 0) { |  | ||||||
|         return { |  | ||||||
|           verified: false, |  | ||||||
|           reason: 'Fill inn username' |  | ||||||
|         } |  | ||||||
|       }  |  | ||||||
|       else if (!password || !passwordRepeat) { |  | ||||||
|         return { |  | ||||||
|           verified: false, |  | ||||||
|           reason: "Fill inn both password fields" |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       else if (password !== passwordRepeat) { |  | ||||||
|         return { |  | ||||||
|           verified: false, |  | ||||||
|           reason: 'Passwords do not match' |  | ||||||
|         } |  | ||||||
|       }  |  | ||||||
|       else { |  | ||||||
|         return { |  | ||||||
|           verified: true, |  | ||||||
|           reason: 'Verified credentials' |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     }, |     }, | ||||||
|     logOut(){ |     logOut(){ | ||||||
|       localStorage.clear(); |       localStorage.clear(); | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| <template> | <template> | ||||||
|   <div> |   <div class="page-container"> | ||||||
|     <list-header :title="title" :info="resultCount" :sticky="true" /> |     <list-header :title="title" :info="resultCount" :sticky="true" /> | ||||||
|  |  | ||||||
|     <results-list :results="results" /> |     <results-list :results="results" /> | ||||||
| @@ -9,7 +9,9 @@ | |||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <div class="notFound" v-if="results.length == 0 && loading == false"> |     <div class="notFound" v-if="results.length == 0 && loading == false"> | ||||||
|       <h1 class="notFound-title">No results for search: <b>{{ query }}</b></h1> |       <h1 class="notFound-title"> | ||||||
|  |         No results for search: <b>{{ query }}</b> | ||||||
|  |       </h1> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <loader v-if="loading" /> |     <loader v-if="loading" /> | ||||||
| @@ -29,11 +31,11 @@ | |||||||
| </style> | </style> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| import { searchTmdb } from '@/api' | import { searchTmdb } from "@/api"; | ||||||
| import ListHeader from '@/components/ListHeader' | import ListHeader from "@/components/ListHeader"; | ||||||
| import ResultsList from '@/components/ResultsList' | import ResultsList from "@/components/ResultsList"; | ||||||
| import SeasonedButton from '@/components/ui/SeasonedButton' | import SeasonedButton from "@/components/ui/SeasonedButton"; | ||||||
| import Loader from '@/components/ui/Loader' | import Loader from "@/components/ui/Loader"; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   components: { ListHeader, ResultsList, SeasonedButton, Loader }, |   components: { ListHeader, ResultsList, SeasonedButton, Loader }, | ||||||
| @@ -58,59 +60,74 @@ export default { | |||||||
|       totalPages: 0, |       totalPages: 0, | ||||||
|       results: [], |       results: [], | ||||||
|       totalResults: [] |       totalResults: [] | ||||||
|     } |     }; | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|     resultCount() { |     resultCount() { | ||||||
|       const loadedResults = this.results.length |       const loadedResults = this.results.length; | ||||||
|       const totalResults = this.totalResults < 10000 ? this.totalResults : '∞' |       const totalResults = this.totalResults < 10000 ? this.totalResults : "∞"; | ||||||
|       return `${loadedResults} of ${totalResults} results` |       return `${loadedResults} of ${totalResults} results`; | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     search(query=this.query, page=this.page, adult=this.adult, mediaType=this.mediaType) { |     search( | ||||||
|       searchTmdb(query, page, adult, mediaType) |       query = this.query, | ||||||
|         .then(this.parseResponse) |       page = this.page, | ||||||
|  |       adult = this.adult, | ||||||
|  |       mediaType = this.mediaType | ||||||
|  |     ) { | ||||||
|  |       searchTmdb(query, page, adult, mediaType).then(this.parseResponse); | ||||||
|     }, |     }, | ||||||
|     parseResponse(data) { |     parseResponse(data) { | ||||||
|       if (this.results.length > 0) { |       if (this.results.length > 0) { | ||||||
|         this.results.push(...data.results) |         this.results.push(...data.results); | ||||||
|       } else { |       } else { | ||||||
|         this.results = data.results |         this.results = data.results; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       this.totalPages = data.total_pages |       this.totalPages = data.total_pages; | ||||||
|       this.totalResults = data.total_results || data.results.length |       this.totalResults = data.total_results || data.results.length; | ||||||
|  |  | ||||||
|       this.loading = false |       this.loading = false; | ||||||
|     }, |     }, | ||||||
|     loadMore() { |     loadMore() { | ||||||
|       this.page++ |       this.page++; | ||||||
|  |  | ||||||
|       window.history.replaceState({}, 'search', `/#/search?query=${this.query}&page=${this.page}`) |       window.history.replaceState( | ||||||
|       this.search() |         {}, | ||||||
|  |         "search", | ||||||
|  |         `/#/search?query=${this.query}&page=${this.page}` | ||||||
|  |       ); | ||||||
|  |       this.search(); | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   created() { |   created() { | ||||||
|     const { query, page, adult, media_type } = this.$route.query |     const { query, page, adult, media_type } = this.$route.query; | ||||||
|  |  | ||||||
|     if (!query) { |     if (!query) { | ||||||
|       // abort |       // abort | ||||||
|       console.error('abort, no query') |       console.error("abort, no query"); | ||||||
|     } |     } | ||||||
|     this.query = decodeURIComponent(query) |     this.query = decodeURIComponent(query); | ||||||
|     this.page = page || 1 |     this.page = page || 1; | ||||||
|     this.adult = adult || this.adult |     this.adult = adult || this.adult; | ||||||
|     this.mediaType = media_type || this.mediaType |     this.mediaType = media_type || this.mediaType; | ||||||
|     this.title = `Search results: ${this.query}` |     this.title = `Search results: ${this.query}`; | ||||||
|  |  | ||||||
|     this.search() |     this.search(); | ||||||
|   } |   } | ||||||
| } | }; | ||||||
|  |  | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
|  | @import "./src/scss/media-queries"; | ||||||
|  |  | ||||||
|  | @include mobile-only { | ||||||
|  |   .page-container { | ||||||
|  |     margin-top: 1rem; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| .fullwidth-button { | .fullwidth-button { | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   margin: 1rem 0; |   margin: 1rem 0; | ||||||
| @@ -118,5 +135,4 @@ export default { | |||||||
|   display: flex; |   display: flex; | ||||||
|   justify-content: center; |   justify-content: center; | ||||||
| } | } | ||||||
|  |  | ||||||
| </style> | </style> | ||||||
| @@ -1,184 +1,290 @@ | |||||||
| <template> | <template> | ||||||
|   <div> |   <!-- <div> --> | ||||||
|      |  | ||||||
|   <div class="search"> |   <div class="search"> | ||||||
|     <input |     <input | ||||||
|       ref="input" |       ref="input" | ||||||
|       type="text" |       type="text" | ||||||
|         placeholder="Search for a movie or show" |       placeholder="Search for movie or show" | ||||||
|  |       aria-label="Search input for finding a movie or show" | ||||||
|       autocorrect="off" |       autocorrect="off" | ||||||
|       autocapitalize="off" |       autocapitalize="off" | ||||||
|  |       tabindex="1" | ||||||
|       v-model="query" |       v-model="query" | ||||||
|       @input="handleInput" |       @input="handleInput" | ||||||
|       @click="focus = true" |       @click="focus = true" | ||||||
|       @keydown.escape="handleEscape" |       @keydown.escape="handleEscape" | ||||||
|       @keyup.enter="handleSubmit" |       @keyup.enter="handleSubmit" | ||||||
|       @keydown.up="navigateUp" |       @keydown.up="navigateUp" | ||||||
|         @keydown.down="navigateDown" /> |       @keydown.down="navigateDown" | ||||||
|  |     /> | ||||||
|  |  | ||||||
|       <svg class="search--icon" fill="currentColor"><use xlink:href="#iconSearch"></use></svg> |     <svg class="search-icon" fill="currentColor" @click="handleSubmit"> | ||||||
|  |       <use xlink:href="#iconSearch"></use> | ||||||
|  |     </svg> | ||||||
|   </div> |   </div> | ||||||
|  |   <!--  | ||||||
|     <transition name="fade"> |     <transition name="fade"> | ||||||
|       <div class="dropdown" v-if="!disabled && focus && query.length > 0"> |       <div class="dropdown" v-if="!disabled && focus && query.length > 0"> | ||||||
|         <div class="dropdown--results"> |         <div class="filter"> | ||||||
|  |           <h2>Filter your search:</h2> | ||||||
|  |  | ||||||
|           <ul v-for="(item, index) in elasticSearchResults" |           <div class="filter-items"> | ||||||
|               @click="$popup.open(item.id, item.type)" |             <toggle-button | ||||||
|               :class="{ active: index + 1 === selectedResult}"> |               :options="searchTypes" | ||||||
|                  |               :selected.sync="selectedSearchType" | ||||||
|               {{ item.name }} |             /> | ||||||
|           </ul> |  | ||||||
|  |  | ||||||
|  |             <label | ||||||
|  |               >Adult | ||||||
|  |               <input type="checkbox" value="adult" v-model="adult" /> | ||||||
|  |             </label> | ||||||
|  |           </div> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|         <seasoned-button class="end-section" fullWidth="true"  |         <hr /> | ||||||
|           @click="focus = false" :active="elasticSearchResults.length + 1 === selectedResult"> |  | ||||||
|  |         <div class="dropdown-results" v-if="elasticSearchResults.length"> | ||||||
|  |           <ul | ||||||
|  |             v-for="(item, index) in elasticSearchResults" | ||||||
|  |             @click="openResult(item, index + 1)" | ||||||
|  |             :class="{ active: index + 1 === selectedResult }" | ||||||
|  |           > | ||||||
|  |             {{ | ||||||
|  |               item.name | ||||||
|  |             }} | ||||||
|  |           </ul> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |         <div v-else class="dropdown"> | ||||||
|  |           <div class="dropdown-results"> | ||||||
|  |             <h2 class="not-found"> | ||||||
|  |               No results for query: <b>{{ query }}</b> | ||||||
|  |             </h2> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |         <seasoned-button | ||||||
|  |           class="end-section" | ||||||
|  |           fullWidth="true" | ||||||
|  |           @click="focus = false" | ||||||
|  |           :active="elasticSearchResults.length + 1 === selectedResult" | ||||||
|  |         > | ||||||
|           close |           close | ||||||
|         </seasoned-button> |         </seasoned-button> | ||||||
|       </div> |       </div> | ||||||
|     </transition> |     </transition> | ||||||
| </div> |   </div> --> | ||||||
|  |  | ||||||
|  |  | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| import SeasonedButton from '@/components/ui/SeasonedButton' | import SeasonedButton from "@/components/ui/SeasonedButton"; | ||||||
|  | import ToggleButton from "@/components/ui/ToggleButton"; | ||||||
|  |  | ||||||
| import { elasticSearchMoviesAndShows } from '@/api' | import { elasticSearchMoviesAndShows } from "@/api"; | ||||||
| import config from '@/config.json' | import config from "@/config.json"; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   name: 'SearchInput', |   name: "SearchInput", | ||||||
|   components: { |   components: { | ||||||
|     SeasonedButton |     SeasonedButton, | ||||||
|  |     ToggleButton | ||||||
|   }, |   }, | ||||||
|   props: ['value'], |   props: ["value"], | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|  |       adult: true, | ||||||
|  |       searchTypes: ["all", "movie", "show", "person"], | ||||||
|  |       selectedSearchType: "all", | ||||||
|  |  | ||||||
|       query: this.value, |       query: this.value, | ||||||
|       focus: false, |       focus: false, | ||||||
|       disabled: false, |       disabled: false, | ||||||
|       scrollListener: undefined, |       scrollListener: undefined, | ||||||
|       scrollDistance: 0, |       scrollDistance: 0, | ||||||
|       elasticSearchResults: '', |       elasticSearchResults: [], | ||||||
|       selectedResult: 0 |       selectedResult: 0 | ||||||
|     } |     }; | ||||||
|   }, |   }, | ||||||
|   watch: { |   watch: { | ||||||
|     focus: function(val) { |     focus: function (val) { | ||||||
|       if (val === true) { |       if (val === true) { | ||||||
|         window.addEventListener('scroll', this.disableFocus) |         window.addEventListener("scroll", this.disableFocus); | ||||||
|       } else { |       } else { | ||||||
|         window.removeEventListener('scroll', this.disableFocus) |         window.removeEventListener("scroll", this.disableFocus); | ||||||
|         this.scrollDistance = 0 |         this.scrollDistance = 0; | ||||||
|       } |       } | ||||||
|  |     }, | ||||||
|  |     adult: function (value) { | ||||||
|  |       this.handleInput(); | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   beforeMount() { |   beforeMount() { | ||||||
|     const elasticUrl = config.ELASTIC_URL |     const elasticUrl = config.ELASTIC_URL; | ||||||
|     if (elasticUrl === undefined || elasticUrl === false || elasticUrl === '') { |     if (elasticUrl === undefined || elasticUrl === false || elasticUrl === "") { | ||||||
|       this.disabled = true |       this.disabled = true; | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   beforeDestroy() { |   beforeDestroy() { | ||||||
|     console.log('scroll eventlistener not removed, destroying!') |     console.log("scroll eventlistener not removed, destroying!"); | ||||||
|     window.removeEventListener('scroll', this.disableFocus) |     window.removeEventListener("scroll", this.disableFocus); | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     navigateDown() { |     navigateDown() { | ||||||
|       this.focus = true |       this.focus = true; | ||||||
|       this.selectedResult++ |       this.selectedResult++; | ||||||
|     }, |     }, | ||||||
|     navigateUp() { |     navigateUp() { | ||||||
|       this.focus = true |       this.focus = true; | ||||||
|       this.selectedResult-- |       this.selectedResult--; | ||||||
|       const input = this.$refs.input; |       const input = this.$refs.input; | ||||||
|       const textLength = input.value.length |       const textLength = input.value.length; | ||||||
|  |  | ||||||
|       setTimeout(() => { |       setTimeout(() => { | ||||||
|         input.focus() |         input.focus(); | ||||||
|         input.setSelectionRange(textLength, textLength + 1) |         input.setSelectionRange(textLength, textLength + 1); | ||||||
|       }, 1) |       }, 1); | ||||||
|     }, |     }, | ||||||
|     handleInput(e){ |     openResult(item, index) { | ||||||
|       this.selectedResult = 0 |       this.selectedResult = index; | ||||||
|       this.$emit('input', this.query); |       this.$popup.open(item.id, item.type); | ||||||
|  |     }, | ||||||
|  |     handleInput(e) { | ||||||
|  |       this.selectedResult = 0; | ||||||
|  |       this.$emit("input", this.query); | ||||||
|  |  | ||||||
|       if (! this.focus) { |       if (!this.focus) { | ||||||
|         this.focus = true; |         this.focus = true; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       elasticSearchMoviesAndShows(this.query) |       elasticSearchMoviesAndShows(this.query).then(resp => { | ||||||
|       .then(resp => { |         const data = resp.hits.hits; | ||||||
|         const data = resp.hits.hits |  | ||||||
|  |  | ||||||
|         this.elasticSearchResults = data.map(item => { |         let results = data.map(item => { | ||||||
|           const index = item._index.slice(0, -1) |           const index = item._index.slice(0, -1); | ||||||
|           if (index === 'movie' || item._source.original_title) { |           if (index === "movie" || item._source.original_title) { | ||||||
|             return { |             return { | ||||||
|               name: item._source.original_title, |               name: item._source.original_title, | ||||||
|               id: item._source.id, |               id: item._source.id, | ||||||
|               type: 'movie' |               adult: item._source.adult, | ||||||
|             } |               type: "movie" | ||||||
|           } else if (index === 'show' || item._source.original_name) { |             }; | ||||||
|  |           } else if (index === "show" || item._source.original_name) { | ||||||
|             return { |             return { | ||||||
|               name: item._source.original_name, |               name: item._source.original_name, | ||||||
|               id: item._source.id, |               id: item._source.id, | ||||||
|               type: 'show' |               adult: item._source.adult, | ||||||
|  |               type: "show" | ||||||
|  |             }; | ||||||
|           } |           } | ||||||
|  |         }); | ||||||
|  |         results = this.removeDuplicates(results); | ||||||
|  |         this.elasticSearchResults = results; | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  |     removeDuplicates(searchResults) { | ||||||
|  |       let filteredResults = []; | ||||||
|  |       searchResults.map(result => { | ||||||
|  |         const numberOfDuplicates = filteredResults.filter( | ||||||
|  |           filterItem => filterItem.id == result.id | ||||||
|  |         ); | ||||||
|  |         if (numberOfDuplicates.length >= 1) { | ||||||
|  |           return null; | ||||||
|         } |         } | ||||||
|         }) |         filteredResults.push(result); | ||||||
|         console.log(this.elasticSearchResults) |       }); | ||||||
|       }) |  | ||||||
|  |       if (this.adult == false) { | ||||||
|  |         filteredResults = filteredResults.filter( | ||||||
|  |           result => result.adult == false | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       return filteredResults; | ||||||
|     }, |     }, | ||||||
|     handleSubmit() { |     handleSubmit() { | ||||||
|       let searchResults = this.elasticSearchResults |       let searchResults = this.elasticSearchResults; | ||||||
|  |  | ||||||
|       if (this.selectedResult > searchResults.length) { |       if (this.selectedResult > searchResults.length) { | ||||||
|         this.focus = false |         this.focus = false; | ||||||
|         this.selectedResult = 0 |         this.selectedResult = 0; | ||||||
|       } else if (this.selectedResult > 0) { |       } else if (this.selectedResult > 0) { | ||||||
|         const resultItem = searchResults[this.selectedResult - 1] |         const resultItem = searchResults[this.selectedResult - 1]; | ||||||
|         this.$popup.open(resultItem.id, resultItem.type) |         this.$popup.open(resultItem.id, resultItem.type); | ||||||
|       } else { |       } else { | ||||||
|         const encodedQuery = encodeURI(this.query.replace('/ /g, "+"')) |         const encodedQuery = encodeURI(this.query.replace('/ /g, "+"')); | ||||||
|         this.$router.push({ name: 'search', query: { query: encodedQuery }}); |         const media_type = | ||||||
|         this.focus = false |           this.selectedSearchType !== "all" ? this.selectedSearchType : null; | ||||||
|         this.selectedResult = 0 |         this.$router.push({ | ||||||
|  |           name: "search", | ||||||
|  |           query: { query: encodedQuery, adult: this.adult, media_type } | ||||||
|  |         }); | ||||||
|  |         this.focus = false; | ||||||
|  |         this.selectedResult = 0; | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     handleEscape() { |     handleEscape() { | ||||||
|       if (this.$popup.isOpen) { |       if (this.$popup.isOpen) { | ||||||
|         console.log('THIS WAS FUCKOING OPEN!') |         console.log("THIS WAS FUCKOING OPEN!"); | ||||||
|       } else { |       } else { | ||||||
|         this.focus = false |         this.focus = false; | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     disableFocus(_) { |     disableFocus(_) { | ||||||
|       this.focus = false |       this.focus = false; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
| @import "./src/scss/variables"; | @import "./src/scss/variables"; | ||||||
| @import "./src/scss/media-queries"; | @import "./src/scss/media-queries"; | ||||||
| @import './src/scss/main'; | @import "./src/scss/main"; | ||||||
|  |  | ||||||
|  |  | ||||||
| .fade-enter-active { | .fade-enter-active { | ||||||
|   transition: opacity .2s; |   transition: opacity 0.2s; | ||||||
| } | } | ||||||
| .fade-leave-active { | .fade-leave-active { | ||||||
|   transition: opacity .2s; |   transition: opacity 0.2s; | ||||||
| } | } | ||||||
| .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ { | .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ { | ||||||
|   opacity: 0; |   opacity: 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .filter { | ||||||
|  |   // background-color: rgba(004, 122, 125, 0.2); | ||||||
|  |   width: 100%; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   margin: 1rem 2rem; | ||||||
|  |  | ||||||
|  |   h2 { | ||||||
|  |     margin-top: 0.5rem; | ||||||
|  |     margin-bottom: 0.5rem; | ||||||
|  |     font-weight: 400; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &-items { | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: row; | ||||||
|  |     align-items: center; | ||||||
|  |  | ||||||
|  |     > :not(:first-child) { | ||||||
|  |       margin-left: 1rem; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | hr { | ||||||
|  |   display: block; | ||||||
|  |   height: 1px; | ||||||
|  |   border: 0; | ||||||
|  |   border-bottom: 1px solid $text-color-50; | ||||||
|  |   margin-top: 10px; | ||||||
|  |   margin-bottom: 10px; | ||||||
|  |   width: 90%; | ||||||
|  | } | ||||||
|  |  | ||||||
| .dropdown { | .dropdown { | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   position: relative; |   position: relative; | ||||||
| @@ -196,7 +302,11 @@ export default { | |||||||
|     width: calc(100%); |     width: calc(100%); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   &--results { |   .not-found { | ||||||
|  |     font-weight: 400; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &-results { | ||||||
|     padding-left: 60px; |     padding-left: 60px; | ||||||
|     width: 100%; |     width: 100%; | ||||||
|  |  | ||||||
| @@ -222,7 +332,9 @@ export default { | |||||||
|       overflow: hidden; |       overflow: hidden; | ||||||
|       color: $text-color-50; |       color: $text-color-50; | ||||||
|  |  | ||||||
|       &.active, &:hover, &:active { |       &.active, | ||||||
|  |       &:hover, | ||||||
|  |       &:active { | ||||||
|         color: $text-color; |         color: $text-color; | ||||||
|         border-bottom: 2px solid $text-color; |         border-bottom: 2px solid $text-color; | ||||||
|       } |       } | ||||||
| @@ -235,16 +347,16 @@ export default { | |||||||
|   display: flex; |   display: flex; | ||||||
|   position: fixed; |   position: fixed; | ||||||
|   flex-wrap: wrap; |   flex-wrap: wrap; | ||||||
|   z-index: 5; |   z-index: 16; | ||||||
|   border: 0; |   border: 0; | ||||||
|   background-color: $background-color-secondary; |   background-color: $background-color-secondary; | ||||||
|  |  | ||||||
|   // TODO check if this is for mobile |   // TODO check if this is for mobile | ||||||
|   width: calc(100% - 110px); |   width: calc(100% - 110px); | ||||||
|   top: 0; |   bottom: 0; | ||||||
|   right: 55px; |   right: 55px; | ||||||
|  |  | ||||||
|   @include tablet-min{ |   @include tablet-min { | ||||||
|     position: relative; |     position: relative; | ||||||
|     width: 100%; |     width: 100%; | ||||||
|     right: 0px; |     right: 0px; | ||||||
| @@ -252,23 +364,26 @@ export default { | |||||||
|  |  | ||||||
|   input { |   input { | ||||||
|     display: block; |     display: block; | ||||||
|  |     height: calc($header-size - 1.5rem); | ||||||
|     width: 100%; |     width: 100%; | ||||||
|     padding: 13px 20px 13px 45px; |     padding: 13px 0 13px 45px; | ||||||
|     outline: none; |     outline: none; | ||||||
|     margin: 0; |     margin: 0; | ||||||
|  |     margin-bottom: auto; | ||||||
|     border: 0; |     border: 0; | ||||||
|     background-color: $background-color-secondary; |     background-color: $background-color-secondary; | ||||||
|     font-weight: 300; |     font-weight: 300; | ||||||
|     font-size: 19px; |     font-size: 19px; | ||||||
|     color: $text-color; |     color: $text-color; | ||||||
|     transition: background-color .5s ease, color .5s ease; |     transition: background-color 0.5s ease, color 0.5s ease; | ||||||
|  |  | ||||||
|     @include tablet-min { |     @include tablet-min { | ||||||
|  |       height: calc($header-size); | ||||||
|       padding: 13px 30px 13px 60px; |       padding: 13px 30px 13px 60px; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   &--icon{ |   &-icon { | ||||||
|     width: 20px; |     width: 20px; | ||||||
|     height: 20px; |     height: 20px; | ||||||
|     fill: $text-color-50; |     fill: $text-color-50; | ||||||
| @@ -278,7 +393,7 @@ export default { | |||||||
|     left: 15px; |     left: 15px; | ||||||
|     top: 15px; |     top: 15px; | ||||||
|  |  | ||||||
|     @include tablet-min{ |     @include tablet-min { | ||||||
|       top: 27px; |       top: 27px; | ||||||
|       left: 25px; |       left: 25px; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -5,14 +5,14 @@ | |||||||
|     <seasoned-input placeholder="username" |     <seasoned-input placeholder="username" | ||||||
|                     icon="Email" |                     icon="Email" | ||||||
|                     type="email" |                     type="email" | ||||||
|  |                     @enter="submit" | ||||||
|                     :value.sync="username" /> |                     :value.sync="username" /> | ||||||
|     <seasoned-input placeholder="password" icon="Keyhole" type="password" :value.sync="password" @enter="signin"/> |     <seasoned-input placeholder="password" icon="Keyhole" type="password" :value.sync="password" @enter="submit"/> | ||||||
|  |  | ||||||
|     <seasoned-button @click="signin">sign in</seasoned-button> |  | ||||||
|  |  | ||||||
|  |     <seasoned-button @click="submit">sign in</seasoned-button> | ||||||
|     <router-link class="link" to="/register">Don't have a user? Register here</router-link> |     <router-link class="link" to="/register">Don't have a user? Register here</router-link> | ||||||
|     <seasoned-messages :messages.sync="messages"></seasoned-messages> |  | ||||||
|  |  | ||||||
|  |     <seasoned-messages :messages.sync="messages"></seasoned-messages> | ||||||
|   </section> |   </section> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| @@ -39,11 +39,25 @@ export default { | |||||||
|     setValue(l, t) { |     setValue(l, t) { | ||||||
|       this[l] = t |       this[l] = t | ||||||
|     }, |     }, | ||||||
|     signin(){ |     submit() { | ||||||
|  |       this.messages = []; | ||||||
|       let username = this.username; |       let username = this.username; | ||||||
|       let password = this.password; |       let password = this.password; | ||||||
|  |  | ||||||
|       login(username, password) |       if (username == null || username.length == 0) { | ||||||
|  |         this.messages.push({ type: 'error', title: 'Missing username' }) | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (password == null || password.length == 0) { | ||||||
|  |         this.messages.push({ type: 'error', title: 'Missing password' }) | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       this.signin(username, password) | ||||||
|  |     }, | ||||||
|  |     signin(username, password) { | ||||||
|  |       login(username, password, true) | ||||||
|         .then(data => { |         .then(data => { | ||||||
|           if (data.success){ |           if (data.success){ | ||||||
|             const jwtData = parseJwt(data.token) |             const jwtData = parseJwt(data.token) | ||||||
| @@ -57,7 +71,7 @@ export default { | |||||||
|         }) |         }) | ||||||
|         .catch(error => { |         .catch(error => { | ||||||
|           if (error.status === 401) { |           if (error.status === 401) { | ||||||
|             this.messages.push({ type: 'warning', title: 'Access denied', message: 'Incorrect username or password' }) |             this.messages.push({ type: 'error', title: 'Access denied', message: 'Incorrect username or password' }) | ||||||
|           } |           } | ||||||
|           else { |           else { | ||||||
|             this.messages.push({ type: 'error', title: 'Unexpected error', message: error.message }) |             this.messages.push({ type: 'error', title: 'Unexpected error', message: error.message }) | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| <template> | <template> | ||||||
|   <div class="seasoned-button"> |   <button type="button" @click="emit('click')" :class="{ active: active }"> | ||||||
|     <button type="button" class="button" @click="emit('click')" :class="{ active: active }"><slot></slot></button> |     <slot></slot> | ||||||
|   </div> |   </button> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| @@ -9,7 +9,11 @@ | |||||||
| export default { | export default { | ||||||
|   name: 'seasonedButton', |   name: 'seasonedButton', | ||||||
|   props: { |   props: { | ||||||
|     active: Boolean |     active: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: false, | ||||||
|  |       required: false | ||||||
|  |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     emit() { |     emit() { | ||||||
| @@ -23,32 +27,39 @@ export default { | |||||||
| @import "./src/scss/variables"; | @import "./src/scss/variables"; | ||||||
| @import "./src/scss/media-queries"; | @import "./src/scss/media-queries"; | ||||||
|  |  | ||||||
| .button{ | button { | ||||||
|   display: inline-block; |   display: inline-block; | ||||||
|   border: 1px solid $text-color; |   border: 1px solid $text-color; | ||||||
|   text-transform: uppercase; |  | ||||||
|   font-weight: 300; |  | ||||||
|   font-size: 11px; |   font-size: 11px; | ||||||
|   line-height: 2; |   font-weight: 300; | ||||||
|   height: 45px; |   line-height: 1.5; | ||||||
|   letter-spacing: 0.5px; |   letter-spacing: 0.5px; | ||||||
|   padding: 5px 20px 4px 20px; |   text-transform: uppercase; | ||||||
|  |   min-height: 45px; | ||||||
|  |   padding: 5px 10px 4px 10px; | ||||||
|   margin: 0; |   margin: 0; | ||||||
|   margin-right: 0.3rem; |   margin-right: 0.3rem; | ||||||
|   cursor: pointer; |  | ||||||
|   color: $text-color; |   color: $text-color; | ||||||
|   background: $background-color-secondary; |   background: $background-color-secondary; | ||||||
|  |   cursor: pointer; | ||||||
|   outline: none; |   outline: none; | ||||||
|   transition: background 0.5s ease, color 0.5s ease, border-color .5s ease; |   transition: background 0.5s ease, color 0.5s ease, border-color .5s ease; | ||||||
|  |  | ||||||
|   @include tablet-min{ |   @include desktop { | ||||||
|     font-size: 12px; |     font-size: 0.8rem; | ||||||
|     padding: 6px 20px 5px 20px; |     padding: 6px 20px 5px 20px; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   body:not(.touch) &:hover, &:focus, &:active, &.active { |   &:focus, &:active, &.active { | ||||||
|     background: $text-color; |     background: $text-color; | ||||||
|     color: $background-color; |     color: $background-color; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   @media (hover: hover) { | ||||||
|  |     &:hover { | ||||||
|  |       background: $text-color; | ||||||
|  |       color: $background-color; | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
| </style> | </style> | ||||||
|   | |||||||
| @@ -3,8 +3,8 @@ | |||||||
|     <div class="message" v-for="(message, index) in reversedMessages" :class="message.type || 'warning'" :key="index"> |     <div class="message" v-for="(message, index) in reversedMessages" :class="message.type || 'warning'" :key="index"> | ||||||
|       <span class="pinstripe"></span> |       <span class="pinstripe"></span> | ||||||
|       <div> |       <div> | ||||||
|         <h2>{{ message.title || defaultTitles[message.type] }}</h2> |         <h2 class="title">{{ message.title || defaultTitles[message.type] }}</h2> | ||||||
|         <span>{{ message.message }}</span> |         <span v-if="message.message" class="message">{{ message.message }}</span> | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|       <button class="dismiss" @click="clicked(message)">X</button> |       <button class="dismiss" @click="clicked(message)">X</button> | ||||||
| @@ -41,14 +41,7 @@ export default { | |||||||
|       const removedMessage = [...this.messages].filter(mes => mes !== e) |       const removedMessage = [...this.messages].filter(mes => mes !== e) | ||||||
|       this.$emit('update:messages', removedMessage) |       this.$emit('update:messages', removedMessage) | ||||||
|     } |     } | ||||||
|   }, |   } | ||||||
|   // watch: { |  | ||||||
|   //   messages(propState, oldState) { |  | ||||||
|   //     const newMessage = propState.filter(msg => !this.localMessages.includes(msg)) |  | ||||||
|   //     console.log('newMessage', newMessage) |  | ||||||
|   //     this.localMessages = this.localMessages.concat(newMessage) |  | ||||||
|   //   } |  | ||||||
|   // } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| </script> | </script> | ||||||
| @@ -70,7 +63,6 @@ export default { | |||||||
| .message { | .message { | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   max-width: 35rem; |   max-width: 35rem; | ||||||
|   min-height: 75px; |  | ||||||
|  |  | ||||||
|   display: flex; |   display: flex; | ||||||
|   margin-top: 1rem; |   margin-top: 1rem; | ||||||
| @@ -78,12 +70,12 @@ export default { | |||||||
|   color: $text-color-70; |   color: $text-color-70; | ||||||
|  |  | ||||||
|   > div { |   > div { | ||||||
|     margin: 6px 24px; |     margin: 10px 24px; | ||||||
|     width: 100%; |     width: 100%; | ||||||
|  |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   h2 { |   .title { | ||||||
|     font-weight: 300; |     font-weight: 300; | ||||||
|     letter-spacing: 0.25px; |     letter-spacing: 0.25px; | ||||||
|     margin: 0; |     margin: 0; | ||||||
| @@ -91,10 +83,11 @@ export default { | |||||||
|     color: $text-color; |     color: $text-color; | ||||||
|     transition: color .5s ease; |     transition: color .5s ease; | ||||||
|   } |   } | ||||||
|   span { |   .message { | ||||||
|     font-weight: 300; |     font-weight: 300; | ||||||
|     color: $text-color-70; |     color: $text-color-70; | ||||||
|     transition: color .5s ease; |     transition: color .5s ease; | ||||||
|  |     margin: 0.2rem 0 0.5rem; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @include mobile-only { |   @include mobile-only { | ||||||
| @@ -112,9 +105,8 @@ export default { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   .pinstripe { |   .pinstripe { | ||||||
|     height: 100%; |  | ||||||
|     width: 0.5rem; |     width: 0.5rem; | ||||||
|     // background-color: $color-error-highlight; |     background-color: $color-error-highlight; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   .dismiss { |   .dismiss { | ||||||
|   | |||||||
| @@ -1,42 +1,38 @@ | |||||||
| <template> | <template> | ||||||
|  |  | ||||||
|   <div class="darkToggle"> |   <div class="darkToggle"> | ||||||
|     <span @click="toggleDarkmode()">{{ darkmodeToggleIcon }}</span> |     <span @click="toggleDarkmode()">{{ darkmodeToggleIcon }}</span> | ||||||
|   </div> |   </div> | ||||||
|  |  | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| export default { | export default { | ||||||
|  |  | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       darkmode: this.supported |       darkmode: this.supported | ||||||
|     } |     }; | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     toggleDarkmode() { |     toggleDarkmode() { | ||||||
|       this.darkmode = !this.darkmode; |       this.darkmode = !this.darkmode; | ||||||
|       document.body.className = this.darkmode ? 'dark' : 'light' |       document.body.className = this.darkmode ? "dark" : "light"; | ||||||
|     }, |     }, | ||||||
|     supported() { |     supported() { | ||||||
|       const computedStyle = window.getComputedStyle(document.body) |       const computedStyle = window.getComputedStyle(document.body); | ||||||
|       if (computedStyle['colorScheme'] != null) |       if (computedStyle["colorScheme"] != null) | ||||||
|         return computedStyle.colorScheme.includes('dark') |         return computedStyle.colorScheme.includes("dark"); | ||||||
|       return false |       return false; | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|     darkmodeToggleIcon() { |     darkmodeToggleIcon() { | ||||||
|       return this.darkmode ? '🌝' : '🌚' |       return this.darkmode ? "🌝" : "🌚"; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | }; | ||||||
|  |  | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
|  | @import "./src/scss/media-queries"; | ||||||
| .darkToggle { | .darkToggle { | ||||||
|   height: 25px; |   height: 25px; | ||||||
|   width: 25px; |   width: 25px; | ||||||
| @@ -49,6 +45,10 @@ export default { | |||||||
|   right: 0; |   right: 0; | ||||||
|   z-index: 10; |   z-index: 10; | ||||||
|  |  | ||||||
|  |   @include mobile-only { | ||||||
|  |     margin-bottom: 5rem; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   -webkit-user-select: none; |   -webkit-user-select: none; | ||||||
|   -moz-user-select: none; |   -moz-user-select: none; | ||||||
|   -ms-user-select: none; |   -ms-user-select: none; | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
|   <div> |   <div> | ||||||
|     <a @click="$emit('click')"> |     <a @click="$emit('click')"> | ||||||
|       <li> |       <li> | ||||||
|         <figure :class="activeClassIfActive"> |         <figure v-if="iconRef" :class="activeClassIfActive"> | ||||||
|           <svg class="icon"><use :xlink:href="iconRefNameIfActive"/></svg> |           <svg class="icon"><use :xlink:href="iconRefNameIfActive"/></svg> | ||||||
|         </figure> |         </figure> | ||||||
|  |  | ||||||
| @@ -23,7 +23,7 @@ export default { | |||||||
|   props: { |   props: { | ||||||
|     iconRef: { |     iconRef: { | ||||||
|       type: String, |       type: String, | ||||||
|       required: true |       required: false | ||||||
|     }, |     }, | ||||||
|     iconRefActive: { |     iconRefActive: { | ||||||
|       type: String, |       type: String, | ||||||
| @@ -85,11 +85,11 @@ li { | |||||||
|   border-bottom: 1px solid $text-color-5; |   border-bottom: 1px solid $text-color-5; | ||||||
|  |  | ||||||
|   &:hover { |   &:hover { | ||||||
|     color: $text-color-70; |     color: $text-color; | ||||||
|     cursor: pointer; |     cursor: pointer; | ||||||
|  |  | ||||||
|     .icon { |     .icon { | ||||||
|       fill: $text-color-70; |       fill: $text-color; | ||||||
|       cursor: pointer; |       cursor: pointer; | ||||||
|         transform: scale(1.1, 1.1); |         transform: scale(1.1, 1.1); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -11,8 +11,8 @@ const setDocumentTitle = (state) => { | |||||||
| export default { | export default { | ||||||
|   namespaced: true, |   namespaced: true, | ||||||
|   state: { |   state: { | ||||||
|     emoji: '🍕', |     emoji: '', | ||||||
|     titlePrefix: 'request', |     titlePrefix: 'seasoned', | ||||||
|     title: undefined |     title: undefined | ||||||
|   }, |   }, | ||||||
|   getters: { |   getters: { | ||||||
|   | |||||||
| @@ -69,6 +69,14 @@ export default { | |||||||
|  |  | ||||||
|       ifMissingSettingsAndTokenExistsFetchSettings() |       ifMissingSettingsAndTokenExistsFetchSettings() | ||||||
|       return undefined |       return undefined | ||||||
|  |     }, | ||||||
|  |     isPlexAuthenticated: (state) => { | ||||||
|  |       const settings = state.settings || getLocalStorageByKey('settings') | ||||||
|  |       if (settings == null) | ||||||
|  |         return false | ||||||
|  |  | ||||||
|  |       const hasPlexId = settings['plex_userid'] | ||||||
|  |       return hasPlexId != null ? true : false | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   mutations: { |   mutations: { | ||||||
|   | |||||||
| @@ -14,11 +14,13 @@ let routes = [ | |||||||
|   { |   { | ||||||
|     name: 'activity', |     name: 'activity', | ||||||
|     path: '/activity', |     path: '/activity', | ||||||
|  |     meta: { requiresAuth: true }, | ||||||
|     component: (resolve) => require(['./components/ActivityPage.vue'], resolve) |     component: (resolve) => require(['./components/ActivityPage.vue'], resolve) | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     name: 'profile', |     name: 'profile', | ||||||
|     path: '/profile', |     path: '/profile', | ||||||
|  |     meta: { requiresAuth: true }, | ||||||
|     component: (resolve) => require(['./components/Profile.vue'], resolve) |     component: (resolve) => require(['./components/Profile.vue'], resolve) | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
| @@ -46,11 +48,13 @@ let routes = [ | |||||||
|   { |   { | ||||||
|     name: 'settings', |     name: 'settings', | ||||||
|     path: '/settings', |     path: '/settings', | ||||||
|  |     meta: { requiresAuth: true }, | ||||||
|     component: (resolve) => require(['./components/Settings.vue'], resolve) |     component: (resolve) => require(['./components/Settings.vue'], resolve) | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     name: 'signin', |     name: 'signin', | ||||||
|     path: '/signin', |     path: '/signin', | ||||||
|  |     alias: '/login', | ||||||
|     component: (resolve) => require(['./components/Signin.vue'], resolve) |     component: (resolve) => require(['./components/Signin.vue'], resolve) | ||||||
|   }, |   }, | ||||||
|   // { |   // { | ||||||
| @@ -65,6 +69,17 @@ let routes = [ | |||||||
|     path: '/404', |     path: '/404', | ||||||
|     component: (resolve) => require(['./components/404.vue'], resolve) |     component: (resolve) => require(['./components/404.vue'], resolve) | ||||||
|   }, |   }, | ||||||
|  |   { | ||||||
|  |     name: 'logout', | ||||||
|  |     path: '/logout', | ||||||
|  |     component: { | ||||||
|  |       template: '<div></div>', | ||||||
|  |       created() { | ||||||
|  |         localStorage.clear(); | ||||||
|  |         this.$router.push({ name: 'home' }); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|   { |   { | ||||||
|     path: '*', |     path: '*', | ||||||
|     redirect: '/' |     redirect: '/' | ||||||
| @@ -76,7 +91,7 @@ let routes = [ | |||||||
| ]; | ]; | ||||||
|  |  | ||||||
| const router =  new VueRouter({ | const router =  new VueRouter({ | ||||||
|   mode: 'hash', |   mode: 'history', | ||||||
|   base: '/', |   base: '/', | ||||||
|   routes, |   routes, | ||||||
|   linkActiveClass: 'is-active' |   linkActiveClass: 'is-active' | ||||||
| @@ -90,6 +105,13 @@ router.beforeEach((to, from, next) => { | |||||||
|     document.querySelector('.nav__hamburger').classList.remove('nav__hamburger--active'); |     document.querySelector('.nav__hamburger').classList.remove('nav__hamburger--active'); | ||||||
|     document.querySelector('.nav__list').classList.remove('nav__list--active'); |     document.querySelector('.nav__list').classList.remove('nav__list--active'); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   if (to.matched.some(record => record.meta.requiresAuth)) { | ||||||
|  |     if (localStorage.getItem('token') == null) { | ||||||
|  |       next({ path: '/signin' }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   next(); |   next(); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,3 @@ | |||||||
|  |  | ||||||
| .noselect { | .noselect { | ||||||
|   -webkit-touch-callout: none; /* iOS Safari */ |   -webkit-touch-callout: none; /* iOS Safari */ | ||||||
|   -webkit-user-select: none; /* Safari */ |   -webkit-user-select: none; /* Safari */ | ||||||
|   | |||||||
| @@ -18,12 +18,12 @@ | |||||||
|  |  | ||||||
|   --background-nav-logo: #081c24; |   --background-nav-logo: #081c24; | ||||||
|   --color-green: #01d277; |   --color-green: #01d277; | ||||||
|   --color-green-90: rgba(1, 210, 119, .9); |   --color-green-90: rgba(1, 210, 119, 0.9); | ||||||
|   --color-green-70: rgba(1, 210, 119, .73); |   --color-green-70: rgba(1, 210, 119, 0.73); | ||||||
|   --color-teal: #091c24; |   --color-teal: #091c24; | ||||||
|   --color-black: #081c24; |   --color-black: #081c24; | ||||||
|   --white: #fff; |   --white: #fff; | ||||||
|   --white-70: rgba(255,255,255,0.7); |   --white-70: rgba(255, 255, 255, 0.7); | ||||||
|  |  | ||||||
|   --color-warning: rgba(241, 188, 53, 0.7); |   --color-warning: rgba(241, 188, 53, 0.7); | ||||||
|   --color-warning-highlight: #f1bc35; |   --color-warning-highlight: #f1bc35; | ||||||
| @@ -31,7 +31,7 @@ | |||||||
|   --color-success-text: #fff; |   --color-success-text: #fff; | ||||||
|   --color-success-highlight: rgb(0, 100, 66); |   --color-success-highlight: rgb(0, 100, 66); | ||||||
|   --color-error: rgba(220, 48, 35, 0.8); |   --color-error: rgba(220, 48, 35, 0.8); | ||||||
|   --color-error-highlight: #DC3023; |   --color-error-highlight: #dc3023; | ||||||
|  |  | ||||||
|   --header-size: 75px; |   --header-size: 75px; | ||||||
| } | } | ||||||
| @@ -55,7 +55,7 @@ | |||||||
|  |  | ||||||
| @include mobile-only { | @include mobile-only { | ||||||
|   :root { |   :root { | ||||||
|     --header-size: 50px; |     --header-size: calc(50px + 1.5rem); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -67,9 +67,9 @@ $green-90: var(--color-green-90); | |||||||
| $green-70: var(--color-green-70); | $green-70: var(--color-green-70); | ||||||
| $teal: #091c24; | $teal: #091c24; | ||||||
| $black: #081c24; | $black: #081c24; | ||||||
| $black-80: rgba(0,0,0,0.8); | $black-80: rgba(0, 0, 0, 0.8); | ||||||
| $white: #fff; | $white: #fff; | ||||||
| $white-80: rgba(255,255,255,0.8); | $white-80: rgba(255, 255, 255, 0.8); | ||||||
|  |  | ||||||
| $text-color: var(--text-color) !default; | $text-color: var(--text-color) !default; | ||||||
| $text-color-70: var(--text-color-70) !default; | $text-color-70: var(--text-color-70) !default; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user