Rewrote the entire component. Now we use are own table with a new design and auto expanding to view more information. Table also supports sorting and collapses columns on smaller screens
This commit is contained in:
@@ -1,129 +1,366 @@
|
|||||||
<template>
|
<template>
|
||||||
<section>
|
<div v-if="show">
|
||||||
<div v-if="listLoaded">
|
<h2 class="title">torrents: {{ query }}</h2>
|
||||||
<div v-if="torrents.length">
|
|
||||||
<data-tablee
|
|
||||||
:rows="torrents"
|
|
||||||
:cols="cols"
|
|
||||||
empty="-"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="data-tablee-icon"
|
|
||||||
slot="sort-icon"
|
|
||||||
slot-scope="{ sortment, sorted, arrow }"
|
|
||||||
>
|
|
||||||
{{ sorted ? arrow + ' ' + (sortment === 'ascending' ? 'ASC' : 'DESC') : '' }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<template
|
<div v-if="listLoaded">
|
||||||
slot="row"
|
<ul class="filter">
|
||||||
slot-scope="{ row, index }"
|
<li class="filter-item" v-for="(item, index) in release_types" @click="applyFilter(item, index)" :class="{'active': item === selectedRelaseType}">{{ item }}</li>
|
||||||
>
|
</ul>
|
||||||
<td class="data-tablee-cell -content data-tablee-text" v-bind:title="row.name" v-if="!renderName">{{ row.name.slice(0, 50) }}</td>
|
|
||||||
<td class="data-tablee-cell -content data-tablee-text" v-on:click="showInfo(row.name)">{{ row.seed }}</td>
|
|
||||||
<td class="data-tablee-cell -content data-tablee-text" v-on:click="showInfo(row.name)">{{ row.size }}</td>
|
<table>
|
||||||
<td class="data-tablee-cell -content data-tablee-text magnet">
|
<tr class="table__header noselect">
|
||||||
<button type='button' class="button" @click="sendTorrent(row.magnet, row.name)">Add</button>
|
<th @click="sortTable('name')">
|
||||||
</td>
|
<span>Name</span>
|
||||||
</template>
|
<span v-if="prevCol === 'name' && direction">↑</span>
|
||||||
</data-tablee>
|
<span v-if="prevCol === 'name' && !direction">↓</span>
|
||||||
</div>
|
</th>
|
||||||
<section v-if="!torrents.length" class="">
|
<th @click="sortTable('seed')">
|
||||||
<div class="not-found__content">
|
<span>Seed</span>
|
||||||
<h2 class="not-found__title">{{ errorMessage }}</h2>
|
<span v-if="prevCol === 'seed' && direction">↑</span>
|
||||||
</div>
|
<span v-if="prevCol === 'seed' && !direction">↓</span>
|
||||||
</section>
|
</th>
|
||||||
|
<th @click="sortTable('size')">
|
||||||
|
<span>Size</span>
|
||||||
|
<span v-if="prevCol === 'size' && direction">↑</span>
|
||||||
|
<span v-if="prevCol === 'size' && !direction">↓</span>
|
||||||
|
<th>
|
||||||
|
<span>Magnet</span>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="torrent in torrents" class="table__content">
|
||||||
|
<td @click="expand($event, torrent.name)">{{ torrent.name }}</td>
|
||||||
|
<td @click="expand($event, torrent.name)">{{ torrent.seed }}</td>
|
||||||
|
<td @click="expand($event, torrent.name)">{{ torrent.size }}</td>
|
||||||
|
<td @click="sendTorrent(torrent.magnet, torrent.name, $event)" class="download">
|
||||||
|
<svg class="download__icon"><use xlink:href="#iconUnmatched"></use></svg>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<i v-if="!listLoaded" class="torrentloader"></i>
|
<i v-else class="torrentloader"></i>
|
||||||
</section>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import storage from '@/storage.js'
|
||||||
import numeral from 'numeral'
|
import { sortableSize } from '@/utils.js'
|
||||||
import storage from '../storage.js'
|
import { searchTorrents, addMagnet } from '@/api.js'
|
||||||
|
|
||||||
// import testTorrents from './torrents.json';
|
|
||||||
|
|
||||||
let tablet = window.innerWidth < 768 ? true : false;
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['query', 'tmdb_id', 'tmdb_type'],
|
props: {
|
||||||
beforeRouteLeave (to, from, next) {
|
query: {
|
||||||
if(from.name == 'search'){
|
type: String,
|
||||||
eventHub.$emit('setSearchQuery', true);
|
require: true
|
||||||
}
|
},
|
||||||
next();
|
tmdb_id: {
|
||||||
|
type: Number,
|
||||||
|
require: true
|
||||||
|
},
|
||||||
|
tmdb_type: String,
|
||||||
|
admin: String,
|
||||||
|
show: Boolean
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
torrents: [],
|
|
||||||
listLoaded: false,
|
listLoaded: false,
|
||||||
errorMessage: '',
|
torrents: undefined,
|
||||||
renderName: tablet,
|
torrentResponse: undefined,
|
||||||
cols: [
|
currentPage: 0,
|
||||||
{ label: 'Name', field: 'name', sort: true, hidden: tablet },
|
prevCol: '',
|
||||||
{ label: 'Seeders', field: 'seed', sort: (a, b) => parseInt(a) - parseInt(b) },
|
direction: false,
|
||||||
{ label: 'Size', field: 'size', sort: (a, b) => this.sortableSize(a) - this.sortableSize(b) },
|
release_types: ['all'],
|
||||||
{ label: 'Add', align: 'center' }
|
selectedRelaseType: 'all'
|
||||||
],
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
beforeMount() {
|
||||||
|
console.log('starting torrent search with:', this.query, this.tmdb_id)
|
||||||
|
this.fetchTorrents()
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchTorrents(){
|
expand(event, name) {
|
||||||
axios.get(`https://api.kevinmidboe.com/api/v1/pirate/search?query=${this.query}&filter=all&page=${this.currentPage}`, {
|
const existingExpandedElement = document.getElementsByClassName('expanded')[0]
|
||||||
headers: {authorization: storage.token},
|
|
||||||
|
if (existingExpandedElement) {
|
||||||
|
console.log('exists')
|
||||||
|
const expandedSibling = event.target.parentNode.nextSibling.className === 'expanded'
|
||||||
|
|
||||||
|
existingExpandedElement.remove()
|
||||||
|
|
||||||
|
if (expandedSibling) {
|
||||||
|
console.log('sibling is here')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('expand event', event)
|
||||||
|
const nameRow = document.createElement('tr')
|
||||||
|
const nameCol = document.createElement('td')
|
||||||
|
nameRow.className = 'expanded'
|
||||||
|
nameCol.innerText = name
|
||||||
|
|
||||||
|
nameRow.appendChild(nameCol)
|
||||||
|
|
||||||
|
event.target.parentNode.insertAdjacentElement('afterend', nameRow)
|
||||||
|
},
|
||||||
|
sendTorrent(magnet, name, event){
|
||||||
|
this.$notifications.info({
|
||||||
|
title: 'Adding torrent 🦜',
|
||||||
|
description: this.query,
|
||||||
|
timeout: 3000
|
||||||
})
|
})
|
||||||
|
|
||||||
|
event.target.parentNode.classList.add('active')
|
||||||
|
|
||||||
|
addMagnet(magnet, name, tmdb_id)
|
||||||
|
.catch((resp) => { console.log('error:', resp.data) })
|
||||||
|
.then((resp) => {
|
||||||
|
console.log('addTorrent resp: ', resp)
|
||||||
|
this.$notifications.success({
|
||||||
|
title: 'Torrent added 🎉',
|
||||||
|
description: this.query,
|
||||||
|
timeout: 3000000
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sortTable(col, sameDirection=false) {
|
||||||
|
if (this.prevCol === col && sameDirection === false) {
|
||||||
|
this.direction = !this.direction
|
||||||
|
}
|
||||||
|
console.log('col and more', col, sameDirection)
|
||||||
|
|
||||||
|
switch (col) {
|
||||||
|
case 'name':
|
||||||
|
this.sortName()
|
||||||
|
break
|
||||||
|
case 'seed':
|
||||||
|
this.sortSeed()
|
||||||
|
break
|
||||||
|
case 'size':
|
||||||
|
this.sortSize()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
this.prevCol = col
|
||||||
|
},
|
||||||
|
sortName() {
|
||||||
|
const torrentsCopy = [...this.torrents]
|
||||||
|
if (this.direction) {
|
||||||
|
this.torrents = torrentsCopy.sort((a, b) => (a.name < b.name) ? 1 : -1)
|
||||||
|
} else {
|
||||||
|
this.torrents = torrentsCopy.sort((a, b) => (a.name > b.name) ? 1 : -1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sortSeed() {
|
||||||
|
const torrentsCopy = [...this.torrents]
|
||||||
|
if (this.direction) {
|
||||||
|
this.torrents = torrentsCopy.sort((a, b) => parseInt(a.seed) - parseInt(b.seed));
|
||||||
|
} else {
|
||||||
|
this.torrents = torrentsCopy.sort((a, b) => parseInt(b.seed) - parseInt(a.seed));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sortSize() {
|
||||||
|
const torrentsCopy = [...this.torrents]
|
||||||
|
if (this.direction) {
|
||||||
|
this.torrents = torrentsCopy.sort((a, b) => parseInt(sortableSize(a.size)) - parseInt(sortableSize(b.size)));
|
||||||
|
} else {
|
||||||
|
this.torrents = torrentsCopy.sort((a, b) => parseInt(sortableSize(b.size)) - parseInt(sortableSize(a.size)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
findRelaseTypes() {
|
||||||
|
this.torrents.forEach(item => this.release_types.push(...item.release_type))
|
||||||
|
this.release_types = [...new Set(this.release_types)]
|
||||||
|
},
|
||||||
|
applyFilter(item, index) {
|
||||||
|
this.selectedRelaseType = item;
|
||||||
|
const torrents = [...this.torrentResponse]
|
||||||
|
|
||||||
|
if (item === 'all') {
|
||||||
|
this.torrents = torrents
|
||||||
|
this.sortTable(this.prevCol, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.torrents = torrents.filter(torrent => torrent.release_type.includes(item))
|
||||||
|
this.sortTable(this.prevCol, true)
|
||||||
|
},
|
||||||
|
fetchTorrents(){
|
||||||
|
searchTorrents(this.query, 'all', this.currentPage, storage.token)
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
let data = resp.data;
|
let data = resp.data;
|
||||||
|
console.log('data results', data.results);
|
||||||
|
this.torrentResponse = data.results;
|
||||||
this.torrents = data.results;
|
this.torrents = data.results;
|
||||||
this.listLoaded = true;
|
this.listLoaded = true;
|
||||||
})
|
})
|
||||||
|
.then(this.findRelaseTypes)
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
const error = e.toString()
|
const error = e.toString()
|
||||||
this.errorMessage = error.indexOf('401') != -1 ? 'Permission denied' : 'Nothing found';
|
this.errorMessage = error.indexOf('401') != -1 ? 'Permission denied' : 'Nothing found';
|
||||||
this.listLoaded = true;
|
this.listLoaded = true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
sendTorrent(magnet, name){
|
}
|
||||||
axios.post(`https://api.kevinmidboe.com/api/v1/pirate/add`, {
|
|
||||||
magnet: magnet, name: name, tmdb_id: this.tmdb_id }, { headers: {authorization: storage.token}
|
|
||||||
})
|
|
||||||
.catch((resp) => { console.log('error:', resp.data) })
|
|
||||||
.then((resp) => { console.log('addTorrent resp: ', resp) })
|
|
||||||
},
|
|
||||||
showInfo(text){
|
|
||||||
alert(text)
|
|
||||||
},
|
|
||||||
sortableSize(string) {
|
|
||||||
const UNITS = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
||||||
const [numStr, unit] = string.split(' ');
|
|
||||||
if (UNITS.indexOf(unit) === -1)
|
|
||||||
return string
|
|
||||||
const exponent = UNITS.indexOf(unit) * 3
|
|
||||||
return numStr * (Math.pow(10, exponent))
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created(){
|
|
||||||
this.fetchTorrents();
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style src="../scss/vue-data-tablee.css"></style>
|
<style lang="scss">
|
||||||
|
@import "./src/scss/variables";
|
||||||
|
.expanded {
|
||||||
|
display: flex;
|
||||||
|
margin: 0 1rem;
|
||||||
|
max-width: 100%;
|
||||||
|
border-left: 1px solid rgba($c-dark, 0.5);
|
||||||
|
border-right: 1px solid rgba($c-dark, 0.5);
|
||||||
|
border-bottom: 1px solid rgba($c-dark, 0.5);
|
||||||
|
|
||||||
|
td {
|
||||||
|
// border-left: 1px solid $c-dark;
|
||||||
|
word-break: break-all;
|
||||||
|
padding: 0.5rem 0.15rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<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";
|
||||||
.magnet{
|
@import "./src/scss/elements";
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 400;
|
||||||
|
text-transform: uppercase;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
font-size: 14px;
|
||||||
.add{
|
color: $c-green;
|
||||||
padding: 3px 15px 3px 15px;
|
padding-bottom: 20px;
|
||||||
&:hover, &:active{
|
@include tablet-min{
|
||||||
background: $c-dark;
|
font-size: 16px;
|
||||||
color: $c-white;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table__content, .table__header {
|
||||||
|
display: flex;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 1rem;
|
||||||
|
border-left: 1px solid rgba($c-dark, 0.8);
|
||||||
|
border-right: 1px solid rgba($c-dark, 0.8);
|
||||||
|
border-bottom: 1px solid rgba($c-dark, 0.8);
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-basis: 100%;
|
||||||
|
|
||||||
|
padding: 0.4rem;
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
th:first-child, td:first-child {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
th:not(:first-child), td:not(:first-child) {
|
||||||
|
flex: 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
th:nth-child(2), td:nth-child(2) {
|
||||||
|
flex: 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include mobile-only {
|
||||||
|
th:first-child, td:first-child {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
display: block;
|
||||||
|
align: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th:not(:first-child), td:not(:first-child) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.table__content {
|
||||||
|
td:not(:last-child) {
|
||||||
|
border-right: 1px solid rgba($c-dark, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table__content:last-child {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
border-bottom-left-radius: 3px;
|
||||||
|
border-bottom-right-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table__header {
|
||||||
|
background-color: white;
|
||||||
|
color: $c-dark;
|
||||||
|
text-transform: uppercase;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
border-top: 1px solid rgba($c-dark, 0.8);
|
||||||
|
border-top-left-radius: 3px;
|
||||||
|
border-top-right-radius: 3px;
|
||||||
|
|
||||||
|
th {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: 0.7px;
|
||||||
|
// font-size: 1.08rem;
|
||||||
|
font-size: 15px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
min-width: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
span:first-child {
|
||||||
|
margin-right: 0.6rem;
|
||||||
|
}
|
||||||
|
span:nth-child(2) {
|
||||||
|
margin-right: 0.1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th:not(:last-child) {
|
||||||
|
border-right: 1px solid rgba($c-dark, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.download {
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
fill: rgba($c-dark, 0.6);
|
||||||
|
height: 1.2rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
fill: $c-dark;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active &__icon {
|
||||||
|
fill: $c-green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.torrentloader{
|
.torrentloader{
|
||||||
animation: load 1s linear infinite;
|
animation: load 1s linear infinite;
|
||||||
border: 2px solid $c-dark;
|
border: 2px solid $c-dark;
|
||||||
@@ -131,7 +368,7 @@ export default {
|
|||||||
display: block;
|
display: block;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
margin: 0 auto;
|
margin: 2rem auto;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
&:after {
|
&:after {
|
||||||
border: 5px solid $c-green;
|
border: 5px solid $c-green;
|
||||||
@@ -145,4 +382,4 @@ export default {
|
|||||||
@keyframes load {
|
@keyframes load {
|
||||||
100% { transform: rotate(360deg); }
|
100% { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user