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": {
|
"node_modules/@ampproject/remapping": {
|
||||||
"version": "2.2.1",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
||||||
"integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
|
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/gen-mapping": "^0.3.0",
|
"@jridgewell/gen-mapping": "^0.3.5",
|
||||||
"@jridgewell/trace-mapping": "^0.3.9"
|
"@jridgewell/trace-mapping": "^0.3.24"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
@ -2859,9 +2859,9 @@
|
|||||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
|
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.24",
|
"version": "0.3.25",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.24.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
||||||
"integrity": "sha512-+VaWXDa6+l6MhflBvVXjIEAzb59nQ2JUK3bwRp2zRpPtU+8TFRy9Gg/5oIcNlkEL5PGlBFGfemUVvIgLnTzq7Q==",
|
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": "^3.1.0",
|
"@jridgewell/resolve-uri": "^3.1.0",
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
@ -6058,9 +6058,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001591",
|
"version": "1.0.30001593",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001593.tgz",
|
||||||
"integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==",
|
"integrity": "sha512-UWM1zlo3cZfkpBysd7AS+z+v007q9G1+fLTUU42rQnY6t2axoogPW/xol6T7juU5EUoOhML4WgBIdG+9yYqAjQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -7259,9 +7259,9 @@
|
|||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.689",
|
"version": "1.4.690",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.689.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.690.tgz",
|
||||||
"integrity": "sha512-GatzRKnGPS1go29ep25reM94xxd1Wj8ritU0yRhCJ/tr1Bg8gKnm6R9O/yPOhGQBoLMZ9ezfrpghNaTw97C/PQ=="
|
"integrity": "sha512-+2OAGjUx68xElQhydpcbqH50hE8Vs2K6TkAeLhICYfndb67CVH0UsZaijmRUE3rHlIxU1u0jxwhgVe6fK3YANA=="
|
||||||
},
|
},
|
||||||
"node_modules/elliptic": {
|
"node_modules/elliptic": {
|
||||||
"version": "6.5.4",
|
"version": "6.5.4",
|
||||||
@ -9670,9 +9670,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/html-entities": {
|
"node_modules/html-entities": {
|
||||||
"version": "2.4.0",
|
"version": "2.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz",
|
||||||
"integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==",
|
"integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -13708,9 +13708,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-polyfill-webpack-plugin/node_modules/type-fest": {
|
"node_modules/node-polyfill-webpack-plugin/node_modules/type-fest": {
|
||||||
"version": "4.10.3",
|
"version": "4.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.3.tgz",
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.11.0.tgz",
|
||||||
"integrity": "sha512-JLXyjizi072smKGGcZiAJDCNweT8J+AuRxmPZ1aG7TERg4ijx9REl8CNhbr36RV4qXqL1gO1FF9HL8OkVmmrsA==",
|
"integrity": "sha512-DPsoHKtnCUqqoB5Y4OPyat7ObSLz1XOkhHTmz+gOkz2p1xs+BBneTvHWriTwc313eozfBWh8b45EpaV3ZrrPPQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -12,19 +12,63 @@
|
|||||||
:title="episode.description"
|
:title="episode.description"
|
||||||
@click="modalEpisode = episode">
|
@click="modalEpisode = episode">
|
||||||
<template #actions>
|
<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>
|
<template #icon>
|
||||||
<PlayButton :size="20" />
|
<PlayButton :size="20" />
|
||||||
</template>
|
</template>
|
||||||
{{ t('repod', 'Play') }}
|
|
||||||
</NcActionButton>
|
</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>
|
<template #icon>
|
||||||
<StopButton :size="20" />
|
<StopButton :size="20" />
|
||||||
</template>
|
</template>
|
||||||
{{ t('repod', 'Stop') }}
|
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</template>
|
</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>
|
<template #icon>
|
||||||
<NcAvatar :display-name="episode.name"
|
<NcAvatar :display-name="episode.name"
|
||||||
:is-no-user="true"
|
:is-no-user="true"
|
||||||
@ -53,34 +97,45 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<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 Loading from '../Atoms/Loading.vue'
|
||||||
import Modal from '../Atoms/Modal.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 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 StopButton from 'vue-material-design-icons/Stop.vue'
|
||||||
import axios from '@nextcloud/axios'
|
import axios from '@nextcloud/axios'
|
||||||
import { decodeUrl } from '../../utils/url.js'
|
import { decodeUrl } from '../../utils/url.js'
|
||||||
import { formatLocaleDate } from '../../utils/time.js'
|
|
||||||
import { generateUrl } from '@nextcloud/router'
|
import { generateUrl } from '@nextcloud/router'
|
||||||
import { showError } from '@nextcloud/dialogs'
|
import { showError } from '@nextcloud/dialogs'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Episodes',
|
name: 'Episodes',
|
||||||
components: {
|
components: {
|
||||||
|
Download,
|
||||||
Loading,
|
Loading,
|
||||||
Modal,
|
Modal,
|
||||||
NcActionButton,
|
NcActionButton,
|
||||||
|
NcActionLink,
|
||||||
|
NcActions,
|
||||||
NcAvatar,
|
NcAvatar,
|
||||||
NcListItem,
|
NcListItem,
|
||||||
NcModal,
|
NcModal,
|
||||||
NcProgressBar,
|
NcProgressBar,
|
||||||
|
OpenInNew,
|
||||||
PlayButton,
|
PlayButton,
|
||||||
|
PlaylistPlay,
|
||||||
|
PlaylistRemove,
|
||||||
StopButton,
|
StopButton,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
episodes: [],
|
episodes: [],
|
||||||
loading: true,
|
loading: true,
|
||||||
|
loadingAction: false,
|
||||||
modalEpisode: null,
|
modalEpisode: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -127,11 +182,10 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
formatLocaleDate,
|
formatLocaleDate,
|
||||||
hasEnded(episode) {
|
hasEnded(episode) {
|
||||||
return episode.action && (episode.action.action === 'DELETE' || (
|
return episode.action
|
||||||
episode.action.position > 0
|
&& episode.action.position > 0
|
||||||
&& episode.action.total > 0
|
&& episode.action.total > 0
|
||||||
&& episode.action.position >= episode.action.total
|
&& episode.action.position >= episode.action.total
|
||||||
))
|
|
||||||
},
|
},
|
||||||
isCurrentEpisode(episode) {
|
isCurrentEpisode(episode) {
|
||||||
return this.currentEpisode && this.currentEpisode.url === episode.url
|
return this.currentEpisode && this.currentEpisode.url === episode.url
|
||||||
@ -142,6 +196,26 @@ export default {
|
|||||||
load(episode) {
|
load(episode) {
|
||||||
this.$store.dispatch('player/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>
|
</script>
|
||||||
|
@ -3,11 +3,10 @@
|
|||||||
:name="feed ? feed.title : url"
|
:name="feed ? feed.title : url"
|
||||||
:to="hash">
|
:to="hash">
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<NcActionButton @click="deleteSubscription">
|
<NcActionButton :name="t(`core`, 'Delete')" @click="deleteSubscription">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<Delete :size="20" />
|
<Delete :size="20" />
|
||||||
</template>
|
</template>
|
||||||
{{ t(`core`, 'Delete') }}
|
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</template>
|
</template>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
|
@ -37,3 +37,16 @@ export const formatEpisodeTimestamp = (date) => {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
export const formatLocaleDate = (date) => date.toLocaleDateString(undefined, { dateStyle: 'medium' })
|
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