chore(front): [WIP] new toplevel channels page
This commit is contained in:
		
							parent
							
								
									4a9f2f99ae
								
							
						
					
					
						commit
						b1ac6612fd
					
				| 
						 | 
				
			
			@ -145,7 +145,7 @@ const logoUrl = computed(() => store.state.auth.authenticated ? 'library.index'
 | 
			
		|||
          {{ t('components.Sidebar.link.artists') }}
 | 
			
		||||
      </Link>
 | 
			
		||||
 | 
			
		||||
      <Link to="/subscriptions"
 | 
			
		||||
      <Link to="/channels"
 | 
			
		||||
          ghost
 | 
			
		||||
          full
 | 
			
		||||
          align-text="left"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -124,6 +124,12 @@ export default [
 | 
			
		|||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: 'channels',
 | 
			
		||||
        name: 'channels',
 | 
			
		||||
        component: () => import('~/views/channels/List.vue'),
 | 
			
		||||
        props: route => ({ defaultQuery: route.query.q })
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: 'subscriptions',
 | 
			
		||||
        name: 'subscriptions',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,195 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import type { Channel } from '~/types'
 | 
			
		||||
 | 
			
		||||
import { useI18n } from 'vue-i18n'
 | 
			
		||||
import { ref, computed } from 'vue'
 | 
			
		||||
 | 
			
		||||
import axios from 'axios'
 | 
			
		||||
 | 
			
		||||
import ChannelsWidget from '~/components/audio/ChannelsWidget.vue'
 | 
			
		||||
import RemoteSearchForm from '~/components/RemoteSearchForm.vue'
 | 
			
		||||
import Layout from '~/components/ui/Layout.vue'
 | 
			
		||||
import Header from '~/components/ui/Header.vue'
 | 
			
		||||
import Modal from '~/components/ui/Modal.vue'
 | 
			
		||||
import Button from '~/components/ui/Button.vue'
 | 
			
		||||
 | 
			
		||||
import useErrorHandler from '~/composables/useErrorHandler'
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  defaultQuery?: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  defaultQuery: ''
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const query = ref(props.defaultQuery)
 | 
			
		||||
const widgetKey = ref(new Date().toLocaleString())
 | 
			
		||||
 | 
			
		||||
const { t } = useI18n()
 | 
			
		||||
const labels = computed(() => ({
 | 
			
		||||
  searchPlaceholder: t('views.channels.SubscriptionsList.placeholder.search')
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
const previousPage = ref()
 | 
			
		||||
const nextPage = ref()
 | 
			
		||||
const channels = ref([] as Channel[])
 | 
			
		||||
const count = ref(0)
 | 
			
		||||
const isLoading = ref(false)
 | 
			
		||||
const fetchData = async () => {
 | 
			
		||||
  isLoading.value = true
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    const response = await axios.get('channels/', { params: { q: query.value } })
 | 
			
		||||
    previousPage.value = response.data.previous
 | 
			
		||||
    nextPage.value = response.data.next
 | 
			
		||||
    channels.value.push(...response.data.results)
 | 
			
		||||
    count.value = response.data.count
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    useErrorHandler(error as Error)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isLoading.value = false
 | 
			
		||||
}
 | 
			
		||||
fetchData()
 | 
			
		||||
 | 
			
		||||
const reloadWidget = () => (widgetKey.value = new Date().toLocaleString())
 | 
			
		||||
const showSubscribeModal = ref(false)
 | 
			
		||||
const showCreateModal = ref(false)
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <Layout stack main
 | 
			
		||||
  >
 | 
			
		||||
    <Header
 | 
			
		||||
      :h1="t('views.channels.SubscriptionsList.title')"
 | 
			
		||||
      :action="{
 | 
			
		||||
        text: t('views.channels.SubscriptionsList.link.addNew'),
 | 
			
		||||
        onClick: ()=> showSubscribeModal = true
 | 
			
		||||
      }"
 | 
			
		||||
      icon="bi-plus"
 | 
			
		||||
      primary
 | 
			
		||||
    />
 | 
			
		||||
    <Modal
 | 
			
		||||
      v-model="showSubscribeModal"
 | 
			
		||||
      :title="t('views.channels.SubscriptionsList.modal.subscription.header')"
 | 
			
		||||
      class="tiny"
 | 
			
		||||
    >
 | 
			
		||||
      <div
 | 
			
		||||
        ref="modalContent"
 | 
			
		||||
        class="scrolling content"
 | 
			
		||||
      >
 | 
			
		||||
        <remote-search-form
 | 
			
		||||
          initial-type="both"
 | 
			
		||||
          :show-submit="false"
 | 
			
		||||
          :standalone="false"
 | 
			
		||||
          :redirect="true"
 | 
			
		||||
          @subscribed="showSubscribeModal = false; reloadWidget()"
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
      <template #actions>
 | 
			
		||||
        <Button
 | 
			
		||||
          secondary
 | 
			
		||||
          @click="showSubscribeModal = false"
 | 
			
		||||
        >
 | 
			
		||||
          {{ t('views.channels.SubscriptionsList.button.cancel') }}
 | 
			
		||||
        </Button>
 | 
			
		||||
        <Button
 | 
			
		||||
          form="remote-search"
 | 
			
		||||
          type="submit"
 | 
			
		||||
          icon="bi-bookmark-check-fill"
 | 
			
		||||
          primary
 | 
			
		||||
        >
 | 
			
		||||
          {{ t('views.channels.SubscriptionsList.button.subscribe') }}
 | 
			
		||||
        </Button>
 | 
			
		||||
      </template>
 | 
			
		||||
    </Modal>
 | 
			
		||||
 | 
			
		||||
    <inline-search-bar
 | 
			
		||||
      v-model="query"
 | 
			
		||||
      :placeholder="labels.searchPlaceholder"
 | 
			
		||||
      @search="reloadWidget"
 | 
			
		||||
    />
 | 
			
		||||
    <channels-widget
 | 
			
		||||
      :key="widgetKey"
 | 
			
		||||
      :limit="50"
 | 
			
		||||
      :show-modification-date="true"
 | 
			
		||||
      :filters="{q: query, ordering: '-name'}"
 | 
			
		||||
    />
 | 
			
		||||
<!-- TODO: Translations -->
 | 
			
		||||
    <Header
 | 
			
		||||
      :h1="t('views.auth.ProfileOverview.header.channels')"
 | 
			
		||||
      :action="{
 | 
			
		||||
        text: t('views.channels.SubscriptionsList.link.addNew'),
 | 
			
		||||
        onClick: ()=> showCreateModal = true
 | 
			
		||||
      }"
 | 
			
		||||
      icon="bi-plus"
 | 
			
		||||
      primary
 | 
			
		||||
    />
 | 
			
		||||
    <channels-widget
 | 
			
		||||
      :show-modification-date="true"
 | 
			
		||||
      :limit="12"
 | 
			
		||||
      :filters="{ordering: '-creation_date', external: 'true'}"
 | 
			
		||||
    >
 | 
			
		||||
      <template #title>
 | 
			
		||||
        {{ t('components.library.Home.header.newChannels') }}
 | 
			
		||||
      </template>
 | 
			
		||||
    </channels-widget>
 | 
			
		||||
  </Layout>
 | 
			
		||||
 | 
			
		||||
  <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"
 | 
			
		||||
    >
 | 
			
		||||
      <channel-form
 | 
			
		||||
        ref="createForm"
 | 
			
		||||
        :object="null"
 | 
			
		||||
        :step="step"
 | 
			
		||||
        @loading="loading = $event"
 | 
			
		||||
        @submittable="submittable = $event"
 | 
			
		||||
        @category="category = $event"
 | 
			
		||||
        @errored="modalContent.scrollTop = 0"
 | 
			
		||||
        @created="router.push({name: 'channels.detail', params: {id: $event.actor.preferred_username}})"
 | 
			
		||||
      />
 | 
			
		||||
    </div>
 | 
			
		||||
    <template #actions>
 | 
			
		||||
      <Button secondary
 | 
			
		||||
        v-if="step === 1"
 | 
			
		||||
        autofocus
 | 
			
		||||
      >
 | 
			
		||||
        {{ t('views.auth.ProfileOverview.button.cancel') }}
 | 
			
		||||
      </Button>
 | 
			
		||||
      <Button secondary
 | 
			
		||||
        v-if="step > 1"
 | 
			
		||||
        @click.stop.prevent="step -= 1"
 | 
			
		||||
      >
 | 
			
		||||
        {{ t('views.auth.ProfileOverview.button.previous') }}
 | 
			
		||||
      </Button>
 | 
			
		||||
      <Button primary
 | 
			
		||||
        v-if="step === 1"
 | 
			
		||||
        @click.stop.prevent="step += 1"
 | 
			
		||||
      >
 | 
			
		||||
        {{ t('views.auth.ProfileOverview.button.next') }}
 | 
			
		||||
      </Button>
 | 
			
		||||
      <Button primary
 | 
			
		||||
        v-if="step === 2"
 | 
			
		||||
        type="submit"
 | 
			
		||||
        :disabled="!submittable && !loading"
 | 
			
		||||
        :isLoading="loading"
 | 
			
		||||
        @click.prevent.stop="createForm.submit"
 | 
			
		||||
      >
 | 
			
		||||
        {{ t('views.auth.ProfileOverview.button.createChannel') }}
 | 
			
		||||
      </Button>
 | 
			
		||||
    </template>
 | 
			
		||||
  </Modal>
 | 
			
		||||
</template>
 | 
			
		||||
		Loading…
	
		Reference in New Issue