added a set of new Vue files and rewrite download and search frontend;

This commit is contained in:
huangjx
2021-10-21 18:04:23 +08:00
parent d409bdfe71
commit 76f6a45ce7
10 changed files with 784 additions and 0 deletions

156
src/App.vue Normal file
View File

@@ -0,0 +1,156 @@
<template>
<section v-if="display.download" class="form-section" id="form-section">
<mainForm
@download="download"
@search="search"
@uploadfile="uploadFile"
:uris="uris"
></mainForm>
</section>
</template>
<script>
import mainForm from "./components/mainForm";
import toggleButton from "./components/toggleButton";
import helper from "./helper";
import { translate as t, translatePlural as n } from "@nextcloud/l10n";
import Http from "./http";
import nctable from "./ncTable";
const successCallback = (data, element) => {
if (!data) {
helper.message(t("ncdownloader", "Something must have gone wrong!"));
return;
}
if (data.hasOwnProperty("error")) {
helper.message(t("ncdownloader", data.error));
} else if (data.hasOwnProperty("message")) {
helper.message(t("ncdownloader", data.message));
} else if (data.hasOwnProperty("file")) {
helper.message(t("ncdownloader", "Downloading" + " " + data.file));
}
};
export default {
name: "mainApp",
data() {
return {
display: { download: true, search: false },
uris: {
ytd_url: helper.generateUrl("/apps/ncdownloader/youtube/new"),
aria2_url: helper.generateUrl("/apps/ncdownloader/new"),
search_url: helper.generateUrl("/apps/ncdownloader/search"),
upload_url: helper.generateUrl("/apps/ncdownloader/upload"),
},
};
},
created() {},
methods: {
download(event) {
let element = event.target;
let formWrapper = element.closest("form");
let formData = helper.getData(formWrapper);
let inputValue = formData["text-input-value"];
//formData.audioOnly = document.getElementById('audio-only').checked;
if (formData.type === "youtube-dl") {
formData["audio-only"] = formData["audio-only"] === "true";
}
if (!helper.isURL(inputValue) && !helper.isMagnetURI(inputValue)) {
helper.message(t("ncdownloader", inputValue + " is Invalid"));
return;
}
let url = formWrapper.getAttribute("action");
Http.getInstance(url)
.setData(formData)
.setHandler(function (data) {
successCallback(data, element);
})
.send();
helper.message(inputValue);
},
search(event, vm) {
let element = event.target;
let formWrapper = element.closest("form");
let formData = helper.getData(formWrapper);
let inputValue = formData["text-input-value"];
if (inputValue && inputValue.length < 2) {
helper.message(t("ncdownloader", "Please enter valid keyword!"));
vm.$data.loading = 0;
return;
}
helper.enabledPolling = 0;
nctable.getInstance().loading();
let url = formWrapper.getAttribute("action");
Http.getInstance(url)
.setData(formData)
.setHandler(function (data) {
if (data && data.title) {
vm.$data.loading = 0;
const tableInst = nctable.getInstance(data.title, data.row);
tableInst.actionLink = false;
tableInst.rowClass = "table-row-search";
tableInst.create();
}
})
.send();
},
uploadFile(event, vm) {
let element = event.target;
const files = element.files || event.dataTransfer.files;
if (files) {
let formWrapper = element.closest("form");
let url = formWrapper.getAttribute("action");
Http.getInstance(url)
.setHandler(function (data) {
successCallback(data, element);
})
.upload(files[0]);
}
return false;
},
},
components: {
mainForm,
toggleButton,
},
mounted() {},
};
</script>
<style lang="scss">
@import "css/dl_variables.scss";
$box-height: 110px;
#app-content-wrapper {
.ncdownloader-form-container {
position: relative;
width: 100%;
max-height: $box-height;
top: 0;
left: 0;
}
.ncdownloader-form-container.top-left {
width: 100%;
top: 0;
left: 0;
}
.form-section {
width: 100%;
display: flex;
flex-flow: column;
gap: 1.2em;
}
}
@media only screen and (max-width: 1024px) {
#app-content-wrapper {
#ncdownloader-form-container {
position: relative;
margin: 2px;
}
}
}
</style>

View File

@@ -0,0 +1,76 @@
<template>
<div v-bind:class="className">
<button class="btn btn-primary" v-if="loading ^ 1" @click.prevent="handler">
<slot>Download</slot>
</button>
<button class="bs-spinner" v-if="loading">
<span
class="spinner-border spinner-border-sm"
role="status"
aria-hidden="true"
disabled
></span
><span class="visually-hidden">Loading...</span>
</button>
</div>
</template>
<script>
export default {
data() {
return {
loading: 0,
};
},
methods: {
handler(event) {
console.log(this.enableLoading);
if (this.enableLoading) this.loading = 1;
this.$emit("clicked", event, this);
},
},
name: "actionButton",
props: {
className: String,
enableLoading: Boolean,
},
};
</script>
<style lang="scss">
@import "../css/dl_variables.scss";
button {
cursor: pointer;
border-radius: 0px;
}
.btn {
display: inline-block;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: center;
text-decoration: none;
vertical-align: middle;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
background-color: transparent;
border: 1px solid transparent;
padding: 0.375rem 0.75rem;
font-size: 1rem;
border-radius: 0.25rem;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,
border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.btn-primary {
color: #fff;
background-color: #7d7f7d;
border-color: #7d7f7d;
}
.btn-primary:hover {
color: #fff;
background-color: #0b5ed7;
border-color: #0a58ca;
}
</style>

209
src/components/mainForm.vue Normal file
View File

@@ -0,0 +1,209 @@
<template>
<form class="main-form" id="nc-vue-unified-form" :action="path">
<div class="options-group">
<button class="magnet-link http-link" @click.prevent="whichType('aria2')">
HTTP/MAGNET
</button>
<button class="youtube-dl-link" @click.prevent="whichType('youtube-dl')">
Youtube-dl
</button>
<button class="search-torrents" @click.prevent="whichType('search')">
Search Torrents
</button>
</div>
<div class="action-group">
<div class="download-input-container" v-if="inputType === 'download'">
<textInput :placeholder="placeholder" :dataType="downloadType"></textInput>
<div class="download-controls-container">
<div v-if="checkboxes" class="checkboxes">
<label for="audio-only" class="checkbox-label"
><input
type="checkbox"
id="audio-only"
v-model="checkedValue"
:value="checkedValue"
name="audio-only"
/><span>Audio Only</span></label
>
</div>
<actionButton className="download-button" @clicked="download"></actionButton>
<uploadFile @uploadfile="uploadFile" :path="uris.upload_url"></uploadFile>
</div>
</div>
<searchInput v-else @search="search"></searchInput>
</div>
</form>
</template>
<script>
import textInput from "./textInput";
import searchInput from "./searchInput.vue";
import actionButton from "./actionButton";
import uploadFile from "./uploadFile";
export default {
data() {
return {
checkedValue: false,
path: this.uris.aria2_url,
inputType: "download",
checkboxes: false,
downloadType: "aria2",
placeholder: "Paste your http/magnet link here",
};
},
components: {
textInput,
actionButton,
searchInput,
uploadFile,
},
computed: {
},
methods: {
whichType(type) {
this.downloadType = type;
if (type === "aria2") {
this.path = this.uris.aria2_url;
this.placeholder = "Paste your http/magnet link here";
} else if (type === "youtube-dl") {
this.placeholder = "Paste your video link here";
this.path = this.uris.ytd_url;
} else {
this.path = this.uris.search_url;
}
this.checkboxes = type === "youtube-dl" ? true : false;
this.inputType = type === "search" ? "search" : "download";
},
download(event) {
this.$emit("download", event);
},
search(event, vm) {
this.$emit("search", event, vm);
},
uploadFile(event, vm) {
this.$emit("uploadfile", event, vm);
},
},
mounted() {
console.log(this.uris);
},
name: "mainForm",
props: {
uris: Object,
uri: String,
},
};
</script>
<style lang="scss">
@import "../css/dl_variables.scss";
#nc-vue-unified-form {
display: flex;
width: 100%;
height: $column-height;
.action-group {
width: 100%;
}
.options-group,
.action-group > div {
display: flex;
width: auto;
height: 100%;
position: relative;
}
.action-group {
flex: 2;
& > div {
border: 1px solid #565687;
& > div,
& > select {
height: 100%;
display: flex;
padding: 0px;
margin: 0px;
}
& > div[class$="-controls-container"] {
display: flex;
& div,
& select {
height: 100%;
}
}
}
}
.checkboxes {
border-radius: 0%;
}
.download-button {
height: $column-height;
.btn-primary {
color: #fff;
background-color: #2d3f59;
border-color: #1e324f;
border-radius: 0%;
}
.btn-primary:hover {
background-color: #191a16;
}
}
.magnet-link,
.choose-file {
background-color: #9f9fcd;
border-radius: 15px 0px 0px 15px;
}
.youtube-dl-link {
background-color: #c4c4d9;
}
.checkboxes {
background-color: #c4c4d9;
padding: 5px 1px;
}
input,
button {
margin: 0px;
border: 0px;
padding: 10px;
}
button {
white-space: nowrap;
}
}
@media only screen and (max-width: 1024px) {
#nc-vue-unified-form {
display: flex;
flex-flow: column;
row-gap: 10px;
height: $column-height * 3 + 10;
.options-group,
.action-group > div {
display: flex;
width: 100%;
height: $column-height;
}
.action-group > div {
border: 0px;
flex-flow: column nowrap;
& > div {
margin: 5px 1px;
}
& > div[class$="-controls-container"] {
display: flex;
justify-content: center;
}
}
.options-group {
& > button {
width: calc(100% / 3);
}
}
}
}
</style>

View File

@@ -0,0 +1,52 @@
<template>
<div class="search-input" id="nc-vue-search-input">
<textInput :placeholder="placeholder" dataType="search"></textInput>
<div class="search-controls-container">
<div id="select-value-search">
<select :value="selected">
<option
v-for="(option, key) in selectOptions"
v-bind:key="key"
:value="option.name"
>
{{ option.label }}
</option>
</select>
</div>
<actionButton className="search-button" :enableLoading="true" @clicked="search"
>Search</actionButton
>
</div>
</div>
</template>
<script>
import textInput from "./textInput";
import actionButton from "./actionButton";
export default {
data() {
return {
placeholder: "Enter keyword to search",
selected: "TPB",
selectOptions: [
{ name: "TPB", label: "THEPIRATEBAY" },
{ name: "bitSearch", label: "BITSEARCH" },
],
};
},
components: {
textInput,
actionButton,
},
methods: {
search(event, btnVm) {
this.$emit("search", event, btnVm);
},
},
name: "searchInput",
props: [],
};
</script>
<style scoped lang="scss">
@import "../css/dl_variables.scss";
</style>

View File

@@ -0,0 +1,47 @@
<template>
<div class="text-input-link" id="text-input-link">
<input
type="text"
name="text-input-value"
id="text-input-value"
v-bind:placeholder="placeholder"
v-bind:data-type="dataType"
/>
</div>
</template>
<script>
export default {
name: "textinput",
props: {
placeholder: String,
dataType: String,
},
};
</script>
<style scoped lang="scss">
@import "../css/dl_variables.scss";
#text-input-link {
position: relative;
width: 100%;
flex: 2;
input {
width: 100%;
height: 100%;
margin: 0px;
background-color: $bg-color;
}
}
@media only screen and (max-width: 1024px) {
#text-input-link {
width: 100%;
& > input {
width: 100%;
}
& > input:focus {
border-color: #86b7fe;
outline: 0;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
}
}
</style>

View File

@@ -0,0 +1,141 @@
<template>
<label :for="name" :class="{ active: isActive }" class="toggle-button">
<span class="toggle-label">{{ text }}</span>
<input
type="checkbox"
:disabled="disabled"
:id="name"
:name="name"
:value="value"
v-model="inputValue"
/>
<span class="toggle-switch"></span>
</label>
</template>
<script>
export default {
props: {
disabled: {
type: Boolean,
default: false,
},
enabledText: {
type: String,
default: "On",
},
disabledText: {
type: String,
default: "Off",
},
name: {
type: String,
default: "check-button",
},
defaultStatus: {
type: Boolean,
default: false,
},
},
data() {
return {
status: this.defaultStatus,
};
},
watch: {
defaultStatus() {
this.status = Boolean(this.defaultStatus);
},
},
computed: {
isActive() {
return this.status;
},
text() {
return this.status ? this.disabledText : this.enabledText;
},
inputValue: {
get() {
return this.status;
},
set(value) {
this.status = value;
this.$emit("change", value);
},
},
},
};
</script>
<style scoped lang="scss">
$toggle-height: 25px;
$toggle-width: 45px;
$bg-color: #e5e5ee;
.toggle-button,
.toggle-label {
user-select: none;
cursor: pointer;
}
.toggle-button,
.toggle-label,
.toggle-switch {
vertical-align: middle;
}
.toggle-button input[type="checkbox"] {
opacity: 0;
position: absolute;
width: 1px;
height: 1px;
}
.toggle-button .toggle-switch {
display: inline-block;
height: $toggle-height;
border-radius: $toggle-height / 3;
width: $toggle-width;
background: $bg-color;
box-shadow: inset 0 0 1px #b1bbc7;
position: relative;
margin-left: 6px;
transition: all 0.25s;
}
.toggle-button .toggle-switch::after {
content: "";
position: absolute;
display: block;
height: $toggle-height;
width: $toggle-width / 2;
border-radius: 50%;
left: 0;
transform: translateX(0);
transition: all 0.25s cubic-bezier(0.5, -0.6, 0.5, 1.6);
}
.toggle-button .toggle-switch::after {
background: #ffffff;
box-shadow: 0 0 1px #666;
}
.active .toggle-switch {
background: #adedcb;
box-shadow: inset 0 0 1px #adedcb;
}
.active .toggle-switch::after {
transform: translateX($toggle-width / 2);
background: #488c68;
box-shadow: 0 0 1px #53b883;
}
</style>

61
src/components/upForm.vue Normal file
View File

@@ -0,0 +1,61 @@
<template>
<form class="upload-form" id="nc-vue-upload-form" :action="path">
<div class="torrent-file-header"></div>
<div class="torrent-file-label">Torrents</div>
<div class="icon-upload uploadfile">
<input type="file" name="torrentfile" id="torrentfile" @change="handler" />
</div>
</form>
</template>
<script>
export default {
name: "uploadForm",
methods: {
handler(event) {
this.$emit("uploadfile", event, this);
},
},
props: ["path"],
};
</script>
<style scoped lang="scss">
@import "../css/dl_variables.scss";
#nc-vue-upload-form {
display: flex;
position: relative;
row-gap: 0px;
padding-right: 2px;
border-radius: 15px 0px 0px 15px;
width: 100%;
height: $column-height;
.torrent-file-header {
width: 45px;
height: 100%;
background: $bg-color url("../../img/folder.svg") bottom left no-repeat;
background-size: 40px 40px;
background-clip: border-box;
}
.icon-upload.uploadfile {
background-color: #9f9fad;
height: 100%;
& input[type="file"] {
cursor: pointer;
font-size: 16px;
opacity: 0;
position: relative;
width: $upload-width;
z-index: 20;
}
}
.torrent-file-label {
padding-top: 10px;
padding-right: 4px;
height: 100%;
background-color: $bg-color;
}
}
</style>

View File

@@ -0,0 +1,38 @@
<template>
<form class="upload-file-form" id="nc-vue-upload-file-form" :action="path">
<div class="icon-upload fileinput">
<input type="file" name="torrentfile" id="torrentfile" accept=".torrent" @change="handler" />
</div>
</form>
</template>
<script>
export default {
name: "uploadFile",
methods: {
handler(event) {
this.$emit("uploadfile", event, this);
},
},
props: ["path"],
};
</script>
<style scoped lang="scss">
@import "../css/dl_variables.scss";
#nc-vue-upload-file-form {
.icon-upload {
background-image: url("../../img/upload.svg");
}
.icon-upload.fileinput {
background-color: #9f9fad;
height: 100%;
& input[type="file"] {
cursor: pointer;
font-size: 16px;
opacity: 0;
position: relative;
width: $upload-width;
z-index: 20;
}
}
}
</style>

View File

@@ -0,0 +1,3 @@
$bg-color: #e5e5ee;
$column-height: 45px;
$upload-width:75px;