fix(fc3b): SourceFormDialog + SubscriptionsView read platforms store; drop sources.loadPlatforms
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,13 @@
|
|||||||
Adding source for <strong>{{ initialArtist.name }}</strong>
|
Adding source for <strong>{{ initialArtist.name }}</strong>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-select v-model="platform" :items="store.platforms" label="Platform" />
|
<v-select
|
||||||
|
v-model="platform"
|
||||||
|
:items="platformsItems"
|
||||||
|
item-value="value"
|
||||||
|
item-title="title"
|
||||||
|
label="Platform"
|
||||||
|
/>
|
||||||
<v-text-field v-model="url" label="URL" :error-messages="urlError" />
|
<v-text-field v-model="url" label="URL" :error-messages="urlError" />
|
||||||
<v-switch v-model="enabled" label="Enabled" color="accent" hide-details />
|
<v-switch v-model="enabled" label="Enabled" color="accent" hide-details />
|
||||||
|
|
||||||
@@ -65,6 +71,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import { useSourcesStore } from '../../stores/sources.js'
|
import { useSourcesStore } from '../../stores/sources.js'
|
||||||
|
import { usePlatformsStore } from '../../stores/platforms.js'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: { type: Boolean, default: false },
|
modelValue: { type: Boolean, default: false },
|
||||||
@@ -73,6 +80,11 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
const emit = defineEmits(['update:modelValue', 'saved'])
|
const emit = defineEmits(['update:modelValue', 'saved'])
|
||||||
const store = useSourcesStore()
|
const store = useSourcesStore()
|
||||||
|
const platformsStore = usePlatformsStore()
|
||||||
|
|
||||||
|
const platformsItems = computed(() =>
|
||||||
|
platformsStore.list.map(p => ({ value: p.key, title: p.name }))
|
||||||
|
)
|
||||||
|
|
||||||
const artistChoice = ref(null)
|
const artistChoice = ref(null)
|
||||||
const artistMatches = ref([])
|
const artistMatches = ref([])
|
||||||
@@ -128,7 +140,7 @@ const canSave = computed(() => !jsonError.value && !!url.value.trim())
|
|||||||
watch(() => props.modelValue, async (open) => {
|
watch(() => props.modelValue, async (open) => {
|
||||||
if (!open) return
|
if (!open) return
|
||||||
urlError.value = ''; jsonError.value = ''
|
urlError.value = ''; jsonError.value = ''
|
||||||
await store.loadPlatforms()
|
await platformsStore.loadAll()
|
||||||
if (props.source) {
|
if (props.source) {
|
||||||
platform.value = props.source.platform
|
platform.value = props.source.platform
|
||||||
url.value = props.source.url
|
url.value = props.source.url
|
||||||
@@ -139,7 +151,7 @@ watch(() => props.modelValue, async (open) => {
|
|||||||
jsonText.value = JSON.stringify(co, null, 2)
|
jsonText.value = JSON.stringify(co, null, 2)
|
||||||
artistChoice.value = { id: props.source.artist_id, name: props.source.artist_name }
|
artistChoice.value = { id: props.source.artist_id, name: props.source.artist_name }
|
||||||
} else {
|
} else {
|
||||||
platform.value = store.platforms[0] || 'patreon'
|
platform.value = platformsStore.list[0]?.key || 'patreon'
|
||||||
url.value = ''; enabled.value = true
|
url.value = ''; enabled.value = true
|
||||||
structuredVideos.value = true; structuredSince.value = ''
|
structuredVideos.value = true; structuredSince.value = ''
|
||||||
jsonText.value = '{}'
|
jsonText.value = '{}'
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ export const useSourcesStore = defineStore('sources', () => {
|
|||||||
|
|
||||||
// keyed cache: null => all sources, number => artist_id filtered
|
// keyed cache: null => all sources, number => artist_id filtered
|
||||||
const byArtist = ref(new Map())
|
const byArtist = ref(new Map())
|
||||||
const platforms = ref([])
|
|
||||||
const platformsLoaded = ref(false)
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const error = ref(null)
|
const error = ref(null)
|
||||||
|
|
||||||
@@ -40,13 +38,6 @@ export const useSourcesStore = defineStore('sources', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadPlatforms() {
|
|
||||||
if (platformsLoaded.value) return
|
|
||||||
const body = await api.get('/api/sources/platforms')
|
|
||||||
platforms.value = body.platforms
|
|
||||||
platformsLoaded.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function _invalidate(artistId) {
|
function _invalidate(artistId) {
|
||||||
byArtist.value.delete(null)
|
byArtist.value.delete(null)
|
||||||
if (artistId != null) byArtist.value.delete(artistId)
|
if (artistId != null) byArtist.value.delete(artistId)
|
||||||
@@ -99,9 +90,9 @@ export const useSourcesStore = defineStore('sources', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
byArtist, platforms, platformsLoaded, loading, error,
|
byArtist, loading, error,
|
||||||
allSources,
|
allSources,
|
||||||
loadAll, loadForArtist, loadPlatforms,
|
loadAll, loadForArtist,
|
||||||
create, update, remove,
|
create, update, remove,
|
||||||
findOrCreateArtist, autocompleteArtist,
|
findOrCreateArtist, autocompleteArtist,
|
||||||
sourcesByArtistGrouped,
|
sourcesByArtistGrouped,
|
||||||
|
|||||||
@@ -52,12 +52,14 @@
|
|||||||
import { computed, onMounted, ref, watch } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { useSourcesStore } from '../stores/sources.js'
|
import { useSourcesStore } from '../stores/sources.js'
|
||||||
|
import { usePlatformsStore } from '../stores/platforms.js'
|
||||||
import ArtistSection from '../components/subscriptions/ArtistSection.vue'
|
import ArtistSection from '../components/subscriptions/ArtistSection.vue'
|
||||||
import SourceFormDialog from '../components/subscriptions/SourceFormDialog.vue'
|
import SourceFormDialog from '../components/subscriptions/SourceFormDialog.vue'
|
||||||
import ArtistCreateDialog from '../components/subscriptions/ArtistCreateDialog.vue'
|
import ArtistCreateDialog from '../components/subscriptions/ArtistCreateDialog.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const store = useSourcesStore()
|
const store = useSourcesStore()
|
||||||
|
const platformsStore = usePlatformsStore()
|
||||||
|
|
||||||
const artistFilter = computed(() => {
|
const artistFilter = computed(() => {
|
||||||
const raw = route.query.artist_id
|
const raw = route.query.artist_id
|
||||||
@@ -73,7 +75,7 @@ const showArtistDialog = ref(false)
|
|||||||
|
|
||||||
async function refresh() {
|
async function refresh() {
|
||||||
await store.loadAll()
|
await store.loadAll()
|
||||||
await store.loadPlatforms()
|
await platformsStore.loadAll()
|
||||||
if (artistFilter.value != null) {
|
if (artistFilter.value != null) {
|
||||||
openSections.value = new Set([artistFilter.value])
|
openSections.value = new Set([artistFilter.value])
|
||||||
expandAll.value = false
|
expandAll.value = false
|
||||||
|
|||||||
@@ -41,16 +41,6 @@ describe('sources store', () => {
|
|||||||
expect(calls[0]).toContain('artist_id=7')
|
expect(calls[0]).toContain('artist_id=7')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('loadPlatforms caches the platforms list', async () => {
|
|
||||||
const s = useSourcesStore()
|
|
||||||
let n = 0
|
|
||||||
stubFetch(() => { n++; return { status: 200, body: { platforms: ['patreon'] } } })
|
|
||||||
await s.loadPlatforms()
|
|
||||||
await s.loadPlatforms() // second call should hit cache
|
|
||||||
expect(n).toBe(1)
|
|
||||||
expect(s.platforms).toEqual(['patreon'])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('create invalidates the all-cache and the artist-cache', async () => {
|
it('create invalidates the all-cache and the artist-cache', async () => {
|
||||||
const s = useSourcesStore()
|
const s = useSourcesStore()
|
||||||
s.byArtist.set(null, [])
|
s.byArtist.set(null, [])
|
||||||
|
|||||||
Reference in New Issue
Block a user