Compare commits

...

5 Commits

Author SHA1 Message Date
5a58b253d2 fix: 🚧 wip
All checks were successful
repod / xml (push) Successful in 17s
repod / php (push) Successful in 45s
repod / nodejs (push) Successful in 1m25s
repod / release (push) Has been skipped
2024-10-23 22:38:56 +02:00
7b4c39499a refactor: 🚧 wip 2024-10-23 22:38:56 +02:00
427c6b2ee8 refactor: 🚧 rework metrics 2024-10-23 22:38:56 +02:00
cbe8750fd2 Merge pull request 'chore(deps): update dependency vite to v5.4.10' (#180) from renovate/vite-5.x-lockfile into main
All checks were successful
repod / xml (push) Successful in 10s
repod / php (push) Successful in 41s
repod / nodejs (push) Successful in 1m10s
repod / release (push) Has been skipped
Reviewed-on: #180
2024-10-23 15:44:42 +00:00
Renovate Bot
bc6c22e3c5 chore(deps): update dependency vite to v5.4.10
All checks were successful
repod / xml (push) Successful in 13s
repod / php (push) Successful in 45s
repod / nodejs (push) Successful in 1m24s
repod / release (push) Has been skipped
2024-10-23 06:47:31 +00:00
9 changed files with 108 additions and 103 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);
}
}

6
package-lock.json generated
View File

@ -12360,9 +12360,9 @@
}
},
"node_modules/vite": {
"version": "5.4.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz",
"integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==",
"version": "5.4.10",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz",
"integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==",
"license": "MIT",
"dependencies": {
"esbuild": "^0.21.3",

View File

@ -7,8 +7,8 @@
"dev": "vite --mode development build",
"dev:watch": "vite --mode development build --watch",
"watch": "npm run dev:watch",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"lint": "vue-tsc && eslint src",
"lint:fix": "vue-tsc && eslint src --fix",
"stylelint": "stylelint src/**/*.vue src/**/*.scss src/**/*.css",
"stylelint:fix": "stylelint src/**/*.vue src/**/*.scss src/**/*.css --fix"
},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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