mirror of
https://github.com/KevinMidboe/seasoned.git
synced 2026-03-11 03:49:07 +00:00
Updated seasonedinput to also handle two-way binded value prop. This changes is reflected most all places that seaoned-input is used
. Fuck, also added the new ResultsList which replaces MoviesList
This commit is contained in:
@@ -1,386 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class='movies-list' v-if="!error">
|
|
||||||
<header class='list-header'>
|
|
||||||
<h2 class='header__title'>{{ listTitle }}</h2>
|
|
||||||
|
|
||||||
<router-link class='header__view-more'
|
|
||||||
:to="'/list/' + list.route"
|
|
||||||
v-if='shortList'>
|
|
||||||
View All</router-link>
|
|
||||||
|
|
||||||
<div v-else style="line-height: 0;">
|
|
||||||
<span class='header__result-count' v-if="totalResults">{{ resultCount }} results</span>
|
|
||||||
<loading-placeholder v-else :count="1" lineClass='short nomargin'></loading-placeholder>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<!-- <ul class="filter">
|
|
||||||
<li class="filter-item" v-for="(item, index) in results" @click="applyFilter(item, index)" :class="{'active': item === selectedRelaseType}">{{ item.title }}</li>
|
|
||||||
</ul> -->
|
|
||||||
|
|
||||||
<ul class='results'>
|
|
||||||
<movies-list-item v-for='movie in results' :movie="movie" :shortList="shortList"></movies-list-item>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<loader v-if="loader" />
|
|
||||||
|
|
||||||
<div class='end-section' v-if="!shortList">
|
|
||||||
<seasoned-button v-if="currentPage < totalPages" @click="loadMore">load more</seasoned-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else style="display: flex; height: 50vh; width: 100%; justify-content: center; align-items: center;">
|
|
||||||
|
|
||||||
<h1 v-if="error">{{ error }}</h1>
|
|
||||||
<h1 v-else>Unable to load list: {{ listTitle }}</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import storage from '@/storage.js'
|
|
||||||
import MoviesListItem from '@/components/MoviesListItem.vue'
|
|
||||||
import SeasonedButton from '@/components/ui/SeasonedButton.vue'
|
|
||||||
import LoadingPlaceholder from '@/components/ui/LoadingPlaceholder.vue'
|
|
||||||
import Loader from '@/components/ui/Loader.vue'
|
|
||||||
import { searchTmdb, getTmdbListByPath } from '@/api.js'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
shortList: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
propList: Object
|
|
||||||
},
|
|
||||||
components: { MoviesListItem, SeasonedButton, LoadingPlaceholder, Loader },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
listTitle: 'No listname found',
|
|
||||||
results: [],
|
|
||||||
currentPage: 1,
|
|
||||||
totalResults: 0,
|
|
||||||
totalPages: -1,
|
|
||||||
fetchingResults: false,
|
|
||||||
error: undefined,
|
|
||||||
loader: false,
|
|
||||||
|
|
||||||
filters: {
|
|
||||||
status: {
|
|
||||||
elms: ['all', 'requested', 'downloading', 'downloaded'],
|
|
||||||
selected: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
resultCount() {
|
|
||||||
return this.totalResults.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeMount() {
|
|
||||||
if (this.propList) {
|
|
||||||
this.list = this.propList
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setPageFromUrlQuery()
|
|
||||||
this.parseURI()
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
setTimeout(() => {
|
|
||||||
if (this.results.length === 0 && this.error === undefined) {
|
|
||||||
this.loader = true
|
|
||||||
}
|
|
||||||
}, 200)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setPageFromUrlQuery() {
|
|
||||||
if (this.$route.query.page)
|
|
||||||
this.currentPage = this.$route.query.page
|
|
||||||
console.log('url page param found', this.currentPage)
|
|
||||||
},
|
|
||||||
getListByName(name) {
|
|
||||||
return storage.homepageLists.filter(list => list.route === name)[0]
|
|
||||||
},
|
|
||||||
parseURI() {
|
|
||||||
const currentRouteName = this.$route.name
|
|
||||||
|
|
||||||
// route name is list - we are in a list view
|
|
||||||
if (currentRouteName === 'list') {
|
|
||||||
const nameParam = this.$route.params.name
|
|
||||||
if (this.getListByName(nameParam)) {
|
|
||||||
this.list = this.getListByName(nameParam)
|
|
||||||
this.listTitle = this.list.title
|
|
||||||
this.fetchListitems()
|
|
||||||
} else {
|
|
||||||
this.error = `Unable to load list: `
|
|
||||||
}
|
|
||||||
} // route name is search - we are searcing
|
|
||||||
else if (currentRouteName === 'search') {
|
|
||||||
if (this.$route.query.query) {
|
|
||||||
this.query = decodeURIComponent(this.$route.query.query)
|
|
||||||
this.listTitle = 'Search results: ' + this.query
|
|
||||||
this.fetchSearchItems()
|
|
||||||
} else {
|
|
||||||
this.error = 'Search query is not defined, please try again'
|
|
||||||
}
|
|
||||||
|
|
||||||
} // no matched route found - using prop to fetch list items
|
|
||||||
else {
|
|
||||||
this.listTitle = this.list.title
|
|
||||||
this.fetchListitems()
|
|
||||||
}
|
|
||||||
|
|
||||||
document.title = this.listTitle
|
|
||||||
},
|
|
||||||
// TODO these should receive a path not get it from list instance
|
|
||||||
fetchListitems() {
|
|
||||||
getTmdbListByPath(this.list.path, this.currentPage)
|
|
||||||
.then(this.parseResponse)
|
|
||||||
.catch(error => {
|
|
||||||
console.error(error)
|
|
||||||
this.error = 'Network error'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
fetchSearchItems() {
|
|
||||||
searchTmdb(this.query, this.currentPage)
|
|
||||||
.then(this.parseResponse)
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO what parts are modular and what parts do we want the component to deal with
|
|
||||||
// if we pass in some object and then as we initialize we set to local variables.
|
|
||||||
// This way we call the http-api from outside and pass the response in to the component[0]
|
|
||||||
// Could also parse the response we are requesting then return a clean object we can
|
|
||||||
// pass down[1].
|
|
||||||
|
|
||||||
// [0] if this is done we should also take the page, total pages, total results and
|
|
||||||
// the list of results. Maybe also the title of the list or use local title as fallback?
|
|
||||||
// [1] an issue with this that duplicate code will be needed for doing the same with
|
|
||||||
// url params and paths.
|
|
||||||
// (What if we eliminated folder based routes and implemented the routes in hashes
|
|
||||||
// with single page applications today the navigation is simple enought that it
|
|
||||||
// would maybe not be needed to have a path-route but a hash-local.storage
|
|
||||||
// implementation; would allow sharing and remembering paths is just silly for most
|
|
||||||
// Single-Page-Applications that are tightly scoped applications)
|
|
||||||
parseResponse(response) {
|
|
||||||
const data = response.data
|
|
||||||
if (data.page > data.total_pages) {
|
|
||||||
console.error('You have reached the end')
|
|
||||||
this.error = 'You have reached the end'
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.results.length) {
|
|
||||||
this.results.push(...data.results)
|
|
||||||
} else {
|
|
||||||
this.results = this.shortList ? data.results.slice(0,12) : data.results
|
|
||||||
}
|
|
||||||
this.page = data.page
|
|
||||||
this.totalPages = data.total_pages
|
|
||||||
this.totalResults = data.total_results || data.results.length
|
|
||||||
|
|
||||||
this.loader = false
|
|
||||||
|
|
||||||
console.info(`Response from list: ${this.listTitle}`, { results: this.results, page: this.page, totalPages: this.totalPages, totalResults: this.totalResults })
|
|
||||||
},
|
|
||||||
loadMore(){
|
|
||||||
this.currentPage++;
|
|
||||||
|
|
||||||
console.log('path and name:', this.$route.path, this.$route.name)
|
|
||||||
let url = ''
|
|
||||||
|
|
||||||
if (this.$route.path.includes('list'))
|
|
||||||
url = `/#${this.$route.path}?page=${this.currentPage}`
|
|
||||||
else if (this.$route.path.includes('search'))
|
|
||||||
url = `/#/search?query=${this.query}&page=${this.currentPage}`
|
|
||||||
|
|
||||||
console.log('new url', url)
|
|
||||||
window.history.replaceState({}, 'foo', url)
|
|
||||||
|
|
||||||
this.parseURI()
|
|
||||||
},
|
|
||||||
// sort() {
|
|
||||||
// console.log(this.showFilters)
|
|
||||||
// },
|
|
||||||
// toggleFilter(item, index){
|
|
||||||
// this.showFilter = this.showFilter ? false : true;
|
|
||||||
// // this.results = this.results.filter(result => result.status != 'downloaded')
|
|
||||||
// },
|
|
||||||
// applyFilter(item, index) {
|
|
||||||
// this.filter = item;
|
|
||||||
// this.filters.status.selected = index;
|
|
||||||
// console.log('applied query filter: ', item, index)
|
|
||||||
// this.fetchCategory()
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
$route: function () {
|
|
||||||
console.log('updated route')
|
|
||||||
this.results = false
|
|
||||||
this.currentPage = 1
|
|
||||||
this.setPageFromUrlQuery()
|
|
||||||
this.parseURI()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import './src/scss/media-queries';
|
|
||||||
@import './src/scss/variables';
|
|
||||||
|
|
||||||
.movies-list {
|
|
||||||
& ul:last-of-type {
|
|
||||||
padding-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-of-type header {
|
|
||||||
padding-top: 1.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 12px;
|
|
||||||
|
|
||||||
&.sticky {
|
|
||||||
background-color: $background-color-secondary;
|
|
||||||
|
|
||||||
position: sticky;
|
|
||||||
position: -webkit-sticky;
|
|
||||||
top: $header-size;
|
|
||||||
z-index: 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 300;
|
|
||||||
text-transform: capitalize;
|
|
||||||
line-height: 18px;
|
|
||||||
margin: 0;
|
|
||||||
color: $text-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-more {
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 300;
|
|
||||||
letter-spacing: .5px;
|
|
||||||
color: $text-color-70;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: color .5s ease;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:after{
|
|
||||||
content: " →";
|
|
||||||
}
|
|
||||||
&:hover{
|
|
||||||
color: $text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-count {
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 300;
|
|
||||||
letter-spacing: .5px;
|
|
||||||
color: $text-color;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.results {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
|
|
||||||
&.shortList > li {
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
&:nth-child(-n+4) {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullwidth-button {
|
|
||||||
width: 100%;
|
|
||||||
margin: 1rem 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@include tablet-min {
|
|
||||||
header {
|
|
||||||
padding-left: 1.25rem;
|
|
||||||
}
|
|
||||||
.results.shortList > li:nth-child(-n+6) {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@include tablet-landscape-min {
|
|
||||||
header {
|
|
||||||
padding-left: 1.5rem;
|
|
||||||
}
|
|
||||||
.results.shortList > li:nth-child(-n+8) {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@include desktop-min {
|
|
||||||
.results.shortList > li:nth-child(-n+12) {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include desktop-lg-min {
|
|
||||||
header {
|
|
||||||
padding-left: 1.75rem;
|
|
||||||
}
|
|
||||||
.results.shortList > li:nth-child(-n+16) {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// .shutter {
|
|
||||||
// $height: 36px;
|
|
||||||
// height: $height;
|
|
||||||
// width: 100%;
|
|
||||||
// background-color: $background-color-secondary;
|
|
||||||
// position: absolute;
|
|
||||||
// margin-bottom: -$height;
|
|
||||||
|
|
||||||
// position: -webkit-sticky; /* Safari */
|
|
||||||
// position: sticky;
|
|
||||||
// top: $header-size;
|
|
||||||
// z-index: 4;
|
|
||||||
|
|
||||||
// @include tablet-min{
|
|
||||||
// background-color: blue;
|
|
||||||
// height: 23px 15px;
|
|
||||||
// }
|
|
||||||
// @include tablet-landscape-min{
|
|
||||||
// background-color: orange;
|
|
||||||
// height: 30px;
|
|
||||||
// }
|
|
||||||
// @include desktop-min{
|
|
||||||
// background-color: navajowhite;
|
|
||||||
// height: 34px;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
.form__group-input {
|
|
||||||
padding: 10px 5px 10px 15px;
|
|
||||||
margin-left: 0;
|
|
||||||
height: 38px;
|
|
||||||
width: 150px;
|
|
||||||
font-size: 15px;
|
|
||||||
@include desktop-min {
|
|
||||||
width: 200px;
|
|
||||||
font-size: 17px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -2,12 +2,13 @@
|
|||||||
<section>
|
<section>
|
||||||
<h1>Register new user</h1>
|
<h1>Register new user</h1>
|
||||||
|
|
||||||
<seasoned-input text="username" icon="Email" type="username"
|
<seasoned-input placeholder="username" icon="Email" type="username" :value.sync="username" />
|
||||||
@inputValue="setValue('username', $event)" />
|
|
||||||
<seasoned-input text="password" icon="Keyhole" type="password"
|
<seasoned-input placeholder="password" icon="Keyhole" type="password"
|
||||||
@inputValue="setValue('password', $event)" @enter="requestNewUser"/>
|
:value.sync="password" @enter="requestNewUser"/>
|
||||||
<seasoned-input text="repeat password" icon="Keyhole" type="password"
|
|
||||||
@inputValue="setValue('password', $event)" @enter="requestNewUser"/>
|
<seasoned-input placeholder="repeat password" icon="Keyhole" type="password"
|
||||||
|
:value.sync="passwordRepeat" @enter="requestNewUser"/>
|
||||||
|
|
||||||
<seasoned-button @click="requestNewUser">Register</seasoned-button>
|
<seasoned-button @click="requestNewUser">Register</seasoned-button>
|
||||||
|
|
||||||
@@ -18,7 +19,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import storage from '@/storage.js'
|
|
||||||
import SeasonedButton from '@/components/ui/SeasonedButton.vue'
|
import SeasonedButton from '@/components/ui/SeasonedButton.vue'
|
||||||
import SeasonedInput from '@/components/ui/SeasonedInput.vue'
|
import SeasonedInput from '@/components/ui/SeasonedInput.vue'
|
||||||
import SeasonedMessages from '@/components/ui/SeasonedMessages.vue'
|
import SeasonedMessages from '@/components/ui/SeasonedMessages.vue'
|
||||||
@@ -28,18 +28,16 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
messages: [],
|
messages: [],
|
||||||
username: undefined,
|
username: null,
|
||||||
password: undefined,
|
password: null,
|
||||||
passwordRepeat: undefined
|
passwordRepeat: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
requestNewUser(){
|
requestNewUser(){
|
||||||
let username = this.username
|
let { username, password, passwordRepeat } = this
|
||||||
let password = this.password
|
|
||||||
let password_re = this.passwordRepeat
|
|
||||||
|
|
||||||
let verifyCredentials = this.checkCredentials(username, password, password_re);
|
let verifyCredentials = this.checkCredentials(username, password, passwordRepeat);
|
||||||
|
|
||||||
if (verifyCredentials.verified) {
|
if (verifyCredentials.verified) {
|
||||||
axios.post(`https://api.kevinmidboe.com/api/v1/user`, {
|
axios.post(`https://api.kevinmidboe.com/api/v1/user`, {
|
||||||
@@ -65,19 +63,25 @@ export default {
|
|||||||
this.messages.push({ type: 'warning', title: 'Parse error', message: verifyCredentials.reason })
|
this.messages.push({ type: 'warning', title: 'Parse error', message: verifyCredentials.reason })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
checkCredentials(username, password, password_re) {
|
checkCredentials(username, password, passwordRepeat) {
|
||||||
if (password !== password_re) {
|
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 {
|
return {
|
||||||
verified: false,
|
verified: false,
|
||||||
reason: 'Passwords do not match'
|
reason: 'Passwords do not match'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (username === undefined) {
|
|
||||||
return {
|
|
||||||
verified: false,
|
|
||||||
reason: 'Please insert username'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
return {
|
return {
|
||||||
verified: true,
|
verified: true,
|
||||||
@@ -85,9 +89,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setValue(l, t) {
|
|
||||||
this[l] = t
|
|
||||||
},
|
|
||||||
logOut(){
|
logOut(){
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
eventHub.$emit('setUserStatus');
|
eventHub.$emit('setUserStatus');
|
||||||
|
|||||||
68
src/components/ResultsList.vue
Normal file
68
src/components/ResultsList.vue
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<ul class="results" :class="{'shortList': shortList}">
|
||||||
|
<movies-list-item v-for='movie in results' :movie="movie" />
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MoviesListItem from '@/components/MoviesListItem'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { MoviesListItem },
|
||||||
|
props: {
|
||||||
|
results: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
shortList: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import './src/scss/media-queries';
|
||||||
|
|
||||||
|
.results {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
&.shortList > li {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&:nth-child(-n+4) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include tablet-min {
|
||||||
|
.results.shortList > li:nth-child(-n+6) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@include tablet-landscape-min {
|
||||||
|
.results.shortList > li:nth-child(-n+8) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@include desktop-min {
|
||||||
|
.results.shortList > li:nth-child(-n+10) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@include desktop-lg-min {
|
||||||
|
.results.shortList > li:nth-child(-n+16) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -6,22 +6,24 @@
|
|||||||
<span class="settings__info">Sign in to your plex account to get information about recently added movies and to see your watch history</span>
|
<span class="settings__info">Sign in to your plex account to get information about recently added movies and to see your watch history</span>
|
||||||
|
|
||||||
<form class="form">
|
<form class="form">
|
||||||
<seasoned-input text="plex username" icon="Email"
|
<seasoned-input placeholder="plex username" icon="Email" :value.sync="plexUsername"/>
|
||||||
@inputValue="setValue('plexUsername', $event)"/>
|
<seasoned-input placeholder="plex password" icon="Keyhole" type="password"
|
||||||
<seasoned-input text="plex password" icon="Keyhole" type="password"
|
:value.sync="plexPassword" @submit="authenticatePlex" />
|
||||||
@inputValue="setValue('plexPassword', $event)"/>
|
|
||||||
|
|
||||||
<seasoned-button @click="authenticatePlex">link plex account</seasoned-button>
|
<seasoned-button @click="authenticatePlex">link plex account</seasoned-button>
|
||||||
|
|
||||||
|
<seasoned-messages :messages.sync="messages" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<hr class='setting__divider'>
|
<hr class='setting__divider'>
|
||||||
|
|
||||||
<h3 class='settings__header'>Change password</h3>
|
<h3 class='settings__header'>Change password</h3>
|
||||||
<form class="form">
|
<form class="form">
|
||||||
<seasoned-input text="new password" icon="Keyhole" type="password"
|
<seasoned-input placeholder="new password" icon="Keyhole" type="password"
|
||||||
@inputValue="setValue('newPass', $event)"/>
|
:value.sync="newPassword" />
|
||||||
<seasoned-input text="repeat new password" icon="Keyhole" type="password"
|
|
||||||
@inputValue="setValue('newPassConfirm', $event)"/>
|
<seasoned-input placeholder="repeat new password" icon="Keyhole" type="password"
|
||||||
|
:value.sync="newPasswordRepeat" />
|
||||||
|
|
||||||
<seasoned-button @click="changePassword">change password</seasoned-button>
|
<seasoned-button @click="changePassword">change password</seasoned-button>
|
||||||
</form>
|
</form>
|
||||||
@@ -45,11 +47,12 @@
|
|||||||
import storage from '@/storage.js'
|
import storage from '@/storage.js'
|
||||||
import SeasonedInput from '@/components/ui/SeasonedInput.vue'
|
import SeasonedInput from '@/components/ui/SeasonedInput.vue'
|
||||||
import SeasonedButton from '@/components/ui/SeasonedButton.vue'
|
import SeasonedButton from '@/components/ui/SeasonedButton.vue'
|
||||||
|
import SeasonedMessages from '@/components/ui/SeasonedMessages.vue'
|
||||||
|
|
||||||
import { plexAuthenticate } from '@/api.js'
|
import { plexAuthenticate } from '@/api.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { SeasonedInput, SeasonedButton },
|
components: { SeasonedInput, SeasonedButton, SeasonedMessages },
|
||||||
data(){
|
data(){
|
||||||
return{
|
return{
|
||||||
userLoggedIn: '',
|
userLoggedIn: '',
|
||||||
@@ -74,10 +77,11 @@ export default {
|
|||||||
plexAuthenticate(username, password)
|
plexAuthenticate(username, password)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
let data = resp.data;
|
let data = resp.data;
|
||||||
console.log('response from plex:', data.user)
|
this.messages.push({ type: 'success', title: 'Authenticated with plex', message: 'Successfully linked plex account with seasoned request' })
|
||||||
|
// console.log('response from plex:', data.user)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log('error: ', error)
|
this.messages.push({ type: 'error', title: 'Something went wrong', message: error.message })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,10 +2,8 @@
|
|||||||
<section>
|
<section>
|
||||||
<h1>Sign in</h1>
|
<h1>Sign in</h1>
|
||||||
|
|
||||||
<seasoned-input text="username" icon="Email" type="username"
|
<seasoned-input placeholder="username" icon="Email" type="username" :value.sync="username" />
|
||||||
@inputValue="setValue('username', $event)" />
|
<seasoned-input placeholder="password" icon="Keyhole" type="password" :value.sync="password" @enter="signin"/>
|
||||||
<seasoned-input text="password" icon="Keyhole" type="password"
|
|
||||||
@inputValue="setValue('password', $event)" @enter="signin"/>
|
|
||||||
|
|
||||||
<seasoned-button @click="signin">sign in</seasoned-button>
|
<seasoned-button @click="signin">sign in</seasoned-button>
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
|
|
||||||
<div class="editQuery" v-if="editSearchQuery">
|
<div class="editQuery" v-if="editSearchQuery">
|
||||||
|
|
||||||
<seasonedInput text="Torrent query" icon="_torrents" @inputValue="(val) => editedSearchQuery = val" @enter="fetchTorrents(editedSearchQuery)" />
|
<seasonedInput placeholder="Torrent query" icon="_torrents" :value.sync="editedSearchQuery" @enter="fetchTorrents(editedSearchQuery)" />
|
||||||
|
|
||||||
<div style="height: 45px; width: 5px;"></div>
|
<div style="height: 45px; width: 5px;"></div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,37 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="group" :class="{ completed: value.length > 0 }">
|
<div class="group" :class="{ completed: value }">
|
||||||
<svg class="group__input-icon"><use v-bind="{'xlink:href':'#icon' + icon}"></use></svg>
|
<svg class="group__input-icon"><use v-bind="{'xlink:href':'#icon' + icon}"></use></svg>
|
||||||
<input class="group__input" :type="tempType || type" ref="plex_username"
|
<input class="group__input" :type="tempType || type" @input="handleInput" v-model="inputValue"
|
||||||
v-model="value" :placeholder="text" @keyup.enter="submit" @input="handleInput" />
|
:placeholder="placeholder" @keyup.enter="submit" />
|
||||||
|
|
||||||
<i v-if="value.length > 0 && type === 'password'" @click="toggleShowPassword" class="group__input-show noselect">show</i>
|
<i v-if="value && type === 'password'" @click="toggleShowPassword" class="group__input-show noselect">show</i>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
text: { type: String },
|
placeholder: { type: String },
|
||||||
icon: { type: String },
|
icon: { type: String },
|
||||||
type: { type: String }
|
type: { type: String, default: 'text' },
|
||||||
|
value: { type: String, default: undefined }
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return { value: '', tempType: undefined }
|
return {
|
||||||
|
inputValue: undefined,
|
||||||
|
tempType: undefined
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
submit(event) {
|
submit(event) {
|
||||||
this.$emit('enter')
|
this.$emit('enter')
|
||||||
},
|
},
|
||||||
handleInput(value) {
|
handleInput(event) {
|
||||||
this.$emit('inputValue', this.value)
|
if (this.value !== undefined) {
|
||||||
|
this.$emit('update:value', this.inputValue)
|
||||||
|
} else {
|
||||||
|
this.$emit('change', this.inputValue, event)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
toggleShowPassword() {
|
toggleShowPassword() {
|
||||||
if (this.tempType === 'text') {
|
if (this.tempType === 'text') {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition-group name="fade">
|
<transition-group name="fade">
|
||||||
<div class="message" v-for="message in messages" :class="message.type || 'warning'" :key="message">
|
<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>{{ message.title || defaultTitles[message.type] }}</h2>
|
||||||
@@ -31,6 +31,11 @@ export default {
|
|||||||
localMessages: [...this.messages]
|
localMessages: [...this.messages]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
reversedMessages() {
|
||||||
|
return [...this.messages].reverse()
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
clicked(e) {
|
clicked(e) {
|
||||||
const removedMessage = [...this.messages].filter(mes => mes !== e)
|
const removedMessage = [...this.messages].filter(mes => mes !== e)
|
||||||
|
|||||||
Reference in New Issue
Block a user