typescript #149 #152
@ -4,6 +4,7 @@ module.exports = {
|
||||
'plugin:pinia/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
parser: 'vue-eslint-parser',
|
||||
rules: {
|
||||
'jsdoc/require-jsdoc': 'off',
|
||||
'vue/first-attribute-linebreak': 'off',
|
||||
|
580
package-lock.json
generated
580
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@ -2,7 +2,7 @@
|
||||
"name": "repod",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"build": "vite build --mode production",
|
||||
"build": "vue-tsc && vite build --mode production",
|
||||
"dev": "vite build --mode development",
|
||||
"dev:watch": "vite build --mode development --watch",
|
||||
"watch": "npm run dev:watch",
|
||||
@ -24,10 +24,11 @@
|
||||
"@nextcloud/vue": "9.0.0-alpha.5",
|
||||
"dompurify": "^3.1.6",
|
||||
"linkify-html": "^4.1.3",
|
||||
"petite-utils": "^0.0.5-3",
|
||||
"pinia": "^2.2.2",
|
||||
"toastify-js": "^1.12.0",
|
||||
"vite": "^5.4.3",
|
||||
"vite-plugin-vue-devtools": "^7.4.4",
|
||||
"vite": "^5.4.4",
|
||||
"vite-plugin-vue-devtools": "^7.4.5",
|
||||
"vue": "^3.5.4",
|
||||
"vue-material-design-icons": "^5.3.0",
|
||||
"vue-router": "^4.4.4"
|
||||
@ -37,8 +38,14 @@
|
||||
"@nextcloud/eslint-config": "^8.4.1",
|
||||
"@nextcloud/prettier-config": "^1.1.0",
|
||||
"@nextcloud/stylelint-config": "^3.0.1",
|
||||
"@types/toastify-js": "^1.12.3",
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-pinia": "^0.4.1",
|
||||
"eslint-plugin-prettier": "^5.2.1"
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.6.2",
|
||||
"vue-eslint-parser": "^9.4.3",
|
||||
"vue-tsc": "^2.1.6"
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import GPodder from './views/GPodder.vue'
|
||||
import { NcContent } from '@nextcloud/vue'
|
||||
import Subscriptions from './components/Sidebar/Subscriptions.vue'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { usePlayer } from './store/player.js'
|
||||
import { usePlayer } from './store/player.ts'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
|
@ -4,10 +4,10 @@
|
||||
</NcAppContent>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { NcAppContent } from '@nextcloud/vue'
|
||||
import { mapState } from 'pinia'
|
||||
import { usePlayer } from '../../store/player.js'
|
||||
import { usePlayer } from '../../store/player.ts'
|
||||
|
||||
export default {
|
||||
name: 'AppContent',
|
||||
|
@ -10,10 +10,10 @@
|
||||
</NcAppNavigation>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { NcAppNavigation } from '@nextcloud/vue'
|
||||
import { mapState } from 'pinia'
|
||||
import { usePlayer } from '../../store/player.js'
|
||||
import { usePlayer } from '../../store/player.ts'
|
||||
|
||||
export default {
|
||||
name: 'AppNavigation',
|
||||
|
@ -16,7 +16,7 @@
|
||||
</NcEmptyContent>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { NcEmptyContent } from '@nextcloud/vue'
|
||||
|
||||
export default {
|
||||
|
@ -2,7 +2,7 @@
|
||||
<NcLoadingIcon class="loading" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { NcLoadingIcon } from '@nextcloud/vue'
|
||||
|
||||
export default {
|
||||
|
@ -29,13 +29,14 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { NcAvatar, NcButton } from '@nextcloud/vue'
|
||||
import DownloadIcon from 'vue-material-design-icons/Download.vue'
|
||||
import OpenInNewIcon from 'vue-material-design-icons/OpenInNew.vue'
|
||||
import SafeHtml from './SafeHtml.vue'
|
||||
import { filenameFromUrl } from '../../utils/url.js'
|
||||
import { humanFileSize } from '../../utils/size.js'
|
||||
import { filenameFromUrl } from '../../utils/url.ts'
|
||||
import { humanFileSize } from '../../utils/size.ts'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
|
||||
export default {
|
||||
name: 'Modal',
|
||||
@ -55,6 +56,7 @@ export default {
|
||||
methods: {
|
||||
filenameFromUrl,
|
||||
humanFileSize,
|
||||
t,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div v-sanitize="source" class="html" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import dompurify from 'dompurify'
|
||||
import linkifyHtml from 'linkify-html'
|
||||
|
||||
|
@ -10,10 +10,11 @@
|
||||
</NcAppNavigationList>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { NcAppNavigationList, NcAppNavigationNewItem } from '@nextcloud/vue'
|
||||
import PlusIcon from 'vue-material-design-icons/Plus.vue'
|
||||
import { toFeedUrl } from '../../utils/url.js'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { toFeedUrl } from '../../utils/url.ts'
|
||||
|
||||
export default {
|
||||
name: 'AddRss',
|
||||
@ -23,6 +24,7 @@ export default {
|
||||
PlusIcon,
|
||||
},
|
||||
methods: {
|
||||
t,
|
||||
toFeedUrl,
|
||||
},
|
||||
}
|
||||
|
@ -34,18 +34,19 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { NcActionButton, NcAvatar, NcListItem } from '@nextcloud/vue'
|
||||
import { mapActions, mapState } from 'pinia'
|
||||
import Loading from '../Atoms/Loading.vue'
|
||||
import PlusIcon from 'vue-material-design-icons/Plus.vue'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { debounce } from '../../utils/debounce.js'
|
||||
import { formatLocaleDate } from '../../utils/time.js'
|
||||
import { debounce } from 'petite-utils'
|
||||
import { formatLocaleDate } from '../../utils/time.ts'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { showError } from '../../utils/toast.js'
|
||||
import { toFeedUrl } from '../../utils/url.js'
|
||||
import { useSubscriptions } from '../../store/subscriptions.js'
|
||||
import { showError } from '../../utils/toast.ts'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { toFeedUrl } from '../../utils/url.ts'
|
||||
import { useSubscriptions } from '../../store/subscriptions.ts'
|
||||
|
||||
export default {
|
||||
name: 'Search',
|
||||
@ -77,8 +78,9 @@ export default {
|
||||
methods: {
|
||||
...mapActions(useSubscriptions, ['fetch']),
|
||||
formatLocaleDate,
|
||||
t,
|
||||
toFeedUrl,
|
||||
async addSubscription(url) {
|
||||
addSubscription: async (url) => {
|
||||
try {
|
||||
await axios.post(
|
||||
generateUrl('/apps/gpoddersync/subscription_change/create'),
|
||||
|
@ -16,8 +16,8 @@
|
||||
import Loading from '../Atoms/Loading.vue'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { showError } from '../../utils/toast.js'
|
||||
import { toFeedUrl } from '../../utils/url.js'
|
||||
import { showError } from '../../utils/toast.ts'
|
||||
import { toFeedUrl } from '../../utils/url.ts'
|
||||
|
||||
export default {
|
||||
name: 'Toplist',
|
||||
|
@ -38,14 +38,14 @@
|
||||
<script>
|
||||
import { NcAppNavigationNew, NcAvatar } from '@nextcloud/vue'
|
||||
import { mapActions, mapState } from 'pinia'
|
||||
import { showError, showSuccess } from '../../utils/toast.js'
|
||||
import { showError, showSuccess } from '../../utils/toast.ts'
|
||||
import PlusIcon from 'vue-material-design-icons/Plus.vue'
|
||||
import RssIcon from 'vue-material-design-icons/Rss.vue'
|
||||
import SafeHtml from '../Atoms/SafeHtml.vue'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { decodeUrl } from '../../utils/url.js'
|
||||
import { decodeUrl } from '../../utils/url.ts'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { useSubscriptions } from '../../store/subscriptions.js'
|
||||
import { useSubscriptions } from '../../store/subscriptions.ts'
|
||||
|
||||
export default {
|
||||
name: 'Banner',
|
||||
|
@ -102,8 +102,8 @@ import {
|
||||
durationToSeconds,
|
||||
formatEpisodeTimestamp,
|
||||
formatLocaleDate,
|
||||
} from '../../utils/time.js'
|
||||
import { hasEnded, isListening } from '../../utils/status.js'
|
||||
} from '../../utils/time.ts'
|
||||
import { hasEnded, isListening } from '../../utils/status.ts'
|
||||
import { mapActions, mapState } from 'pinia'
|
||||
import DownloadIcon from 'vue-material-design-icons/Download.vue'
|
||||
import Modal from '../Atoms/Modal.vue'
|
||||
@ -113,10 +113,10 @@ import PlaylistPlayIcon from 'vue-material-design-icons/PlaylistPlay.vue'
|
||||
import PlaylistRemoveIcon from 'vue-material-design-icons/PlaylistRemove.vue'
|
||||
import StopIcon from 'vue-material-design-icons/Stop.vue'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { filenameFromUrl } from '../../utils/url.js'
|
||||
import { filenameFromUrl } from '../../utils/url.ts'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { showError } from '../../utils/toast.js'
|
||||
import { usePlayer } from '../../store/player.js'
|
||||
import { showError } from '../../utils/toast.ts'
|
||||
import { usePlayer } from '../../store/player.ts'
|
||||
|
||||
export default {
|
||||
name: 'Episode',
|
||||
|
@ -12,16 +12,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { hasEnded, isListening } from '../../utils/status.js'
|
||||
import { hasEnded, isListening } from '../../utils/status.ts'
|
||||
import Episode from './Episode.vue'
|
||||
import Loading from '../Atoms/Loading.vue'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { decodeUrl } from '../../utils/url.js'
|
||||
import { decodeUrl } from '../../utils/url.ts'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { mapState } from 'pinia'
|
||||
import { showError } from '../../utils/toast.js'
|
||||
import { usePlayer } from '../../store/player.js'
|
||||
import { useSettings } from '../../store/settings.js'
|
||||
import { showError } from '../../utils/toast.ts'
|
||||
import { usePlayer } from '../../store/player.ts'
|
||||
import { useSettings } from '../../store/settings.ts'
|
||||
|
||||
export default {
|
||||
name: 'Episodes',
|
||||
|
@ -29,10 +29,10 @@ import Episode from './Episode.vue'
|
||||
import Loading from '../Atoms/Loading.vue'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { hasEnded } from '../../utils/status.js'
|
||||
import { hasEnded } from '../../utils/status.ts'
|
||||
import { mapState } from 'pinia'
|
||||
import { showError } from '../../utils/toast.js'
|
||||
import { useSubscriptions } from '../../store/subscriptions.js'
|
||||
import { showError } from '../../utils/toast.ts'
|
||||
import { useSubscriptions } from '../../store/subscriptions.ts'
|
||||
|
||||
export default {
|
||||
name: 'Favorite',
|
||||
|
@ -21,7 +21,7 @@ import ProgressBar from './ProgressBar.vue'
|
||||
import Timer from './Timer.vue'
|
||||
import Volume from './Volume.vue'
|
||||
import { mapState } from 'pinia'
|
||||
import { usePlayer } from '../../store/player.js'
|
||||
import { usePlayer } from '../../store/player.ts'
|
||||
|
||||
export default {
|
||||
name: 'Bar',
|
||||
|
@ -9,7 +9,7 @@
|
||||
import { mapActions, mapState } from 'pinia'
|
||||
import PauseIcon from 'vue-material-design-icons/Pause.vue'
|
||||
import PlayIcon from 'vue-material-design-icons/Play.vue'
|
||||
import { usePlayer } from '../../store/player.js'
|
||||
import { usePlayer } from '../../store/player.ts'
|
||||
|
||||
export default {
|
||||
name: 'Controls',
|
||||
|
@ -16,8 +16,8 @@
|
||||
import Modal from '../Atoms/Modal.vue'
|
||||
import { NcModal } from '@nextcloud/vue'
|
||||
import { mapState } from 'pinia'
|
||||
import { toFeedUrl } from '../../utils/url.js'
|
||||
import { usePlayer } from '../../store/player.js'
|
||||
import { toFeedUrl } from '../../utils/url.ts'
|
||||
import { usePlayer } from '../../store/player.ts'
|
||||
|
||||
export default {
|
||||
name: 'Infos',
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
<script>
|
||||
import { mapActions, mapState } from 'pinia'
|
||||
import { usePlayer } from '../../store/player.js'
|
||||
import { usePlayer } from '../../store/player.ts'
|
||||
|
||||
export default {
|
||||
name: 'ProgressBar',
|
||||
|
@ -7,9 +7,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { formatTimer } from '../../utils/time.js'
|
||||
import { formatTimer } from '../../utils/time.ts'
|
||||
import { mapState } from 'pinia'
|
||||
import { usePlayer } from '../../store/player.js'
|
||||
import { usePlayer } from '../../store/player.ts'
|
||||
|
||||
export default {
|
||||
name: 'Timer',
|
||||
|
@ -36,7 +36,7 @@ import VolumeHighIcon from 'vue-material-design-icons/VolumeHigh.vue'
|
||||
import VolumeLowIcon from 'vue-material-design-icons/VolumeLow.vue'
|
||||
import VolumeMediumIcon from 'vue-material-design-icons/VolumeMedium.vue'
|
||||
import VolumeMuteIcon from 'vue-material-design-icons/VolumeMute.vue'
|
||||
import { usePlayer } from '../../store/player.js'
|
||||
import { usePlayer } from '../../store/player.ts'
|
||||
|
||||
export default {
|
||||
name: 'Volume',
|
||||
|
@ -44,7 +44,7 @@ import { NcActionCheckbox, NcAppNavigationItem } from '@nextcloud/vue'
|
||||
import { mapActions, mapState } from 'pinia'
|
||||
import FilterIcon from 'vue-material-design-icons/Filter.vue'
|
||||
import FilterSettingsIcon from 'vue-material-design-icons/FilterSettings.vue'
|
||||
import { useSettings } from '../../store/settings.js'
|
||||
import { useSettings } from '../../store/settings.ts'
|
||||
|
||||
export default {
|
||||
name: 'Filters',
|
||||
|
@ -23,7 +23,7 @@ import PlusIcon from 'vue-material-design-icons/Plus.vue'
|
||||
import SpeedometerIcon from 'vue-material-design-icons/Speedometer.vue'
|
||||
import SpeedometerMediumIcon from 'vue-material-design-icons/SpeedometerMedium.vue'
|
||||
import SpeedometerSlowIcon from 'vue-material-design-icons/SpeedometerSlow.vue'
|
||||
import { usePlayer } from '../../store/player.js'
|
||||
import { usePlayer } from '../../store/player.ts'
|
||||
|
||||
export default {
|
||||
name: 'Speed',
|
||||
|
@ -47,9 +47,9 @@ import StarPlusIcon from 'vue-material-design-icons/StarPlus.vue'
|
||||
import StarRemoveIcon from 'vue-material-design-icons/StarRemove.vue'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { showError } from '../../utils/toast.js'
|
||||
import { toFeedUrl } from '../../utils/url.js'
|
||||
import { useSubscriptions } from '../../store/subscriptions.js'
|
||||
import { showError } from '../../utils/toast.ts'
|
||||
import { toFeedUrl } from '../../utils/url.ts'
|
||||
import { useSubscriptions } from '../../store/subscriptions.ts'
|
||||
|
||||
export default {
|
||||
name: 'Subscription',
|
||||
|
@ -43,8 +43,8 @@ import Loading from '../Atoms/Loading.vue'
|
||||
import PlusIcon from 'vue-material-design-icons/Plus.vue'
|
||||
import Settings from '../Settings/Settings.vue'
|
||||
import Subscription from './Subscription.vue'
|
||||
import { showError } from '../../utils/toast.js'
|
||||
import { useSubscriptions } from '../../store/subscriptions.js'
|
||||
import { showError } from '../../utils/toast.ts'
|
||||
import { useSubscriptions } from '../../store/subscriptions.ts'
|
||||
|
||||
export default {
|
||||
name: 'Subscriptions',
|
||||
|
@ -1,13 +1,11 @@
|
||||
import { n, t } from '@nextcloud/l10n'
|
||||
import App from './App.vue'
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import router from './router.js'
|
||||
import router from './router'
|
||||
|
||||
const Vue = createApp(App)
|
||||
const pinia = createPinia()
|
||||
|
||||
Vue.mixin({ methods: { t, n } })
|
||||
Vue.use(pinia)
|
||||
Vue.use(router)
|
||||
Vue.mount('#content')
|
@ -1,18 +1,19 @@
|
||||
import type { EpisodeInterface } from '../utils/types'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { defineStore } from 'pinia'
|
||||
import { formatEpisodeTimestamp } from '../utils/time.js'
|
||||
import { formatEpisodeTimestamp } from '../utils/time'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
|
||||
const audio = new Audio()
|
||||
|
||||
export const usePlayer = defineStore('player', {
|
||||
state: () => ({
|
||||
currentTime: null,
|
||||
duration: null,
|
||||
episode: null,
|
||||
currentTime: null as number | null,
|
||||
duration: null as number | null,
|
||||
episode: null as EpisodeInterface | null,
|
||||
loaded: false,
|
||||
paused: null,
|
||||
podcastUrl: null,
|
||||
paused: true,
|
||||
podcastUrl: null as string | null,
|
||||
volume: 1,
|
||||
rate: 1,
|
||||
started: 0,
|
||||
@ -29,11 +30,11 @@ export const usePlayer = defineStore('player', {
|
||||
audio.ontimeupdate = () => (this.currentTime = audio.currentTime)
|
||||
audio.onvolumechange = () => (this.volume = audio.volume)
|
||||
},
|
||||
async load(episode, podcastUrl) {
|
||||
async load(episode: EpisodeInterface | null, podcastUrl?: string) {
|
||||
this.episode = episode
|
||||
this.podcastUrl = podcastUrl
|
||||
this.podcastUrl = podcastUrl || null
|
||||
|
||||
if (this.episode) {
|
||||
if (this.episode?.url) {
|
||||
audio.src = this.episode.url
|
||||
audio.load()
|
||||
|
||||
@ -72,7 +73,7 @@ export const usePlayer = defineStore('player', {
|
||||
this.paused = false
|
||||
this.started = audio.currentTime
|
||||
},
|
||||
seek(currentTime) {
|
||||
seek(currentTime: number) {
|
||||
audio.currentTime = currentTime
|
||||
this.time()
|
||||
},
|
||||
@ -81,7 +82,13 @@ export const usePlayer = defineStore('player', {
|
||||
this.episode = null
|
||||
},
|
||||
time() {
|
||||
this.episode.action = {
|
||||
if (!this.podcastUrl || !this.episode?.url) {
|
||||
return
|
||||
}
|
||||
|
||||
this.episode = {
|
||||
...this.episode,
|
||||
action: {
|
||||
podcast: this.podcastUrl,
|
||||
episode: this.episode.url,
|
||||
guid: this.episode.guid,
|
||||
@ -90,15 +97,17 @@ export const usePlayer = defineStore('player', {
|
||||
started: Math.round(this.started),
|
||||
position: Math.round(audio.currentTime),
|
||||
total: Math.round(audio.duration),
|
||||
},
|
||||
}
|
||||
|
||||
axios.post(generateUrl('/apps/gpoddersync/episode_action/create'), [
|
||||
this.episode.action,
|
||||
])
|
||||
},
|
||||
setVolume(volume) {
|
||||
setVolume(volume: number) {
|
||||
audio.volume = volume
|
||||
},
|
||||
setRate(rate) {
|
||||
setRate(rate: number) {
|
||||
audio.playbackRate = rate
|
||||
},
|
||||
},
|
@ -1,31 +0,0 @@
|
||||
import { getCookie, setCookie } from '../utils/cookies.js'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useSettings = defineStore('settings', {
|
||||
state: () => {
|
||||
try {
|
||||
const filters = JSON.parse(getCookie('repod.filters'))
|
||||
return {
|
||||
filters: {
|
||||
listened: filters.listened,
|
||||
listening: filters.listening,
|
||||
unlistened: filters.unlistened,
|
||||
},
|
||||
}
|
||||
} catch {
|
||||
return {
|
||||
filters: {
|
||||
listened: true,
|
||||
listening: true,
|
||||
unlistened: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setFilters(filters) {
|
||||
this.filters = { ...this.filters, ...filters }
|
||||
setCookie('repod.filters', JSON.stringify(this.filters), 365)
|
||||
},
|
||||
},
|
||||
})
|
36
src/store/settings.ts
Normal file
36
src/store/settings.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { getCookie, setCookie } from '../utils/cookies'
|
||||
import type { FiltersInterface } from '../utils/types'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useSettings = defineStore('settings', {
|
||||
state: () => {
|
||||
const cookie = getCookie('repod.filters')
|
||||
|
||||
if (cookie) {
|
||||
try {
|
||||
const filters = JSON.parse(cookie)
|
||||
return {
|
||||
filters: {
|
||||
listened: filters.listened,
|
||||
listening: filters.listening,
|
||||
unlistened: filters.unlistened,
|
||||
},
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return {
|
||||
filters: {
|
||||
listened: true,
|
||||
listening: true,
|
||||
unlistened: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setFilters(filters: FiltersInterface) {
|
||||
this.filters = { ...this.filters, ...filters }
|
||||
setCookie('repod.filters', JSON.stringify(this.filters), 365)
|
||||
},
|
||||
},
|
||||
})
|
@ -1,4 +1,4 @@
|
||||
import { getCookie, setCookie } from '../utils/cookies.js'
|
||||
import { getCookie, setCookie } from '../utils/cookies'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { defineStore } from 'pinia'
|
||||
import { generateUrl } from '@nextcloud/router'
|
@ -4,7 +4,7 @@
|
||||
* @param {string} name Nom du cookie à récupérer
|
||||
* @return {string|null}
|
||||
*/
|
||||
export const getCookie = (name) => {
|
||||
export const getCookie = (name: string): string | null => {
|
||||
const cookies = document.cookie.split('; ')
|
||||
const value = cookies.find((c) => c.startsWith(name + '='))?.split('=')[1]
|
||||
if (value === undefined) {
|
||||
@ -19,7 +19,7 @@ export const getCookie = (name) => {
|
||||
* @param {string} value Value du cookie
|
||||
* @param {number} days Durée de vie du cookie (en jours)
|
||||
*/
|
||||
export const setCookie = (name, value, days) => {
|
||||
export const setCookie = (name: string, value: string, days: number) => {
|
||||
const date = new Date()
|
||||
date.setDate(date.getDate() + days)
|
||||
document.cookie = `${name}=${encodeURIComponent(value)}; expires=${date.toUTCString()}; SameSite=Strict;`
|
@ -1,12 +0,0 @@
|
||||
// https://stackoverflow.com/a/53486112
|
||||
export const debounce = (fn, delay) => {
|
||||
let timeoutID = null
|
||||
return function () {
|
||||
clearTimeout(timeoutID)
|
||||
const args = arguments
|
||||
const that = this
|
||||
timeoutID = setTimeout(function () {
|
||||
fn.apply(that, args)
|
||||
}, delay)
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
// https://stackoverflow.com/a/20732091
|
||||
export const humanFileSize = (size) => {
|
||||
export const humanFileSize = (size: number) => {
|
||||
const i = size === 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024))
|
||||
return (
|
||||
(size / Math.pow(1024, i)).toFixed(2) * 1 +
|
||||
(size / Math.pow(1024, i)).toFixed(2) +
|
||||
' ' +
|
||||
['B', 'kB', 'MB', 'GB', 'TB'][i]
|
||||
)
|
@ -1,4 +1,6 @@
|
||||
export const hasEnded = (episode) =>
|
||||
import type { EpisodeInterface } from './types'
|
||||
|
||||
export const hasEnded = (episode: EpisodeInterface) =>
|
||||
episode.action &&
|
||||
episode.action.action &&
|
||||
(episode.action.action.toLowerCase() === 'delete' ||
|
||||
@ -6,7 +8,7 @@ export const hasEnded = (episode) =>
|
||||
episode.action.total > 0 &&
|
||||
episode.action.position >= episode.action.total))
|
||||
|
||||
export const isListening = (episode) =>
|
||||
export const isListening = (episode: EpisodeInterface) =>
|
||||
episode.action &&
|
||||
episode.action.action &&
|
||||
episode.action.action.toLowerCase() === 'play' &&
|
@ -3,9 +3,9 @@
|
||||
* @param {Date} date The date
|
||||
* @return {string}
|
||||
*/
|
||||
export const formatTimer = (date) => {
|
||||
const minutes = date.getUTCMinutes().toString().padStart(2, 0)
|
||||
const seconds = date.getUTCSeconds().toString().padStart(2, 0)
|
||||
export const formatTimer = (date: Date): string => {
|
||||
const minutes = date.getUTCMinutes().toString().padStart(2, '0')
|
||||
const seconds = date.getUTCSeconds().toString().padStart(2, '0')
|
||||
let timer = `${minutes}:${seconds}`
|
||||
|
||||
if (date.getUTCHours()) {
|
||||
@ -20,7 +20,7 @@ export const formatTimer = (date) => {
|
||||
* @param {Date} date The date
|
||||
* @return {string}
|
||||
*/
|
||||
export const formatEpisodeTimestamp = (date) => {
|
||||
export const formatEpisodeTimestamp = (date: Date): string => {
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
@ -36,7 +36,7 @@ export const formatEpisodeTimestamp = (date) => {
|
||||
* @param {Date} date The date
|
||||
* @return {string}
|
||||
*/
|
||||
export const formatLocaleDate = (date) =>
|
||||
export const formatLocaleDate = (date: Date): string =>
|
||||
date.toLocaleDateString(undefined, { dateStyle: 'medium' })
|
||||
|
||||
/**
|
||||
@ -44,7 +44,7 @@ export const formatLocaleDate = (date) =>
|
||||
* @param {string} duration The duration feed's entry
|
||||
* @return {number}
|
||||
*/
|
||||
export const durationToSeconds = (duration) => {
|
||||
export const durationToSeconds = (duration: string): number => {
|
||||
const splitDuration = duration.split(':').reverse()
|
||||
let seconds = parseInt(splitDuration[0])
|
||||
seconds += splitDuration.length > 1 ? parseInt(splitDuration[1]) * 60 : 0
|
@ -1,12 +0,0 @@
|
||||
import toastify from 'toastify-js'
|
||||
|
||||
export const showMessage = (text, backgroundColor) =>
|
||||
toastify({
|
||||
text,
|
||||
backgroundColor,
|
||||
}).showToast()
|
||||
|
||||
export const showError = (text) => showMessage(text, 'var(--color-error)')
|
||||
export const showWarning = (text) => showMessage(text, 'var(--color-warning)')
|
||||
export const showInfo = (text) => showMessage(text, 'var(--color-primary)')
|
||||
export const showSuccess = (text) => showMessage(text, 'var(--color-success)')
|
14
src/utils/toast.ts
Normal file
14
src/utils/toast.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import toastify from 'toastify-js'
|
||||
|
||||
export const showMessage = (text: string, backgroundColor: string) =>
|
||||
toastify({
|
||||
text,
|
||||
backgroundColor,
|
||||
}).showToast()
|
||||
|
||||
export const showError = (text: string) => showMessage(text, 'var(--color-error)')
|
||||
export const showWarning = (text: string) =>
|
||||
showMessage(text, 'var(--color-warning)')
|
||||
export const showInfo = (text: string) => showMessage(text, 'var(--color-primary)')
|
||||
export const showSuccess = (text: string) =>
|
||||
showMessage(text, 'var(--color-success)')
|
33
src/utils/types.ts
Normal file
33
src/utils/types.ts
Normal file
@ -0,0 +1,33 @@
|
||||
export interface EpisodeActionInterface {
|
||||
podcast: string
|
||||
episode: string
|
||||
action: string
|
||||
timestamp: string
|
||||
started: number
|
||||
position: number
|
||||
total: number
|
||||
guid?: string
|
||||
id?: number
|
||||
}
|
||||
|
||||
export interface EpisodeInterface {
|
||||
title: string
|
||||
url?: string
|
||||
name: string
|
||||
link?: string
|
||||
image?: string
|
||||
description?: string
|
||||
fetchedAtUnix: number
|
||||
guid: string
|
||||
type?: string
|
||||
size?: number
|
||||
pubDate?: Date
|
||||
duration?: string
|
||||
action?: EpisodeActionInterface
|
||||
}
|
||||
|
||||
export interface FiltersInterface {
|
||||
listened: boolean
|
||||
listening: boolean
|
||||
unlistened: boolean
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
export const encodeUrl = (url) => encodeURIComponent(btoa(url))
|
||||
export const decodeUrl = (url) => atob(decodeURIComponent(url))
|
||||
export const toFeedUrl = (url) => `/feed/${encodeUrl(url)}`
|
||||
export const filenameFromUrl = (url) => new URL(url).pathname.split('/').pop()
|
5
src/utils/url.ts
Normal file
5
src/utils/url.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export const encodeUrl = (url: string) => encodeURIComponent(btoa(url))
|
||||
export const decodeUrl = (url: string) => atob(decodeURIComponent(url))
|
||||
export const toFeedUrl = (url: string) => `/feed/${encodeUrl(url)}`
|
||||
export const filenameFromUrl = (url: string) =>
|
||||
new URL(url).pathname.split('/').pop()
|
@ -25,7 +25,7 @@ import EmptyContent from '../components/Atoms/EmptyContent.vue'
|
||||
import Episodes from '../components/Feed/Episodes.vue'
|
||||
import Loading from '../components/Atoms/Loading.vue'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { decodeUrl } from '../utils/url.js'
|
||||
import { decodeUrl } from '../utils/url.ts'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
|
||||
export default {
|
||||
|
@ -24,7 +24,7 @@ import EmptyContent from '../components/Atoms/EmptyContent.vue'
|
||||
import Favorite from '../components/Feed/Favorite.vue'
|
||||
import StarOffIcon from 'vue-material-design-icons/StarOff.vue'
|
||||
import { mapState } from 'pinia'
|
||||
import { useSubscriptions } from '../store/subscriptions.js'
|
||||
import { useSubscriptions } from '../store/subscriptions.ts'
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
|
19
tsconfig.json
Normal file
19
tsconfig.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.json",
|
||||
"include": ["./src/**/*.ts", "./src/**/*.vue", "**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"noImplicitAny": false,
|
||||
"rootDir": ".",
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
},
|
||||
"vueCompilerOptions": {
|
||||
"target": 3.3,
|
||||
},
|
||||
}
|
@ -19,7 +19,7 @@ const config = defineConfig(({ mode }) => ({
|
||||
|
||||
export default createAppConfig(
|
||||
{
|
||||
main: 'src/main.js',
|
||||
main: 'src/main.ts',
|
||||
},
|
||||
{ config, inlineCSS: true },
|
||||
)
|
Loading…
Reference in New Issue
Block a user