select all episodes (fix #81) #220

Merged
Xefir merged 4 commits from multiselect into main 2024-12-11 14:01:21 +00:00
4 changed files with 210 additions and 66 deletions
Showing only changes of commit 4163f10c81 - Show all commits

View File

@ -80,7 +80,7 @@
:url="episode.image" :url="episode.image"
@click.stop="$emit('select', episode)"> @click.stop="$emit('select', episode)">
<template #icon> <template #icon>
<CheckIcon v-if="selected" class="progress" :size="20" /> <CheckIcon v-if="episode.selected" class="progress" :size="20" />
</template> </template>
</NcAvatar> </NcAvatar>
</template> </template>
@ -161,10 +161,6 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
selected: {
type: Boolean,
default: false,
},
url: { url: {
type: String, type: String,
required: true, required: true,

View File

@ -2,29 +2,112 @@
<div> <div>
<Loading v-if="loading" /> <Loading v-if="loading" />
<ul v-if="!loading"> <ul v-if="!loading">
<NcListItem
v-if="!episodes.every((e) => !e.selected)"
:active="true"
:force-display-actions="true"
:name="
n(
'repod',
'%n episode selected',
'%n episodes selected',
episodes.filter((e) => e.selected).length,
)
"
:one-line="true">
<template #actions>
<NcActionButton
v-if="
episodes
.filter((e) => e.selected)
.filter((e) => !hasEnded(e)).length
"
:aria-label="t('repod', 'Read all')"
:disabled="
!episodes
.filter((e) => e.selected)
.every((e) => e.duration)
"
:name="t('repod', 'Read all')"
:title="t('repod', 'Read all')"
@click="markAs(true)">
<template #icon>
<PlaylistPlayIcon :size="20" />
</template>
</NcActionButton>
<NcActionButton
v-if="
episodes
.filter((e) => e.selected)
.every((e) => hasEnded(e))
"
:aria-label="t('repod', 'Unread all')"
:disabled="
!episodes
.filter((e) => e.selected)
.every((e) => e.duration)
"
:name="t('repod', 'Unread all')"
:title="t('repod', 'Unread all')"
@click="markAs(false)">
<template #icon>
<PlaylistRemoveIcon :size="20" />
</template>
</NcActionButton>
</template>
<template #icon>
<NcAvatar
:display-name="t('repod', 'Select all')"
:is-no-user="true">
<template #icon>
<SelectAllIcon
v-if="
episodes.filter((e) => e.selected).length <
episodes.length
"
class="progress"
:size="20"
@click="select(true)" />
<SelectRemoveIcon
v-if="
episodes.filter((e) => e.selected).length >=
episodes.length
"
class="progress"
:size="20"
@click="select(false)" />
</template>
</NcAvatar>
</template>
</NcListItem>
<Episode <Episode
v-for="episode in filteredEpisodes" v-for="episode in filteredEpisodes"
:key="episode.guid" :key="episode.guid"
:display-actions="!selected.length" :display-actions="episodes.every((e) => !e.selected)"
:episode="episode" :episode="episode"
:selected="selected.includes(episode)"
:url="url" :url="url"
@select="select" /> @select="episode.selected = !episode.selected" />
</ul> </ul>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { NcActionButton, NcAvatar, NcListItem } from '@nextcloud/vue'
import { durationToSeconds, formatEpisodeTimestamp } from '../../utils/time.ts'
import { hasEnded, isListening } from '../../utils/status.ts' import { hasEnded, isListening } from '../../utils/status.ts'
import { n, t } from '@nextcloud/l10n'
import Episode from './Episode.vue' import Episode from './Episode.vue'
import type { EpisodeInterface } from '../../utils/types.ts' import type { EpisodeInterface } from '../../utils/types.ts'
import Loading from '../Atoms/Loading.vue' import Loading from '../Atoms/Loading.vue'
import PlaylistPlayIcon from 'vue-material-design-icons/PlaylistPlay.vue'
import PlaylistRemoveIcon from 'vue-material-design-icons/PlaylistRemove.vue'
import SelectAllIcon from 'vue-material-design-icons/SelectAll.vue'
import SelectRemoveIcon from 'vue-material-design-icons/SelectRemove.vue'
import axios from '@nextcloud/axios' import axios from '@nextcloud/axios'
import { decodeUrl } from '../../utils/url.ts' import { decodeUrl } from '../../utils/url.ts'
import { generateUrl } from '@nextcloud/router' import { generateUrl } from '@nextcloud/router'
import { mapState } from 'pinia' import { mapState } from 'pinia'
import { showError } from '../../utils/toast.ts' import { showError } from '../../utils/toast.ts'
import { t } from '@nextcloud/l10n'
import { usePlayer } from '../../store/player.ts' import { usePlayer } from '../../store/player.ts'
import { useSettings } from '../../store/settings.ts' import { useSettings } from '../../store/settings.ts'
@ -33,11 +116,17 @@ export default {
components: { components: {
Episode, Episode,
Loading, Loading,
NcActionButton,
NcAvatar,
NcListItem,
PlaylistPlayIcon,
PlaylistRemoveIcon,
SelectAllIcon,
SelectRemoveIcon,
}, },
data: () => ({ data: () => ({
episodes: [] as EpisodeInterface[], episodes: [] as EpisodeInterface[],
loading: true, loading: true,
selected: [] as EpisodeInterface[],
}), }),
computed: { computed: {
...mapState(usePlayer, ['episode']), ...mapState(usePlayer, ['episode']),
@ -92,13 +181,48 @@ export default {
methods: { methods: {
hasEnded, hasEnded,
isListening, isListening,
select(episode: EpisodeInterface) { n,
if (this.selected.includes(episode)) { t,
this.selected = this.selected.filter((e) => e !== episode) async markAs(read: boolean) {
} else { try {
this.selected.push(episode) this.episodes = this.episodes.map((episode) => {
if (episode.selected) {
episode.action = {
podcast: this.url,
episode: episode.url,
guid: episode.guid,
action: 'play',
timestamp: formatEpisodeTimestamp(new Date()),
started: episode.action?.started || 0,
position: read
? durationToSeconds(episode.duration || '')
: 0,
total: durationToSeconds(episode.duration || ''),
}
}
return episode
})
await axios.post(
generateUrl('/apps/gpoddersync/episode_action/create'),
this.episodes.filter((e) => e.selected).map((e) => e.action),
)
} catch (e) {
console.error(e)
showError(t('repod', 'Could not change the status of the episode'))
} }
}, },
select(all: boolean) {
this.episodes = this.episodes.map((episode) => {
episode.selected = all
return episode
})
},
}, },
} }
</script> </script>
<style scoped>
.progress {
margin-top: 0.4rem;
}
</style>

View File

@ -28,6 +28,7 @@ export interface EpisodeInterface {
} }
duration?: string duration?: string
action?: EpisodeActionInterface action?: EpisodeActionInterface
selected?: boolean
} }
export interface FiltersInterface { export interface FiltersInterface {

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Nextcloud 3.14159\n" "Project-Id-Version: Nextcloud 3.14159\n"
"Report-Msgid-Bugs-To: translations\\@example.com\n" "Report-Msgid-Bugs-To: translations\\@example.com\n"
"POT-Creation-Date: 2024-11-12 20:57+0000\n" "POT-Creation-Date: 2024-12-11 10:38+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -106,7 +106,7 @@ msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:16 #: /app/specialVueFakeDummyForL10nScript.js:16
#: /app/specialVueFakeDummyForL10nScript.js:17 #: /app/specialVueFakeDummyForL10nScript.js:17
#: /app/specialVueFakeDummyForL10nScript.js:32 #: /app/specialVueFakeDummyForL10nScript.js:41
msgid "Play" msgid "Play"
msgstr "" msgstr ""
@ -127,143 +127,166 @@ msgid "Open website"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:27 #: /app/specialVueFakeDummyForL10nScript.js:27
#: /app/specialVueFakeDummyForL10nScript.js:36
msgid "Could not change the status of the episode" msgid "Could not change the status of the episode"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:28 #: /app/specialVueFakeDummyForL10nScript.js:28
#: /app/specialVueFakeDummyForL10nScript.js:29 #: /app/specialVueFakeDummyForL10nScript.js:29
msgid "Could not fetch episodes"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:30 #: /app/specialVueFakeDummyForL10nScript.js:30
msgid "Rewind 10 seconds" msgid "Read all"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:31 #: /app/specialVueFakeDummyForL10nScript.js:31
msgid "Pause" #: /app/specialVueFakeDummyForL10nScript.js:32
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:33 #: /app/specialVueFakeDummyForL10nScript.js:33
msgid "Fast forward 30 seconds" msgid "Unread all"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:34 #: /app/specialVueFakeDummyForL10nScript.js:34
msgid "Select all"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:35 #: /app/specialVueFakeDummyForL10nScript.js:35
#: /app/specialVueFakeDummyForL10nScript.js:36 #: /app/specialVueFakeDummyForL10nScript.js:38
msgid "Mute" msgid "Could not fetch episodes"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:37 #: /app/specialVueFakeDummyForL10nScript.js:37
msgid "Unmute" msgid "%n episode selected"
msgstr "" msgid_plural "%n episodes selected"
msgstr[0] ""
#: /app/specialVueFakeDummyForL10nScript.js:38 msgstr[1] ""
msgid "Export subscriptions"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:39 #: /app/specialVueFakeDummyForL10nScript.js:39
msgid "Filtering episodes" msgid "Rewind 10 seconds"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:40 #: /app/specialVueFakeDummyForL10nScript.js:40
msgid "Show all" msgid "Pause"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:41
msgid "Listened"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:42 #: /app/specialVueFakeDummyForL10nScript.js:42
msgid "Listening" msgid "Fast forward 30 seconds"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:43 #: /app/specialVueFakeDummyForL10nScript.js:43
msgid "Unlistened"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:44 #: /app/specialVueFakeDummyForL10nScript.js:44
msgid "Import subscriptions"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:45 #: /app/specialVueFakeDummyForL10nScript.js:45
msgid "Import OPML file" msgid "Mute"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:46 #: /app/specialVueFakeDummyForL10nScript.js:46
msgid "Rate RePod ❤️" msgid "Unmute"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:47 #: /app/specialVueFakeDummyForL10nScript.js:47
msgid "Sleep timer" msgid "Export subscriptions"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:48 #: /app/specialVueFakeDummyForL10nScript.js:48
msgid "Minutes" msgid "Filtering episodes"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:49 #: /app/specialVueFakeDummyForL10nScript.js:49
msgid "Show all"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:50
msgid "Listened"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:51
msgid "Listening"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:52
msgid "Unlistened"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:53
msgid "Import subscriptions"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:54
msgid "Import OPML file"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:55
msgid "Rate RePod ❤️"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:56
msgid "Sleep timer"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:57
msgid "Minutes"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:58
msgid "%n min" msgid "%n min"
msgid_plural "%n mins" msgid_plural "%n mins"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: /app/specialVueFakeDummyForL10nScript.js:50 #: /app/specialVueFakeDummyForL10nScript.js:59
msgid "%n sec" msgid "%n sec"
msgid_plural "%n secs" msgid_plural "%n secs"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: /app/specialVueFakeDummyForL10nScript.js:51 #: /app/specialVueFakeDummyForL10nScript.js:60
msgid "Playback speed" msgid "Playback speed"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:52 #: /app/specialVueFakeDummyForL10nScript.js:61
#: /app/specialVueFakeDummyForL10nScript.js:53 #: /app/specialVueFakeDummyForL10nScript.js:62
#: /app/specialVueFakeDummyForL10nScript.js:54 #: /app/specialVueFakeDummyForL10nScript.js:63
msgid "Favorite" msgid "Favorite"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:55 #: /app/specialVueFakeDummyForL10nScript.js:64
msgid "Are you sure you want to delete this subscription?" msgid "Are you sure you want to delete this subscription?"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:56 #: /app/specialVueFakeDummyForL10nScript.js:65
msgid "Error while removing the feed" msgid "Error while removing the feed"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:57 #: /app/specialVueFakeDummyForL10nScript.js:66
msgid "You can only have 10 favorites" msgid "You can only have 10 favorites"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:58 #: /app/specialVueFakeDummyForL10nScript.js:67
msgid "Add a podcast" msgid "Add a podcast"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:59 #: /app/specialVueFakeDummyForL10nScript.js:68
msgid "Could not fetch subscriptions" msgid "Could not fetch subscriptions"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:60 #: /app/specialVueFakeDummyForL10nScript.js:69
msgid "Find a podcast" msgid "Find a podcast"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:61 #: /app/specialVueFakeDummyForL10nScript.js:70
msgid "Error loading feed" msgid "Error loading feed"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:62 #: /app/specialVueFakeDummyForL10nScript.js:71
msgid "Missing required app" msgid "Missing required app"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:63 #: /app/specialVueFakeDummyForL10nScript.js:72
msgid "Install GPodder Sync" msgid "Install GPodder Sync"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:64 #: /app/specialVueFakeDummyForL10nScript.js:73
msgid "Pin some subscriptions to see their latest updates" msgid "Pin some subscriptions to see their latest updates"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:65 #: /app/specialVueFakeDummyForL10nScript.js:74
msgid "No favorites" msgid "No favorites"
msgstr "" msgstr ""