Migrate a bunch of components
This commit is contained in:
parent
de4f445e9b
commit
c5f7022869
|
@ -1,3 +1,20 @@
|
|||
<script setup lang="ts">
|
||||
import type { Artist } from '~/types'
|
||||
|
||||
import { computed } from 'vue'
|
||||
|
||||
interface Props {
|
||||
artist: Artist
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const route = computed(() => props.artist.channel
|
||||
? { name: 'channels.detail', params: { id: props.artist.channel.uuid } }
|
||||
: { name: 'library.artists.detail', params: { id: props.artist.id } }
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-link
|
||||
class="artist-label ui image label"
|
||||
|
@ -16,20 +33,3 @@
|
|||
{{ artist.name }}
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
artist: { type: Object, required: true }
|
||||
},
|
||||
computed: {
|
||||
route () {
|
||||
if (this.artist.channel) {
|
||||
return { name: 'channels.detail', params: { id: this.artist.channel.uuid } }
|
||||
}
|
||||
return { name: 'library.artists.detail', params: { id: this.artist.id } }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,44 @@
|
|||
<script setup lang="ts">
|
||||
import type { Channel } from '~/types'
|
||||
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
import { momentFormat } from '~/utils/filters'
|
||||
import { useStore } from '~/store'
|
||||
import { computed } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import moment from 'moment'
|
||||
|
||||
interface Props {
|
||||
// TODO (wvffle) : Find type
|
||||
object: Channel
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const store = useStore()
|
||||
|
||||
const imageUrl = computed(() => props.object.artist?.cover
|
||||
? store.getters['instance/absoluteUrl'](props.object.artist.cover.urls.medium_square_crop)
|
||||
: null
|
||||
)
|
||||
|
||||
const urlId = computed(() => props.object.actor?.is_local
|
||||
? props.object.actor.preferred_username
|
||||
: props.object.actor
|
||||
? props.object.actor.full_username
|
||||
: props.object.uuid
|
||||
)
|
||||
|
||||
const { $pgettext } = useGettext()
|
||||
const updatedTitle = computed(() => {
|
||||
const date = momentFormat(new Date(props.object.artist?.modification_date ?? '1970-01-01'))
|
||||
return $pgettext('*/*/*', 'Updated on %{ date }', { date })
|
||||
})
|
||||
|
||||
// TODO (wvffle): Use time ago
|
||||
const updatedAgo = computed(() => moment(props.object.artist?.modification_date).fromNow())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card app-card">
|
||||
<div
|
||||
|
@ -52,7 +93,8 @@
|
|||
</div>
|
||||
<div class="extra content">
|
||||
<time
|
||||
v-translate
|
||||
v-translate="{ updatedAgo }"
|
||||
:translate-params="{ updatedAgo }"
|
||||
class="meta ellipsis"
|
||||
:datetime="object.artist.modification_date"
|
||||
:title="updatedTitle"
|
||||
|
@ -71,46 +113,3 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
|
||||
import { momentFormat } from '~/utils/filters'
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PlayButton,
|
||||
TagsList
|
||||
},
|
||||
props: {
|
||||
object: { type: Object, required: true }
|
||||
},
|
||||
computed: {
|
||||
imageUrl () {
|
||||
if (this.object.artist.cover) {
|
||||
return this.$store.getters['instance/absoluteUrl'](this.object.artist.cover.urls.medium_square_crop)
|
||||
}
|
||||
return null
|
||||
},
|
||||
urlId () {
|
||||
if (this.object.actor && this.object.actor.is_local) {
|
||||
return this.object.actor.preferred_username
|
||||
} else if (this.object.actor) {
|
||||
return this.object.actor.full_username
|
||||
} else {
|
||||
return this.object.uuid
|
||||
}
|
||||
},
|
||||
updatedTitle () {
|
||||
const d = momentFormat(this.object.artist.modification_date)
|
||||
const message = this.$pgettext('*/*/*', 'Updated on %{ date }')
|
||||
return this.$gettextInterpolate(message, { date: d })
|
||||
},
|
||||
updatedAgo () {
|
||||
return moment(this.object.artist.modification_date).fromNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,30 @@
|
|||
<script setup lang="ts">
|
||||
import type { Cover, Track } from '~/types'
|
||||
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
|
||||
import useQueue from '~/composables/audio/useQueue'
|
||||
import usePlayer from '~/composables/audio/usePlayer'
|
||||
import { computed } from 'vue'
|
||||
|
||||
|
||||
interface Props {
|
||||
// TODO (wvffle): Is it correct type?
|
||||
entry: Track
|
||||
defaultCover: Cover
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const { currentTrack } = useQueue()
|
||||
const { playing } = usePlayer()
|
||||
|
||||
const cover = computed(() => props.entry.cover ?? null)
|
||||
const duration = computed(() => props.entry.uploads.find(upload => upload.duration)?.duration ?? null)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="[{active: currentTrack && isPlaying && entry.id === currentTrack.id}, 'channel-entry-card']">
|
||||
<div :class="[{active: currentTrack && playing && entry.id === currentTrack.id}, 'channel-entry-card']">
|
||||
<div class="controls">
|
||||
<play-button
|
||||
class="basic circular icon"
|
||||
|
@ -75,45 +100,3 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PlayButton,
|
||||
TrackFavoriteIcon
|
||||
},
|
||||
props: {
|
||||
entry: { type: Object, required: true },
|
||||
defaultCover: { type: Object, required: true }
|
||||
},
|
||||
computed: {
|
||||
|
||||
...mapGetters({
|
||||
currentTrack: 'queue/currentTrack'
|
||||
}),
|
||||
|
||||
isPlaying () {
|
||||
return this.$store.state.player.playing
|
||||
},
|
||||
cover () {
|
||||
if (this.entry.cover) {
|
||||
return this.entry.cover
|
||||
}
|
||||
return null
|
||||
},
|
||||
duration () {
|
||||
const uploads = this.entry.uploads.filter((e) => {
|
||||
return e.duration
|
||||
})
|
||||
if (uploads.length > 0) {
|
||||
return uploads[0].duration
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
<script setup lang="ts">
|
||||
import type { Album } from '~/types'
|
||||
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
interface Props {
|
||||
serie: Album
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const cover = computed(() => props.serie?.cover ?? null)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="channel-serie-card">
|
||||
<div class="two-images">
|
||||
|
@ -60,28 +75,3 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PlayButton
|
||||
},
|
||||
props: { serie: { type: Object, required: true } },
|
||||
computed: {
|
||||
cover () {
|
||||
if (this.serie.cover) {
|
||||
return this.serie.cover
|
||||
}
|
||||
return null
|
||||
},
|
||||
duration () {
|
||||
const uploads = this.serie.uploads.filter((e) => {
|
||||
return e.duration
|
||||
})
|
||||
return uploads[0].duration
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,32 @@
|
|||
<script setup lang="ts">
|
||||
import type { Library } from '~/types'
|
||||
|
||||
import { computed } from 'vue'
|
||||
import { useStore } from '~/store'
|
||||
|
||||
interface Props {
|
||||
library: Library
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const store = useStore()
|
||||
const follow = computed(() => store.getters['libraries/follow'](props.library.uuid))
|
||||
const isPending = computed(() => follow.value && follow.value.approved === null)
|
||||
const isApproved = computed(() => follow.value && (follow.value?.approved === true || (isPending.value && props.library.privacy_level === 'everyone')))
|
||||
|
||||
const emit = defineEmits(['followed', 'unfollowed'])
|
||||
const toggle = () => {
|
||||
if (isPending.value || isApproved.value) {
|
||||
emit('unfollowed')
|
||||
} else {
|
||||
emit('followed')
|
||||
}
|
||||
|
||||
return store.dispatch('libraries/toggle', props.library.uuid)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
:class="['ui', 'pink', {'inverted': isApproved || isPending}, {'favorited': isApproved}, 'icon', 'labeled', 'button']"
|
||||
|
@ -24,33 +53,3 @@
|
|||
</translate>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
library: { type: Object, required: true }
|
||||
},
|
||||
computed: {
|
||||
isPending () {
|
||||
return this.follow && this.follow.approved === null
|
||||
},
|
||||
isApproved () {
|
||||
return this.follow && (this.follow.approved === true || (this.follow.approved === null && this.library.privacy_level === 'everyone'))
|
||||
},
|
||||
follow () {
|
||||
return this.$store.getters['libraries/follow'](this.library.uuid)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle () {
|
||||
if (this.isApproved || this.isPending) {
|
||||
this.$emit('unfollowed')
|
||||
} else {
|
||||
this.$emit('followed')
|
||||
}
|
||||
this.$store.dispatch('libraries/toggle', this.library.uuid)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,30 @@
|
|||
<script setup lang="ts">
|
||||
import type { Artist } from '~/types'
|
||||
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
import { computed } from 'vue'
|
||||
import { useStore } from '~/store'
|
||||
import { truncate } from '~/utils/filters'
|
||||
|
||||
interface Props {
|
||||
artist: Artist
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const cover = computed(() => !props.artist.cover?.urls.original
|
||||
? props.artist.albums.find(album => !!album.cover?.urls.original)?.cover
|
||||
: props.artist.cover
|
||||
)
|
||||
|
||||
const store = useStore()
|
||||
const imageUrl = computed(() => cover.value?.urls.original
|
||||
? store.getters['instance/absoluteUrl'](cover.value.urls.medium_square_crop)
|
||||
: null
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="app-card card">
|
||||
<router-link
|
||||
|
@ -63,45 +90,3 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
import { truncate } from '~/utils/filters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PlayButton,
|
||||
TagsList
|
||||
},
|
||||
props: { artist: { type: Object, required: true } },
|
||||
setup () {
|
||||
return { truncate }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
initialAlbums: 30,
|
||||
showAllAlbums: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
imageUrl () {
|
||||
const cover = this.cover
|
||||
if (cover && cover.urls.original) {
|
||||
return this.$store.getters['instance/absoluteUrl'](cover.urls.medium_square_crop)
|
||||
}
|
||||
return null
|
||||
},
|
||||
cover () {
|
||||
if (this.artist.cover && this.artist.cover.urls.original) {
|
||||
return this.artist.cover
|
||||
}
|
||||
return this.artist.albums.map((a) => {
|
||||
return a.cover
|
||||
}).filter((c) => {
|
||||
return c && c.urls.original
|
||||
})[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,40 @@
|
|||
<script setup lang="ts">
|
||||
import type { Track } from '~/types'
|
||||
|
||||
import PodcastRow from '~/components/audio/podcast/Row.vue'
|
||||
import TrackMobileRow from '~/components/audio/track/MobileRow.vue'
|
||||
import Pagination from '~/components/vui/Pagination.vue'
|
||||
|
||||
interface Props {
|
||||
tracks: Track[]
|
||||
showPosition?: boolean
|
||||
showArt?: boolean
|
||||
showDuration?: boolean
|
||||
displayActions?: boolean
|
||||
isArtist?: boolean
|
||||
isAlbum?: boolean
|
||||
isPodcast?: boolean
|
||||
paginateResults?: boolean
|
||||
paginateBy?: number
|
||||
page?: number
|
||||
total?: number
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
showPosition: false,
|
||||
showArt: true,
|
||||
showDuration: true,
|
||||
displayActions: true,
|
||||
isArtist: false,
|
||||
isAlbum: false,
|
||||
isPodcast: true,
|
||||
paginateResults: true,
|
||||
paginateBy: 25,
|
||||
page: 1,
|
||||
total: 0
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="ui hidden divider" />
|
||||
|
@ -36,12 +73,6 @@
|
|||
</div>
|
||||
|
||||
<div :class="['track-table', 'ui', 'unstackable', 'grid', 'tablet-and-below']">
|
||||
<div
|
||||
v-if="isLoading"
|
||||
class="ui inverted active dimmer"
|
||||
>
|
||||
<div class="ui loader" />
|
||||
</div>
|
||||
|
||||
<!-- For each item, build a row -->
|
||||
|
||||
|
@ -73,54 +104,3 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PodcastRow from '~/components/audio/podcast/Row.vue'
|
||||
import TrackMobileRow from '~/components/audio/track/MobileRow.vue'
|
||||
import Pagination from '~/components/vui/Pagination.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TrackMobileRow,
|
||||
Pagination,
|
||||
PodcastRow
|
||||
},
|
||||
|
||||
props: {
|
||||
tracks: { type: Array, required: true },
|
||||
showAlbum: { type: Boolean, required: false, default: true },
|
||||
showArtist: { type: Boolean, required: false, default: true },
|
||||
showPosition: { type: Boolean, required: false, default: false },
|
||||
showArt: { type: Boolean, required: false, default: true },
|
||||
search: { type: Boolean, required: false, default: false },
|
||||
filters: { type: Object, required: false, default: null },
|
||||
nextUrl: { type: String, required: false, default: null },
|
||||
displayActions: { type: Boolean, required: false, default: true },
|
||||
showDuration: { type: Boolean, required: false, default: true },
|
||||
isArtist: { type: Boolean, required: false, default: false },
|
||||
isAlbum: { type: Boolean, required: false, default: false },
|
||||
paginateResults: { type: Boolean, required: false, default: true },
|
||||
total: { type: Number, required: false, default: 0 },
|
||||
page: { type: Number, required: false, default: 1 },
|
||||
paginateBy: { type: Number, required: false, default: 25 },
|
||||
isPodcast: { type: Boolean, required: true },
|
||||
defaultCover: { type: Object, required: false, default: () => { return {} } }
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
labels () {
|
||||
return {
|
||||
title: this.$pgettext('*/*/*/Noun', 'Title'),
|
||||
album: this.$pgettext('*/*/*/Noun', 'Album'),
|
||||
artist: this.$pgettext('*/*/*/Noun', 'Artist')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,32 @@
|
|||
<script setup lang="ts">
|
||||
import ApplicationForm from '~/components/auth/ApplicationForm.vue'
|
||||
import { computed, reactive } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
interface Props {
|
||||
name?: string
|
||||
scopes?: string
|
||||
redirectUris?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
name: '',
|
||||
scopes: '',
|
||||
redirectUris: ''
|
||||
})
|
||||
|
||||
const defaults = reactive({
|
||||
name: props.name,
|
||||
scopes: props.scopes,
|
||||
redirectUris: props.redirectUris,
|
||||
})
|
||||
|
||||
const { $pgettext } = useGettext()
|
||||
const labels = computed(() => ({
|
||||
title: $pgettext('Content/Settings/Button.Label', 'Create a new application')
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main
|
||||
v-title="labels.title"
|
||||
|
@ -23,36 +52,3 @@
|
|||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ApplicationForm from '~/components/auth/ApplicationForm.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ApplicationForm
|
||||
},
|
||||
props: {
|
||||
name: { type: String, default: '' },
|
||||
redirectUris: { type: String, default: '' },
|
||||
scopes: { type: String, default: '' }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
application: null,
|
||||
isLoading: false,
|
||||
defaults: {
|
||||
name: this.name,
|
||||
redirectUris: this.redirectUris,
|
||||
scopes: this.scopes
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
labels () {
|
||||
return {
|
||||
title: this.$pgettext('Content/Settings/Button.Label', 'Create a new application')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
const { $pgettext } = useGettext()
|
||||
const labels = computed(() => ({
|
||||
title: $pgettext('Head/Login/Title', 'Log Out')
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main
|
||||
v-title="labels.title"
|
||||
|
@ -49,15 +59,3 @@
|
|||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
computed: {
|
||||
labels () {
|
||||
return {
|
||||
title: this.$pgettext('Head/Login/Title', 'Log Out')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,28 @@
|
|||
<script setup lang="ts">
|
||||
import type { Channel } from '~/types'
|
||||
import SemanticModal from '~/components/semantic/Modal.vue'
|
||||
import ChannelAlbumForm from '~/components/channels/AlbumForm.vue'
|
||||
import { watch, ref } from 'vue'
|
||||
|
||||
interface Props {
|
||||
channel: Channel
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
||||
const isLoading = ref(false)
|
||||
const submittable = ref(false)
|
||||
const show = ref(false)
|
||||
|
||||
watch(show, () => {
|
||||
isLoading.value = false
|
||||
submittable.value = false
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<modal
|
||||
<semantic-modal
|
||||
v-model:show="show"
|
||||
class="small"
|
||||
>
|
||||
|
@ -34,7 +57,7 @@
|
|||
</button>
|
||||
<button
|
||||
:class="['ui', 'primary', {loading: isLoading}, 'button']"
|
||||
:disabled="!submittable || null"
|
||||
:disabled="!submittable"
|
||||
@click.stop.prevent="$refs.albumForm.submit()"
|
||||
>
|
||||
<translate translate-context="*/*/Button.Label">
|
||||
|
@ -42,31 +65,5 @@
|
|||
</translate>
|
||||
</button>
|
||||
</div>
|
||||
</modal>
|
||||
</semantic-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Modal from '~/components/semantic/Modal.vue'
|
||||
import ChannelAlbumForm from '~/components/channels/AlbumForm.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Modal,
|
||||
ChannelAlbumForm
|
||||
},
|
||||
props: { channel: { type: Object, required: true } },
|
||||
data () {
|
||||
return {
|
||||
isLoading: false,
|
||||
submittable: false,
|
||||
show: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show () {
|
||||
this.isLoading = false
|
||||
this.submittable = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
<script setup lang="ts">
|
||||
import type { Actor } from '~/types'
|
||||
|
||||
import { hashCode, intToRGB } from '~/utils/color'
|
||||
import { computed } from 'vue'
|
||||
|
||||
interface Props {
|
||||
actor: Actor
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const actorColor = computed(() => intToRGB(hashCode(props.actor.full_username)))
|
||||
const defaultAvatarStyle = computed(() => ({ backgroundColor: `#${actorColor.value}` }))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<img
|
||||
v-if="actor.icon && actor.icon.urls.original"
|
||||
|
@ -11,21 +27,3 @@
|
|||
class="ui avatar circular label"
|
||||
>{{ actor.preferred_username[0] }}</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { hashCode, intToRGB } from '~/utils/color'
|
||||
|
||||
export default {
|
||||
props: { actor: { type: Object, required: true } },
|
||||
computed: {
|
||||
actorColor () {
|
||||
return intToRGB(hashCode(this.actor.full_username))
|
||||
},
|
||||
defaultAvatarStyle () {
|
||||
return {
|
||||
'background-color': `#${this.actorColor}`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
<script setup lang="ts">
|
||||
interface Props {
|
||||
refresh?: boolean
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
refresh: false
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ui small placeholder segment component-placeholder component-empty-state">
|
||||
<h4 class="ui header">
|
||||
|
@ -23,11 +33,4 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
refresh: { type: Boolean, default: false }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</template>
|
|
@ -1,15 +1,15 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
interface Props {
|
||||
content: string
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span
|
||||
class="tooltip"
|
||||
:data-tooltip="content"
|
||||
><i class="question circle icon" /></span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
content: { type: String, required: true }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
<script setup lang="ts">
|
||||
import type { User } from '~/types'
|
||||
|
||||
import { hashCode, intToRGB } from '~/utils/color'
|
||||
import { computed } from 'vue'
|
||||
|
||||
interface Props {
|
||||
user: User
|
||||
avatar?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
avatar: true
|
||||
})
|
||||
|
||||
const userColor = computed(() => intToRGB(hashCode(props.user.username + props.user.id)))
|
||||
const defaultAvatarStyle = computed(() => ({ backgroundColor: `#${userColor.value}` }))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span class="component-user-link">
|
||||
<template v-if="avatar">
|
||||
|
@ -17,24 +36,3 @@
|
|||
@{{ user.username }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { hashCode, intToRGB } from '~/utils/color'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
user: { type: Object, required: true },
|
||||
avatar: { type: Boolean, default: true }
|
||||
},
|
||||
computed: {
|
||||
userColor () {
|
||||
return intToRGB(hashCode(this.user.username + String(this.user.id)))
|
||||
},
|
||||
defaultAvatarStyle () {
|
||||
return {
|
||||
'background-color': `#${this.userColor}`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
interface Props {
|
||||
username: string
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span>{{ username }}</span>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: { username: { type: String, required: true } }
|
||||
}
|
||||
</script>
|
||||
</template>
|
|
@ -1,3 +1,34 @@
|
|||
<script setup lang="ts">
|
||||
import type { Note } from '~/types'
|
||||
|
||||
import axios from 'axios'
|
||||
import showdown from 'showdown'
|
||||
import { ref } from 'vue'
|
||||
|
||||
interface Props {
|
||||
notes: Note[]
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
||||
const markdown = new showdown.Converter()
|
||||
|
||||
const emit = defineEmits(['deleted'])
|
||||
const isLoading = ref(false)
|
||||
const remove = async (note: Note) => {
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
await axios.delete(`manage/moderation/notes/${note.uuid}/`)
|
||||
emit('deleted', note.uuid)
|
||||
} catch (error) {
|
||||
// TODO (wvffle): Handle error
|
||||
}
|
||||
|
||||
isLoading.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ui feed">
|
||||
<div
|
||||
|
@ -61,32 +92,3 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import showdown from 'showdown'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
notes: { type: Array, required: true }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
markdown: new showdown.Converter(),
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
remove (obj) {
|
||||
const self = this
|
||||
this.isLoading = true
|
||||
axios.delete(`manage/moderation/notes/${obj.uuid}/`).then((response) => {
|
||||
self.$emit('deleted', obj.uuid)
|
||||
self.isLoading = false
|
||||
}, () => {
|
||||
self.isLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,30 @@
|
|||
<script setup lang="ts">
|
||||
import type { Playlist } from '~/types'
|
||||
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import defaultCover from '~/assets/audio/default-cover.png'
|
||||
import { computed } from 'vue'
|
||||
import { useStore } from '~/store'
|
||||
|
||||
interface Props {
|
||||
playlist: Playlist
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const store = useStore()
|
||||
|
||||
const images = computed(() => {
|
||||
// TODO (wvffle): What is slicing for? is it 'http'?
|
||||
const urls = props.playlist.album_covers.map(url => store.getters['instance/absoluteUrl'](url).slice(0, 4))
|
||||
|
||||
while (urls.length < 4) {
|
||||
urls.push(defaultCover)
|
||||
}
|
||||
|
||||
return urls
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ui app-card card">
|
||||
<div
|
||||
|
@ -53,27 +80,3 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import defaultCover from '~/assets/audio/default-cover.png'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PlayButton
|
||||
},
|
||||
props: { playlist: { type: Object, required: true } },
|
||||
computed: {
|
||||
images () {
|
||||
const self = this
|
||||
const urls = this.playlist.album_covers.map((url) => {
|
||||
return self.$store.getters['instance/absoluteUrl'](url)
|
||||
}).slice(0, 4)
|
||||
while (urls.length < 4) {
|
||||
urls.push(defaultCover)
|
||||
}
|
||||
return urls
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
<script setup lang="ts">
|
||||
import type { Playlist } from '~/types'
|
||||
|
||||
import PlaylistCard from '~/components/playlists/Card.vue'
|
||||
|
||||
interface Props {
|
||||
playlists: Playlist[]
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="playlists.length > 0">
|
||||
<div class="ui app-cards cards">
|
||||
|
@ -9,15 +21,3 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import PlaylistCard from '~/components/playlists/Card.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PlaylistCard
|
||||
},
|
||||
props: { playlists: { type: Array, required: true } }
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -51,6 +51,8 @@ export interface Artist {
|
|||
tracks_count: number
|
||||
attributed_to: Actor
|
||||
is_local: boolean
|
||||
is_playable: boolean
|
||||
modification_date?: string
|
||||
}
|
||||
|
||||
export interface Album {
|
||||
|
@ -109,6 +111,7 @@ export interface Channel {
|
|||
rss_url: string
|
||||
subscriptions_count: number
|
||||
downloads_count: number
|
||||
content_category: ContentCategory
|
||||
}
|
||||
|
||||
export type PrivacyLevel = 'everyone' | 'instance' | 'me'
|
||||
|
@ -164,6 +167,7 @@ export interface Playlist {
|
|||
privacy_level: PrivacyLevel
|
||||
tracks_count: number
|
||||
duration: number
|
||||
album_covers: string[]
|
||||
|
||||
is_playable: boolean
|
||||
}
|
||||
|
@ -283,7 +287,7 @@ export interface Actor {
|
|||
|
||||
export interface User {
|
||||
id: string
|
||||
avatar?: string
|
||||
avatar?: Cover
|
||||
username: string
|
||||
full_username: string
|
||||
instance_support_message_display_date: string
|
||||
|
@ -326,4 +330,12 @@ export interface SettingsDataEntry {
|
|||
additional_data: {
|
||||
choices: [string, string]
|
||||
}
|
||||
}
|
||||
|
||||
// Note stuff
|
||||
export interface Note {
|
||||
uuid: string
|
||||
author: Actor // TODO (wvffle): Check if is valid
|
||||
summary: string
|
||||
creation_date: string
|
||||
}
|
|
@ -1,3 +1,14 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
const { $pgettext } = useGettext()
|
||||
const labels = computed(() => ({
|
||||
title: $pgettext('Head/Admin/Title', 'Manage library'),
|
||||
secondaryMenu: $pgettext('Menu/*/Hidden text', 'Secondary menu')
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-title="labels.title"
|
||||
|
@ -76,18 +87,3 @@
|
|||
<router-view :key="$route.fullPath" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
computed: {
|
||||
labels () {
|
||||
const title = this.$pgettext('Head/Admin/Title', 'Manage library')
|
||||
const secondaryMenu = this.$pgettext('Menu/*/Hidden text', 'Secondary menu')
|
||||
return {
|
||||
title,
|
||||
secondaryMenu
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
Loading…
Reference in New Issue