chore(front): replace semantic-modal with Modal component (+ some modernization)

This commit is contained in:
upsiflu 2025-02-06 17:51:23 +01:00
parent eb4258d66e
commit a463cc305a
16 changed files with 184 additions and 299 deletions

View File

@ -1,11 +1,13 @@
<script setup lang="ts">
import SemanticModal from '~/components/semantic/Modal.vue'
import Modal from '~/components/ui/Modal.vue'
import axios from 'axios'
import { uniq } from 'lodash-es'
import { useVModel } from '@vueuse/core'
import { ref, computed, watch, nextTick } from 'vue'
import { useStore } from '~/store'
// import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
interface Props {
show: boolean
@ -63,13 +65,13 @@ const checkAndSwitch = async (url: string) => {
</script>
<template>
<semantic-modal
v-model:show="show"
@update:show="isError = false"
<Modal :title="t('views.ChooseInstance.header.chooseInstance')"
v-model="show"
@update="isError = false"
>
<h3 class="header">
<!-- TODO: translate -->
Choose your instance
<!-- <translate translate-context="Popup/Instance/Title">
</translate> -->
</h3>
@ -177,5 +179,5 @@ const checkAndSwitch = async (url: string) => {
</translate> -->
</button>
</div>
</semantic-modal>
</Modal>
</template>

View File

@ -1,12 +1,11 @@
<script setup lang="ts">
import type { Track, Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
import type { Track, SimpleArtist as Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
// import type { Track } from '~/types'
import { useStore } from '~/store'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import SemanticModal from '~/components/semantic/Modal.vue'
import Modal from '~/components/ui/Modal.vue'
import { computed, ref } from 'vue'
import usePlayOptions from '~/composables/audio/usePlayOptions'
import useReport from '~/composables/moderation/useReport'
@ -93,13 +92,14 @@ const labels = computed(() => ({
</script>
<template>
<semantic-modal
<Modal
:title="track.title"
ref="modal"
v-model:show="show"
v-model="show"
:scrolling="true"
:additional-classes="['scrolling-track-options']"
class="scrolling-track-options"
>
<div class="header">
<template #topright>
<div class="ui large centered rounded image">
<img
v-if="track.album && track.album.cover && track.album.cover.urls.original"
@ -122,7 +122,7 @@ const labels = computed(() => ({
class="ui centered image"
>
<img
v-else-if="!!track.artist_credit?.length && track.artist_credit[0].artist.cover"
v-else-if="!!track.artist_credit?.length && track.artist_credit[0].artist.attachment_cover"
v-lazy="
getArtistCoverUrl(track.artist_credit)
"
@ -136,13 +136,10 @@ const labels = computed(() => ({
src="../../../assets/audio/default-cover.png"
>
</div>
<h3 class="track-modal-title">
{{ track.title }}
</h3>
<h4 class="track-modal-subtitle">
{{ generateTrackCreditString(track) }}
</h4>
</div>
</template>
<div class="ui hidden divider" />
<div class="content">
<div class="ui one column unstackable grid">
@ -178,7 +175,7 @@ const labels = computed(() => ({
:aria-label="labels.addToQueue"
@click.stop.prevent="
enqueue();
modal.closeModal();
show=false
"
>
<i class="plus icon track-modal list-icon" />
@ -192,7 +189,7 @@ const labels = computed(() => ({
:aria-label="labels.playNext"
@click.stop.prevent="
enqueueNext(true);
modal.closeModal();
show=false
"
>
<i class="step forward icon track-modal list-icon" />
@ -209,7 +206,7 @@ const labels = computed(() => ({
type: 'similar',
objectId: track.id,
});
modal.closeModal();
show=false
"
>
<i class="rss icon track-modal list-icon" />
@ -301,5 +298,5 @@ const labels = computed(() => ({
</div>
</div>
</div>
</semantic-modal>
</Modal>
</template>

View File

@ -1,17 +1,18 @@
<script setup lang="ts">
import type { Track, Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
import type { Track, SimpleArtist as Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
// import type { Track } from '~/types'
import usePlayOptions from '~/composables/audio/usePlayOptions'
import useReport from '~/composables/moderation/useReport'
import { useStore } from '~/store'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import SemanticModal from '~/components/semantic/Modal.vue'
import { computed, ref } from 'vue'
import usePlayOptions from '~/composables/audio/usePlayOptions'
import useReport from '~/composables/moderation/useReport'
import { useVModel } from '@vueuse/core'
import { generateTrackCreditString, getArtistCoverUrl } from '~/utils/utils'
import Modal from '~/components/ui/Modal.vue'
interface Events {
(e: 'update:show', value: boolean): void
}
@ -92,11 +93,11 @@ const labels = computed(() => ({
</script>
<template>
<semantic-modal
<Modal :title="track.title"
ref="modal"
v-model:show="show"
v-model="show"
:scrolling="true"
:additional-classes="['scrolling-track-options']"
class="scrolling-track-options"
>
<div class="header">
<div class="ui large centered rounded image">
@ -125,9 +126,6 @@ const labels = computed(() => ({
src="../../../assets/audio/default-cover.png"
>
</div>
<h3 class="track-modal-title">
{{ track.title }}
</h3>
<h4 class="track-modal-subtitle">
{{ generateTrackCreditString(track) }}
</h4>
@ -155,7 +153,7 @@ const labels = computed(() => ({
class="column"
role="button"
:aria-label="labels.addToQueue"
@click.stop.prevent="enqueue(); modal.closeModal()"
@click.stop.prevent="enqueue(); show = false"
>
<i class="plus icon track-modal list-icon" />
<span class="track-modal list-item">{{ labels.addToQueue }}</span>
@ -166,7 +164,7 @@ const labels = computed(() => ({
class="column"
role="button"
:aria-label="labels.playNext"
@click.stop.prevent="enqueueNext(true);modal.closeModal()"
@click.stop.prevent="enqueueNext(true);show = false"
>
<i class="step forward icon track-modal list-icon" />
<span class="track-modal list-item">{{ labels.playNext }}</span>
@ -177,7 +175,7 @@ const labels = computed(() => ({
class="column"
role="button"
:aria-label="labels.startRadio"
@click.stop.prevent="() => { store.dispatch('radios/start', { type: 'similar', objectId: track.id }); modal.closeModal() }"
@click.stop.prevent="() => { store.dispatch('radios/start', { type: 'similar', objectId: track.id }); show = false }"
>
<i class="rss icon track-modal list-icon" />
<span class="track-modal list-item">{{ labels.startRadio }}</span>
@ -253,5 +251,5 @@ const labels = computed(() => ({
</div>
</div>
</div>
</semantic-modal>
</Modal>
</template>

View File

@ -1,12 +1,14 @@
<script setup lang="ts">
import type { Channel } from '~/types'
import SemanticModal from '~/components/semantic/Modal.vue'
import Modal from '~/components/ui/Modal.vue'
import ChannelAlbumForm from '~/components/channels/AlbumForm.vue'
import Button from '~/components/ui/Button.vue'
import { watch, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import Button from '~/components/ui/Button.vue'
import Spacer from '~/components/ui/Spacer.vue'
interface Events {
(e: 'created'): void
}
@ -36,18 +38,11 @@ defineExpose({
</script>
<template>
<semantic-modal
v-model:show="show"
<Modal :title="t(channel.artist.content_category === 'podcast' ? 'components.channels.AlbumModal.header.newSeries' : 'components.channels.AlbumModal.header.newAlbum')"
v-model="show"
class="small"
:cancel="t('components.channels.AlbumModal.button.cancel')"
>
<h4 class="header">
<span v-if="channel.content_category === 'podcast'">
{{ t('components.channels.AlbumModal.header.newSeries') }}
</span>
<span v-else>
{{ t('components.channels.AlbumModal.header.newAlbum') }}
</span>
</h4>
<div class="scrolling content">
<channel-album-form
ref="albumForm"
@ -57,10 +52,8 @@ defineExpose({
@created="emit('created')"
/>
</div>
<div class="actions">
<Button secondary>
{{ t('components.channels.AlbumModal.button.cancel') }}
</Button>
<template #actions>
<Spacer h grow />
<Button
:is-loading="isLoading"
:disabled="!submittable"
@ -68,6 +61,6 @@ defineExpose({
>
{{ t('components.channels.AlbumModal.button.create') }}
</Button>
</div>
</semantic-modal>
</template>
</Modal>
</template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import SemanticModal from '~/components/semantic/Modal.vue'
import Modal from '~/components/ui/Modal.vue'
import ChannelUploadForm from '~/components/channels/UploadForm.vue'
import Popover from '~/components/ui/Popover.vue'
import PopoverItem from '~/components/ui/popover/PopoverItem.vue'
@ -53,24 +53,11 @@ const open = ref(false)
</script>
<template>
<semantic-modal
v-model:show="store.state.channels.showUploadModal"
<Modal
:title="t(`components.channels.UploadModal.header.${['', 'publish', 'uploadFiles', 'uploadDetails', 'processing'][step]}`)"
v-model="store.state.channels.showUploadModal"
class="small"
>
<h4 class="header">
<span v-if="step === 1">
{{ t('components.channels.UploadModal.header.publish') }}
</span>
<span v-else-if="step === 2">
{{ t('components.channels.UploadModal.header.uploadFiles') }}
</span>
<span v-else-if="step === 3">
{{ t('components.channels.UploadModal.header.uploadDetails') }}
</span>
<span v-else-if="step === 4">
{{ t('components.channels.UploadModal.header.processing') }}
</span>
</h4>
<div class="scrolling content">
<channel-upload-form
ref="uploadForm"
@ -156,5 +143,5 @@ const open = ref(false)
{{ t('components.channels.UploadModal.button.close') }}
</Button>
</div>
</semantic-modal>
</Modal>
</template>

View File

@ -2,11 +2,14 @@
import type { RouteLocationRaw } from 'vue-router'
import type { Cover } from '~/types'
import SemanticModal from '~/components/semantic/Modal.vue'
import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useStore } from '~/store'
import Modal from '~/components/ui/Modal.vue'
import Link from '~/components/ui/Link.vue'
import Spacer from '~/components/ui/Spacer.vue'
interface Props {
nextRoute: RouteLocationRaw
message: string
@ -29,10 +32,7 @@ const labels = computed(() => ({
</script>
<template>
<semantic-modal v-model:show="show">
<h4 class="header">
{{ labels.header }}
</h4>
<Modal v-model="show" :title="labels.header">
<div
v-if="cover"
class="image content"
@ -60,22 +60,21 @@ const labels = computed(() => ({
{{ message }}
</p>
</div>
<div class="actions">
<router-link
<template #actions>
<Spacer grow />
<Link
:to="{path: '/login', query: { next: nextRoute as string }}"
class="ui labeled icon button"
icon="bi-key-fill"
>
<i class="key icon" />
{{ labels.login }}
</router-link>
<router-link
</Link>
<Link
v-if="store.state.instance.settings.users.registration_enabled.value"
:to="{path: '/signup'}"
class="ui labeled icon button"
icon="bi-person-fill"
>
<i class="user icon" />
{{ labels.signup }}
</router-link>
</div>
</semantic-modal>
</Link>
</template>
</Modal>
</template>

View File

@ -2,7 +2,7 @@
import type { BackendError } from '~/types'
import axios from 'axios'
import SemanticModal from '~/components/semantic/Modal.vue'
import Modal from '~/components/ui/Modal.vue'
import Button from '~/components/ui/Button.vue'
import { useTimeoutFn } from '@vueuse/core'
import { ref } from 'vue'
@ -80,13 +80,11 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
<div>
<slot />
</div>
<semantic-modal
v-model:show="showModal"
<Modal :title="t('components.federation.FetchButton.header.refresh')"
v-model="showModal"
class="small"
:cancel="t('components.federation.FetchButton.button.close')"
>
<h3 class="header">
{{ t('components.federation.FetchButton.header.refresh') }}
</h3>
<div class="scrolling content">
<template v-if="data && data.status != 'pending'">
<div
@ -213,9 +211,6 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
</div>
</div>
<div class="actions">
<Button color="secondary">
{{ t('components.federation.FetchButton.button.close') }}
</Button>
<Button
v-if="data && data.status === 'finished'"
@click.prevent="showModal = false; emit('refresh')"
@ -223,6 +218,6 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
{{ t('components.federation.FetchButton.button.reload') }}
</Button>
</div>
</semantic-modal>
</Modal>
</div>
</template>

View File

@ -10,7 +10,8 @@ import { getDomain } from '~/utils'
import useReport from '~/composables/moderation/useReport'
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
import SemanticModal from '~/components/semantic/Modal.vue'
import Modal from '~/components/ui/Modal.vue'
import Button from '~/components/ui/Button.vue'
import Popover from '~/components/ui/Popover.vue'
import PopoverItem from '~/components/ui/popover/PopoverItem.vue'
@ -45,7 +46,10 @@ const labels = computed(() => ({
more: t('components.library.AlbumDropdown.button.more')
}))
const isEmbedable = computed(() => (props.isChannel && props.artistCredit[0].artist?.channel?.actor) || props.publicLibraries.length)
// TODO: What is the condition for an album to be embeddable?
// (a) props.publicLibraries.length
// (b) I am the channel's artist: props.isChannel && props.artistCredit[0].artist?.channel?.actor)
const isEmbedable = computed(() => (props.publicLibraries.length))
const musicbrainzUrl = computed(() => props.object?.mbid ? `https://musicbrainz.org/release/${props.object.mbid}` : null)
const discogsUrl = computed(() => `https://discogs.com/search/?type=release&title=${encodeURI(props.object?.title)}&artist=${encodeURI(props.object?.artist_credit[0].artist.name)}`)
@ -56,32 +60,20 @@ const open = ref(false)
<template>
<span>
<semantic-modal
<Modal :title="t('components.library.AlbumDropdown.modal.embed.header')"
v-if="isEmbedable"
v-model:show="showEmbedModal"
v-model="showEmbedModal"
:cancel="t('components.library.AlbumDropdown.button.cancel')"
>
<h4 class="header">
{{ t('components.library.AlbumDropdown.modal.embed.header') }}
</h4>
<div class="scrolling content">
<div class="description">
<embed-wizard
:id="object.id"
type="album"
/>
</div>
</div>
<div class="actions">
<Button
color="secondary"
variant="outline"
@click="showEmbedModal = false"
>
{{ t('components.library.AlbumDropdown.button.cancel') }}
</Button>
</div>
</semantic-modal>
</Modal>
<Popover v-model:open="open">
<template #default="{ toggleOpen }">
<OptionsButton

View File

@ -3,7 +3,7 @@ import type { Upload } from '~/types'
import { useVModel } from '@vueuse/core'
import { useI18n } from 'vue-i18n'
import SemanticModal from '~/components/semantic/Modal.vue'
import Modal from '~/components/ui/Modal.vue'
import Button from '~/components/ui/Button.vue'
interface ErrorEntry {
@ -76,10 +76,10 @@ const getErrorData = (upload: Upload) => {
</script>
<template>
<semantic-modal v-model:show="show">
<h4 class="header">
{{ t('components.library.ImportStatusModal.header.importDetail') }}
</h4>
<Modal :title="t('components.library.ImportStatusModal.header.importDetail')"
v-model="show"
:cancel="t('components.library.ImportStatusModal.button.close')"
>
<div
v-if="Object.keys(upload).length > 0"
class="content"
@ -183,10 +183,5 @@ const getErrorData = (upload: Upload) => {
</template>
</div>
</div>
<div class="actions">
<Button color="secondary">
{{ t('components.library.ImportStatusModal.button.close') }}
</Button>
</div>
</semantic-modal>
</Modal>
</template>

View File

@ -12,7 +12,7 @@ import { ref, onMounted, watch, computed } from 'vue'
import { useStore } from '~/store'
import { clone } from 'lodash-es'
import SemanticModal from '~/components/semantic/Modal.vue'
import Modal from '~/components/ui/Modal.vue'
import TrackTable from '~/components/audio/track/Table.vue'
import Button from '~/components/ui/Button.vue'
@ -49,6 +49,10 @@ const exclude = computed({
})
const el = useCurrentElement()
// This component appears on "create new radio" and offers filters. "New filter" => Dropdown search field
// TODO: Re-implement with <Input>, <select>
onMounted(() => {
for (const field of data.value.filter.fields) {
const settings: SemanticUI.DropdownSettings = {
@ -182,13 +186,10 @@ fetchCandidates()
>
{{ t('components.library.radios.Filter.matchingTracks', checkResult.candidates.count) }}
</a>
<semantic-modal
<Modal :title="t('components.library.radios.Filter.matchingTracksModalHeader')"
v-if="checkResult"
v-model:show="showCandidadesModal"
>
<h4 class="header">
{{ t('components.library.radios.Filter.matchingTracksModalHeader') }}
</h4>
<div class="content">
<div class="description">
<track-table
@ -202,7 +203,7 @@ fetchCandidates()
{{ t('components.library.radios.Filter.cancelButton') }}
</Button>
</div>
</semantic-modal>
</Modal>
</td>
<td>
<Button

View File

@ -3,12 +3,12 @@ import type { BackendError } from '~/types'
import axios from 'axios'
import { ref, computed } from 'vue'
import { ref, computed, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import InstancePolicyForm from '~/components/manage/moderation/InstancePolicyForm.vue'
import InstancePolicyCard from '~/components/manage/moderation/InstancePolicyCard.vue'
import SemanticModal from '~/components/semantic/Modal.vue'
import Modal from '~/components/ui/Modal.vue'
import Button from '~/components/ui/Button.vue'
interface Props {
@ -29,6 +29,11 @@ const result = ref()
const obj = computed(() => result.value?.results[0] ?? null)
const isLoading = ref(false)
watch (show, (newValue) => {
if (newValue) fetchData()
})
const fetchData = async () => {
const [username, domain] = props.target.split('@')
@ -67,13 +72,10 @@ const fetchData = async () => {
<slot>
{{ t('components.manage.moderation.InstancePolicyModal.button.show') }}
</slot>
<semantic-modal
v-model:show="show"
@show="fetchData"
<Modal :title="t('components.manage.moderation.InstancePolicyModal.modal.manage.header', {obj: target})"
v-model="show"
:cancel="t('components.manage.moderation.InstancePolicyModal.button.close')"
>
<h4 class="header">
{{ t('components.manage.moderation.InstancePolicyModal.modal.manage.header', {obj: target}) }}
</h4>
<div class="content">
<div class="description">
<div
@ -104,11 +106,6 @@ const fetchData = async () => {
<div class="ui hidden divider" />
<div class="ui hidden divider" />
</div>
<div class="actions">
<Button color="secondary">
{{ t('components.manage.moderation.InstancePolicyModal.button.close') }}
</Button>
</div>
</semantic-modal>
</Modal>
</Button>
</template>

View File

@ -7,9 +7,11 @@ import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useStore } from '~/store'
import SemanticModal from '~/components/semantic/Modal.vue'
import useLogger from '~/composables/useLogger'
import Modal from '~/components/ui/Modal.vue'
import Alert from '~/components/ui/Alert.vue'
const logger = useLogger()
const { t } = useI18n()
@ -58,23 +60,15 @@ const hide = async () => {
</script>
<template>
<semantic-modal v-model:show="show">
<h4
v-if="type === 'artist'"
class="header"
>
{{ t('components.moderation.FilterModal.header.modal', {name: target?.name}) }}
</h4>
<Modal v-model="show"
:title="type==='artist' ? t('components.moderation.FilterModal.header.modal', {name: target?.name}) : errors.length > 0 ? t('components.moderation.FilterModal.header.failure') : ''"
:cancel="t('components.moderation.FilterModal.button.cancel')"
>
<div class="scrolling content">
<div class="description">
<div
<Alert red
v-if="errors.length > 0"
role="alert"
class="ui negative message"
>
<h4 class="header">
{{ t('components.moderation.FilterModal.header.failure') }}
</h4>
<ul class="list">
<li
v-for="(error, key) in errors"
@ -83,7 +77,7 @@ const hide = async () => {
{{ error }}
</li>
</ul>
</div>
</Alert>
<template v-if="type === 'artist'">
<p>
{{ t('components.moderation.FilterModal.warning.createFilter.listIntro') }}
@ -109,9 +103,6 @@ const hide = async () => {
</div>
</div>
<div class="actions">
<button class="ui basic cancel button">
{{ t('components.moderation.FilterModal.button.cancel') }}
</button>
<button
:class="['ui', 'success', {loading: isLoading}, 'button']"
@click="hide"
@ -119,5 +110,5 @@ const hide = async () => {
{{ t('components.moderation.FilterModal.button.hide') }}
</button>
</div>
</semantic-modal>
</Modal>
</template>

View File

@ -3,11 +3,13 @@ import type { BackendError } from '~/types'
import axios from 'axios'
import ReportCategoryDropdown from '~/components/moderation/ReportCategoryDropdown.vue'
import SemanticModal from '~/components/semantic/Modal.vue'
import { computed, ref, watchEffect } from 'vue'
import { useStore } from '~/store'
import { useI18n } from 'vue-i18n'
import Modal from '~/components/ui/Modal.vue'
import Button from '~/components/ui/Button.vue'
interface ReportType {
anonymous: boolean
type: string
@ -120,12 +122,14 @@ watchEffect(async () => {
</script>
<template>
<semantic-modal v-model:show="show">
<Modal :title="target ? t('components.moderation.ReportModal.header.modal') : errors.length > 0 ? t('components.moderation.ReportModal.header.submissionFailure') : ''"
v-model="show"
:cancel="t('components.moderation.ReportModal.button.cancel')"
>
<h2
v-if="target"
class="ui header"
>
{{ t('components.moderation.ReportModal.header.modal') }}
<div class="ui sub header">
{{ target.typeLabel }}
<span class="middle hyphen symbol" />
@ -134,14 +138,9 @@ watchEffect(async () => {
</h2>
<div class="scrolling content">
<div class="description">
<div
<Alert red
v-if="errors.length > 0"
role="alert"
class="ui negative message"
>
<h4 class="header">
{{ t('components.moderation.ReportModal.header.submissionFailure') }}
</h4>
<ul class="list">
<li
v-for="(error, key) in errors"
@ -150,7 +149,7 @@ watchEffect(async () => {
{{ error }}
</li>
</ul>
</div>
</Alert>
</div>
<p>
{{ t('components.moderation.ReportModal.description.modal') }}
@ -238,18 +237,15 @@ watchEffect(async () => {
</h4>
</div>
</div>
<div class="actions">
<button class="ui basic cancel button">
{{ t('components.moderation.ReportModal.button.cancel') }}
</button>
<button
<template #actions>
<Button
v-if="canSubmit"
:class="['ui', 'success', {loading: isLoading}, 'button']"
type="submit"
form="report-form"
>
{{ t('components.moderation.ReportModal.button.submit') }}
</button>
</div>
</semantic-modal>
</Button>
</template>
</Modal>
</template>

View File

@ -1,33 +0,0 @@
<script setup lang="ts">
import { ref } from 'vue'
import { computed } from 'vue'
import { useStore } from '~/store'
import Modal from '~/components/ui/Modal.vue'
interface Props {
fullscreen?: boolean
scrolling?: boolean
additionalClasses?: string[]
}
const props = withDefaults(defineProps<Props>(), {
fullscreen: true,
scrolling: false,
additionalClasses: () => []
})
const store = useStore()
const classes = computed(() => [
...props.additionalClasses,
{
scrolling: props.scrolling,
'overlay fullscreen': props.fullscreen && ['phone', 'tablet'].includes(store.getters['ui/windowSize'])
}
])
const isOpen = ref(false)
</script>
<template>
(Semantic Modal)
</template>

View File

@ -1,7 +1,6 @@
<script setup lang="ts">
import type { Actor } from '~/types'
import SemanticModal from '~/components/semantic/Modal.vue'
import LibraryWidget from '~/components/federation/LibraryWidget.vue'
import ChannelsWidget from '~/components/audio/ChannelsWidget.vue'
import ChannelForm from '~/components/audio/ChannelForm.vue'
@ -10,6 +9,9 @@ import { useI18n } from 'vue-i18n'
import { useStore } from '~/store'
import { useRouter } from 'vue-router'
import Modal from '~/components/ui/Modal.vue'
import Button from '~/components/ui/Button.vue'
interface Events {
(e: 'updated', value: Actor): void
}
@ -83,24 +85,16 @@ const createForm = ref()
</library-widget>
</div>
<semantic-modal v-model:show="showCreateModal">
<h4 class="header">
<span
v-if="step === 1"
>
{{ t('views.auth.ProfileOverview.modal.createChannel.header') }}
</span>
<span
v-else-if="category === 'podcast'"
>
{{ t('views.auth.ProfileOverview.modal.createChannel.podcast.header') }}
</span>
<span
v-else
>
{{ t('views.auth.ProfileOverview.modal.createChannel.artist.header') }}
</span>
</h4>
<Modal v-model="showCreateModal"
:title="t(`views.auth.ProfileOverview.modal.createChannel.${
step === 1 ?
'header' :
category === 'podcast' ?
'podcast.header'
:
'artist.header'
}`)"
>
<div
ref="modalContent"
class="scrolling content"
@ -117,37 +111,35 @@ const createForm = ref()
/>
<div class="ui hidden divider" />
</div>
<div class="actions">
<button
<template #actions>
<Button destructive
v-if="step === 1"
class="ui basic deny button"
autofocus
>
{{ t('views.auth.ProfileOverview.button.cancel') }}
</button>
<button
</Button>
<Button secondary
v-if="step > 1"
class="ui basic button"
@click.stop.prevent="step -= 1"
>
{{ t('views.auth.ProfileOverview.button.previous') }}
</button>
<button
</Button>
<Button primary
v-if="step === 1"
class="ui primary button"
@click.stop.prevent="step += 1"
>
{{ t('views.auth.ProfileOverview.button.next') }}
</button>
<button
</Button>
<Button primary
v-if="step === 2"
:class="['ui', 'primary button', { loading }]"
type="submit"
:disabled="!submittable && !loading"
:isLoading="loading"
@click.prevent.stop="createForm.submit"
>
{{ t('views.auth.ProfileOverview.button.createChannel') }}
</button>
</div>
</semantic-modal>
</Button>
</template>
</Modal>
</section>
</template>

View File

@ -8,14 +8,17 @@ import { useStore } from '~/store'
import axios from 'axios'
import useErrorHandler from '~/composables/useErrorHandler'
import useReport from '~/composables/moderation/useReport'
import SubscribeButton from '~/components/channels/SubscribeButton.vue'
import ChannelForm from '~/components/audio/ChannelForm.vue'
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
import HumanDuration from '~/components/common/HumanDuration.vue'
import PlayButton from '~/components/audio/PlayButton.vue'
import TagsList from '~/components/tags/List.vue'
import SemanticModal from '~/components/semantic/Modal.vue'
import RadioButton from '~/components/radios/Button.vue'
import Loader from '~/components/ui/Loader.vue'
import Button from '~/components/ui/Button.vue'
import Tabs from '~/components/ui/Tabs.vue'
@ -24,11 +27,8 @@ import OptionsButton from '~/components/ui/button/Options.vue'
import Popover from '~/components/ui/Popover.vue'
import PopoverItem from '~/components/ui/popover/PopoverItem.vue'
import Layout from '~/components/ui/Layout.vue'
import Modal from '~/components/ui/Modal.vue'
import Spacer from '~/components/ui/Spacer.vue'
import useErrorHandler from '~/composables/useErrorHandler'
import useReport from '~/composables/moderation/useReport'
import Modal from '~/components/ui/Modal.vue'
interface Events {
(e: 'deleted'): void
@ -347,13 +347,12 @@ const updateSubscriptionCount = (delta: number) => {
@unsubscribed="updateSubscriptionCount(-1)"
/>
<semantic-modal
<Modal
:title="t('views.channels.DetailBase.modal.embed.header')"
v-if="totalTracks > 0"
v-model:show="showEmbedModal"
v-model="showEmbedModal"
:cancel="t('views.channels.DetailBase.button.cancel')"
>
<h4 class="header">
{{ t('views.channels.DetailBase.modal.embed.header') }}
</h4>
<div class="scrolling content">
<div class="description">
<embed-wizard
@ -362,28 +361,19 @@ const updateSubscriptionCount = (delta: number) => {
/>
</div>
</div>
<div class="actions">
<template #actions>
<button class="ui basic deny button">
{{ t('views.channels.DetailBase.button.cancel') }}
</button>
</div>
</semantic-modal>
<semantic-modal
</template>
</Modal>
<Modal
:title="t(`views.channels.DetailBase.header.${
object.artist?.content_category === 'podcast' ? 'podcastChannel' : 'artistChannel'
}`)"
v-if="isOwner"
v-model:show="showEditModal"
v-model="showEditModal"
>
<h4 class="header">
<span
v-if="object.artist?.content_category === 'podcast'"
>
{{ t('views.channels.DetailBase.header.podcastChannel') }}
</span>
<span
v-else
>
{{ t('views.channels.DetailBase.header.artistChannel') }}
</span>
</h4>
<div class="scrolling content">
<channel-form
ref="editForm"
@ -394,31 +384,29 @@ const updateSubscriptionCount = (delta: number) => {
/>
<div class="ui hidden divider" />
</div>
<div class="actions">
<button class="ui left floated basic deny button">
{{ t('views.channels.DetailBase.button.cancel') }}
</button>
<button
:class="['ui', 'primary', 'confirm', {loading: edit.loading}, 'button']"
<template #actions>
<Button
primary
autofocus
:isLoading="edit.loading"
:disabled="!edit.submittable"
@click.stop="editForm?.submit"
>
{{ t('views.channels.DetailBase.button.updateChannel') }}
</button>
</div>
</semantic-modal>
</Button>
</template>
</Modal>
<Button
secondary
icon="bi-rss"
@click.stop.prevent="showSubscribeModal = true"
/>
<semantic-modal
<Modal
:title="t('views.channels.DetailBase.modal.subscribe.header')"
v-model:show="showSubscribeModal"
class="tiny"
:cancel="t('views.channels.DetailBase.button.cancel')"
>
<h4 class="header">
{{ t('views.channels.DetailBase.modal.subscribe.header') }}
</h4>
<div class="scrollable content">
<div class="description">
<template v-if="store.state.auth.authenticated">
@ -457,12 +445,7 @@ const updateSubscriptionCount = (delta: number) => {
</template>
</div>
</div>
<div class="actions">
<button class="ui basic deny button">
{{ t('views.channels.DetailBase.button.cancel') }}
</button>
</div>
</semantic-modal>
</Modal>
</Layout>
</Layout>
</Layout>