-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* wip * Update async data call in all.vue * Remove unused imports in tv.ts * Update TV episode component to use TV URL for image source and format TV date * Add formatTvDate function to utils/dates.ts * Refactor formatDate function to use formatTvDate * Remove unused import in index.vue * cleanup * Add schema.org markup for video metadata * Add website.svg social icon * Add provider option to DirectusImageProps and update Nuxt Image configuration * Add TVByline and TVReactions components * Add visitor ID tracking for TV pages * fix popper hydration issues * update TVShow to accept URL * Add recommendations section to TV episode page * fix css warnings * fix padding for transcript * add divider between content and meta
- Loading branch information
1 parent
8063379
commit 0cc4117
Showing
9 changed files
with
614 additions
and
39 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
<script setup lang="ts"> | ||
interface BaseBylineProps { | ||
name?: string; | ||
title?: string; | ||
image?: string; | ||
links?: [ | ||
{ | ||
service: string; | ||
url: string; | ||
}, | ||
]; | ||
} | ||
defineProps<BaseBylineProps>(); | ||
</script> | ||
|
||
<template> | ||
<address rel="author" class="base-byline"> | ||
<BaseDirectusImage | ||
v-if="image" | ||
:width="44" | ||
:height="44" | ||
class="avatar" | ||
:uuid="image" | ||
:alt="name ?? ''" | ||
provider="directusTv" | ||
/> | ||
<div> | ||
<p v-if="name" class="name">{{ name }}</p> | ||
<p v-if="title" class="title">{{ title }}</p> | ||
<div v-if="links" class="share-icons"> | ||
<template v-for="{ service, url } in links" :key="service"> | ||
<NuxtLink :href="url" target="_blank"> | ||
<img :src="dynamicAsset(`/svg/social/${service}.svg`)" :alt="service" /> | ||
</NuxtLink> | ||
</template> | ||
</div> | ||
</div> | ||
</address> | ||
</template> | ||
<style scoped lang="scss"> | ||
.base-byline { | ||
--color: var(--foreground); | ||
--title-color: var(--gray-400); | ||
--text-shadow: none; | ||
color: var(--color); | ||
display: flex; | ||
gap: var(--space-2); | ||
font-style: normal; | ||
align-items: flex-start; | ||
.avatar { | ||
border-radius: var(--rounded-full); | ||
inline-size: var(--space-11); | ||
block-size: var(--space-11); | ||
object-fit: cover; | ||
background: var(--gray-100); | ||
} | ||
div { | ||
text-shadow: var(--text-shadow); | ||
} | ||
.name, | ||
.title { | ||
margin: 0; | ||
} | ||
.title { | ||
color: var(--title-color); | ||
font-size: var(--font-size-sm); | ||
line-height: var(--line-height-sm); | ||
text-wrap: balance; | ||
} | ||
.share-icons { | ||
display: flex; | ||
align-items: center; | ||
gap: var(--space-5); | ||
img { | ||
margin-block-start: var(--space-1); | ||
width: var(--space-6); | ||
height: auto; | ||
filter: brightness(1); | ||
transition: filter var(--duration-150) var(--ease-out); | ||
&:hover { | ||
transition: none; | ||
filter: brightness(50); | ||
} | ||
} | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
<script setup> | ||
import { createItem, updateItem } from '@directus/sdk'; | ||
const { $directusTv } = useNuxtApp(); | ||
const ariaId = useId(); | ||
const props = defineProps({ | ||
episodeId: { | ||
type: String, | ||
required: true, | ||
}, | ||
}); | ||
const visitorId = useCookie('visitor_id'); | ||
const loading = ref(false); | ||
const error = ref(null); | ||
const success = ref(false); | ||
const reaction = reactive({ | ||
comments: '', | ||
}); | ||
const textarea = ref(null); | ||
const isOpen = ref(false); | ||
const ratingMessages = { | ||
dislike: 'Woof! 🤦♂️ How can we do better?', | ||
like: 'Nice! 👍 Anything we can improve upon?', | ||
love: `Awesome! The whole team is rejoicing in celebration! 🥳🎉🎊 Anything you'd like to say to them?`, | ||
}; | ||
async function submitReaction(type) { | ||
isOpen.value = true; | ||
loading.value = true; | ||
if (type) { | ||
reaction.type = type; | ||
} | ||
try { | ||
if (reaction.id) { | ||
const response = await $directusTv.request( | ||
updateItem('tv_episode_reactions', reaction.id, { | ||
episode: props.episodeId, | ||
type: reaction.type, | ||
comments: reaction.comments, | ||
visitor_id: visitorId.value, | ||
}), | ||
); | ||
if (response.comments) { | ||
success.value = true; | ||
await setTimeout(() => { | ||
isOpen.value = false; | ||
}, 2000); | ||
} | ||
} else { | ||
const data = await $directusTv.request( | ||
createItem('tv_episode_reactions', { | ||
episode: props.episodeId, | ||
type: reaction.type, | ||
comments: reaction.comments, | ||
visitor_id: visitorId.value, | ||
}), | ||
); | ||
reaction.id = data.id; | ||
} | ||
} catch (e) { | ||
error.value = e; | ||
} finally { | ||
loading.value = false; | ||
} | ||
} | ||
watch(isOpen, (value) => { | ||
if (value) { | ||
setTimeout(() => { | ||
textarea.value.focus(); | ||
}, 100); | ||
} | ||
}); | ||
onKeyStroke('Escape', () => { | ||
isOpen.value = false; | ||
}); | ||
</script> | ||
<template> | ||
<div> | ||
<VDropdown :aria-id class="reactions" :triggers="[]" :shown="isOpen" :auto-hide="false"> | ||
<button | ||
v-tooltip="`I do not like this`" | ||
class="feedback-button" | ||
:aria-pressed="reaction.type === 'dislike'" | ||
@click="() => submitReaction('dislike')" | ||
> | ||
<BaseIcon name="thumb_down" /> | ||
</button> | ||
<button | ||
v-tooltip="'I like this'" | ||
class="feedback-button" | ||
:aria-pressed="reaction.type === 'like'" | ||
@click="() => submitReaction('like')" | ||
> | ||
<BaseIcon name="thumb_up" /> | ||
</button> | ||
<button | ||
v-tooltip="'I freaking love this'" | ||
:aria-pressed="reaction.type === 'love'" | ||
class="feedback-button" | ||
@click="() => submitReaction('love')" | ||
> | ||
<BaseIcon name="favorite" /> | ||
</button> | ||
<template #popper> | ||
<ThemeProvider variant="dark"> | ||
<div class="popover"> | ||
<template v-if="!success"> | ||
<p>{{ ratingMessages[reaction.type] }}</p> | ||
<textarea ref="textarea" v-model="reaction.comments" class="input" placeholder="Give us your feedback" /> | ||
<BaseButton | ||
type="button" | ||
:loading="loading" | ||
:disabled="loading" | ||
color="primary" | ||
label="Send Your Feedback" | ||
@click="submitReaction(reaction.type)" | ||
/> | ||
</template> | ||
<template v-else-if="error"> | ||
<p>Whoops! There was an error submitting your feedback.</p> | ||
</template> | ||
<template v-else-if="success"> | ||
<p>Thank you for your feedback!</p> | ||
</template> | ||
<button class="close-button" @click="() => (isOpen = false)"> | ||
<BaseIcon name="close" /> | ||
</button> | ||
</div> | ||
</ThemeProvider> | ||
</template> | ||
</VDropdown> | ||
</div> | ||
</template> | ||
|
||
<style lang="scss" scoped> | ||
.reactions { | ||
display: flex; | ||
align-content: center; | ||
border-radius: var(--rounded-full); | ||
background: var(--gray-100); | ||
padding: var(--space-1); | ||
gap: var(--space-1); | ||
} | ||
.feedback-button { | ||
transition: background-color 0.2s; | ||
background: none; | ||
border: none; | ||
cursor: pointer; | ||
padding: var(--space-2); | ||
&:hover { | ||
background: var(--gray-200); | ||
} | ||
border-radius: var(--rounded-full); | ||
&[aria-pressed='true'] { | ||
background: var(--primary-500); | ||
color: var(--white); | ||
} | ||
} | ||
.popover { | ||
position: relative; | ||
width: 350px; | ||
padding: var(--space-5); | ||
background-color: var(--gray-100); | ||
border-radius: var(--rounded-xl); | ||
border: 2px solid var(--gray-200); | ||
display: flex; | ||
flex-direction: column; | ||
gap: var(--space-2); | ||
box-shadow: var(--shadow-lg); | ||
button { | ||
width: auto; | ||
} | ||
.close-button { | ||
position: absolute; | ||
top: var(--space-2); | ||
right: var(--space-2); | ||
background: none; | ||
border: none; | ||
cursor: pointer; | ||
padding: var(--space-2); | ||
border-radius: var(--rounded-full); | ||
&:hover { | ||
background: var(--gray-200); | ||
} | ||
} | ||
} | ||
.input { | ||
color: var(--gray-900); | ||
width: 100%; | ||
height: 100px; | ||
border-radius: 4px; | ||
padding: 0.375rem 0.75rem; | ||
background-color: var(--gray-50); | ||
border: 1px solid var(--gray-200); | ||
&:focus { | ||
border-color: var(--primary); | ||
outline: none; | ||
box-shadow: 0px 0px var(--space-1) 0px var(--primary-100); | ||
} | ||
} | ||
</style> | ||
|
||
<style lang="css"> | ||
.v-popper--theme-dropdown { | ||
.v-popper__inner { | ||
background-color: transparent !important; | ||
border: none !important; | ||
border-radius: 6px; | ||
} | ||
.v-popper__arrow-container { | ||
.v-popper__arrow-outer { | ||
border-color: #334155; | ||
} | ||
.v-popper__arrow-inner { | ||
border-color: #334155; | ||
} | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.