Stashr
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
stashr/stashr/templates/reading_list_page.html

301 lines
11 KiB

{% extends "base.html" %}
{% block header_script_files %}
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/sortablejs@1.8.4/Sortable.min.js"></script>
{% endblock %}
{% block header %}
{{ emit_tep('reading_list_page_header') }}
{% endblock %}
{% block content %}
<div id="app">
<issues v-bind:issues='issues' ref="issues"></issues>
<modals v-bind:issue='issue'></modals>
</div>
{% endblock %}
{% block modals %}
{{ emit_tep('reading_list_page_modals') }}
{% endblock %}
{% block button_container %}
<button type="button" class="btn btn-outline-danger btn-circle btn-md" data-bs-toggle="tooltip" data-bs-placement="top" title="Clear Reading List" onclick="app.clearList()">
<i class="text-white fas fa-2x fa-minus"></i>
</button>
{{ emit_tep('reading_list_page_button_container') }}
{% endblock %}
{% block script %}
Vue.directive('sortable', {
inserted (el, binding, vnode) {
console.log(binding.value);
let options = binding.value;
options.onUpdate = (e) => vnode.data.on.sorted(e);
const sortable = Sortable.create(el, binding.value);
}
})
Vue.component('modals', {
props: ['issue'],
template: `
<div>
<div class="modal" id="modalRead" tabindex="-1" role="dialog" aria-labelledby="readModal" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="notesModalTitle">
[[ issue.volume.volume_name ]] #[[ issue.issue_number ]]
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body text-center">
<a :href="'/read/'+issue.issue_id" id="readRead" class="btn btn-success">
<i class="fas fa-book-open"></i>
Read
</a>
{% if (current_user.role.lower() == 'admin') or
(current_user.role.lower() == 'librarian') or
(current_user.role.lower() == 'patron') %}
<a :href="'/api/downloads/'+issue.issue_id" id="readDownload" class="btn btn-success">
<i class="fas fa-download"></i>
Download
</a>
{% endif %}
<hr />
<button id="actionToggle" class="btn btn-info my-1" @click="toggleRead" data-bs-dismiss="modal">
<i class="fas fa-check"></i>
Toggle Read
</button>
<button id="readRemove" class="btn btn-danger" @click="removeIssue" data-bs-dismiss="modal">
<i class="fas fa-minus"></i>
Remove From Reading List
</button>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
{{ emit_tep('reading_list_page_issue_modals') }}
`,
methods: {
toggleRead() {
app.$refs.issues.$children.find(child => { return child.$vnode.key == this.issue.issue_id }).toggleRead()
},
removeIssue() {
app.removeIssue(this.issue.issue_id)
},
},
delimiters: ["[[","]]"]
})
Vue.component('issues', {
props: ['issues'],
template: `
<div>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-top shadow">
<div class="row d-flex flex-nowrap w-100 m-0 p-3 bg-mine">
<input type="text" v-model="search" class="form-control flex-shrink-1" placeholder="Search Reading List..." />
<a class="fa-stack p-0 text-white">
<i class="fas fa-bars fa-stack-1x" data-bs-toggle="offcanvas" href="#offcanvasRight" role="button"></i>
</a>
</div>
</div>
<ul class="d-flex flex-wrap w-100 m-0 p-0 justify-content-center" v-sortable="$options.sortOptions" @sorted='handleSorted'>
<issue
v-for="issue in filteredList"
v-bind:issue="issue"
v-bind:key="issue.issue_id"
></issue>
</ul>
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasRight" aria-labelledby="offcanvasRightLabel">
<div class="offcanvas-header">
<h5 id="offcanvasRightLabel">Reading List</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<div class="text-center w-100 m-0 p-3">
<button type="button" class="btn btn-danger w-100" data-bs-toggle="tooltip" data-bs-placement="top" title="Clear Reading List" onclick="app.clearList()">
<i class="fas fa-minus"></i>
Clear Reading List
</button>
</div>
</div>
</div>
</div>
`,
data() { return { loading: true, search: '', } },
computed: {
filteredList() {
return this.issues.filter(issue => {
return issue.volume.volume_name.toLowerCase().includes(this.search.toLowerCase())
})
},
},
methods: {
handleSorted(event) {
app.handleSorted(event)
},
},
sortOptions: {
draggable: '.js-sortable-block',
handle: '.js-drag-handle',
delay: 300,
delayOnTouchOnly: true
},
delimiters: ["[[","]]"]
})
Vue.component('issue', {
props: ['issue'],
template: `
<li class='stashr-item_container m-2 js-sortable-block'
@mouseover="hover = true"
@mouseleave="hover = false"
>
<div class="stashr-poster_container border border-dark rounded-3 js-drag-handle">
<div class="stashr-badge_top_left bg-dark">
<span class="fa-stack p-0">
<i class="fas fa-arrows-alt fa-stack-1x text-white"></i>
</span>
</div>
<div class="stashr-badge_bottom_right bg-dark">
<span class="fa-stack p-0" v-on:click="toggleRead">
<i class="fas fa-eye fa-stack-1x" :class="statusRead"></i>
<i class="fas fa-slash fa-stack-1x text-danger" v-if="!this.issue.read_status[0].read_status"></i>
</span>
</div>
<div data-bs-toggle="modal" data-bs-target="#modalRead" v-on:click="this.changeModal">
<img class="stashr-background w-100" loading="eager" src="/static/assets/cover.svg" />
<img class="stashr-poster w-100" loading="lazy" v-bind:src="'/images/issues/'+issue.issue_id+'.jpg'" />
</div>
<div class="stashr-overlay_bottom w-100 text-center shadow" v-if="hover" data-bs-toggle="modal" data-bs-target="#modalRead" v-on:click="this.changeModal">
[[ issue.volume.volume_name ]] #[[ issue.issue_number ]]
</div>
</div>
</li>
`,
computed: {
statusRead() {
let classname = 'text-danger';
try {
if (this.issue.read_status[0].read_status) {
classname = 'text-success';
}
} finally {
return classname;
}
},
},
methods: {
toggleRead() {
axios.put('{{ url_for('api.api_put_single_issue', issue_id='ISSUEID') }}'.replace('ISSUEID', this.issue.issue_id), {
data: {
read_status: !this.issue.read_status[0].read_status
}
})
.then(res => {
if(res.data.status_code == '200') {
stashrToast('Toggled Read Status', 'success');
this.issue.read_status[0].read_status = !this.issue.read_status[0].read_status;
} else {
stashrToast(res.data.message, 'error')
}
})
.catch(err => console.log(err))
},
changeModal() {
app.changeModal(this.issue)
},
},
data() { return { hover: false } },
delimiters: ["[[","]]"]
})
var app = new Vue({
el: '#app',
data: {
issues: [],
issue: [],
},
created() {
this.getIssues()
},
methods: {
getIssues() {
axios.get('{{ url_for('api.api_get_reading_list') }}', {
params: {
offset: this.issues.length
}
})
.then( res => {
if(res.data.number_of_page_results > 0) {
res.data.results.forEach(result => {
this.issues.push(result)
})
if(this.issues.length < res.data.number_of_total_results) {
this.getIssues()
}
}
this.issue = this.issues[0]
})
.catch()
},
removeIssue(id) {
axios.delete('{{ url_for('api.api_delete_reading_list_single', issue_id='ISSUEID') }}'.replace('ISSUEID', id))
.then(
this.issues = this.issues.filter(issue => issue.issue_id !== id)
)
.catch(err => console.log(err))
},
handleSorted(event) {
axios.put('{{ url_for('api.api_put_reading_list') }}', {
data: {
old_index: event.oldIndex,
new_index: event.newIndex
}
})
.then(res => {
if(res.data.status_code == '200') {
stashrToast('Updated Order', 'success')
} else {
stashrToast(res.data.message, 'error')
}
})
.catch(err => console.log(err))
},
clearList() {
axios.delete('{{ url_for('api.api_delete_reading_list') }}')
.then(res => {
if(res.data.status_code == '200') {
stashrToast('Cleared Reading List', 'success')
this.issues = []
} else {
stashrToast(res.data.message, 'error')
}
})
.catch(err => console.log(err))
},
changeModal(issue) {
this.issue = issue
},
},
delimiters: ["[[","]]"]
})
{{ emit_tep('reading_list_page_script') }}
{% endblock %}