feat(ui): Select upload destination with funkwhale-ui Card component
This commit is contained in:
parent
0592a68a2f
commit
be6df0fc3e
|
@ -0,0 +1,80 @@
|
|||
<script setup lang="ts">
|
||||
import type { Channel } from '~/types'
|
||||
|
||||
import { momentFormat } from '~/utils/filters'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useStore } from '~/store'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import moment from 'moment'
|
||||
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
|
||||
interface Props {
|
||||
channel: Channel
|
||||
callback: (object:Channel)=>void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const store = useStore()
|
||||
|
||||
const fallbackImageUrl = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzODQgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNi4wIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTM4MS45IDM4OC4yYy02LjQgMjcuNC0yNy4yIDQyLjgtNTUuMSA0OC0yNC41IDQuNS00NC45IDUuNi02NC41LTEwLjItMjMuOS0yMC4xLTI0LjItNTMuNC0yLjctNzQuNCAxNy0xNi4yIDQwLjktMTkuNSA3Ni44LTI1LjggNi0xLjEgMTEuMi0yLjUgMTUuNi03LjQgNi40LTcuMiA0LjQtNC4xIDQuNC0xNjMuMiAwLTExLjItNS41LTE0LjMtMTctMTIuMy04LjIgMS40LTE4NS43IDM0LjYtMTg1LjcgMzQuNi0xMC4yIDIuMi0xMy40IDUuMi0xMy40IDE2LjcgMCAyMzQuNyAxLjEgMjIzLjktMi41IDIzOS41LTQuMiAxOC4yLTE1LjQgMzEuOS0zMC4yIDM5LjUtMTYuOCA5LjMtNDcuMiAxMy40LTYzLjQgMTAuNC00My4yLTguMS01OC40LTU4LTI5LjEtODYuNiAxNy0xNi4yIDQwLjktMTkuNSA3Ni44LTI1LjggNi0xLjEgMTEuMi0yLjUgMTUuNi03LjQgMTAuMS0xMS41IDEuOC0yNTYuNiA1LjItMjcwLjIgLjgtNS4yIDMtOS42IDcuMS0xMi45IDQuMi0zLjUgMTEuOC01LjUgMTMuNC01LjUgMjA0LTM4LjIgMjI4LjktNDMuMSAyMzIuNC00My4xIDExLjUtLjggMTguMSA2IDE4LjEgMTcuNiAuMiAzNDQuNSAxLjEgMzI2LTEuOCAzMzguNXoiLz48L3N2Zz4='
|
||||
|
||||
/* TODO: Replace with actual target: */
|
||||
const imageUrl = computed(() => props.channel.artist?.cover
|
||||
? store.getters['instance/absoluteUrl'](props.channel.artist?.cover.urls.medium_square_crop)
|
||||
: fallbackImageUrl
|
||||
)
|
||||
const urlId = computed(() => props.channel.actor?.is_local
|
||||
? props.channel.actor.preferred_username
|
||||
: props.channel.actor
|
||||
? props.channel.actor.full_username
|
||||
: props.channel.uuid
|
||||
)
|
||||
|
||||
const { t } = useI18n()
|
||||
const updatedTitle = computed(() => {
|
||||
const date = momentFormat(new Date(props.channel.artist?.modification_date ?? '1970-01-01'))
|
||||
return t('components.audio.ChannelCard.title', { date })
|
||||
})
|
||||
|
||||
const updatedAgo = computed(() => moment(props.channel.artist?.modification_date).fromNow())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<fw-button @click="callback(channel)" style="border:transparent; background:transparent;">
|
||||
<fw-card
|
||||
v-bind:title="channel.artist?.name"
|
||||
v-bind:image="imageUrl"
|
||||
>
|
||||
|
||||
<div class="description" style="display:flex; justify-content:space-between; min-height:32px; align-items: baseline;" :title="updatedTitle">
|
||||
<!-- <p>
|
||||
{{ channel.artist?.description.text }}
|
||||
</p> -->
|
||||
<span
|
||||
class="meta ellipsis"
|
||||
>
|
||||
{{ $t('components.audio.ChannelCard.meta.tracks', channel.artist?.tracks_count ?? 0) }}
|
||||
</span>
|
||||
<tags-list
|
||||
style="pointer-events:none;"
|
||||
label-classes="tiny"
|
||||
:truncate-size="20"
|
||||
:limit="2"
|
||||
:show-more="false"
|
||||
:tags="channel.artist?.tags ?? []"
|
||||
/>
|
||||
</div>
|
||||
<!-- <div class="extra content">
|
||||
<time
|
||||
class="meta ellipsis"
|
||||
:datetime="channel.artist?.modification_date"
|
||||
:title="updatedTitle"
|
||||
>
|
||||
{{ updatedAgo }}
|
||||
</time>
|
||||
</div> -->
|
||||
</fw-card>
|
||||
</fw-button>
|
||||
</template>
|
|
@ -0,0 +1,80 @@
|
|||
<script setup lang="ts">
|
||||
import type { Library, PrivacyLevel } from '~/types'
|
||||
|
||||
import { humanSize } from '~/utils/filters'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
interface Props {
|
||||
library: Library
|
||||
}
|
||||
interface Props {
|
||||
new : boolean
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const title = computed(() => props.library?.name || props.new)
|
||||
|
||||
const imageUrl = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNi4wIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTI1NiA4MEMxNDkuOSA4MCA2Mi40IDE1OS40IDQ5LjYgMjYyYzkuNC0zLjggMTkuNi02IDMwLjQtNmMyNi41IDAgNDggMjEuNSA0OCA0OGwwIDEyOGMwIDI2LjUtMjEuNSA0OC00OCA0OGMtNDQuMiAwLTgwLTM1LjgtODAtODBsMC0xNiAwLTQ4IDAtNDhDMCAxNDYuNiAxMTQuNiAzMiAyNTYgMzJzMjU2IDExNC42IDI1NiAyNTZsMCA0OCAwIDQ4IDAgMTZjMCA0NC4yLTM1LjggODAtODAgODBjLTI2LjUgMC00OC0yMS41LTQ4LTQ4bDAtMTI4YzAtMjYuNSAyMS41LTQ4IDQ4LTQ4YzEwLjggMCAyMSAyLjEgMzAuNCA2QzQ0OS42IDE1OS40IDM2Mi4xIDgwIDI1NiA4MHoiLz48L3N2Zz4='
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const sharedLabels = useSharedLabels()
|
||||
|
||||
const sizeLabel = computed(() => t('views.content.libraries.Card.label.size'))
|
||||
|
||||
const privacyTooltips = (level: PrivacyLevel) => `Visibility: ${sharedLabels.fields.privacy_level.choices[level].toLowerCase()}`
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<fw-card
|
||||
v-bind:title="title"
|
||||
v-bind:image="imageUrl"
|
||||
>
|
||||
<div class="content">
|
||||
<h4 class="header">
|
||||
<span
|
||||
v-if="library.privacy_level === 'me'"
|
||||
class="right floated"
|
||||
:data-tooltip="privacyTooltips('me')"
|
||||
>
|
||||
<i class="small lock icon" />
|
||||
</span>
|
||||
<span
|
||||
v-else-if="library.privacy_level === 'instance'"
|
||||
class="right floated"
|
||||
:data-tooltip="privacyTooltips('instance')"
|
||||
>
|
||||
<i class="small circle outline icon" />
|
||||
</span>
|
||||
<span
|
||||
v-else-if="library.privacy_level === 'everyone'"
|
||||
class="right floated"
|
||||
:data-tooltip="privacyTooltips('everyone')"
|
||||
>
|
||||
<i class="small globe icon" />
|
||||
</span>
|
||||
</h4>
|
||||
|
||||
<div class="description">
|
||||
{{ library.description }}
|
||||
<div class="ui hidden divider" />
|
||||
</div>
|
||||
<div class="content">
|
||||
<i class="music icon" />
|
||||
{{ $t('views.content.libraries.Card.meta.tracks', library.uploads_count) }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="ui bottom basic attached buttons">
|
||||
<router-link
|
||||
:to="{name: 'library.detail.upload', params: {id: library.uuid}}"
|
||||
class="ui button"
|
||||
>
|
||||
{{ $t('views.content.libraries.Card.button.upload') }}
|
||||
</router-link>
|
||||
</div> -->
|
||||
</fw-card>
|
||||
</template>
|
|
@ -0,0 +1,161 @@
|
|||
<script setup lang="ts">
|
||||
import { humanSize } from '~/utils/filters'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { computed, ref, type Ref, defineAsyncComponent } from 'vue'
|
||||
import { useStore } from '~/store'
|
||||
|
||||
// LIBRARIES BEGIN
|
||||
|
||||
import type { Library, Channel } from '~/types'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
import LibraryForm from '../libraries/Form.vue'
|
||||
import LibraryCard from '../libraries/CardUpload.vue'
|
||||
import ChannelCard from '../channels/CardUpload.vue'
|
||||
import Quota from '../libraries/Quota.vue'
|
||||
import Upload from '~/ui/pages/upload.vue'
|
||||
// import UploadModal from '~/ui/pages/upload.vue'
|
||||
// const ChannelUploadModal = defineAsyncComponent(() => import('~/components/channels/UploadModal.vue'))
|
||||
|
||||
import useErrorHandler from '~/composables/useErrorHandler'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const libraries = ref([] as Library[])
|
||||
const channels = ref([] as Channel[])
|
||||
|
||||
const musicChannels = computed(() => channels.value.filter((channel=>channel.artist?.content_category !== 'podcast')))
|
||||
const podcastChannels = computed(() => channels.value.filter((channel=>channel.artist?.content_category === 'podcast')))
|
||||
|
||||
const isLoading = ref(false)
|
||||
const hiddenForm = ref(true)
|
||||
const fetchLibraries = async () => {
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
const response = await axios.get('libraries/', { params: { scope: 'me' } })
|
||||
libraries.value = response.data.results
|
||||
if (libraries.value.length === 0) {
|
||||
hiddenForm.value = false
|
||||
}
|
||||
} catch (error) {
|
||||
useErrorHandler(error as Error)
|
||||
}
|
||||
|
||||
isLoading.value = false
|
||||
}
|
||||
const fetchChannels = async () => {
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
const response = await axios.get('channels/', { params: { scope: 'me' } })
|
||||
channels.value = response.data.results
|
||||
if (channels.value.length === 0) {
|
||||
hiddenForm.value = false
|
||||
}
|
||||
} catch (error) {
|
||||
useErrorHandler(error as Error)
|
||||
}
|
||||
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
fetchLibraries()
|
||||
fetchChannels()
|
||||
|
||||
const libraryCreated = (library: Library) => {
|
||||
router.SpEush({ name: 'library.detail', params: { id: library.uuid } })
|
||||
}
|
||||
|
||||
// LIBRARIES END
|
||||
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const labels = computed(() => ({
|
||||
title: t('views.content.Home.title')
|
||||
}))
|
||||
|
||||
const store = useStore()
|
||||
|
||||
console.log(store.state)
|
||||
|
||||
const quota = computed(() => store.state.instance.settings.users.upload_quota.value)
|
||||
const defaultQuota = computed(() => humanSize(quota.value * 1e6))
|
||||
|
||||
|
||||
|
||||
// Plus Icon
|
||||
|
||||
const plusIcon = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBVcGxvYWRlZCB0bzogU1ZHIFJlcG8sIHd3dy5zdmdyZXBvLmNvbSwgR2VuZXJhdG9yOiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4NCjxzdmcgZmlsbD0iIzAwMDAwMCIgaGVpZ2h0PSI4MDBweCIgd2lkdGg9IjgwMHB4IiB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIA0KCSB2aWV3Qm94PSIwIDAgNDkwIDQ5MCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8Zz4NCgk8Zz4NCgkJPGc+DQoJCQk8cGF0aCBkPSJNMjI3LjgsMTc0LjF2NTMuN2gtNTMuN2MtOS41LDAtMTcuMiw3LjctMTcuMiwxNy4yczcuNywxNy4yLDE3LjIsMTcuMmg1My43djUzLjdjMCw5LjUsNy43LDE3LjIsMTcuMiwxNy4yDQoJCQkJczE3LjEtNy43LDE3LjEtMTcuMnYtNTMuN2g1My43YzkuNSwwLDE3LjItNy43LDE3LjItMTcuMnMtNy43LTE3LjItMTcuMi0xNy4yaC01My43di01My43YzAtOS41LTcuNy0xNy4yLTE3LjEtMTcuMg0KCQkJCVMyMjcuOCwxNjQuNiwyMjcuOCwxNzQuMXoiLz4NCgkJCTxwYXRoIGQ9Ik03MS43LDcxLjdDMjUuNSwxMTgsMCwxNzkuNSwwLDI0NXMyNS41LDEyNyw3MS44LDE3My4zQzExOCw0NjQuNSwxNzkuNiw0OTAsMjQ1LDQ5MHMxMjctMjUuNSwxNzMuMy03MS44DQoJCQkJQzQ2NC41LDM3Miw0OTAsMzEwLjQsNDkwLDI0NXMtMjUuNS0xMjctNzEuOC0xNzMuM0MzNzIsMjUuNSwzMTAuNSwwLDI0NSwwQzE3OS42LDAsMTE4LDI1LjUsNzEuNyw3MS43eiBNNDU1LjcsMjQ1DQoJCQkJYzAsNTYuMy0yMS45LDEwOS4yLTYxLjcsMTQ5cy05Mi43LDYxLjctMTQ5LDYxLjdTMTM1LjgsNDMzLjgsOTYsMzk0cy02MS43LTkyLjctNjEuNy0xNDlTNTYuMiwxMzUuOCw5Niw5NnM5Mi43LTYxLjcsMTQ5LTYxLjcNCgkJCQlTMzU0LjIsNTYuMiwzOTQsOTZTNDU1LjcsMTg4LjcsNDU1LjcsMjQ1eiIvPg0KCQk8L2c+DQoJPC9nPg0KCTxnPg0KCTwvZz4NCgk8Zz4NCgk8L2c+DQoJPGc+DQoJPC9nPg0KCTxnPg0KCTwvZz4NCgk8Zz4NCgk8L2c+DQoJPGc+DQoJPC9nPg0KCTxnPg0KCTwvZz4NCgk8Zz4NCgk8L2c+DQoJPGc+DQoJPC9nPg0KCTxnPg0KCTwvZz4NCgk8Zz4NCgk8L2c+DQoJPGc+DQoJPC9nPg0KCTxnPg0KCTwvZz4NCgk8Zz4NCgk8L2c+DQoJPGc+DQoJPC9nPg0KPC9nPg0KPC9zdmc+"
|
||||
|
||||
|
||||
|
||||
// Open modal
|
||||
|
||||
const upload = ref(false)
|
||||
const object:Ref<Library | Channel | undefined> = ref()
|
||||
|
||||
const openModal = (object_: Library | Channel) => {
|
||||
// upload.value = true;
|
||||
object.value = object_;
|
||||
|
||||
//open old modal model
|
||||
store.state.channels.showUploadModal = true;
|
||||
store.state.channels.uploadModalConfig = { channel: "artist" in object ? (object as Ref<Channel>).value : null }
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section
|
||||
v-title="labels.title"
|
||||
class="ui vertical aligned stripe segment"
|
||||
>
|
||||
<div class="ui text container">
|
||||
<h1>{{ labels.title }}</h1>
|
||||
<p>
|
||||
<strong>{{ $t('views.content.Home.help.uploadQuota', { quota: defaultQuota }) }}</strong>
|
||||
</p>
|
||||
<hr/>
|
||||
<h3>Choose a library:</h3>
|
||||
<fw-card
|
||||
v-bind:image="plusIcon"
|
||||
:title="'New Library'"
|
||||
></fw-card>
|
||||
|
||||
<library-card v-for="library in libraries"
|
||||
:key="library.uuid" :library="library" />
|
||||
|
||||
<h3>Choose a music channel:</h3>
|
||||
<section style="display:flex;margin:-16px;flex-wrap:wrap;">
|
||||
|
||||
<channel-card v-for="channel in musicChannels"
|
||||
:key="channel.uuid" :channel="channel" :callback="openModal" />
|
||||
|
||||
</section>
|
||||
|
||||
<h3>Choose a podcast channel:</h3>
|
||||
<section style="display:flex;gap:16px;flex-wrap:wrap;">
|
||||
|
||||
<channel-card
|
||||
v-for="channel in podcastChannels"
|
||||
:key="channel.uuid"
|
||||
class="column"
|
||||
:channel="channel" :callback="openModal" />
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<fw-modal v-model="upload" :title="`Upload to ${
|
||||
object ?
|
||||
'artist' in object ?
|
||||
object.artist?.name
|
||||
: 'I am a library'
|
||||
: 'I am undefined'}`">
|
||||
<upload></upload>
|
||||
</fw-modal>
|
||||
<!-- <channel-upload-modal v-if="store.state.auth.authenticated" /> -->
|
||||
</template>
|
Loading…
Reference in New Issue