mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
1 line
No EOL
14 KiB
Text
1 line
No EOL
14 KiB
Text
{"version":3,"file":"DiscoverTypePost-DV867n2W.chunk.mjs","sources":["../build/frontend/apps/appstore/src/composables/useGetLocalizedValue.ts","../build/frontend/apps/appstore/src/components/DiscoverType/common.ts","../build/frontend/apps/appstore/src/components/DiscoverType/DiscoverTypePost.vue"],"sourcesContent":["/*!\n * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { Ref } from 'vue'\nimport type { ILocalizedValue } from '../apps-discover.d.ts'\n\nimport { getLanguage } from '@nextcloud/l10n'\nimport { computed } from 'vue'\n\n/**\n * Get the localized value of the dictionary provided\n *\n * @param dict Dictionary\n * @return String or null if invalid dictionary\n */\nexport function useLocalizedValue<T>(dict: Ref<ILocalizedValue<T | undefined> | undefined | null>) {\n\t/**\n\t * Language of the current user\n\t */\n\tconst language = getLanguage()\n\n\treturn computed(() => !dict?.value ? null : getLocalizedValue<T>(dict.value as ILocalizedValue<T>, language))\n}\n\n/**\n * Helper to get the localized value for the current users language\n *\n * @param dict The dictionary to get the value from\n * @param language The language to use\n */\nfunction getLocalizedValue<T>(dict: ILocalizedValue<T>, language: string) {\n\treturn dict[language] ?? dict[language.split('_')[0]!] ?? dict.en ?? null\n}\n","/**\n * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\nimport type { PropType } from 'vue'\nimport type { IAppDiscoverElement } from '../../apps-discover.d.ts'\n\nimport { APP_DISCOVER_KNOWN_TYPES } from '../../constants.ts'\n\n/**\n * Common Props for all app discover types\n */\nexport const commonAppDiscoverProps = {\n\ttype: {\n\t\ttype: String as PropType<IAppDiscoverElement['type']>,\n\t\trequired: true,\n\t\tvalidator: (v: unknown) => typeof v === 'string' && APP_DISCOVER_KNOWN_TYPES.includes(v as never),\n\t},\n\n\tid: {\n\t\ttype: String as PropType<IAppDiscoverElement['id']>,\n\t\trequired: true,\n\t},\n\n\tdate: {\n\t\ttype: Number as PropType<IAppDiscoverElement['date']>,\n\t\trequired: false,\n\t\tdefault: undefined,\n\t},\n\n\texpiryDate: {\n\t\ttype: Number as PropType<IAppDiscoverElement['expiryDate']>,\n\t\trequired: false,\n\t\tdefault: undefined,\n\t},\n\n\theadline: {\n\t\ttype: Object as PropType<IAppDiscoverElement['headline']>,\n\t\trequired: false,\n\t\tdefault: () => null,\n\t},\n\n\tlink: {\n\t\ttype: String as PropType<IAppDiscoverElement['link']>,\n\t\trequired: false,\n\t\tdefault: () => null,\n\t},\n} as const\n","<!--\n - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<article\n\t\t:id=\"domId\"\n\t\tref=\"container\"\n\t\tclass=\"app-discover-post\"\n\t\t:class=\"{\n\t\t\t'app-discover-post--reverse': media && media.alignment === 'start',\n\t\t\t'app-discover-post--small': isSmallWidth,\n\t\t}\">\n\t\t<component\n\t\t\t:is=\"link ? 'AppLink' : 'div'\"\n\t\t\tv-if=\"headline || text\"\n\t\t\t:href=\"link\"\n\t\t\tclass=\"app-discover-post__text\">\n\t\t\t<component :is=\"inline ? 'h4' : 'h3'\">\n\t\t\t\t{{ translatedHeadline }}\n\t\t\t</component>\n\t\t\t<p>{{ translatedText }}</p>\n\t\t</component>\n\t\t<component\n\t\t\t:is=\"mediaLink ? 'AppLink' : 'div'\"\n\t\t\tv-if=\"mediaSources\"\n\t\t\t:href=\"mediaLink\"\n\t\t\tclass=\"app-discover-post__media\"\n\t\t\t:class=\"{\n\t\t\t\t'app-discover-post__media--fullwidth': isFullWidth,\n\t\t\t\t'app-discover-post__media--start': media?.alignment === 'start',\n\t\t\t\t'app-discover-post__media--end': media?.alignment === 'end',\n\t\t\t}\">\n\t\t\t<component\n\t\t\t\t:is=\"isImage ? 'picture' : 'video'\"\n\t\t\t\tref=\"mediaElement\"\n\t\t\t\tclass=\"app-discover-post__media-element\"\n\t\t\t\t:muted=\"!isImage\"\n\t\t\t\t:playsinline=\"!isImage\"\n\t\t\t\t:preload=\"!isImage && 'auto'\"\n\t\t\t\t@ended=\"hasPlaybackEnded = true\">\n\t\t\t\t<source\n\t\t\t\t\tv-for=\"source of mediaSources\"\n\t\t\t\t\t:key=\"source.src\"\n\t\t\t\t\t:src=\"isImage ? undefined : generatePrivacyUrl(source.src)\"\n\t\t\t\t\t:srcset=\"isImage ? generatePrivacyUrl(source.src) : undefined\"\n\t\t\t\t\t:type=\"source.mime\">\n\t\t\t\t<img\n\t\t\t\t\tv-if=\"isImage\"\n\t\t\t\t\t:src=\"generatePrivacyUrl(mediaSources[0]!.src)\"\n\t\t\t\t\t:alt=\"mediaAlt\">\n\t\t\t</component>\n\t\t\t<div class=\"app-discover-post__play-icon-wrapper\">\n\t\t\t\t<NcIconSvgWrapper\n\t\t\t\t\tv-if=\"!isImage && showPlayVideo\"\n\t\t\t\t\tclass=\"app-discover-post__play-icon\"\n\t\t\t\t\t:path=\"mdiPlayCircleOutline\"\n\t\t\t\t\t:size=\"92\" />\n\t\t\t</div>\n\t\t</component>\n\t</article>\n</template>\n\n<script setup lang=\"ts\">\nimport type { PropType } from 'vue'\nimport type { IAppDiscoverPost } from '../../apps-discover.d.ts'\n\nimport { mdiPlayCircleOutline } from '@mdi/js'\nimport { generateOcsUrl } from '@nextcloud/router'\nimport { useElementSize, useElementVisibility } from '@vueuse/core'\nimport { computed, ref, watchEffect } from 'vue'\nimport NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'\nimport { useLocalizedValue } from '../../composables/useGetLocalizedValue.ts'\nimport { commonAppDiscoverProps } from './common.ts'\n\nconst props = defineProps({\n\t...commonAppDiscoverProps,\n\n\ttext: {\n\t\ttype: Object as PropType<IAppDiscoverPost['text']>,\n\t\trequired: false,\n\t\tdefault: () => null,\n\t},\n\n\tmedia: {\n\t\ttype: Object as PropType<IAppDiscoverPost['media']>,\n\t\trequired: false,\n\t\tdefault: () => null,\n\t},\n\n\tinline: {\n\t\ttype: Boolean,\n\t\trequired: false,\n\t\tdefault: false,\n\t},\n\n\tdomId: {\n\t\ttype: String,\n\t\trequired: false,\n\t\tdefault: null,\n\t},\n})\n\nconst translatedHeadline = useLocalizedValue(computed(() => props.headline))\nconst translatedText = useLocalizedValue(computed(() => props.text))\nconst localizedMedia = useLocalizedValue(computed(() => props.media?.content))\n\nconst mediaSources = computed(() => localizedMedia.value !== null ? [localizedMedia.value.src].flat() : undefined)\nconst mediaAlt = computed(() => localizedMedia.value?.alt ?? '')\n\nconst isImage = computed(() => mediaSources.value?.[0]?.mime.startsWith('image/') === true)\n/**\n * Is the media is shown full width\n */\nconst isFullWidth = computed(() => !translatedHeadline.value && !translatedText.value)\n\n/**\n * Link on the media\n * Fallback to post link to prevent link inside link (which is invalid HTML)\n */\nconst mediaLink = computed(() => localizedMedia.value?.link ?? props.link)\n\nconst hasPlaybackEnded = ref(false)\nconst showPlayVideo = computed(() => localizedMedia.value?.link && hasPlaybackEnded.value)\n\n/**\n * The content is sized / styles are applied based on the container width\n * To make it responsive even for inline usage and when opening / closing the sidebar / navigation\n */\nconst container = ref<HTMLElement>()\nconst { width: containerWidth } = useElementSize(container)\nconst isSmallWidth = computed(() => containerWidth.value < 600)\n\n/**\n * Generate URL for cached media to prevent user can be tracked\n *\n * @param url The URL to resolve\n */\nfunction generatePrivacyUrl(url: string) {\n\treturn url.startsWith('/')\n\t\t? url\n\t\t: generateOcsUrl('/apps/appstore/api/v1/discover/media?fileName={fileName}', { fileName: url })\n}\n\nconst mediaElement = ref<HTMLVideoElement | HTMLPictureElement>()\nconst mediaIsVisible = useElementVisibility(mediaElement, { threshold: 0.3 })\nwatchEffect(() => {\n\t// Only if media is video\n\tif (!isImage.value && mediaElement.value) {\n\t\tconst video = mediaElement.value as HTMLVideoElement\n\n\t\tif (mediaIsVisible.value) {\n\t\t\t// Ensure video is muted - otherwise .play() will be blocked by browsers\n\t\t\tvideo.muted = true\n\t\t\t// If visible start playback\n\t\t\tvideo.play()\n\t\t} else {\n\t\t\t// If not visible pause the playback\n\t\t\tvideo.pause()\n\t\t\t// If the animation has ended reset\n\t\t\tif (video.ended) {\n\t\t\t\tvideo.currentTime = 0\n\t\t\t\thasPlaybackEnded.value = false\n\t\t\t}\n\t\t}\n\t}\n})\n</script>\n\n<style scoped lang=\"scss\">\n.app-discover-post {\n\tmax-height: 300px;\n\twidth: 100%;\n\tbackground-color: var(--color-primary-element-light);\n\tborder-radius: var(--border-radius-rounded);\n\n\tdisplay: flex;\n\tflex-direction: row;\n\tjustify-content: start;\n\n\t&--reverse {\n\t\tflex-direction: row-reverse;\n\t}\n\n\th3, h4 {\n\t\tfont-size: 24px;\n\t\tfont-weight: 600;\n\t\tmargin-block: 0 1em;\n\t}\n\n\t&__text {\n\t\tdisplay: block;\n\t\twidth: 100%;\n\t\tpadding: var(--border-radius-rounded);\n\t\toverflow-y: scroll;\n\t}\n\n\t// If there is media next to the text we do not want a padding on the bottom as this looks weird when scrolling\n\t&:has(&__media) &__text {\n\t\tpadding-block-end: 0;\n\t}\n\n\t&__media {\n\t\tdisplay: block;\n\t\toverflow: hidden;\n\n\t\tmax-width: 450px;\n\t\tborder-radius: var(--border-radius-rounded);\n\n\t\t&--fullwidth {\n\t\t\tmax-width: unset;\n\t\t\tmax-height: unset;\n\t\t}\n\n\t\t&--end {\n\t\t\tborder-end-start-radius: 0;\n\t\t\tborder-start-start-radius: 0;\n\t\t}\n\n\t\t&--start {\n\t\t\tborder-end-end-radius: 0;\n\t\t\tborder-start-end-radius: 0;\n\t\t}\n\n\t\timg, &-element {\n\t\t\theight: 100%;\n\t\t\twidth: 100%;\n\t\t\tobject-fit: cover;\n\t\t\tobject-position: center;\n\t\t}\n\t}\n\n\t&__play-icon {\n\t\tposition: absolute;\n\t\ttop: -46px; // half of the icon height\n\t\tinset-inline-end: -46px; // half of the icon width\n\n\t\t&-wrapper {\n\t\t\tposition: relative;\n\t\t\ttop: -50%;\n\t\t\tinset-inline-start: -50%;\n\t\t}\n\t}\n}\n\n.app-discover-post--small {\n\t&.app-discover-post {\n\t\tflex-direction: column;\n\t\tmax-height: 500px;\n\n\t\t&--reverse {\n\t\t\tflex-direction: column-reverse;\n\t\t}\n\t}\n\n\t.app-discover-post {\n\t\t&__text {\n\t\t\tflex: 1 1 50%;\n\t\t}\n\n\t\t&__media {\n\t\t\tmin-width: 100%;\n\n\t\t\t&--end {\n\t\t\t\tborder-radius: var(--border-radius-rounded);\n\t\t\t\tborder-start-end-radius: 0;\n\t\t\t\tborder-start-start-radius: 0;\n\t\t\t}\n\n\t\t\t&--start {\n\t\t\t\tborder-radius: var(--border-radius-rounded);\n\t\t\t\tborder-end-end-radius: 0;\n\t\t\t\tborder-end-start-radius: 0;\n\t\t\t}\n\t\t}\n\t}\n}\n</style>\n"],"names":["useLocalizedValue","dict","language","getLanguage","computed","getLocalizedValue","commonAppDiscoverProps","v","APP_DISCOVER_KNOWN_TYPES","props","__props","translatedHeadline","translatedText","localizedMedia","mediaSources","mediaAlt","isImage","isFullWidth","mediaLink","hasPlaybackEnded","ref","showPlayVideo","container","containerWidth","useElementSize","isSmallWidth","generatePrivacyUrl","url","generateOcsUrl","mediaElement","mediaIsVisible","useElementVisibility","watchEffect","video","_createElementBlock","headline","_openBlock","_createBlock","_resolveDynamicComponent","link","_unref","_createElementVNode","_Fragment","_renderList","source","_hoisted_4","NcIconSvgWrapper","mdiPlayCircleOutline"],"mappings":"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"} |