wip bsky client for the web & android
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: wait for user to trigger video loading

willow efc47c87 71f6e01a

+82 -5
+82 -5
src/components/Feed/Embeds/VideoEmbed.vue
··· 1 1 <script setup lang="ts"> 2 - import { ref, onMounted, onUnmounted, computed } from 'vue' 2 + import { ref, onUnmounted, computed, nextTick } from 'vue' 3 3 import Hls from 'hls.js' 4 + import { IconPlayArrowRounded } from '@iconify-prerendered/vue-material-symbols' 4 5 5 6 import { AppBskyEmbedVideo } from '@atcute/bluesky' 6 7 ··· 9 10 }>() 10 11 11 12 const videoRef = ref<HTMLVideoElement | null>(null) 13 + const isLoaded = ref(false) 12 14 let hls: Hls | null = null 13 15 14 16 const aspectRatio = computed(() => { ··· 16 18 return `${props.embed.aspectRatio.width} / ${props.embed.aspectRatio.height}` 17 19 }) 18 20 19 - onMounted(() => { 21 + const loadVideo = async () => { 22 + isLoaded.value = true 23 + await nextTick() 24 + 20 25 const video = videoRef.value 21 26 if (!video) return 22 27 ··· 26 31 }) 27 32 hls.loadSource(props.embed.playlist) 28 33 hls.attachMedia(video) 34 + hls.on(Hls.Events.MANIFEST_PARSED, () => { 35 + video.play() 36 + }) 29 37 } else if (video.canPlayType('application/vnd.apple.mpegurl')) { 30 38 video.src = props.embed.playlist 39 + video.play() 31 40 } 32 - }) 41 + } 33 42 34 43 onUnmounted(() => { 35 44 if (hls) { ··· 48 57 :alt="embed.alt" 49 58 controls 50 59 playsinline 51 - preload="metadata" 60 + preload="none" 61 + v-if="isLoaded" 52 62 /> 63 + 64 + <div v-else class="video-overlay" @click="loadVideo"> 65 + <img 66 + v-if="embed.thumbnail" 67 + :src="embed.thumbnail" 68 + class="video-poster" 69 + alt="Video thumbnail" 70 + /> 71 + <div class="play-button"> 72 + <IconPlayArrowRounded /> 73 + </div> 74 + </div> 53 75 </div> 54 76 </template> 55 77 ··· 63 85 overflow: hidden; 64 86 position: relative; 65 87 border: 1px solid hsla(var(--surface2) / 0.5); 88 + cursor: pointer; 66 89 } 67 90 68 - .video-player { 91 + video.video-player { 69 92 width: 100%; 70 93 height: 100%; 71 94 display: block; 72 95 object-fit: contain; 96 + } 97 + 98 + .video-overlay { 99 + width: 100%; 100 + height: 100%; 101 + position: relative; 102 + display: flex; 103 + align-items: center; 104 + justify-content: center; 105 + 106 + .video-poster { 107 + position: absolute; 108 + top: 0; 109 + left: 0; 110 + width: 100%; 111 + height: 100%; 112 + object-fit: contain; 113 + z-index: 1; 114 + filter: brightness(0.7); 115 + } 116 + 117 + .play-button { 118 + z-index: 2; 119 + background: hsla(var(--surface1) / 0.8); 120 + color: var(--text1); 121 + border-radius: 50%; 122 + width: 3.5rem; 123 + height: 3.5rem; 124 + display: flex; 125 + align-items: center; 126 + justify-content: center; 127 + backdrop-filter: blur(0.25rem); 128 + 129 + svg { 130 + width: 3rem; 131 + height: 3rem; 132 + } 133 + } 134 + 135 + &:hover { 136 + .video-poster { 137 + filter: brightness(0.6); 138 + } 139 + .play-button { 140 + transform: scale(1.1); 141 + filter: brightness(1.1); 142 + } 143 + } 144 + &:active { 145 + .play-button { 146 + transform: scale(0.95); 147 + filter: brightness(0.9); 148 + } 149 + } 73 150 } 74 151 </style>