메인 인피드 광고 슬롯 추가
This commit is contained in:
@@ -137,6 +137,7 @@ const siteCodeSnapshot = reactive({
|
||||
/** 편집 시작 시점의 광고 슬롯(취소 시 복원용) */
|
||||
const adsSnapshot = reactive({
|
||||
adHomeFeedCode: '',
|
||||
adHomeInfeedCode: '',
|
||||
adSidebarCode: '',
|
||||
adPostTopCode: '',
|
||||
adPostBottomCode: ''
|
||||
@@ -178,6 +179,7 @@ const form = reactive({
|
||||
customHeadCode: settings.value?.customHeadCode || '',
|
||||
customFooterCode: settings.value?.customFooterCode || '',
|
||||
adHomeFeedCode: settings.value?.adHomeFeedCode || '',
|
||||
adHomeInfeedCode: settings.value?.adHomeInfeedCode || '',
|
||||
adSidebarCode: settings.value?.adSidebarCode || '',
|
||||
adPostTopCode: settings.value?.adPostTopCode || '',
|
||||
adPostBottomCode: settings.value?.adPostBottomCode || ''
|
||||
@@ -271,6 +273,7 @@ const hasSiteCodeChanges = computed(() => editSiteCode.value && (
|
||||
*/
|
||||
const hasAdsChanges = computed(() => editAds.value && (
|
||||
form.adHomeFeedCode !== adsSnapshot.adHomeFeedCode
|
||||
|| form.adHomeInfeedCode !== adsSnapshot.adHomeInfeedCode
|
||||
|| form.adSidebarCode !== adsSnapshot.adSidebarCode
|
||||
|| form.adPostTopCode !== adsSnapshot.adPostTopCode
|
||||
|| form.adPostBottomCode !== adsSnapshot.adPostBottomCode
|
||||
@@ -1128,6 +1131,7 @@ const buildSiteSettingsPayload = () => ({
|
||||
customHeadCode: form.customHeadCode || '',
|
||||
customFooterCode: form.customFooterCode || '',
|
||||
adHomeFeedCode: form.adHomeFeedCode || '',
|
||||
adHomeInfeedCode: form.adHomeInfeedCode || '',
|
||||
adSidebarCode: form.adSidebarCode || '',
|
||||
adPostTopCode: form.adPostTopCode || '',
|
||||
adPostBottomCode: form.adPostBottomCode || ''
|
||||
@@ -1720,6 +1724,7 @@ const saveSiteCodeSection = async () => {
|
||||
*/
|
||||
const beginEditAds = () => {
|
||||
adsSnapshot.adHomeFeedCode = form.adHomeFeedCode
|
||||
adsSnapshot.adHomeInfeedCode = form.adHomeInfeedCode
|
||||
adsSnapshot.adSidebarCode = form.adSidebarCode
|
||||
adsSnapshot.adPostTopCode = form.adPostTopCode
|
||||
adsSnapshot.adPostBottomCode = form.adPostBottomCode
|
||||
@@ -1732,6 +1737,7 @@ const beginEditAds = () => {
|
||||
*/
|
||||
const cancelEditAds = () => {
|
||||
form.adHomeFeedCode = adsSnapshot.adHomeFeedCode
|
||||
form.adHomeInfeedCode = adsSnapshot.adHomeInfeedCode
|
||||
form.adSidebarCode = adsSnapshot.adSidebarCode
|
||||
form.adPostTopCode = adsSnapshot.adPostTopCode
|
||||
form.adPostBottomCode = adsSnapshot.adPostBottomCode
|
||||
@@ -1754,6 +1760,7 @@ const saveAdsSection = async () => {
|
||||
|
||||
if (ok) {
|
||||
adsSnapshot.adHomeFeedCode = form.adHomeFeedCode
|
||||
adsSnapshot.adHomeInfeedCode = form.adHomeInfeedCode
|
||||
adsSnapshot.adSidebarCode = form.adSidebarCode
|
||||
adsSnapshot.adPostTopCode = form.adPostTopCode
|
||||
adsSnapshot.adPostBottomCode = form.adPostBottomCode
|
||||
|
||||
191
pages/index.vue
191
pages/index.vue
@@ -15,6 +15,7 @@ const postFeedStyleStorageKey = 'POST_FEED_STYLE'
|
||||
|
||||
const postFeedStyleOpen = ref(false)
|
||||
const postFeedStyle = ref('compact')
|
||||
const homeInfeedAdInsertIndex = ref(-1)
|
||||
|
||||
/** @typedef {'list' | 'compact' | 'cards'} PostFeedStyle */
|
||||
|
||||
@@ -165,6 +166,7 @@ const mapLatestPost = (post) => {
|
||||
|
||||
const featuredPosts = computed(() => posts.value.filter((post) => post.isFeatured).slice(0, 6))
|
||||
const latestPosts = computed(() => posts.value.map(mapLatestPost))
|
||||
const hasHomeInfeedAd = computed(() => Boolean(String(siteSettings.value?.adHomeInfeedCode || '').trim()))
|
||||
|
||||
const featuredTrackRef = ref(null)
|
||||
/** Featured 트랙이 스크롤 시작에 붙었는지 — 이전 화살표 비활성 */
|
||||
@@ -236,6 +238,7 @@ onMounted(() => {
|
||||
|
||||
const storedStyle = localStorage.getItem(postFeedStyleStorageKey)
|
||||
postFeedStyle.value = normalizePostFeedStyle(storedStyle)
|
||||
randomizeHomeInfeedAdInsertIndex()
|
||||
|
||||
document.addEventListener('pointerdown', onDocumentPointerDown)
|
||||
})
|
||||
@@ -277,6 +280,29 @@ const scrollFeatured = (direction) => {
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 메인 인피드 광고 삽입 위치를 현재 브라우저 렌더에서 한 번 정한다.
|
||||
* @returns {void}
|
||||
*/
|
||||
const randomizeHomeInfeedAdInsertIndex = () => {
|
||||
if (!hasHomeInfeedAd.value || latestPosts.value.length < 2) {
|
||||
homeInfeedAdInsertIndex.value = -1
|
||||
return
|
||||
}
|
||||
|
||||
const minIndex = 0
|
||||
const maxIndex = latestPosts.value.length - 2
|
||||
homeInfeedAdInsertIndex.value = minIndex + Math.floor(Math.random() * (maxIndex - minIndex + 1))
|
||||
}
|
||||
|
||||
watch([latestPosts, hasHomeInfeedAd], () => {
|
||||
if (!import.meta.client) {
|
||||
return
|
||||
}
|
||||
|
||||
randomizeHomeInfeedAdInsertIndex()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -465,89 +491,100 @@ const scrollFeatured = (direction) => {
|
||||
data-post-feed="latest"
|
||||
:class="getPostFeedContainerClass(postFeedStyle)"
|
||||
>
|
||||
<article
|
||||
v-for="post in latestPosts"
|
||||
<template
|
||||
v-for="(post, index) in latestPosts"
|
||||
:key="post.to"
|
||||
data-post-card
|
||||
:data-featured="post.isFeatured ? '' : undefined"
|
||||
:class="getPostFeedArticleClass(postFeedStyle)"
|
||||
>
|
||||
<PostCardMedia
|
||||
v-if="showPostFeedMedia"
|
||||
:to="post.to"
|
||||
:title="post.title"
|
||||
:featured-image="post.featuredImage"
|
||||
:link-class="isPostFeedCards ? 'post-feed__media post-feed__media--cards mb-3 block aspect-video w-full' : 'post-feed__media post-feed__media--list relative flex-1 aspect-square min-w-16 sm:aspect-video'"
|
||||
:aspect-class="isPostFeedCards ? 'aspect-video' : 'aspect-square sm:aspect-video'"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="post-feed__content relative min-w-0"
|
||||
:class="isPostFeedCards ? 'flex flex-col' : 'flex flex-[3] flex-col gap-1.5 md:flex-[4]'"
|
||||
<article
|
||||
data-post-card
|
||||
:data-featured="post.isFeatured ? '' : undefined"
|
||||
:class="getPostFeedArticleClass(postFeedStyle)"
|
||||
>
|
||||
<div class="post-feed__content-inner flex min-h-0 flex-1 flex-col gap-1">
|
||||
<h2 class="max-w-[90%] text-sm font-medium leading-tight">
|
||||
<NuxtLink :to="post.to" class="flex items-center transition-opacity duration-200 hover:opacity-75">
|
||||
<span v-if="post.isFeatured" class="post-feed__featured-icon mr-1 inline-flex h-4 w-4 items-center justify-center text-[var(--site-accent)]">
|
||||
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M13 3v7h6l-8 11v-7H5l8-11" />
|
||||
</svg>
|
||||
</span>
|
||||
{{ post.title }}
|
||||
</NuxtLink>
|
||||
</h2>
|
||||
<PostCardMedia
|
||||
v-if="showPostFeedMedia"
|
||||
:to="post.to"
|
||||
:title="post.title"
|
||||
:featured-image="post.featuredImage"
|
||||
:link-class="isPostFeedCards ? 'post-feed__media post-feed__media--cards mb-3 block aspect-video w-full' : 'post-feed__media post-feed__media--list relative flex-1 aspect-square min-w-16 sm:aspect-video'"
|
||||
:aspect-class="isPostFeedCards ? 'aspect-video' : 'aspect-square sm:aspect-video'"
|
||||
/>
|
||||
|
||||
<p
|
||||
v-if="post.excerpt"
|
||||
class="flex-1 text-[0.8rem] leading-tight site-muted text-[#6E6661]"
|
||||
:class="getPostFeedExcerptClass(postFeedStyle)"
|
||||
>
|
||||
{{ post.excerpt }}
|
||||
</p>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2 text-xs site-muted sm:gap-1.5">
|
||||
<time v-if="post.publishedAt" :datetime="post.publishedAtIso">{{ post.publishedAt }}</time>
|
||||
<template v-if="post.tagName">
|
||||
<span v-if="post.publishedAt" class="text-[var(--site-line)]">/</span>
|
||||
<span
|
||||
class="rounded-md px-1.5 py-px font-medium text-[var(--site-text)]"
|
||||
:style="{ backgroundColor: `${post.tagColor}1a` }"
|
||||
>
|
||||
{{ post.tagName }}
|
||||
</span>
|
||||
</template>
|
||||
<span v-if="post.publishedAt || post.tagName" class="text-[var(--site-line)]">/</span>
|
||||
<span class="flex items-center gap-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="-mt-px">
|
||||
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
|
||||
</svg>
|
||||
<span>{{ post.commentCount }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="absolute top-0 right-0 flex cursor-pointer items-center gap-1 hover:opacity-75"
|
||||
:class="isPostFeedCards ? '' : 'md:top-auto md:right-0 md:bottom-0 md:invisible md:opacity-0 md:transition-[opacity,visibility] md:duration-200 md:group-hover:visible md:group-hover:opacity-100'"
|
||||
type="button"
|
||||
aria-label="Share this post"
|
||||
<div
|
||||
class="post-feed__content relative min-w-0"
|
||||
:class="isPostFeedCards ? 'flex flex-col' : 'flex flex-[3] flex-col gap-1.5 md:flex-[4]'"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="h-4 w-4"
|
||||
<div class="post-feed__content-inner flex min-h-0 flex-1 flex-col gap-1">
|
||||
<h2 class="max-w-[90%] text-sm font-medium leading-tight">
|
||||
<NuxtLink :to="post.to" class="flex items-center transition-opacity duration-200 hover:opacity-75">
|
||||
<span v-if="post.isFeatured" class="post-feed__featured-icon mr-1 inline-flex h-4 w-4 items-center justify-center text-[var(--site-accent)]">
|
||||
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M13 3v7h6l-8 11v-7H5l8-11" />
|
||||
</svg>
|
||||
</span>
|
||||
{{ post.title }}
|
||||
</NuxtLink>
|
||||
</h2>
|
||||
|
||||
<p
|
||||
v-if="post.excerpt"
|
||||
class="flex-1 text-[0.8rem] leading-tight site-muted text-[#6E6661]"
|
||||
:class="getPostFeedExcerptClass(postFeedStyle)"
|
||||
>
|
||||
{{ post.excerpt }}
|
||||
</p>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2 text-xs site-muted sm:gap-1.5">
|
||||
<time v-if="post.publishedAt" :datetime="post.publishedAtIso">{{ post.publishedAt }}</time>
|
||||
<template v-if="post.tagName">
|
||||
<span v-if="post.publishedAt" class="text-[var(--site-line)]">/</span>
|
||||
<span
|
||||
class="rounded-md px-1.5 py-px font-medium text-[var(--site-text)]"
|
||||
:style="{ backgroundColor: `${post.tagColor}1a` }"
|
||||
>
|
||||
{{ post.tagName }}
|
||||
</span>
|
||||
</template>
|
||||
<span v-if="post.publishedAt || post.tagName" class="text-[var(--site-line)]">/</span>
|
||||
<span class="flex items-center gap-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="-mt-px">
|
||||
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
|
||||
</svg>
|
||||
<span>{{ post.commentCount }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="absolute top-0 right-0 flex cursor-pointer items-center gap-1 hover:opacity-75"
|
||||
:class="isPostFeedCards ? '' : 'md:top-auto md:right-0 md:bottom-0 md:invisible md:opacity-0 md:transition-[opacity,visibility] md:duration-200 md:group-hover:visible md:group-hover:opacity-100'"
|
||||
type="button"
|
||||
aria-label="Share this post"
|
||||
>
|
||||
<path d="M17 7 7 17" />
|
||||
<path d="M8 7h9v9" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="h-4 w-4"
|
||||
>
|
||||
<path d="M17 7 7 17" />
|
||||
<path d="M8 7h9v9" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<SiteAdSlot
|
||||
v-if="index === homeInfeedAdInsertIndex"
|
||||
class="home-page__infeed-ad-slot py-3"
|
||||
:class="isPostFeedCards ? 'sm:col-span-2' : ''"
|
||||
:code="siteSettings?.adHomeInfeedCode"
|
||||
location="home-infeed"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user