Compare commits
	
		
			21 Commits
		
	
	
		
			v1.0.0
			...
			feat/submi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 841ee4327e | |||
| a2c00d929d | |||
| 0a84223778 | |||
| 20ad976939 | |||
| 5121aec6ee | |||
| 5bc2709231 | |||
| 53c0aca460 | |||
| cdf2ddae1c | |||
| a5c68ffd8d | |||
| 5e33d8cfef | |||
| c34a867387 | |||
| a11ad2f651 | |||
| 755bd116d5 | |||
| 9e33784781 | |||
| 470bcdd72e | |||
| d56a7d4dfe | |||
| b46e586c92 | |||
| 563eb3f1ef | |||
| 98644513ad | |||
| 3033db02b8 | |||
| 70a6ed189b | 
| @@ -123,6 +123,10 @@ img{ | ||||
|   height: auto; | ||||
| } | ||||
|  | ||||
| .no-scroll { | ||||
|   overflow: hidden; | ||||
| } | ||||
|  | ||||
| .wrapper{ | ||||
|   position: relative; | ||||
| } | ||||
|   | ||||
							
								
								
									
										13
									
								
								src/api.js
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/api.js
									
									
									
									
									
								
							| @@ -135,14 +135,21 @@ const searchTorrents = (query, authorization_token) => { | ||||
| const addMagnet = (magnet, name, tmdb_id) => { | ||||
|   const url = new URL('v1/pirate/add', SEASONED_URL) | ||||
|  | ||||
|   const body = { | ||||
|   const body = JSON.stringify({ | ||||
|     magnet: magnet, | ||||
|     name: name, | ||||
|     tmdb_id: tmdb_id | ||||
|   }) | ||||
|   const headers = { | ||||
|     'Content-Type': 'application/json', | ||||
|     authorization: storage.token | ||||
|   } | ||||
|   const headers = { authorization: storage.token } | ||||
|  | ||||
|   return fetch(url.href, { method: 'POST', headers, body }) | ||||
|   return fetch(url.href, { | ||||
|       method: 'POST', | ||||
|       headers, | ||||
|       body | ||||
|     }) | ||||
|     .then(resp => resp.json()) | ||||
|     .catch(error => { console.error(`api error adding magnet: ${name} ${error}`); throw error }) | ||||
| } | ||||
|   | ||||
| @@ -32,23 +32,28 @@ | ||||
|  | ||||
|         <!-- SIDEBAR ACTIONS --> | ||||
|         <div class="movie__actions" v-if="movie"> | ||||
|  | ||||
|           <sidebar-list-element :iconRef="'#iconNot_exsits'" :active="matched" | ||||
|             :iconRefActive="'#iconExists'" :textActive="'Already in plex 🎉'"> | ||||
|  | ||||
|           <sidebar-list-element :iconRef="'#iconNot_exsits'" :active="requested" | ||||
|             :iconRefActive="'#iconExists'" :textActive="'Already in plex 🎉'" :class="requested ? 'rotate-180' : null"> | ||||
|             Not yet in plex | ||||
|           </sidebar-list-element> | ||||
|  | ||||
|           <sidebar-list-element @click="sendRequest" :iconRef="'#iconSent'" | ||||
|             :active="requested" :textActive="'Requested to be downloaded'"> | ||||
|  | ||||
|             Request to be downloaded? | ||||
|           </sidebar-list-element> | ||||
|  | ||||
|           <sidebar-list-element v-if="admin" @click="showTorrents=!showTorrents" | ||||
|             :iconRef="'#icon_torrents'" :active="showTorrents" | ||||
|             :supplementaryText="numberOfTorrentResults"> | ||||
|  | ||||
|             Search for torrents | ||||
|           </sidebar-list-element> | ||||
|  | ||||
|           <sidebar-list-element @click="showIssueForm = !showIssueForm" | ||||
|                                 :iconRef="null" | ||||
|                                 :active="showIssueForm"> | ||||
|              ⚠️  Report an issue! | ||||
|           </sidebar-list-element> | ||||
|  | ||||
|           <sidebar-list-element @click="openTmdb" :iconRef="'#icon_info'"> | ||||
|             See more info | ||||
|           </sidebar-list-element> | ||||
| @@ -64,14 +69,16 @@ | ||||
|  | ||||
|         <!-- MOVIE INFO --> | ||||
|         <div class="movie__info"> | ||||
|           <div class="movie__description" v-if="movie"> {{ movie.overview }}</div> | ||||
|  | ||||
|           <!-- Loading placeholder --> | ||||
|           <div v-else class="movie__description"> | ||||
|           <div v-if="!movie" class="movie__description"> | ||||
|             <loading-placeholder :count="12" /> | ||||
|           </div> | ||||
|  | ||||
|           <div class="movie__details" v-if="movie"> | ||||
|           <div class="movie__details" v-if="movie && !showIssueForm"> | ||||
|             <div class="movie__description"> | ||||
|               {{ movie.overview }} | ||||
|             </div> | ||||
|             <div v-if="movie.year" class="movie__details-block"> | ||||
|               <h2 class="movie__details-title">Release Date</h2> | ||||
|               <div class="movie__details-text">{{ movie.year }}</div> | ||||
| @@ -93,6 +100,17 @@ | ||||
|             </div> | ||||
|           </div> | ||||
|  | ||||
|  | ||||
|  | ||||
|           <div v-if="showIssueForm" class="issueForm"> | ||||
|             <h2 class="movie__details-title">Report an issue</h2> | ||||
|             <RadioButtons class="issueOptions" | ||||
|                           :options="issueOptions" | ||||
|                           :value.sync="selectedIssue" /> | ||||
|             <TextArea title="Additional information" :rows="3" | ||||
|                       placeholder="Placeholder text" /> | ||||
|             <SeasonedButton @click="reportIssue">Report issue</SeasonedButton> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
|         <!-- TODO: change this classname, this is general  --> | ||||
| @@ -122,12 +140,23 @@ import Person from './Person' | ||||
| import SidebarListElement from './ui/sidebarListElem' | ||||
| import store from '@/store' | ||||
| import LoadingPlaceholder from './ui/LoadingPlaceholder' | ||||
| import RadioButtons from './ui/RadioButtons' | ||||
| import TextArea from './ui/TextArea' | ||||
| import SeasonedButton from './ui/SeasonedButton' | ||||
|  | ||||
| import { getMovie, getShow, request, getRequestStatus } from '@/api' | ||||
|  | ||||
| export default { | ||||
|   props: ['id', 'type'], | ||||
|   components: { TorrentList, Person, LoadingPlaceholder, SidebarListElement }, | ||||
|   components: { | ||||
|     TorrentList, | ||||
|     Person, | ||||
|     LoadingPlaceholder, | ||||
|     SidebarListElement, | ||||
|     RadioButtons, | ||||
|     TextArea, | ||||
|     SeasonedButton | ||||
|   }, | ||||
|   directives: { img: img }, // TODO decide to remove or use | ||||
|   data(){ | ||||
|     return{ | ||||
| @@ -142,7 +171,9 @@ export default { | ||||
|       requested: false, | ||||
|       admin: localStorage.getItem('admin'), | ||||
|       showTorrents: false, | ||||
|       compact: false | ||||
|       compact: false, | ||||
|       showIssueForm: false, | ||||
|       selectedIssue: null | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
| @@ -177,6 +208,15 @@ export default { | ||||
|       const tmdbType = this.type === 'show' ? 'tv' : this.type | ||||
|       window.location.href = 'https://www.themoviedb.org/' + tmdbType + '/' + this.id | ||||
|     }, | ||||
|     reportIssue() { | ||||
|       if (this.showIssueForm) { | ||||
|         this.$notifications.success({ | ||||
|           title: 'Issue successfully submitted', | ||||
|           description: 'Reported issue: Missing subtitles', | ||||
|           timeout: 300000 | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     id: function(val){ | ||||
| @@ -191,6 +231,35 @@ export default { | ||||
|     numberOfTorrentResults: () => { | ||||
|       let numTorrents = store.getters['torrentModule/resultCount'] | ||||
|       return numTorrents !== null ? numTorrents + ' results' : null | ||||
|     }, | ||||
|     issueOptions: function() { | ||||
|       return [{ | ||||
|           value: 'playback', | ||||
|           text: 'Unable to play' | ||||
|         }, { | ||||
|           value: 'missing-episode', | ||||
|           text: 'Missing Episode', | ||||
|           subElements: this.seasonOptions | ||||
|         }, { | ||||
|           value: 'missing-subtitle', | ||||
|           text: 'Missing subtitles' | ||||
|         }] | ||||
|     }, | ||||
|     seasonOptions: function() { | ||||
|       if (this.movie.type !== 'show') { | ||||
|         return [] | ||||
|       } | ||||
|  | ||||
|       const options = [] | ||||
|       const length = this.movie.seasons; | ||||
|  | ||||
|       for (var i = 0; i < length; i++) { | ||||
|         options.push({ | ||||
|           value: i+1, | ||||
|           text: `Season ${i+1}` | ||||
|         }) | ||||
|       } | ||||
|       return options; | ||||
|     } | ||||
|   }, | ||||
|   beforeDestroy() { | ||||
| @@ -224,6 +293,9 @@ export default { | ||||
| @import "./src/scss/media-queries"; | ||||
|  | ||||
| .movie { | ||||
|   background-color: $background-color; | ||||
|   color: $text-color; | ||||
|  | ||||
|   &__wrap { | ||||
|     display: flex; | ||||
|     &--header { | ||||
| @@ -237,9 +309,6 @@ export default { | ||||
|       @include tablet-min{ | ||||
|         flex-direction: row; | ||||
|       } | ||||
|  | ||||
|       background-color: $background-color; | ||||
|       color: $text-color; | ||||
|     } | ||||
|   } | ||||
|   &__header { | ||||
| @@ -360,15 +429,19 @@ export default { | ||||
|       font-size: 13px; | ||||
|       line-height: 1.8; | ||||
|       margin-bottom: 20px; | ||||
|       flex: 0 0 100%; | ||||
|  | ||||
|       @include tablet-min { | ||||
|         margin-bottom: 30px; | ||||
|         font-size: 14px; | ||||
|       } | ||||
|     } | ||||
|     &__details { | ||||
|       &-block { | ||||
|         float: left; | ||||
|       } | ||||
|       display: flex; | ||||
|       width: 100%; | ||||
|       flex-direction: row; | ||||
|       flex-wrap: wrap; | ||||
|  | ||||
|       &-block:not(:last-child) { | ||||
|         margin-bottom: 20px; | ||||
|         margin-right: 20px; | ||||
| @@ -417,4 +490,24 @@ export default { | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| .issueForm { | ||||
|   // padding: 40px; | ||||
|  | ||||
|   .issueOptions { | ||||
|     margin-top: 1rem; | ||||
|   } | ||||
|  | ||||
|   .seasonOptions { | ||||
|     margin-top: 2rem; | ||||
|  | ||||
|     h2 { | ||||
|       margin-bottom: 1rem; | ||||
|     } | ||||
|  | ||||
|     > :not(h2) { | ||||
|       margin-left: 1rem; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -32,9 +32,11 @@ export default { | ||||
|   }, | ||||
|   created(){ | ||||
|     window.addEventListener('keyup', this.checkEventForEscapeKey) | ||||
|     document.getElementsByTagName("body")[0].classList += " no-scroll"; | ||||
|   }, | ||||
|   beforeDestroy() { | ||||
|     window.removeEventListener('keyup', this.checkEventForEscapeKey) | ||||
|     document.getElementsByTagName("body")[0].classList.remove("no-scroll"); | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
|      | ||||
|     <div class="search"> | ||||
|       <input | ||||
|         ref="input" | ||||
|         type="text" | ||||
|         placeholder="Search for a movie or show" | ||||
|         autocorrect="off" | ||||
| @@ -93,6 +94,13 @@ export default { | ||||
|     navigateUp() { | ||||
|       this.focus = true | ||||
|       this.selectedResult-- | ||||
|       const input = this.$refs.input; | ||||
|       const textLength = input.value.length | ||||
|  | ||||
|       setTimeout(() => { | ||||
|         input.focus() | ||||
|         input.setSelectionRange(textLength, textLength + 1) | ||||
|       }, 1) | ||||
|     }, | ||||
|     handleInput(e){ | ||||
|       this.selectedResult = 0 | ||||
|   | ||||
| @@ -129,7 +129,11 @@ a { | ||||
|   } | ||||
| } | ||||
| .settings { | ||||
|    padding: 35px; | ||||
|   padding: 3rem; | ||||
|  | ||||
|   @include mobile-only { | ||||
|     padding: 1rem; | ||||
|   } | ||||
|  | ||||
|    &__header { | ||||
|       margin: 0; | ||||
|   | ||||
							
								
								
									
										133
									
								
								src/components/ui/RadioButtons.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								src/components/ui/RadioButtons.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <label v-for="option in options" class="radio" @click="selected = option.value"> | ||||
|       <input type="radio" v-model="selected" :value="option.value" /> | ||||
|       <label>{{ option.text }}</label> | ||||
|  | ||||
|  | ||||
|       <div class="sub-radios" v-if="option.subElements && selected === option.value"> | ||||
|         <label class="radio" v-for="elem in option.subElements"> | ||||
|           <input type="radio" v-model="selectedSubItem" :value="option.value + '-' + elem.value" /> | ||||
|           <label>{{ elem.text }}</label> | ||||
|         </label> | ||||
|       </div> | ||||
|     </label> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   props: { | ||||
|     options: { | ||||
|       type: Array, | ||||
|       required: true | ||||
|     }, | ||||
|     value: { | ||||
|       required: false, | ||||
|       default: undefined | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       selected: this.value || this.options[0].value, | ||||
|       selectedSubItem: null | ||||
|     }; | ||||
|   }, | ||||
|   beforeMount() { | ||||
|     this.handleChange() | ||||
|   }, | ||||
|   watch: { | ||||
|     selected() { | ||||
|       this.handleChange(); | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     handleChange() { | ||||
|       if (this.value !== undefined) { | ||||
|         this.$emit("update:value", this.selected); | ||||
|       } else { | ||||
|         this.$emit("changed", this.selected); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @import "./src/scss/variables.scss"; | ||||
|  | ||||
| $radioSize: 16px; | ||||
| $ui-border-width: 2px; | ||||
|  | ||||
| .sub-radios { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   flex: 0 0 100%; | ||||
|   margin-left: 1rem; | ||||
|    | ||||
|   &:first-of-type { | ||||
|     margin-top: 1rem; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .radio { | ||||
|   display: flex; | ||||
|   flex-direction: row; | ||||
|   flex-wrap: wrap; | ||||
|   margin-bottom: 14px; | ||||
|   width: max-content; | ||||
|  | ||||
|   input[type="radio"] { | ||||
|     display: block; | ||||
|     opacity: 0; | ||||
|  | ||||
|     + label { | ||||
|       position: relative; | ||||
|       display: inline-block; | ||||
|       cursor: pointer; | ||||
|       padding-left: 1.25rem; | ||||
|       font-weight: 300; | ||||
|  | ||||
|       &::before { | ||||
|         content: ""; | ||||
|         display: inline-block; | ||||
|         position: absolute; | ||||
|         left: -($radioSize / 4) * 4; | ||||
|         border-radius: 50%; | ||||
|         border: $ui-border-width solid $text-color-70; | ||||
|         width: $radioSize; | ||||
|         height: $radioSize; | ||||
|       } | ||||
|  | ||||
|       &::after { | ||||
|         content: ""; | ||||
|         position: absolute; | ||||
|         display: inline-block; | ||||
|         left: -($radioSize / 4) * 3; | ||||
|         top: $radioSize / 4; | ||||
|         border-radius: 50%; | ||||
|         width: ($radioSize / 4) * 3; | ||||
|         height: ($radioSize / 4) * 3; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     &:checked, | ||||
|     &:hover { | ||||
|       + label::after { | ||||
|         background-color: $green; | ||||
|       } | ||||
|       + label::before { | ||||
|         border-color: $text-color; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     &:focus { | ||||
|       + label::before { | ||||
|         outline: $ui-border-width solid Highlight; | ||||
|         outline-style: auto; | ||||
|         outline-color: -webkit-focus-ring-color; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -31,7 +31,7 @@ export default { | ||||
|   font-size: 11px; | ||||
|   line-height: 2; | ||||
|   height: 45px; | ||||
|   letter-spacing: 0.5px; | ||||
|   letter-spacing: 1.2px; | ||||
|   padding: 5px 20px 4px 20px; | ||||
|   margin: 0; | ||||
|   margin-right: 0.3rem; | ||||
|   | ||||
| @@ -55,6 +55,8 @@ export default { | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @import "./src/scss/variables"; | ||||
| @import "./src/scss/media-queries"; | ||||
|  | ||||
| .fade-enter-active { | ||||
|   transition: opacity .4s; | ||||
| } | ||||
| @@ -95,6 +97,20 @@ export default { | ||||
|     transition: color .5s ease; | ||||
|   } | ||||
|  | ||||
|   @include mobile-only { | ||||
|     > div { | ||||
|       margin: 6px 6px; | ||||
|       line-height: 1.3rem; | ||||
|     } | ||||
|     h2 { | ||||
|       font-size: 1.1rem; | ||||
|     } | ||||
|     span { | ||||
|       font-size: 0.9rem; | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   .pinstripe { | ||||
|     height: 100%; | ||||
|     width: 0.5rem; | ||||
|   | ||||
							
								
								
									
										79
									
								
								src/components/ui/TextArea.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/components/ui/TextArea.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| <template> | ||||
|   <div class="wrapper"> | ||||
|     <h3 v-if="title" class="title">{{ title }}</h3> | ||||
|     <textarea :placeholder="placeholder" @input="handleInput" v-model="value" :rows="rows" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
|  | ||||
| <script> | ||||
|  | ||||
| export default { | ||||
|   props: { | ||||
|     placeholder: { | ||||
|       type: String, | ||||
|       required: false | ||||
|     }, | ||||
|     title: { | ||||
|       type: String, | ||||
|       required: false | ||||
|     }, | ||||
|     rows: { | ||||
|       type: Number, | ||||
|       required: false, | ||||
|       default: 10 | ||||
|     }, | ||||
|     value: { | ||||
|       type: String, | ||||
|       required: false, | ||||
|       default: undefined | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     handleInput(event) { | ||||
|       if (this.value !== undefined) { | ||||
|         this.$emit('update:value', this.value) | ||||
|       } else { | ||||
|         this.$emit('input', this.value, event) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @import "./src/scss/variables.scss"; | ||||
|  | ||||
| .wrapper { | ||||
|   width: 100%; | ||||
| } | ||||
|  | ||||
| .title { | ||||
|   margin: 0; | ||||
|   font-weight: 400; | ||||
|   text-transform: uppercase; | ||||
|   font-size: 14px; | ||||
|   color: $green; | ||||
|   margin-bottom: 0.5rem; | ||||
|   @include tablet-min { | ||||
|     font-size: 16px; | ||||
|   } | ||||
| } | ||||
|  | ||||
| textarea { | ||||
|   width: 100%; | ||||
|   font-size: 14px; | ||||
|   padding: 0.5rem; | ||||
|   border: 2px solid $text-color-50; | ||||
|  | ||||
|   &:focus { | ||||
|     border-color: $text-color; | ||||
|  | ||||
|     outline: none; | ||||
|  | ||||
|     -webkit-box-shadow: none; | ||||
|     -moz-box-shadow: none; | ||||
|     box-shadow: none; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -1,8 +1,10 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <a @click="$emit('click')"><li> | ||||
|       <figure :class="activeClassIfActive"> | ||||
|         <svg><use :xlink:href="iconRefNameIfActive"/></svg> | ||||
|       <figure :class="activeClassIfActive" v-if="iconRefNameIfActive"> | ||||
|         <svg class="icon"> | ||||
|           <use :xlink:href="iconRefNameIfActive"/> | ||||
|         </svg> | ||||
|       </figure> | ||||
|  | ||||
|       <span :class="activeClassIfActive">{{ contentTextToDisplay }}</span> | ||||
| @@ -21,7 +23,7 @@ export default { | ||||
|   props: { | ||||
|     iconRef: { | ||||
|       type: String, | ||||
|       required: true | ||||
|       required: false | ||||
|     }, | ||||
|     iconRefActive: { | ||||
|       type: String, | ||||
| @@ -44,7 +46,7 @@ export default { | ||||
|     iconRefNameIfActive() { | ||||
|       const { iconRefActive, iconRef, active } = this | ||||
|  | ||||
|       if ((iconRefActive && iconRef) & active) { | ||||
|       if ((iconRefActive && iconRef) && active) { | ||||
|         return iconRefActive | ||||
|       } | ||||
|       return iconRef | ||||
| @@ -98,7 +100,7 @@ li { | ||||
|     text-align: right; | ||||
|   } | ||||
|  | ||||
|   figure, figure > svg { | ||||
|   figure, figure > .icon { | ||||
|     width: 18px; | ||||
|     height: 18px; | ||||
|     margin: 0 7px 0 0; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user