@meeovi/layer-shared 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -0
- package/app/components/Gallery/Gallery.vue +187 -0
- package/app/components/Gallery/__tests__/Gallery.spec.ts +14 -0
- package/app/components/Heading/Heading.vue +14 -0
- package/app/components/Heading/__tests__/Heading.spec.ts +14 -0
- package/app/components/Heading/types.ts +5 -0
- package/app/components/media/audioGallery.vue +70 -0
- package/app/components/media/dragDropUpload.vue +67 -0
- package/app/components/media/fullscreenMediaModal.vue +66 -0
- package/app/components/media/imageCard.vue +65 -0
- package/app/components/media/imageGallery.vue +40 -0
- package/app/components/media/mediaCard.vue +89 -0
- package/app/components/media/mediaCarousel.vue +65 -0
- package/app/components/media/mediaFolderSidebar.vue +72 -0
- package/app/components/media/mediaPlayer.vue +40 -0
- package/app/components/media/mediaSearchBar.vue +16 -0
- package/app/components/media/videoGallery.vue +77 -0
- package/app/components/ui/AccordionItem/AccordionItem.vue +24 -0
- package/app/components/ui/AccordionItem/__tests__/AccordionItem.spec.ts +14 -0
- package/app/components/ui/AccordionItem/types.ts +5 -0
- package/app/components/ui/Alert/Alert.vue +34 -0
- package/app/components/ui/Alert/types.ts +5 -0
- package/app/components/ui/Breadcrumbs/Breadcrumbs.vue +76 -0
- package/app/components/ui/Breadcrumbs/__tests__/Breadcrumbs.spec.ts +14 -0
- package/app/components/ui/Breadcrumbs/types.ts +8 -0
- package/app/components/ui/CartProductCard/CartProductCard.vue +66 -0
- package/app/components/ui/CartProductCard/types.ts +18 -0
- package/app/components/ui/CategoryCard/CategoryCard.vue +41 -0
- package/app/components/ui/CategoryCard/types.ts +9 -0
- package/app/components/ui/Display/Display.vue +55 -0
- package/app/components/ui/Display/types.ts +12 -0
- package/app/components/ui/Divider/Divider.vue +3 -0
- package/app/components/ui/Divider/__tests__/Divider.spec.tsx +10 -0
- package/app/components/ui/Footer.vue +92 -0
- package/app/components/ui/Form/FormHelperText.vue +5 -0
- package/app/components/ui/Form/FormLabel.vue +5 -0
- package/app/components/ui/Form/FormPasswordInput.vue +15 -0
- package/app/components/ui/Form/__tests__/FormHelperText.spec.ts +10 -0
- package/app/components/ui/Form/__tests__/FormLabel.spec.ts +10 -0
- package/app/components/ui/Hero/Hero.vue +44 -0
- package/app/components/ui/Hero/types.ts +10 -0
- package/app/components/ui/Modal/Modal.vue +19 -0
- package/app/components/ui/Modal/types.ts +8 -0
- package/app/components/ui/Motionable.vue +45 -0
- package/app/components/ui/NavbarBottom.vue +63 -0
- package/app/components/ui/NavbarTop.vue +25 -0
- package/app/components/ui/Overlay/Overlay.vue +14 -0
- package/app/components/ui/Overlay/__tests__/Overlay.spec.ts +14 -0
- package/app/components/ui/Overlay/types.ts +3 -0
- package/app/components/ui/PageBuilder.vue +39 -0
- package/app/components/ui/PageContainer.vue +5 -0
- package/app/components/ui/Pagination/Pagination.vue +151 -0
- package/app/components/ui/Pagination/__tests__/Pagination.spec.ts +17 -0
- package/app/components/ui/Pagination/types.ts +6 -0
- package/app/components/ui/ProductCard/ProductCard.vue +55 -0
- package/app/components/ui/ProductCard/__tests__/ProductCard.spec.ts +16 -0
- package/app/components/ui/ProductCard/types.ts +12 -0
- package/app/components/ui/ProductCardHorizontal/ProductCardHorizontal.vue +34 -0
- package/app/components/ui/ProductCardHorizontal/__tests__/ProductCardHorizontal.spec.ts +36 -0
- package/app/components/ui/ProductCardHorizontal/types.ts +8 -0
- package/app/components/ui/PurchaseCard/PurchaseCard.vue +109 -0
- package/app/components/ui/PurchaseCard/__tests__/PurchaseCard.spec.ts +15 -0
- package/app/components/ui/PurchaseCard/types.ts +5 -0
- package/app/components/ui/QuantitySelector/QuantitySelector.vue +69 -0
- package/app/components/ui/QuantitySelector/__tests__/QuantitySelector.spec.ts +15 -0
- package/app/components/ui/QuantitySelector/types.ts +5 -0
- package/app/components/ui/RadialProgress.vue +44 -0
- package/app/components/ui/Review/Review.vue +57 -0
- package/app/components/ui/Review/__tests__/Review.spec.ts +15 -0
- package/app/components/ui/Review/types.ts +5 -0
- package/app/components/ui/ScrollTop.vue +82 -0
- package/app/components/ui/Search.vue +54 -0
- package/app/components/ui/Tag/Tag.vue +38 -0
- package/app/components/ui/Tag/__tests__/Tag.spec.ts +10 -0
- package/app/components/ui/Tag/types.ts +16 -0
- package/app/components/ui/VsfLogo.vue +7 -0
- package/app/components/ui/forms/BooleanInput.vue +34 -0
- package/app/components/ui/forms/DateTime.vue +44 -0
- package/app/components/ui/forms/DirectusFormElement.vue +60 -0
- package/app/components/ui/forms/DynamicTableElement.vue +57 -0
- package/app/components/ui/forms/FileInput.vue +85 -0
- package/app/components/ui/forms/FormField.vue +34 -0
- package/app/components/ui/forms/RelationSelect.vue +63 -0
- package/app/components/ui/forms/RepeaterInput.vue +121 -0
- package/app/components/ui/forms/SelectInput.vue +65 -0
- package/app/components/ui/forms/TextArea.vue +59 -0
- package/app/components/ui/forms/TextInput.vue +42 -0
- package/app/components/ui/forms/TiptapEditor.vue +136 -0
- package/app/components/ui/forms/[collection].vue +22 -0
- package/app/components/ui/studio/builder.vue +57 -0
- package/app/components/ui/studio/document.vue +69 -0
- package/app/components/ui/studio/email.vue +57 -0
- package/app/composables/globals/uploadFiles.js +41 -0
- package/app/composables/globals/useAdminTable.ts +12 -0
- package/app/composables/globals/useCustomFetch.ts +13 -0
- package/app/composables/globals/useDirectusField.ts +144 -0
- package/app/composables/globals/useDirectusForm.ts +70 -0
- package/app/composables/globals/useDirectusSchema.js +9 -0
- package/app/composables/globals/useFileManager.ts +76 -0
- package/app/composables/globals/useLivePreview.ts +17 -0
- package/app/composables/globals/useLoading.ts +23 -0
- package/app/composables/globals/useNavigation.js +19 -0
- package/app/composables/globals/useNotifications.ts +153 -0
- package/app/composables/globals/usePages.js +36 -0
- package/app/composables/globals/useRichText.ts +33 -0
- package/app/composables/globals/useServerRootMixin.ts +19 -0
- package/app/composables/globals/useVisualEditing.ts +38 -0
- package/app/composables/media/useFile.ts +6 -0
- package/app/composables/media/useMediaCenter.ts +353 -0
- package/app/composables/media/useVideojs.ts +45 -0
- package/app/composables/registry.ts +13 -0
- package/app/composables/types.ts +12 -0
- package/app/composables/useContent.ts +13 -0
- package/app/composables/useDirectusRequest.ts +32 -0
- package/app/stores/index.ts +0 -0
- package/app/types/api/global-search.ts +8 -0
- package/app/types/blocks/block-button-group.ts +7 -0
- package/app/types/blocks/block-button.ts +14 -0
- package/app/types/blocks/block-column.ts +20 -0
- package/app/types/blocks/block-cta.ts +10 -0
- package/app/types/blocks/block-divider.ts +4 -0
- package/app/types/blocks/block-faq.ts +12 -0
- package/app/types/blocks/block-form.ts +8 -0
- package/app/types/blocks/block-gallery.ts +14 -0
- package/app/types/blocks/block-hero.ts +12 -0
- package/app/types/blocks/block-html.ts +4 -0
- package/app/types/blocks/block-logocloud.ts +14 -0
- package/app/types/blocks/block-quote.ts +11 -0
- package/app/types/blocks/block-richtext.ts +7 -0
- package/app/types/blocks/block-steps.ts +22 -0
- package/app/types/blocks/block-team.ts +6 -0
- package/app/types/blocks/block-testimonial.ts +14 -0
- package/app/types/blocks/block-video.ts +10 -0
- package/app/types/blocks/block.ts +49 -0
- package/app/types/blocks/index.ts +18 -0
- package/app/types/componentMap.ts +15 -0
- package/app/types/content/category.ts +11 -0
- package/app/types/content/form.ts +20 -0
- package/app/types/content/index.ts +6 -0
- package/app/types/content/page.ts +76 -0
- package/app/types/content/post.ts +39 -0
- package/app/types/content/team.ts +16 -0
- package/app/types/content/testimonial.ts +19 -0
- package/app/types/directus.d.ts +47 -0
- package/app/types/env.d.ts +8 -0
- package/app/types/help/index.ts +53 -0
- package/app/types/index.d.ts +9 -0
- package/app/types/index.ts +7 -0
- package/app/types/meta/analytics.ts +18 -0
- package/app/types/meta/config.ts +21 -0
- package/app/types/meta/globals.ts +30 -0
- package/app/types/meta/index.ts +6 -0
- package/app/types/meta/navigation.ts +32 -0
- package/app/types/meta/redirect.ts +13 -0
- package/app/types/meta/seo.ts +19 -0
- package/app/types/os/contact.ts +23 -0
- package/app/types/os/conversation.ts +25 -0
- package/app/types/os/index.ts +16 -0
- package/app/types/os/organization.ts +54 -0
- package/app/types/os/os-activity.ts +28 -0
- package/app/types/os/os-deal.ts +45 -0
- package/app/types/os/os-expense.ts +22 -0
- package/app/types/os/os-invoice.ts +48 -0
- package/app/types/os/os-item.ts +18 -0
- package/app/types/os/os-payment.ts +29 -0
- package/app/types/os/os-project.ts +47 -0
- package/app/types/os/os-proposal.ts +84 -0
- package/app/types/os/os-settings.ts +19 -0
- package/app/types/os/os-subscription.ts +12 -0
- package/app/types/os/os-task.ts +34 -0
- package/app/types/os/os-tax-rate.ts +13 -0
- package/app/types/pageComponentMap.ts +8 -0
- package/app/types/schema.d.ts +39 -0
- package/app/types/schema.ts +151 -0
- package/app/types/system/file.ts +46 -0
- package/app/types/system/folder.ts +8 -0
- package/app/types/system/index.ts +4 -0
- package/app/types/system/role.ts +21 -0
- package/app/types/system/user.ts +56 -0
- package/app/utils/Timer.js +44 -0
- package/app/utils/billing-address.ts +24 -0
- package/app/utils/color.ts +14 -0
- package/app/utils/currency.ts +29 -0
- package/app/utils/embed.ts +57 -0
- package/app/utils/errors.ts +9 -0
- package/app/utils/fieldRegistry.js +89 -0
- package/app/utils/fonts.ts +24 -0
- package/app/utils/formkit.ts +75 -0
- package/app/utils/icons.ts +62 -0
- package/app/utils/index.js +0 -0
- package/app/utils/links.ts +28 -0
- package/app/utils/lodash.ts +33 -0
- package/app/utils/markdown.ts +9 -0
- package/app/utils/math.ts +25 -0
- package/app/utils/navigation.ts +11 -0
- package/app/utils/objects.ts +11 -0
- package/app/utils/paths.ts +21 -0
- package/app/utils/relations.ts +33 -0
- package/app/utils/strings.ts +113 -0
- package/app/utils/time.ts +148 -0
- package/app/utils/url.ts +22 -0
- package/app/utils/user-name.ts +21 -0
- package/app/utils/video/README.md +51 -0
- package/app/utils/video/playlist.js +0 -0
- package/dist/api/global-search.d.ts +8 -0
- package/dist/api/global-search.js +1 -0
- package/dist/blocks/block-button-group.d.ts +6 -0
- package/dist/blocks/block-button-group.js +1 -0
- package/dist/blocks/block-button.d.ts +13 -0
- package/dist/blocks/block-button.js +1 -0
- package/dist/blocks/block-column.d.ts +18 -0
- package/dist/blocks/block-column.js +1 -0
- package/dist/blocks/block-cta.d.ts +11 -0
- package/dist/blocks/block-cta.js +1 -0
- package/dist/blocks/block-divider.d.ts +4 -0
- package/dist/blocks/block-divider.js +1 -0
- package/dist/blocks/block-faq.d.ts +11 -0
- package/dist/blocks/block-faq.js +1 -0
- package/dist/blocks/block-form.d.ts +7 -0
- package/dist/blocks/block-form.js +1 -0
- package/dist/blocks/block-gallery.d.ts +13 -0
- package/dist/blocks/block-gallery.js +1 -0
- package/dist/blocks/block-hero.d.ts +11 -0
- package/dist/blocks/block-hero.js +1 -0
- package/dist/blocks/block-html.d.ts +4 -0
- package/dist/blocks/block-html.js +1 -0
- package/dist/blocks/block-logocloud.d.ts +13 -0
- package/dist/blocks/block-logocloud.js +1 -0
- package/dist/blocks/block-quote.d.ts +10 -0
- package/dist/blocks/block-quote.js +1 -0
- package/dist/blocks/block-richtext.d.ts +7 -0
- package/dist/blocks/block-richtext.js +1 -0
- package/dist/blocks/block-steps.d.ts +21 -0
- package/dist/blocks/block-steps.js +1 -0
- package/dist/blocks/block-team.d.ts +6 -0
- package/dist/blocks/block-team.js +1 -0
- package/dist/blocks/block-testimonial.d.ts +13 -0
- package/dist/blocks/block-testimonial.js +1 -0
- package/dist/blocks/block-video.d.ts +9 -0
- package/dist/blocks/block-video.js +1 -0
- package/dist/blocks/block.d.ts +17 -0
- package/dist/blocks/block.js +1 -0
- package/dist/blocks/index.d.ts +18 -0
- package/dist/blocks/index.js +1 -0
- package/dist/componentMap.d.ts +6 -0
- package/dist/componentMap.js +8 -0
- package/dist/content/category.d.ts +10 -0
- package/dist/content/category.js +1 -0
- package/dist/content/form.d.ts +21 -0
- package/dist/content/form.js +1 -0
- package/dist/content/index.d.ts +6 -0
- package/dist/content/index.js +1 -0
- package/dist/content/page.d.ts +38 -0
- package/dist/content/page.js +1 -0
- package/dist/content/post.d.ts +38 -0
- package/dist/content/post.js +1 -0
- package/dist/content/team.d.ts +17 -0
- package/dist/content/team.js +1 -0
- package/dist/content/testimonial.d.ts +18 -0
- package/dist/content/testimonial.js +1 -0
- package/dist/help/index.d.ts +51 -0
- package/dist/help/index.js +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1 -0
- package/dist/meta/analytics.d.ts +21 -0
- package/dist/meta/analytics.js +1 -0
- package/dist/meta/config.d.ts +22 -0
- package/dist/meta/config.js +1 -0
- package/dist/meta/globals.d.ts +33 -0
- package/dist/meta/globals.js +1 -0
- package/dist/meta/index.d.ts +6 -0
- package/dist/meta/index.js +1 -0
- package/dist/meta/navigation.d.ts +31 -0
- package/dist/meta/navigation.js +1 -0
- package/dist/meta/redirect.d.ts +12 -0
- package/dist/meta/redirect.js +1 -0
- package/dist/meta/seo.d.ts +19 -0
- package/dist/meta/seo.js +1 -0
- package/dist/os/contact.d.ts +22 -0
- package/dist/os/contact.js +1 -0
- package/dist/os/conversation.d.ts +23 -0
- package/dist/os/conversation.js +1 -0
- package/dist/os/index.d.ts +16 -0
- package/dist/os/index.js +1 -0
- package/dist/os/organization.d.ts +51 -0
- package/dist/os/organization.js +1 -0
- package/dist/os/os-activity.d.ts +26 -0
- package/dist/os/os-activity.js +1 -0
- package/dist/os/os-deal.d.ts +42 -0
- package/dist/os/os-deal.js +1 -0
- package/dist/os/os-expense.d.ts +21 -0
- package/dist/os/os-expense.js +1 -0
- package/dist/os/os-invoice.d.ts +46 -0
- package/dist/os/os-invoice.js +1 -0
- package/dist/os/os-item.d.ts +17 -0
- package/dist/os/os-item.js +1 -0
- package/dist/os/os-payment.d.ts +27 -0
- package/dist/os/os-payment.js +1 -0
- package/dist/os/os-project.d.ts +45 -0
- package/dist/os/os-project.js +1 -0
- package/dist/os/os-proposal.d.ts +61 -0
- package/dist/os/os-proposal.js +1 -0
- package/dist/os/os-settings.d.ts +17 -0
- package/dist/os/os-settings.js +1 -0
- package/dist/os/os-subscription.d.ts +12 -0
- package/dist/os/os-subscription.js +1 -0
- package/dist/os/os-task.d.ts +32 -0
- package/dist/os/os-task.js +1 -0
- package/dist/os/os-tax-rate.d.ts +12 -0
- package/dist/os/os-tax-rate.js +1 -0
- package/dist/pageComponentMap.d.ts +2 -0
- package/dist/pageComponentMap.js +7 -0
- package/dist/schema.d.ts +78 -0
- package/dist/schema.js +1 -0
- package/dist/system/file.d.ts +47 -0
- package/dist/system/file.js +1 -0
- package/dist/system/folder.d.ts +8 -0
- package/dist/system/folder.js +1 -0
- package/dist/system/index.d.ts +4 -0
- package/dist/system/index.js +1 -0
- package/dist/system/role.d.ts +20 -0
- package/dist/system/role.js +1 -0
- package/dist/system/user.d.ts +57 -0
- package/dist/system/user.js +1 -0
- package/nuxt.config.ts +5 -0
- package/package.json +42 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { ref, computed, unref } from 'vue'
|
|
2
|
+
import { useNuxtApp } from '#imports'
|
|
3
|
+
import useDirectusRequest from '../useDirectusRequest'
|
|
4
|
+
import { useAuth } from '~/composables/globals/useAuth'
|
|
5
|
+
|
|
6
|
+
export function useMediaCenter() {
|
|
7
|
+
// Prefer a BetterAuth `useAuth()` composable when available (provided by the auth layer).
|
|
8
|
+
// Fallback to null if not present so the rest of the composable degrades gracefully.
|
|
9
|
+
// `useAuth()` typically exposes `user` and `session` refs and a `fetchSession()` method.
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
const auth: any = (globalThis as any).$useAuth ?? (typeof useAuth !== 'undefined' ? useAuth() : null)
|
|
12
|
+
|
|
13
|
+
const user = computed(() => {
|
|
14
|
+
if (!auth) return null
|
|
15
|
+
// If auth exposes `user` ref directly
|
|
16
|
+
if (typeof auth.user !== 'undefined') return unref(auth.user) || null
|
|
17
|
+
// If auth exposes `session` ref containing `{ user }`
|
|
18
|
+
if (typeof auth.session !== 'undefined') return unref(auth.session)?.user || null
|
|
19
|
+
return null
|
|
20
|
+
})
|
|
21
|
+
const userId = computed(() => user.value?.id || null)
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
$directus,
|
|
25
|
+
$readItems,
|
|
26
|
+
$createItem,
|
|
27
|
+
$uploadFiles,
|
|
28
|
+
} = useNuxtApp()
|
|
29
|
+
|
|
30
|
+
// CORE MEDIA STATE
|
|
31
|
+
const allMedia = ref<any[]>([])
|
|
32
|
+
const isInitialLoading = ref(true)
|
|
33
|
+
const isLoadingMore = ref(false)
|
|
34
|
+
const hasMore = ref(true)
|
|
35
|
+
const limit = 24
|
|
36
|
+
const offset = ref(0)
|
|
37
|
+
|
|
38
|
+
// FOLDERS
|
|
39
|
+
const folders = ref<any[]>([])
|
|
40
|
+
const currentFolder = ref<string | null>(null)
|
|
41
|
+
|
|
42
|
+
// SHARED & SEARCH
|
|
43
|
+
const sharedWithMe = ref<any[]>([])
|
|
44
|
+
const searchResults = ref<any[]>([])
|
|
45
|
+
|
|
46
|
+
// SMART ALBUMS
|
|
47
|
+
const smartAlbums = ref<any[]>([])
|
|
48
|
+
|
|
49
|
+
/* ---------- MIME HELPERS ---------- */
|
|
50
|
+
const mime = (i: any) => i?.directus_files_id?.type || ''
|
|
51
|
+
const isAudio = (i: any) => mime(i).startsWith('audio')
|
|
52
|
+
const isVideo = (i: any) => mime(i).startsWith('video')
|
|
53
|
+
const isImage = (i: any) => mime(i).startsWith('image')
|
|
54
|
+
const isText = (i: any) => mime(i).startsWith('text')
|
|
55
|
+
const isDoc = (i: any) =>
|
|
56
|
+
[
|
|
57
|
+
'application/pdf',
|
|
58
|
+
'application/msword',
|
|
59
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
60
|
+
'application/vnd.ms-excel',
|
|
61
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
62
|
+
].includes(mime(i))
|
|
63
|
+
|
|
64
|
+
/* ---------- FILTERED LISTS ---------- */
|
|
65
|
+
const filteredMedia = computed(() => {
|
|
66
|
+
if (!currentFolder.value) return allMedia.value
|
|
67
|
+
return allMedia.value.filter(m => m.folder === currentFolder.value)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const audioMedia = computed(() => filteredMedia.value.filter(isAudio))
|
|
71
|
+
const videoMedia = computed(() => filteredMedia.value.filter(isVideo))
|
|
72
|
+
const imageMedia = computed(() => filteredMedia.value.filter(isImage))
|
|
73
|
+
const textMedia = computed(() => filteredMedia.value.filter(isText))
|
|
74
|
+
const documentMedia = computed(() => filteredMedia.value.filter(isDoc))
|
|
75
|
+
|
|
76
|
+
/* ---------- RESET PAGINATION ---------- */
|
|
77
|
+
function resetPagination() {
|
|
78
|
+
allMedia.value = []
|
|
79
|
+
offset.value = 0
|
|
80
|
+
hasMore.value = true
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* ---------- FETCH USER MEDIA (PAGINATED) ---------- */
|
|
84
|
+
async function fetchNextPage() {
|
|
85
|
+
if (!userId.value) {
|
|
86
|
+
isInitialLoading.value = false
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
if (!hasMore.value) return
|
|
90
|
+
|
|
91
|
+
isLoadingMore.value = true
|
|
92
|
+
|
|
93
|
+
const filter: any = { user: { _eq: userId.value } }
|
|
94
|
+
if (currentFolder.value) {
|
|
95
|
+
filter.folder = { _eq: currentFolder.value }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const items = await $readItems('media', {
|
|
100
|
+
fields: [
|
|
101
|
+
'*',
|
|
102
|
+
{ directus_files_id: ['id', 'type', 'filename_download', 'title'] },
|
|
103
|
+
],
|
|
104
|
+
filter,
|
|
105
|
+
sort: ['-date_created'],
|
|
106
|
+
limit,
|
|
107
|
+
offset: offset.value,
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
if (!items.length) {
|
|
111
|
+
hasMore.value = false
|
|
112
|
+
} else {
|
|
113
|
+
allMedia.value.push(...items)
|
|
114
|
+
offset.value += items.length
|
|
115
|
+
if (items.length < limit) hasMore.value = false
|
|
116
|
+
}
|
|
117
|
+
} finally {
|
|
118
|
+
isLoadingMore.value = false
|
|
119
|
+
isInitialLoading.value = false
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ---------- UPLOAD FILES ---------- */
|
|
124
|
+
/**
|
|
125
|
+
* Upload files to Directus and create `media` entries.
|
|
126
|
+
* Uploads are performed one-by-one so callers can track progress.
|
|
127
|
+
*
|
|
128
|
+
* @param files - array of File objects
|
|
129
|
+
* @param onProgress - optional callback called with { index, total, status, name }
|
|
130
|
+
*/
|
|
131
|
+
async function uploadFiles(files: File[], onProgress?: (info: { index: number; total: number; status: 'starting' | 'done' | 'error'; name?: string }) => void) {
|
|
132
|
+
if (!userId.value) {
|
|
133
|
+
// Try to fetch session once in case the auth state wasn't loaded yet
|
|
134
|
+
try {
|
|
135
|
+
if (auth && typeof auth.fetchSession === 'function') await auth.fetchSession()
|
|
136
|
+
else if (auth && typeof auth.fetch === 'function') await auth.fetch()
|
|
137
|
+
} catch (_) {}
|
|
138
|
+
|
|
139
|
+
if (!userId.value) {
|
|
140
|
+
try {
|
|
141
|
+
const nuxt = useNuxtApp() as any
|
|
142
|
+
const toast = nuxt?.$toast
|
|
143
|
+
if (toast && typeof toast.error === 'function') toast.error('You must be signed in to upload files')
|
|
144
|
+
} catch (_) {}
|
|
145
|
+
throw new Error('User not authenticated')
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const { request } = useDirectusRequest()
|
|
150
|
+
const newItems: any[] = []
|
|
151
|
+
|
|
152
|
+
for (let i = 0; i < files.length; i++) {
|
|
153
|
+
const file = files[i]
|
|
154
|
+
if (!file) {
|
|
155
|
+
onProgress && onProgress({ index: i, total: files.length, status: 'error' })
|
|
156
|
+
continue
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
onProgress && onProgress({ index: i, total: files.length, status: 'starting', name: file.name })
|
|
160
|
+
|
|
161
|
+
const formData = new FormData()
|
|
162
|
+
formData.append('file', file)
|
|
163
|
+
|
|
164
|
+
const resp = await request($uploadFiles(formData))
|
|
165
|
+
const uploaded = Array.isArray(resp) ? resp : (resp ? [resp] : [])
|
|
166
|
+
|
|
167
|
+
for (const up of uploaded) {
|
|
168
|
+
const mediaItem = await $createItem('media', {
|
|
169
|
+
user: userId.value,
|
|
170
|
+
directus_files_id: up.id,
|
|
171
|
+
title: up.filename_download || up.title || up.id,
|
|
172
|
+
folder: currentFolder.value || null,
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
newItems.push({ ...mediaItem, directus_files_id: up })
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
onProgress && onProgress({ index: i, total: files.length, status: 'done', name: file.name })
|
|
179
|
+
} catch (e) {
|
|
180
|
+
onProgress && onProgress({ index: i, total: files.length, status: 'error', name: file.name })
|
|
181
|
+
try {
|
|
182
|
+
const nuxt = useNuxtApp() as any
|
|
183
|
+
const toast = nuxt?.$toast
|
|
184
|
+
if (toast && typeof toast.error === 'function') toast.error(`Upload failed: ${file.name}`)
|
|
185
|
+
} catch (_) {}
|
|
186
|
+
console.error('uploadFiles failed for file', file.name, e)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (newItems.length) {
|
|
191
|
+
// add newly created items to the beginning of allMedia
|
|
192
|
+
allMedia.value.unshift(...newItems)
|
|
193
|
+
try {
|
|
194
|
+
const nuxt = useNuxtApp() as any
|
|
195
|
+
const toast = nuxt?.$toast
|
|
196
|
+
if (toast && typeof toast.success === 'function') toast.success(`Uploaded ${newItems.length} file${newItems.length === 1 ? '' : 's'}`)
|
|
197
|
+
} catch (_) {}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return newItems
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* ---------- FOLDERS ---------- */
|
|
204
|
+
async function fetchFolders() {
|
|
205
|
+
if (!userId.value) return
|
|
206
|
+
folders.value = await $readItems('folders', {
|
|
207
|
+
filter: { user: { _eq: userId.value } },
|
|
208
|
+
sort: ['sort', 'name'],
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function createFolder(name: string, parent: string | null = null) {
|
|
213
|
+
await $createItem('folders', {
|
|
214
|
+
name,
|
|
215
|
+
user: userId.value,
|
|
216
|
+
parent_folder: parent,
|
|
217
|
+
})
|
|
218
|
+
await fetchFolders()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function filterByFolder(folderId: string | null) {
|
|
222
|
+
currentFolder.value = folderId
|
|
223
|
+
resetPagination()
|
|
224
|
+
fetchNextPage()
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function reorderFolders(updated: any[]) {
|
|
228
|
+
// You can persist a "sort" field here if you add it to folders.
|
|
229
|
+
// Example (pseudo-batched):
|
|
230
|
+
//
|
|
231
|
+
// for (let index = 0; index < updated.length; index++) {
|
|
232
|
+
// const folder = updated[index]
|
|
233
|
+
// await $directus.request(
|
|
234
|
+
// $updateItem('folders', folder.id, { sort: index })
|
|
235
|
+
// )
|
|
236
|
+
// }
|
|
237
|
+
//
|
|
238
|
+
// For now, we just update local state:
|
|
239
|
+
folders.value = [...updated]
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* ---------- SHARED WITH ME ---------- */
|
|
243
|
+
async function fetchSharedWithMe() {
|
|
244
|
+
if (!userId.value) return
|
|
245
|
+
sharedWithMe.value = await $readItems('media', {
|
|
246
|
+
fields: [
|
|
247
|
+
'*',
|
|
248
|
+
{ directus_files_id: ['id', 'type', 'filename_download', 'title'] },
|
|
249
|
+
],
|
|
250
|
+
filter: { shared_with: { _contains: userId.value } },
|
|
251
|
+
sort: ['-date_created'],
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* ---------- SEARCH ---------- */
|
|
256
|
+
async function searchMedia(query: string) {
|
|
257
|
+
if (!query) {
|
|
258
|
+
searchResults.value = []
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
searchResults.value = await $readItems('media', {
|
|
263
|
+
fields: [
|
|
264
|
+
'*',
|
|
265
|
+
{ directus_files_id: ['id', 'type', 'filename_download', 'title'] },
|
|
266
|
+
],
|
|
267
|
+
filter: {
|
|
268
|
+
_or: [
|
|
269
|
+
{ title: { _icontains: query } },
|
|
270
|
+
{ tags: { _icontains: query } },
|
|
271
|
+
{
|
|
272
|
+
directus_files_id: {
|
|
273
|
+
title: { _icontains: query },
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
},
|
|
278
|
+
sort: ['-date_created'],
|
|
279
|
+
})
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* ---------- SMART ALBUMS ---------- */
|
|
283
|
+
const smartAlbumsComputed = computed(() => {
|
|
284
|
+
const albums: { id: string; label: string; items: any[] }[] = []
|
|
285
|
+
|
|
286
|
+
const now = new Date()
|
|
287
|
+
const todayKey = now.toISOString().slice(0, 10)
|
|
288
|
+
|
|
289
|
+
const buckets: Record<string, any[]> = {}
|
|
290
|
+
|
|
291
|
+
for (const item of allMedia.value) {
|
|
292
|
+
const dateKey = (item.date_created || '').slice(0, 10) || 'unknown'
|
|
293
|
+
const typeBucket = isImage(item)
|
|
294
|
+
? 'Images'
|
|
295
|
+
: isVideo(item)
|
|
296
|
+
? 'Videos'
|
|
297
|
+
: isAudio(item)
|
|
298
|
+
? 'Audio'
|
|
299
|
+
: 'Other'
|
|
300
|
+
|
|
301
|
+
const key =
|
|
302
|
+
dateKey === todayKey
|
|
303
|
+
? `Today / ${typeBucket}`
|
|
304
|
+
: `${dateKey} / ${typeBucket}`
|
|
305
|
+
|
|
306
|
+
if (!buckets[key]) buckets[key] = []
|
|
307
|
+
buckets[key].push(item)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
for (const key of Object.keys(buckets)) {
|
|
311
|
+
albums.push({
|
|
312
|
+
id: key,
|
|
313
|
+
label: key,
|
|
314
|
+
items: buckets[key]!,
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return albums
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
smartAlbums.value = smartAlbumsComputed.value
|
|
322
|
+
|
|
323
|
+
/* ---------- RETURN API ---------- */
|
|
324
|
+
return {
|
|
325
|
+
// state
|
|
326
|
+
allMedia,
|
|
327
|
+
audioMedia,
|
|
328
|
+
videoMedia,
|
|
329
|
+
imageMedia,
|
|
330
|
+
textMedia,
|
|
331
|
+
documentMedia,
|
|
332
|
+
folders,
|
|
333
|
+
currentFolder,
|
|
334
|
+
sharedWithMe,
|
|
335
|
+
searchResults,
|
|
336
|
+
smartAlbums,
|
|
337
|
+
|
|
338
|
+
// flags
|
|
339
|
+
isInitialLoading,
|
|
340
|
+
isLoadingMore,
|
|
341
|
+
hasMore,
|
|
342
|
+
|
|
343
|
+
// actions
|
|
344
|
+
fetchNextPage,
|
|
345
|
+
uploadFiles,
|
|
346
|
+
fetchFolders,
|
|
347
|
+
createFolder,
|
|
348
|
+
filterByFolder,
|
|
349
|
+
reorderFolders,
|
|
350
|
+
fetchSharedWithMe,
|
|
351
|
+
searchMedia,
|
|
352
|
+
}
|
|
353
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useNuxtApp } from '#imports'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Composable wrapper for the shared `videojs` Nuxt plugin.
|
|
5
|
+
* Provides access to the provided `videojs` instance and registration helpers.
|
|
6
|
+
*/
|
|
7
|
+
export default function useVideojs() {
|
|
8
|
+
const nuxtApp = useNuxtApp()
|
|
9
|
+
|
|
10
|
+
// Provided by layers/shared-app/app/plugins/videojs.client.js
|
|
11
|
+
const videojs = (nuxtApp as any).$videojs || null
|
|
12
|
+
const registerVideojsPlugin = (nuxtApp as any).$registerVideojsPlugin || ((name: string, plugin: any) => {})
|
|
13
|
+
const registerVideojsPlugins = (nuxtApp as any).$registerVideojsPlugins || ((defs: any) => {})
|
|
14
|
+
|
|
15
|
+
function createPlayer(el: Element | string | null, options?: any, ready?: (this: any) => void) {
|
|
16
|
+
if (!videojs) {
|
|
17
|
+
// fallback: try to import dynamically (client-side only)
|
|
18
|
+
// but avoid bundling here; return null instead
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
return videojs(el as any, options, ready)
|
|
23
|
+
} catch (e) {
|
|
24
|
+
// eslint-disable-next-line no-console
|
|
25
|
+
console.warn('[useVideojs] createPlayer failed', e)
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function disposePlayer(player: any) {
|
|
31
|
+
try {
|
|
32
|
+
player && typeof player.dispose === 'function' && player.dispose()
|
|
33
|
+
} catch (e) {
|
|
34
|
+
// ignore
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
videojs,
|
|
40
|
+
registerVideojsPlugin,
|
|
41
|
+
registerVideojsPlugins,
|
|
42
|
+
createPlayer,
|
|
43
|
+
disposePlayer,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ContentProvider } from "./types"
|
|
2
|
+
|
|
3
|
+
const providers: Record<string, ContentProvider> = {}
|
|
4
|
+
|
|
5
|
+
export function registerContentProvider(name: string, provider: ContentProvider) {
|
|
6
|
+
providers[name] = provider
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getContentProvider(name: string) {
|
|
10
|
+
const provider = providers[name]
|
|
11
|
+
if (!provider) throw new Error(`Content provider "${name}" not found`)
|
|
12
|
+
return provider
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ContentItem {
|
|
2
|
+
id: string
|
|
3
|
+
title: string
|
|
4
|
+
body: any
|
|
5
|
+
slug?: string
|
|
6
|
+
[key: string]: any
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ContentProvider {
|
|
10
|
+
getContent(slug: string): Promise<ContentItem>
|
|
11
|
+
listContent(params?: Record<string, any>): Promise<ContentItem[]>
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getContentProvider } from './registry'
|
|
2
|
+
import { useRuntimeConfig } from '#imports'
|
|
3
|
+
|
|
4
|
+
export function useContent() {
|
|
5
|
+
const config = useRuntimeConfig()
|
|
6
|
+
const providerName = config.public.contentProvider || 'directus'
|
|
7
|
+
const provider = getContentProvider(providerName)
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
getContent: provider.getContent,
|
|
11
|
+
listContent: provider.listContent
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useNuxtApp } from '#imports'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Centralized safe wrapper around `$directus.request`.
|
|
5
|
+
* Shows a toast error when the Directus client or `.request` is not available.
|
|
6
|
+
*/
|
|
7
|
+
export default function useDirectusRequest() {
|
|
8
|
+
const nuxt = useNuxtApp() as any
|
|
9
|
+
|
|
10
|
+
async function request(config: any) {
|
|
11
|
+
try {
|
|
12
|
+
const client = nuxt?.$directus
|
|
13
|
+
if (!client || typeof client.request !== 'function') {
|
|
14
|
+
try {
|
|
15
|
+
const toast = nuxt?.$toast
|
|
16
|
+
if (toast && typeof toast.error === 'function') toast.error('Directus client unavailable')
|
|
17
|
+
} catch (_) {}
|
|
18
|
+
throw new Error('Directus client.request is not available')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return await client.request(config)
|
|
22
|
+
} catch (e) {
|
|
23
|
+
try {
|
|
24
|
+
const toast = nuxt?.$toast
|
|
25
|
+
if (toast && typeof toast.error === 'function') toast.error('Request failed')
|
|
26
|
+
} catch (_) {}
|
|
27
|
+
throw e
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { request }
|
|
32
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Post, Page } from '../content';
|
|
2
|
+
|
|
3
|
+
export interface BlockButton {
|
|
4
|
+
id: string;
|
|
5
|
+
sort: number | null;
|
|
6
|
+
type: ('pages' | 'posts' | 'external') | null;
|
|
7
|
+
label: string | null;
|
|
8
|
+
color: 'primary' | 'white' | 'gray' | 'white' | 'black';
|
|
9
|
+
variant: 'solid' | 'outline' | 'ghost' | 'link' | 'soft';
|
|
10
|
+
page: string | Page | null;
|
|
11
|
+
post: string | Post | null;
|
|
12
|
+
external_url: string | null;
|
|
13
|
+
icon: string | null;
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { File } from '../system';
|
|
2
|
+
import type { BlockButtonGroup } from '.';
|
|
3
|
+
|
|
4
|
+
export interface BlockColumn {
|
|
5
|
+
headline?: string | null;
|
|
6
|
+
id?: string;
|
|
7
|
+
title?: string | null;
|
|
8
|
+
rows?: (number | BlockColumnRow)[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface BlockColumnRow {
|
|
12
|
+
block_columns?: (string | BlockColumn) | null;
|
|
13
|
+
content?: string | null;
|
|
14
|
+
headline?: string | null;
|
|
15
|
+
id?: string;
|
|
16
|
+
image?: (string | File) | null;
|
|
17
|
+
image_position?: string | null;
|
|
18
|
+
title?: string | null;
|
|
19
|
+
button_group?: (string | BlockButtonGroup) | null;
|
|
20
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BlockButtonGroup } from '.';
|
|
2
|
+
|
|
3
|
+
export interface BlockCta {
|
|
4
|
+
buttons?: { [key: string]: any } | null;
|
|
5
|
+
content?: string | null;
|
|
6
|
+
headline?: string | null;
|
|
7
|
+
id?: string;
|
|
8
|
+
title?: string | null;
|
|
9
|
+
button_group?: (string | BlockButtonGroup) | null;
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface BlockFaq {
|
|
2
|
+
faqs?: BlockFaqQuestion[] | null;
|
|
3
|
+
headline?: string | null;
|
|
4
|
+
id?: string;
|
|
5
|
+
title?: string | null;
|
|
6
|
+
alignment?: 'left' | 'center' | null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface BlockFaqQuestion {
|
|
10
|
+
title: string | null;
|
|
11
|
+
answer: string | null;
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { File } from '../system';
|
|
2
|
+
|
|
3
|
+
export interface BlockGallery {
|
|
4
|
+
headline?: string | null;
|
|
5
|
+
id?: string;
|
|
6
|
+
title?: string | null;
|
|
7
|
+
gallery_items?: BlockGalleryFile[] | null;
|
|
8
|
+
}
|
|
9
|
+
export interface BlockGalleryFile {
|
|
10
|
+
block_gallery?: (string | BlockGallery) | null;
|
|
11
|
+
directus_files_id?: (string | File) | null;
|
|
12
|
+
id?: number;
|
|
13
|
+
sort?: number | null;
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { File } from '../system';
|
|
2
|
+
import type { BlockButtonGroup } from '.';
|
|
3
|
+
|
|
4
|
+
export interface BlockHero {
|
|
5
|
+
id?: string;
|
|
6
|
+
title?: string | null;
|
|
7
|
+
headline?: string | null;
|
|
8
|
+
content?: string | null;
|
|
9
|
+
image?: (string | File) | null;
|
|
10
|
+
image_position?: 'left' | 'right' | null;
|
|
11
|
+
button_group?: (string | BlockButtonGroup) | null;
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { File } from '../system';
|
|
2
|
+
|
|
3
|
+
export interface BlockLogocloud {
|
|
4
|
+
headline?: string | null;
|
|
5
|
+
id?: string;
|
|
6
|
+
title?: string | null;
|
|
7
|
+
logos?: (string | BlockLogocloudFile)[];
|
|
8
|
+
}
|
|
9
|
+
export interface BlockLogocloudFile {
|
|
10
|
+
id?: string;
|
|
11
|
+
sort?: number | null;
|
|
12
|
+
block_logocloud_id?: (string | BlockLogocloud) | null;
|
|
13
|
+
directus_files_id?: (string | File) | null;
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { File } from '../system';
|
|
2
|
+
|
|
3
|
+
export interface BlockQuote {
|
|
4
|
+
background_color?: string | null;
|
|
5
|
+
content?: string | null;
|
|
6
|
+
headline?: string | null;
|
|
7
|
+
id?: string;
|
|
8
|
+
image?: (string | File) | null;
|
|
9
|
+
subtitle?: string | null;
|
|
10
|
+
title?: string | null;
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { File } from '../system';
|
|
2
|
+
import type { BlockButtonGroup } from '.';
|
|
3
|
+
|
|
4
|
+
export interface BlockStep {
|
|
5
|
+
id?: string;
|
|
6
|
+
title?: string | null;
|
|
7
|
+
headline?: string | null;
|
|
8
|
+
/** If enabled, image position is alternated between left and right. */
|
|
9
|
+
alternate_image_position?: boolean;
|
|
10
|
+
/** Show the step numbers on the website. For example: (Step 1, Step 2, etc) */
|
|
11
|
+
show_step_numbers?: boolean | null;
|
|
12
|
+
steps?: (number | BlockStepItem)[];
|
|
13
|
+
}
|
|
14
|
+
export interface BlockStepItem {
|
|
15
|
+
id?: string;
|
|
16
|
+
title?: string | null;
|
|
17
|
+
content?: string | null;
|
|
18
|
+
image?: (string | File) | null;
|
|
19
|
+
sort?: number | null;
|
|
20
|
+
block_steps?: (string | BlockStep) | null;
|
|
21
|
+
button_group?: (string | BlockButtonGroup) | null;
|
|
22
|
+
}
|