feat: ✨ add actions to episodes view
This commit is contained in:
parent
5a510016a7
commit
e52d20ffbc
40
package-lock.json
generated
40
package-lock.json
generated
@ -43,12 +43,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
|
||||
"integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
||||
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
@ -2859,9 +2859,9 @@
|
||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.24",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.24.tgz",
|
||||
"integrity": "sha512-+VaWXDa6+l6MhflBvVXjIEAzb59nQ2JUK3bwRp2zRpPtU+8TFRy9Gg/5oIcNlkEL5PGlBFGfemUVvIgLnTzq7Q==",
|
||||
"version": "0.3.25",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
||||
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
@ -6058,9 +6058,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001591",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz",
|
||||
"integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==",
|
||||
"version": "1.0.30001593",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001593.tgz",
|
||||
"integrity": "sha512-UWM1zlo3cZfkpBysd7AS+z+v007q9G1+fLTUU42rQnY6t2axoogPW/xol6T7juU5EUoOhML4WgBIdG+9yYqAjQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -7259,9 +7259,9 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.689",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.689.tgz",
|
||||
"integrity": "sha512-GatzRKnGPS1go29ep25reM94xxd1Wj8ritU0yRhCJ/tr1Bg8gKnm6R9O/yPOhGQBoLMZ9ezfrpghNaTw97C/PQ=="
|
||||
"version": "1.4.690",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.690.tgz",
|
||||
"integrity": "sha512-+2OAGjUx68xElQhydpcbqH50hE8Vs2K6TkAeLhICYfndb67CVH0UsZaijmRUE3rHlIxU1u0jxwhgVe6fK3YANA=="
|
||||
},
|
||||
"node_modules/elliptic": {
|
||||
"version": "6.5.4",
|
||||
@ -9670,9 +9670,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/html-entities": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz",
|
||||
"integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==",
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz",
|
||||
"integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -13708,9 +13708,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/node-polyfill-webpack-plugin/node_modules/type-fest": {
|
||||
"version": "4.10.3",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.3.tgz",
|
||||
"integrity": "sha512-JLXyjizi072smKGGcZiAJDCNweT8J+AuRxmPZ1aG7TERg4ijx9REl8CNhbr36RV4qXqL1gO1FF9HL8OkVmmrsA==",
|
||||
"version": "4.11.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.11.0.tgz",
|
||||
"integrity": "sha512-DPsoHKtnCUqqoB5Y4OPyat7ObSLz1XOkhHTmz+gOkz2p1xs+BBneTvHWriTwc313eozfBWh8b45EpaV3ZrrPPQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
|
@ -12,19 +12,63 @@
|
||||
:title="episode.description"
|
||||
@click="modalEpisode = episode">
|
||||
<template #actions>
|
||||
<NcActionButton v-if="!isCurrentEpisode(episode)" @click="load(episode)">
|
||||
<NcActionButton v-if="!isCurrentEpisode(episode)"
|
||||
:name="t('repod', 'Play')"
|
||||
:title="t('repod', 'Play')"
|
||||
@click="load(episode)">
|
||||
<template #icon>
|
||||
<PlayButton :size="20" />
|
||||
</template>
|
||||
{{ t('repod', 'Play') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="isCurrentEpisode(episode)" @click="load(null)">
|
||||
<NcActionButton v-if="isCurrentEpisode(episode)"
|
||||
:name="t('repod', 'Stop')"
|
||||
:title="t('repod', 'Stop')"
|
||||
@click="load(null)">
|
||||
<template #icon>
|
||||
<StopButton :size="20" />
|
||||
</template>
|
||||
{{ t('repod', 'Stop') }}
|
||||
</NcActionButton>
|
||||
</template>
|
||||
<template #extra>
|
||||
<NcActions>
|
||||
<NcActionButton v-if="episode.duration && !hasEnded(episode)"
|
||||
:disabled="loadingAction"
|
||||
:name="t('repod', 'Mark as read')"
|
||||
:title="t('repod', 'Mark as read')"
|
||||
@click="markAs(episode, true)">
|
||||
<template #icon>
|
||||
<PlaylistPlay :size="20" />
|
||||
</template>
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="episode.duration && hasEnded(episode)"
|
||||
:disabled="loadingAction"
|
||||
:name="t('repod', 'Mark as unread')"
|
||||
:title="t('repod', 'Mark as unread')"
|
||||
@click="markAs(episode, false)">
|
||||
<template #icon>
|
||||
<PlaylistRemove :size="20" />
|
||||
</template>
|
||||
</NcActionButton>
|
||||
<NcActionLink v-if="episode.link"
|
||||
:href="episode.link"
|
||||
:name="t('repod', 'Episode webpage')"
|
||||
target="_blank"
|
||||
:title="t('repod', 'Episode webpage')">
|
||||
<template #icon>
|
||||
<OpenInNew :size="20" />
|
||||
</template>
|
||||
</NcActionLink>
|
||||
<NcActionLink v-if="episode.url"
|
||||
:href="episode.url"
|
||||
:name="t('repod', 'Download')"
|
||||
target="_blank"
|
||||
:title="t('repod', 'Download')">
|
||||
<template #icon>
|
||||
<Download :size="20" />
|
||||
</template>
|
||||
</NcActionLink>
|
||||
</NcActions>
|
||||
</template>
|
||||
<template #icon>
|
||||
<NcAvatar :display-name="episode.name"
|
||||
:is-no-user="true"
|
||||
@ -53,34 +97,45 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { NcActionButton, NcAvatar, NcListItem, NcModal, NcProgressBar } from '@nextcloud/vue'
|
||||
import { NcActionButton, NcActionLink, NcActions, NcAvatar, NcListItem, NcModal, NcProgressBar } from '@nextcloud/vue'
|
||||
import { durationToSeconds, formatEpisodeTimestamp, formatLocaleDate } from '../../utils/time.js'
|
||||
import Download from 'vue-material-design-icons/Download.vue'
|
||||
import Loading from '../Atoms/Loading.vue'
|
||||
import Modal from '../Atoms/Modal.vue'
|
||||
import OpenInNew from 'vue-material-design-icons/OpenInNew.vue'
|
||||
import PlayButton from 'vue-material-design-icons/Play.vue'
|
||||
import PlaylistPlay from 'vue-material-design-icons/PlaylistPlay.vue'
|
||||
import PlaylistRemove from 'vue-material-design-icons/PlaylistRemove.vue'
|
||||
import StopButton from 'vue-material-design-icons/Stop.vue'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { decodeUrl } from '../../utils/url.js'
|
||||
import { formatLocaleDate } from '../../utils/time.js'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
|
||||
export default {
|
||||
name: 'Episodes',
|
||||
components: {
|
||||
Download,
|
||||
Loading,
|
||||
Modal,
|
||||
NcActionButton,
|
||||
NcActionLink,
|
||||
NcActions,
|
||||
NcAvatar,
|
||||
NcListItem,
|
||||
NcModal,
|
||||
NcProgressBar,
|
||||
OpenInNew,
|
||||
PlayButton,
|
||||
PlaylistPlay,
|
||||
PlaylistRemove,
|
||||
StopButton,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
episodes: [],
|
||||
loading: true,
|
||||
loadingAction: false,
|
||||
modalEpisode: null,
|
||||
}
|
||||
},
|
||||
@ -127,11 +182,10 @@ export default {
|
||||
methods: {
|
||||
formatLocaleDate,
|
||||
hasEnded(episode) {
|
||||
return episode.action && (episode.action.action === 'DELETE' || (
|
||||
episode.action.position > 0
|
||||
return episode.action
|
||||
&& episode.action.position > 0
|
||||
&& episode.action.total > 0
|
||||
&& episode.action.position >= episode.action.total
|
||||
))
|
||||
},
|
||||
isCurrentEpisode(episode) {
|
||||
return this.currentEpisode && this.currentEpisode.url === episode.url
|
||||
@ -142,6 +196,26 @@ export default {
|
||||
load(episode) {
|
||||
this.$store.dispatch('player/load', episode)
|
||||
},
|
||||
async markAs(episode, read) {
|
||||
try {
|
||||
this.loadingAction = true
|
||||
await axios.post(generateUrl('/apps/gpoddersync/episode_action/create'), [{
|
||||
podcast: this.url,
|
||||
episode: episode.url,
|
||||
guid: episode.guid,
|
||||
action: 'play',
|
||||
timestamp: formatEpisodeTimestamp(new Date()),
|
||||
started: episode.action ? episode.action.started : 0,
|
||||
position: read ? durationToSeconds(episode.duration) : 0,
|
||||
total: durationToSeconds(episode.duration),
|
||||
}])
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
showError(t('repod', 'Could not change the status of the episode'))
|
||||
} finally {
|
||||
this.loadingAction = false
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -3,11 +3,10 @@
|
||||
:name="feed ? feed.title : url"
|
||||
:to="hash">
|
||||
<template #actions>
|
||||
<NcActionButton @click="deleteSubscription">
|
||||
<NcActionButton :name="t(`core`, 'Delete')" @click="deleteSubscription">
|
||||
<template #icon>
|
||||
<Delete :size="20" />
|
||||
</template>
|
||||
{{ t(`core`, 'Delete') }}
|
||||
</NcActionButton>
|
||||
</template>
|
||||
<template #icon>
|
||||
|
@ -37,3 +37,16 @@ export const formatEpisodeTimestamp = (date) => {
|
||||
* @return {string}
|
||||
*/
|
||||
export const formatLocaleDate = (date) => date.toLocaleDateString(undefined, { dateStyle: 'medium' })
|
||||
|
||||
/**
|
||||
* Returns the number of seconds from a duration feed's entry
|
||||
* @param {string} duration The duration feed's entry
|
||||
* @return {number}
|
||||
*/
|
||||
export const durationToSeconds = (duration) => {
|
||||
const splitDuration = duration.split(':').reverse()
|
||||
let seconds = splitDuration[0]
|
||||
seconds += (splitDuration.length > 1) ? splitDuration[1] * 60 : 0
|
||||
seconds += (splitDuration.length > 2) ? splitDuration[2] * 60 * 60 : 0
|
||||
return seconds
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user