refine the subscription loading time (fix #178) #181

Merged
Xefir merged 4 commits from metrics into main 2024-10-23 21:46:31 +00:00
9 changed files with 172 additions and 165 deletions

View File

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace OCA\RePod\Controller;
use OCA\GPodderSync\Core\PodcastData\PodcastDataReader;
use OCA\GPodderSync\Core\PodcastData\PodcastMetrics;
use OCA\GPodderSync\Core\PodcastData\PodcastMetricsReader;
use OCA\RePod\AppInfo\Application;
use OCA\RePod\Service\UserService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
class MetricsController extends Controller
{
public function __construct(
IRequest $request,
private PodcastDataReader $podcastDataReader,
private PodcastMetricsReader $podcastMetricsReader,
private UserService $userService
) {
parent::__construct(Application::APP_ID, $request);
}
#[NoAdminRequired]
#[NoCSRFRequired]
#[FrontpageRoute(verb: 'GET', url: '/metrics')]
public function index(): JSONResponse {
$metrics = $this->podcastMetricsReader->metrics($this->userService->getUserUID());
usort($metrics, fn (PodcastMetrics $a, PodcastMetrics $b) => $b->getListenedSeconds() <=> $a->getListenedSeconds());
$subscriptions = [];
foreach ($metrics as $metric) {
$subscription = $metric->toArray();
try {
$subscription['data'] = $this->podcastDataReader->getCachedOrFetchPodcastData($metric->getUrl(), $this->userService->getUserUID());
} catch (\Exception $e) {
}
$subscriptions[] = $subscription;
}
return new JSONResponse($subscriptions);
}
}

130
package-lock.json generated
View File

@ -18,7 +18,7 @@
"linkify-html": "^4.1.3", "linkify-html": "^4.1.3",
"pinia": "^2.2.4", "pinia": "^2.2.4",
"toastify-js": "^1.12.0", "toastify-js": "^1.12.0",
"vite": "^5.4.9", "vite": "^5.4.10",
"vite-plugin-vue-devtools": "^7.5.3", "vite-plugin-vue-devtools": "^7.5.3",
"vue": "^3.5.12", "vue": "^3.5.12",
"vue-material-design-icons": "^5.3.1", "vue-material-design-icons": "^5.3.1",
@ -1671,9 +1671,9 @@
} }
}, },
"node_modules/@nextcloud/event-bus/node_modules/@types/node": { "node_modules/@nextcloud/event-bus/node_modules/@types/node": {
"version": "20.16.14", "version": "20.17.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.14.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.0.tgz",
"integrity": "sha512-vtgGzjxLF7QT88qRHtXMzCWpAAmwonE7fwgVjFtXosUva2oSpnIEc3gNO9P7uIfOxKnii2f79/xtOnfreYtDaA==", "integrity": "sha512-a7zRo0f0eLo9K5X9Wp5cAqTUNGzuFLDG2R7C4HY2BhcMAsxgSPuRvAC1ZB6QkuUQXf0YZAgfOX2ZyrBa2n4nHQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.19.2" "undici-types": "~6.19.2"
@ -2291,14 +2291,14 @@
} }
}, },
"node_modules/@rollup/pluginutils": { "node_modules/@rollup/pluginutils": {
"version": "5.1.2", "version": "5.1.3",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.3.tgz",
"integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==", "integrity": "sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/estree": "^1.0.0", "@types/estree": "^1.0.0",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"picomatch": "^2.3.1" "picomatch": "^4.0.2"
}, },
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=14.0.0"
@ -2771,9 +2771,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.7.8", "version": "22.7.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.8.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.9.tgz",
"integrity": "sha512-a922jJy31vqR5sk+kAdIENJjHblqcZ4RmERviFsER4WJcEONqxKcjNOlk0q7OUfrF5sddT+vng070cdfMlrPLg==", "integrity": "sha512-jrTfRC7FM6nChvU7X2KqcrgquofrWLFDeYC1hKfwNWomVvrn7JIksqf344WN2X/y8xrgqBd2dJATZV4GbatBfg==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true, "peer": true,
@ -2782,9 +2782,9 @@
} }
}, },
"node_modules/@types/sizzle": { "node_modules/@types/sizzle": {
"version": "2.3.8", "version": "2.3.9",
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.9.tgz",
"integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==", "integrity": "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/toastify-js": { "node_modules/@types/toastify-js": {
@ -4566,25 +4566,29 @@
} }
}, },
"node_modules/crypto-browserify": { "node_modules/crypto-browserify": {
"version": "3.12.0", "version": "3.12.1",
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz",
"integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"browserify-cipher": "^1.0.0", "browserify-cipher": "^1.0.1",
"browserify-sign": "^4.0.0", "browserify-sign": "^4.2.3",
"create-ecdh": "^4.0.0", "create-ecdh": "^4.0.4",
"create-hash": "^1.1.0", "create-hash": "^1.2.0",
"create-hmac": "^1.1.0", "create-hmac": "^1.1.7",
"diffie-hellman": "^5.0.0", "diffie-hellman": "^5.0.3",
"inherits": "^2.0.1", "hash-base": "~3.0.4",
"pbkdf2": "^3.0.3", "inherits": "^2.0.4",
"public-encrypt": "^4.0.0", "pbkdf2": "^3.1.2",
"randombytes": "^2.0.0", "public-encrypt": "^4.0.3",
"randomfill": "^1.0.3" "randombytes": "^2.1.0",
"randomfill": "^1.0.4"
}, },
"engines": { "engines": {
"node": "*" "node": ">= 0.10"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/css-functions-list": { "node_modules/css-functions-list": {
@ -5039,9 +5043,9 @@
} }
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.42", "version": "1.5.43",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.42.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.43.tgz",
"integrity": "sha512-gIfKavKDw1mhvic9nbzA5lZw8QSHpdMwLwXc0cWidQz9B15pDoDdDH4boIatuFfeoCatb3a/NGL6CYRVFxGZ9g==", "integrity": "sha512-NxnmFBHDl5Sachd2P46O7UJiMaMHMLSofoIWVJq3mj8NJgG0umiSeljAVP9lGzjI0UDLJJ5jjoGjcrB8RSbjLQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/elliptic": { "node_modules/elliptic": {
@ -6451,6 +6455,20 @@
"reusify": "^1.0.4" "reusify": "^1.0.4"
} }
}, },
"node_modules/fdir": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.3.0.tgz",
"integrity": "sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==",
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/file-entry-cache": { "node_modules/file-entry-cache": {
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@ -9114,6 +9132,18 @@
"node": ">=8.6" "node": ">=8.6"
} }
}, },
"node_modules/micromatch/node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/miller-rabin": { "node_modules/miller-rabin": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
@ -9772,12 +9802,12 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "2.3.1", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=8.6" "node": ">=12"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
@ -10553,34 +10583,6 @@
"rollup": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" "rollup": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0"
} }
}, },
"node_modules/rollup-plugin-license/node_modules/fdir": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.3.0.tgz",
"integrity": "sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==",
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/rollup-plugin-license/node_modules/picomatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/rollup-plugin-node-externals": { "node_modules/rollup-plugin-node-externals": {
"version": "7.1.3", "version": "7.1.3",
"resolved": "https://registry.npmjs.org/rollup-plugin-node-externals/-/rollup-plugin-node-externals-7.1.3.tgz", "resolved": "https://registry.npmjs.org/rollup-plugin-node-externals/-/rollup-plugin-node-externals-7.1.3.tgz",

View File

@ -7,8 +7,8 @@
"dev": "vite --mode development build", "dev": "vite --mode development build",
"dev:watch": "vite --mode development build --watch", "dev:watch": "vite --mode development build --watch",
"watch": "npm run dev:watch", "watch": "npm run dev:watch",
"lint": "eslint src", "lint": "vue-tsc && eslint src",
"lint:fix": "eslint src --fix", "lint:fix": "vue-tsc && eslint src --fix",
"stylelint": "stylelint src/**/*.vue src/**/*.scss src/**/*.css", "stylelint": "stylelint src/**/*.vue src/**/*.scss src/**/*.css",
"stylelint:fix": "stylelint src/**/*.vue src/**/*.scss src/**/*.css --fix" "stylelint:fix": "stylelint src/**/*.vue src/**/*.scss src/**/*.css --fix"
}, },
@ -25,7 +25,7 @@
"linkify-html": "^4.1.3", "linkify-html": "^4.1.3",
"pinia": "^2.2.4", "pinia": "^2.2.4",
"toastify-js": "^1.12.0", "toastify-js": "^1.12.0",
"vite": "^5.4.9", "vite": "^5.4.10",
"vite-plugin-vue-devtools": "^7.5.3", "vite-plugin-vue-devtools": "^7.5.3",
"vue": "^3.5.12", "vue": "^3.5.12",
"vue-material-design-icons": "^5.3.1", "vue-material-design-icons": "^5.3.1",

View File

@ -1,15 +1,15 @@
<template> <template>
<NcGuestContent class="guest"> <NcGuestContent class="guest">
<Loading v-if="!feed.data" /> <Loading v-if="!metric.data" />
<NcAvatar <NcAvatar
v-if="feed.data" v-if="metric.data"
class="avatar" class="avatar"
:display-name="feed.data.author || feed.data.title" :display-name="metric.data.author || metric.data.title"
:is-no-user="true" :is-no-user="true"
:size="222" :size="222"
:url="feed.data.imageUrl" /> :url="metric.data.imageUrl" />
<div v-if="feed.data" class="list"> <div v-if="metric.data" class="list">
<h2 class="title">{{ feed.data.title }}</h2> <h2 class="title">{{ metric.data.title }}</h2>
<Loading v-if="loading" /> <Loading v-if="loading" />
<ul v-if="!loading"> <ul v-if="!loading">
<Episode <Episode
@ -17,14 +17,14 @@
:key="episode.guid" :key="episode.guid"
:episode="episode" :episode="episode"
:one-line="true" :one-line="true"
:url="feed.metrics.url" /> :url="metric.url" />
</ul> </ul>
</div> </div>
</NcGuestContent> </NcGuestContent>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { EpisodeInterface, SubscriptionInterface } from '../../utils/types.ts' import type { EpisodeInterface, PodcastMetricsInterface } from '../../utils/types.ts'
import { NcAvatar, NcGuestContent } from '@nextcloud/vue' import { NcAvatar, NcGuestContent } from '@nextcloud/vue'
import Episode from './Episode.vue' import Episode from './Episode.vue'
import Loading from '../Atoms/Loading.vue' import Loading from '../Atoms/Loading.vue'
@ -43,8 +43,8 @@ export default {
NcGuestContent, NcGuestContent,
}, },
props: { props: {
feed: { metric: {
type: Object as () => SubscriptionInterface, type: Object as () => PodcastMetricsInterface,
required: true, required: true,
}, },
}, },
@ -57,7 +57,7 @@ export default {
this.loading = true this.loading = true
const episodes = await axios.get<EpisodeInterface[]>( const episodes = await axios.get<EpisodeInterface[]>(
generateUrl('/apps/repod/episodes/list?url={url}', { generateUrl('/apps/repod/episodes/list?url={url}', {
url: this.feed.metrics.url, url: this.metric.url,
}), }),
) )
this.episodes = [...episodes.data] this.episodes = [...episodes.data]

View File

@ -1,18 +1,18 @@
<template> <template>
<NcAppNavigationItem <NcAppNavigationItem
:loading="loading" :loading="loading"
:name="feed?.data?.title || url" :name="metric.data?.title || metric.url"
:to="toFeedUrl(url)"> :to="toFeedUrl(metric.url)">
<template #actions> <template #actions>
<NcActionButton <NcActionButton
:aria-label="t('repod', 'Favorite')" :aria-label="t('repod', 'Favorite')"
:model-value="feed?.isFavorite" :model-value="metric.isFavorite"
:name="t('repod', 'Favorite')" :name="t('repod', 'Favorite')"
:title="t('repod', 'Favorite')" :title="t('repod', 'Favorite')"
@update:modelValue="switchFavorite($event)"> @update:modelValue="switchFavorite($event)">
<template #icon> <template #icon>
<StarPlusIcon v-if="!feed?.isFavorite" :size="20" /> <StarPlusIcon v-if="!metric.isFavorite" :size="20" />
<StarRemoveIcon v-if="feed?.isFavorite" :size="20" /> <StarRemoveIcon v-if="metric.isFavorite" :size="20" />
</template> </template>
</NcActionButton> </NcActionButton>
<NcActionButton <NcActionButton
@ -27,11 +27,10 @@
</template> </template>
<template #icon> <template #icon>
<NcAvatar <NcAvatar
:display-name="feed?.data?.author || feed?.data?.title" :display-name="metric.data?.author || metric.data?.title"
:is-no-user="true" :is-no-user="true"
:url="feed?.data?.imageUrl" /> :url="metric.data?.imageUrl" />
<StarIcon v-if="feed?.isFavorite" class="star" :size="20" /> <StarIcon v-if="metric.isFavorite" class="star" :size="20" />
<AlertIcon v-if="failed" />
</template> </template>
</NcAppNavigationItem> </NcAppNavigationItem>
</template> </template>
@ -39,9 +38,8 @@
<script lang="ts"> <script lang="ts">
import { NcActionButton, NcAppNavigationItem, NcAvatar } from '@nextcloud/vue' import { NcActionButton, NcAppNavigationItem, NcAvatar } from '@nextcloud/vue'
import { mapActions, mapState } from 'pinia' import { mapActions, mapState } from 'pinia'
import AlertIcon from 'vue-material-design-icons/Alert.vue'
import DeleteIcon from 'vue-material-design-icons/Delete.vue' import DeleteIcon from 'vue-material-design-icons/Delete.vue'
import type { PersonalSettingsPodcastDataInterface } from '../../utils/types.ts' import type { PodcastMetricsInterface } from '../../utils/types.ts'
import StarIcon from 'vue-material-design-icons/Star.vue' import StarIcon from 'vue-material-design-icons/Star.vue'
import StarPlusIcon from 'vue-material-design-icons/StarPlus.vue' import StarPlusIcon from 'vue-material-design-icons/StarPlus.vue'
import StarRemoveIcon from 'vue-material-design-icons/StarRemove.vue' import StarRemoveIcon from 'vue-material-design-icons/StarRemove.vue'
@ -55,7 +53,6 @@ import { useSubscriptions } from '../../store/subscriptions.ts'
export default { export default {
name: 'Subscription', name: 'Subscription',
components: { components: {
AlertIcon,
DeleteIcon, DeleteIcon,
NcActionButton, NcActionButton,
NcAppNavigationItem, NcAppNavigationItem,
@ -65,42 +62,21 @@ export default {
StarRemoveIcon, StarRemoveIcon,
}, },
props: { props: {
url: { metric: {
type: String, type: Object as () => PodcastMetricsInterface,
required: true, required: true,
}, },
}, },
data: () => ({ data() {
failed: false, return {
loading: true, loading: false,
}),
computed: {
...mapState(useSubscriptions, ['subs']),
feed() {
return this.subs.find((sub) => sub.metrics.url === this.url)
},
},
async mounted() {
try {
const podcastData =
await axios.get<PersonalSettingsPodcastDataInterface>(
generateUrl(
'/apps/gpoddersync/personal_settings/podcast_data?url={url}',
{
url: this.url,
},
),
)
this.addMetadatas(this.url, podcastData.data.data)
} catch (e) {
this.failed = true
console.error(e)
} finally {
this.loading = false
} }
}, },
computed: {
...mapState(useSubscriptions, ['getSubByUrl', 'subs']),
},
methods: { methods: {
...mapActions(useSubscriptions, ['fetch', 'addMetadatas', 'setFavorite']), ...mapActions(useSubscriptions, ['fetch', 'setFavorite']),
t, t,
toFeedUrl, toFeedUrl,
async deleteSubscription() { async deleteSubscription() {
@ -113,14 +89,14 @@ export default {
this.loading = true this.loading = true
await axios.post( await axios.post(
generateUrl('/apps/gpoddersync/subscription_change/create'), generateUrl('/apps/gpoddersync/subscription_change/create'),
{ add: [], remove: [this.url] }, { add: [], remove: [this.metric.url] },
) )
} catch (e) { } catch (e) {
console.error(e) console.error(e)
showError(t('repod', 'Error while removing the feed')) showError(t('repod', 'Error while removing the feed'))
} finally { } finally {
this.setFavorite(this.url, false) this.setFavorite(this.metric.url, false)
this.loading = false this.loading = true
this.fetch() this.fetch()
} }
} }
@ -132,7 +108,7 @@ export default {
return return
} }
} }
this.setFavorite(this.url, value) this.setFavorite(this.metric.url, value)
}, },
}, },
} }

View File

@ -13,12 +13,12 @@
<NcAppNavigationList v-if="!loading"> <NcAppNavigationList v-if="!loading">
<Subscription <Subscription
v-for="sub of subs.filter((sub) => sub.isFavorite)" v-for="sub of subs.filter((sub) => sub.isFavorite)"
:key="sub.metrics.url" :key="sub.url"
:url="sub.metrics.url" /> :metric="sub" />
<Subscription <Subscription
v-for="sub of subs.filter((sub) => !sub.isFavorite)" v-for="sub of subs.filter((sub) => !sub.isFavorite)"
:key="sub.metrics.url" :key="sub.url"
:url="sub.metrics.url" /> :metric="sub" />
</NcAppNavigationList> </NcAppNavigationList>
</NcAppContentList> </NcAppContentList>
</template> </template>

View File

@ -1,20 +1,16 @@
import type {
PersonalSettingsMetricsInterface,
PodcastDataInterface,
SubscriptionInterface,
} from '../utils/types.ts'
import { getCookie, setCookie } from '../utils/cookies.ts' import { getCookie, setCookie } from '../utils/cookies.ts'
import type { PodcastMetricsInterface } from '../utils/types.ts'
import axios from '@nextcloud/axios' import axios from '@nextcloud/axios'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { generateUrl } from '@nextcloud/router' import { generateUrl } from '@nextcloud/router'
export const useSubscriptions = defineStore('subscriptions', { export const useSubscriptions = defineStore('subscriptions', {
state: () => ({ state: () => ({
subs: [] as SubscriptionInterface[], subs: [] as PodcastMetricsInterface[],
}), }),
getters: { getters: {
getSubByUrl: (state) => (url: string) => getSubByUrl: (state) => (url: string) =>
state.subs.find((sub) => sub.metrics.url === url), state.subs.find((sub) => sub.url === url),
}, },
actions: { actions: {
async fetch() { async fetch() {
@ -23,34 +19,24 @@ export const useSubscriptions = defineStore('subscriptions', {
favorites = JSON.parse(getCookie('repod.favorites') || '[]') || [] favorites = JSON.parse(getCookie('repod.favorites') || '[]') || []
} catch {} } catch {}
const metrics = await axios.get<PersonalSettingsMetricsInterface>( const metrics = await axios.get<PodcastMetricsInterface[]>(
generateUrl('/apps/gpoddersync/personal_settings/metrics'), generateUrl('/apps/repod/metrics'),
) )
this.subs = [...metrics.data.subscriptions] this.subs = [...metrics.data].map((sub) => ({
.sort((a, b) => b.listenedSeconds - a.listenedSeconds) ...sub,
.map((sub) => ({ isFavorite: favorites.includes(sub.url),
metrics: sub, }))
isFavorite: favorites.includes(sub.url),
data: this.subs.find((s) => s.metrics.url === sub.url)?.data,
}))
},
addMetadatas(link: string, data: PodcastDataInterface) {
this.subs = this.subs.map((sub) =>
sub.metrics.url === link ? { ...sub, data } : sub,
)
}, },
setFavorite(link: string, isFavorite: boolean) { setFavorite(link: string, isFavorite: boolean) {
this.subs = this.subs.map((sub) => this.subs = this.subs.map((sub) =>
sub.metrics.url === link ? { ...sub, isFavorite } : sub, sub.url === link ? { ...sub, isFavorite } : sub,
) )
setCookie( setCookie(
'repod.favorites', 'repod.favorites',
JSON.stringify( JSON.stringify(
this.subs this.subs.filter((sub) => sub.isFavorite).map((sub) => sub.url),
.filter((sub) => sub.isFavorite)
.map((sub) => sub.metrics.url),
), ),
365, 365,
) )

View File

@ -56,16 +56,8 @@ export interface PodcastMetricsInterface {
new: number new: number
play: number play: number
} }
} data: PodcastDataInterface
isFavorite?: boolean
export interface SubscriptionInterface {
data?: PodcastDataInterface
isFavorite: boolean
metrics: PodcastMetricsInterface
}
export interface PersonalSettingsMetricsInterface {
subscriptions: PodcastMetricsInterface[]
} }
export interface PersonalSettingsPodcastDataInterface { export interface PersonalSettingsPodcastDataInterface {

View File

@ -11,8 +11,8 @@
</template> </template>
</EmptyContent> </EmptyContent>
<ul v-if="favorites.length"> <ul v-if="favorites.length">
<li v-for="favorite in favorites" :key="favorite.metrics.url"> <li v-for="favorite in favorites" :key="favorite.url">
<Favorite :feed="favorite" /> <Favorite :metric="favorite" />
</li> </li>
</ul> </ul>
</AppContent> </AppContent>