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>
|
<template>
|
||||||
<router-link
|
<router-link
|
||||||
class="artist-label ui image label"
|
class="artist-label ui image label"
|
||||||
|
@ -16,20 +33,3 @@
|
||||||
{{ artist.name }}
|
{{ artist.name }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<div class="card app-card">
|
<div class="card app-card">
|
||||||
<div
|
<div
|
||||||
|
@ -52,7 +93,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="extra content">
|
<div class="extra content">
|
||||||
<time
|
<time
|
||||||
v-translate
|
v-translate="{ updatedAgo }"
|
||||||
|
:translate-params="{ updatedAgo }"
|
||||||
class="meta ellipsis"
|
class="meta ellipsis"
|
||||||
:datetime="object.artist.modification_date"
|
:datetime="object.artist.modification_date"
|
||||||
:title="updatedTitle"
|
:title="updatedTitle"
|
||||||
|
@ -71,46 +113,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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>
|
<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">
|
<div class="controls">
|
||||||
<play-button
|
<play-button
|
||||||
class="basic circular icon"
|
class="basic circular icon"
|
||||||
|
@ -75,45 +100,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<div class="channel-serie-card">
|
<div class="channel-serie-card">
|
||||||
<div class="two-images">
|
<div class="two-images">
|
||||||
|
@ -60,28 +75,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<button
|
<button
|
||||||
:class="['ui', 'pink', {'inverted': isApproved || isPending}, {'favorited': isApproved}, 'icon', 'labeled', 'button']"
|
:class="['ui', 'pink', {'inverted': isApproved || isPending}, {'favorited': isApproved}, 'icon', 'labeled', 'button']"
|
||||||
|
@ -24,33 +53,3 @@
|
||||||
</translate>
|
</translate>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<div class="app-card card">
|
<div class="app-card card">
|
||||||
<router-link
|
<router-link
|
||||||
|
@ -63,45 +90,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="ui hidden divider" />
|
<div class="ui hidden divider" />
|
||||||
|
@ -36,12 +73,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :class="['track-table', 'ui', 'unstackable', 'grid', 'tablet-and-below']">
|
<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 -->
|
<!-- For each item, build a row -->
|
||||||
|
|
||||||
|
@ -73,54 +104,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<main
|
<main
|
||||||
v-title="labels.title"
|
v-title="labels.title"
|
||||||
|
@ -23,36 +52,3 @@
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<main
|
<main
|
||||||
v-title="labels.title"
|
v-title="labels.title"
|
||||||
|
@ -49,15 +59,3 @@
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<modal
|
<semantic-modal
|
||||||
v-model:show="show"
|
v-model:show="show"
|
||||||
class="small"
|
class="small"
|
||||||
>
|
>
|
||||||
|
@ -34,7 +57,7 @@
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:class="['ui', 'primary', {loading: isLoading}, 'button']"
|
:class="['ui', 'primary', {loading: isLoading}, 'button']"
|
||||||
:disabled="!submittable || null"
|
:disabled="!submittable"
|
||||||
@click.stop.prevent="$refs.albumForm.submit()"
|
@click.stop.prevent="$refs.albumForm.submit()"
|
||||||
>
|
>
|
||||||
<translate translate-context="*/*/Button.Label">
|
<translate translate-context="*/*/Button.Label">
|
||||||
|
@ -42,31 +65,5 @@
|
||||||
</translate>
|
</translate>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</modal>
|
</semantic-modal>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<img
|
<img
|
||||||
v-if="actor.icon && actor.icon.urls.original"
|
v-if="actor.icon && actor.icon.urls.original"
|
||||||
|
@ -11,21 +27,3 @@
|
||||||
class="ui avatar circular label"
|
class="ui avatar circular label"
|
||||||
>{{ actor.preferred_username[0] }}</span>
|
>{{ actor.preferred_username[0] }}</span>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<div class="ui small placeholder segment component-placeholder component-empty-state">
|
<div class="ui small placeholder segment component-placeholder component-empty-state">
|
||||||
<h4 class="ui header">
|
<h4 class="ui header">
|
||||||
|
@ -24,10 +34,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
refresh: { type: Boolean, default: false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<span
|
<span
|
||||||
class="tooltip"
|
class="tooltip"
|
||||||
:data-tooltip="content"
|
:data-tooltip="content"
|
||||||
><i class="question circle icon" /></span>
|
><i class="question circle icon" /></span>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<span class="component-user-link">
|
<span class="component-user-link">
|
||||||
<template v-if="avatar">
|
<template v-if="avatar">
|
||||||
|
@ -17,24 +36,3 @@
|
||||||
@{{ user.username }}
|
@{{ user.username }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<span>{{ username }}</span>
|
<span>{{ username }}</span>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: { username: { type: String, required: true } }
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -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>
|
<template>
|
||||||
<div class="ui feed">
|
<div class="ui feed">
|
||||||
<div
|
<div
|
||||||
|
@ -61,32 +92,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<div class="ui app-card card">
|
<div class="ui app-card card">
|
||||||
<div
|
<div
|
||||||
|
@ -53,27 +80,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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>
|
<template>
|
||||||
<div v-if="playlists.length > 0">
|
<div v-if="playlists.length > 0">
|
||||||
<div class="ui app-cards cards">
|
<div class="ui app-cards cards">
|
||||||
|
@ -9,15 +21,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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
|
tracks_count: number
|
||||||
attributed_to: Actor
|
attributed_to: Actor
|
||||||
is_local: boolean
|
is_local: boolean
|
||||||
|
is_playable: boolean
|
||||||
|
modification_date?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Album {
|
export interface Album {
|
||||||
|
@ -109,6 +111,7 @@ export interface Channel {
|
||||||
rss_url: string
|
rss_url: string
|
||||||
subscriptions_count: number
|
subscriptions_count: number
|
||||||
downloads_count: number
|
downloads_count: number
|
||||||
|
content_category: ContentCategory
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PrivacyLevel = 'everyone' | 'instance' | 'me'
|
export type PrivacyLevel = 'everyone' | 'instance' | 'me'
|
||||||
|
@ -164,6 +167,7 @@ export interface Playlist {
|
||||||
privacy_level: PrivacyLevel
|
privacy_level: PrivacyLevel
|
||||||
tracks_count: number
|
tracks_count: number
|
||||||
duration: number
|
duration: number
|
||||||
|
album_covers: string[]
|
||||||
|
|
||||||
is_playable: boolean
|
is_playable: boolean
|
||||||
}
|
}
|
||||||
|
@ -283,7 +287,7 @@ export interface Actor {
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string
|
id: string
|
||||||
avatar?: string
|
avatar?: Cover
|
||||||
username: string
|
username: string
|
||||||
full_username: string
|
full_username: string
|
||||||
instance_support_message_display_date: string
|
instance_support_message_display_date: string
|
||||||
|
@ -327,3 +331,11 @@ export interface SettingsDataEntry {
|
||||||
choices: [string, string]
|
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>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-title="labels.title"
|
v-title="labels.title"
|
||||||
|
@ -76,18 +87,3 @@
|
||||||
<router-view :key="$route.fullPath" />
|
<router-view :key="$route.fullPath" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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