Browse Source

fix: youtube video player and twitter recents page

pull/9323/head
DarkPhoenix2704 2 months ago
parent
commit
f27e8c8c86
  1. 2
      packages/nc-gui/components/feed/Changelog/index.vue
  2. 77
      packages/nc-gui/components/feed/Recents/Card.vue
  3. 36
      packages/nc-gui/components/feed/Recents/index.vue
  4. 8
      packages/nc-gui/components/feed/Twitter/index.vue
  5. 44
      packages/nc-gui/components/feed/Youtube/Player.vue
  6. 2
      packages/nc-gui/components/feed/Youtube/index.vue
  7. 1
      packages/nc-gui/lib/types.ts
  8. 24
      packages/nc-gui/utils/urlUtils.ts

2
packages/nc-gui/components/feed/Changelog/index.vue

@ -14,7 +14,7 @@ const { isLoading } = useInfiniteScroll(
githubFeed.value = [...githubFeed.value, ...data] githubFeed.value = [...githubFeed.value, ...data]
}, },
{ distance: 1 }, { distance: 1, interval: 2000 },
) )
</script> </script>

77
packages/nc-gui/components/feed/Recents/Card.vue

@ -0,0 +1,77 @@
<script setup lang="ts">
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeSanitize from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'
import { YoutubeVue3 } from 'youtube-vue3'
import type { ProductFeedItem } from '../../../lib/types'
import { extractYoutubeVideoId } from '../../../utils/urlUtils'
import { timeAgo } from '~/utils/datetimeUtils'
const props = defineProps<{
item: ProductFeedItem
}>()
const { getPossibleAttachmentSrc } = useAttachment()
const {
item: { CreatedAt, Description, Url, Title, Tags, 'Feed Source': source, Images },
} = props
const feedIcon = {
'Twitter': iconMap.twitter,
'Youtube': iconMap.youtube,
'Github Release': iconMap.githubSolid,
}
const renderedText = computedAsync(async () => {
return await unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeSanitize)
.use(rehypeStringify)
.process(
Description.replace(/!\[.*?\]\(.*?\)/g, '')
.substring(0, 300)
.concat('...'),
)
})
</script>
<template>
<div class="bg-white rounded-2xl" style="width: 656px">
<div class="flex items-center justify-between px-5 py-5">
<div class="flex items-center gap-3">
<component :is="feedIcon[source]" class="w-4 h-4 stroke-transparent" />
<span class="font-weight-medium leading-5">
{{ source }}
</span>
</div>
<div class="text-sm text-gray-700 leading-5">
{{ timeAgo(CreatedAt) }}
</div>
</div>
<template v-if="source === 'Github Release'">
<LazyCellAttachmentPreviewImage v-if="Images?.length" :srcs="getPossibleAttachmentSrc(Images[0], 'card_cover')" />
<div class="prose pl-5 mt-5" v-html="renderedText"></div>
</template>
<template v-else-if="source === 'Youtube'">
<YoutubeVue3 :videoid="extractYoutubeVideoId(Url)" :height="410" :width="656" :autoplay="0" />
<div class="p-5 flex flex-col text-gray-900 gap-4">
<div class="text-2xl font-semibold">
{{ Title }}
</div>
<div class="font-weight-base">
{{ Description.substring(0, 200).concat('...') }}
</div>
</div>
</template>
<!--
<template v-else-if="source === 'Twitter'">
<Tweet align="center" conversation="all" class="mt-6" :tweet-url="Url" />
</template> -->
</div>
</template>
<style scoped lang="scss"></style>

36
packages/nc-gui/components/feed/Recents/index.vue

@ -1,5 +1,37 @@
<script setup lang="ts"></script> <script setup lang="ts">
const { socialFeed, loadFeed } = useProductFeed()
<template>Recents Feed</template> const scrollContainer = ref<HTMLElement>()
const { isLoading } = useInfiniteScroll(
scrollContainer,
async () => {
if (isLoading.value) return
const data = (
await loadFeed({
type: 'all',
loadMore: true,
})
).filter((item) => item['Feed Source'] !== 'Twitter')
socialFeed.value = [...socialFeed.value, ...data]
},
{ distance: 1, interval: 2000 },
)
</script>
<template>
<div
ref="scrollContainer"
:style="{
height: 'calc(100dvh - var(--toolbar-height) - 3.25rem)',
}"
class="overflow-y-auto nc-scrollbar-md w-full"
>
<div class="flex flex-col items-center gap-6">
<FeedRecentsCard v-for="feed in socialFeed" :key="feed.Id" :item="feed" />
</div>
</div>
</template>
<style scoped lang="scss"></style> <style scoped lang="scss"></style>

8
packages/nc-gui/components/feed/Twitter/index.vue

@ -15,7 +15,7 @@ const { isLoading } = useInfiniteScroll(
}) })
twitterFeed.value = [...twitterFeed.value, ...data] twitterFeed.value = [...twitterFeed.value, ...data]
}, },
{ distance: 1 }, { distance: 1, interval: 2000 },
) )
</script> </script>
@ -27,8 +27,10 @@ const { isLoading } = useInfiniteScroll(
}" }"
class="overflow-y-auto nc-scrollbar-md w-full" class="overflow-y-auto nc-scrollbar-md w-full"
> >
<div class="flex items-center flex-col"> <div class="mx-auto flex flex-col items-center">
<Tweet v-for="feed in twitterFeed" :key="feed.Id" :tweet-url="feed.Url" /> <div style="min-width: 650px">
<Tweet v-for="feed in twitterFeed" :key="feed.Id" align="center" conversation="all" class="mt-6" :tweet-url="feed.Url" />
</div>
</div> </div>
</div> </div>
</template> </template>

44
packages/nc-gui/components/feed/Youtube/Player.vue

@ -1,50 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import Plyr from 'plyr' import { YoutubeVue3 } from 'youtube-vue3'
import 'plyr/dist/plyr.css'
import type { ProductFeedItem } from '../../../lib/types' import type { ProductFeedItem } from '../../../lib/types'
import { extractYoutubeVideoId } from '../../../utils/urlUtils'
const props = defineProps<{ const props = defineProps<{
item: ProductFeedItem item: ProductFeedItem
isRecent?: boolean
}>() }>()
const { const {
item: { Title, Description, Url }, item: { Title, Description, Url },
} = props } = props
const videoPlayer = ref<HTMLElement>()
const player = ref()
onMounted(() => {
if (!videoPlayer.value) return
player.value = new Plyr(videoPlayer.value, {
previewThumbnails: {},
quality: {
default: 1080,
options: [720, 1080, 2160],
},
})
})
onBeforeUnmount(() => {
if (player.value) {
player.value.destroy()
}
})
</script> </script>
<template> <template>
<div class="flex flex-col mt-6 gap-5"> <div class="flex flex-col mt-6 gap-5">
<div class="aspect-video !rounded-lg mx-auto !h-[428px]"> <YoutubeVue3 :videoid="extractYoutubeVideoId(Url)" :height="470" :width="764" :autoplay="0" :controls="1" />
<div id="player" ref="videoPlayer" class="plyr__video-embed">
<iframe
:src="`${Url}?origin=https://plyr.io&amp;iv_load_policy=3&amp;modestbranding=1&amp;playsinline=1&amp;showinfo=0&amp;rel=0&amp;enablejsapi=1`"
allowfullscreen
allowtransparency
allow="autoplay"
></iframe>
</div>
</div>
<div class="text-gray-900 font-bold text-2xl"> <div class="text-gray-900 font-bold text-2xl">
{{ Title }} {{ Title }}
</div> </div>
@ -54,8 +26,4 @@ onBeforeUnmount(() => {
</div> </div>
</template> </template>
<style lang="scss"> <style lang="scss"></style>
.plyr--video {
@apply !rounded-lg;
}
</style>

2
packages/nc-gui/components/feed/Youtube/index.vue

@ -13,7 +13,7 @@ const { isLoading } = useInfiniteScroll(
}) })
youtubeFeed.value = [...youtubeFeed.value, ...data] youtubeFeed.value = [...youtubeFeed.value, ...data]
}, },
{ distance: 1 }, { distance: 1, interval: 2000 },
) )
const gotoChannel = () => { const gotoChannel = () => {

1
packages/nc-gui/lib/types.ts

@ -285,6 +285,7 @@ interface ProductFeedItem {
Url: string Url: string
Tags?: string Tags?: string
CreatedAt: string CreatedAt: string
Images?: Record<string, any>[] | null
} }
type SordDirectionType = 'asc' | 'desc' | undefined type SordDirectionType = 'asc' | 'desc' | undefined

24
packages/nc-gui/utils/urlUtils.ts

@ -63,3 +63,27 @@ export const isLinkExpired = async (url: string) => {
return true return true
} }
export const extractYoutubeVideoId = (url: string) => {
if (typeof url !== 'string') {
return ''
}
// Regular expressions to match different YouTube URL formats
const patterns = [
/(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/,
/(?:https?:\/\/)?(?:www\.)?youtube\.com\/embed\/([^?]+)/,
/(?:https?:\/\/)?youtu\.be\/([^?]+)/,
/(?:https?:\/\/)?(?:www\.)?youtube\.com\/v\/([^?]+)/,
/(?:https?:\/\/)?(?:www\.)?youtube\.com\/shorts\/([^?]+)/,
]
for (const pattern of patterns) {
const match = url.match(pattern)
if (match && match[1]) {
return match[1]
}
}
return ''
}

Loading…
Cancel
Save