Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 24 additions & 24 deletions .firebase/hosting.ZGlzdA.cache
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
robots.txt,1764000733139,4b9cf624931e727c0919569b25aef74749ba3d7d8013b4203fd674819b8913b8
sw.js,1764000736414,c59bc26896d6bc867a6922193e91d78518ce49b103aee17e998b79f98a3c3c2a
sitemap.xml,1764000733140,166ac3ce344adcbc0558dc9d9081483ea5932043b519b7fb53879f9a6f28622d
registerSW.js,1764000733875,5b6445b5215737c53ef0d379c151d57de165a056de2d1c5812ed614f158ebcbd
index.html,1764000733875,41466f692c55f4bb05dd0a4e5034d968605bdca87034cc8c1a4071849df530b2
ads.txt,1764000733122,ab73b82480afe402b81a0009dc9aa2dd054c5d3308413585762ee10fab799dde
workbox-1ef09536.js,1764000736415,6e7ec8e1c2e45d9146923b40ccf2523be5303224abffb4a192100a9103df88c4
favicon.ico,1764000733123,06e1fde5c923aac3b7daaf0ac876ce0b3babfa8d8106372dae49ed6c5836181b
manifest.webmanifest,1764000733875,90853d14881989216e7f25601110614c72a3a294cfad3087ea0d0e5c26fa228c
assets/BlogPostPage-DG-5HPW2.js,1764000733874,b66c66c09a5852c510765dbe03e39e3a5b60b351867da03829024b85f020aff0
assets/BlogPostPage-DyAM8Unu.css,1764000733874,253eae97f24eebce82b6eaab8fe88a80ad60e95e5cb387a18954cd4e4bf4c47e
assets/BlogListPage-Bes5ru0e.js,1764000733875,a1f3520d616e91ef8035ffbe51dfc6667a6cd04fa0814dac6db9dcb74e484158
assets/AboutPage-CWLrBD6A.js,1764000733875,7a91fdb95501c267bb47aeea6894c6423f018326c345d201ea0ee1be60cc24b2
assets/vuetify-BYJP39XG.js,1764000733875,05544bd83e5042f5edcc1a126ce9c2cc39d2ed456698dfab50935bb66ca0ec11
assets/vue-B3w6wtCi.js,1764000733874,486589667cb51e2c2da5f4f8787e2bf973164699776bcbce6c9e8c49a3f8819b
assets/index-C84cUeHC.js,1764000733876,1cef31e6de1ac036af934f8026f8189c618fefef021cba11b1ebccd1ce4d227f
assets/materialdesignicons-webfont-Dp5v-WZN.woff2,1764000733875,1ef6230119dac710f3d257778a5e33aaf49582ef395ccd486c5ca166fc28e1b7
assets/materialdesignicons-webfont-PXm3-2wK.woff,1764000733877,9311ef0007a6a4e58a880aae6d871e36e29dcfbb6b63f085a197ecf6ef5fbea4
assets/index-DCD_mW0U.css,1764000733876,2eacdef7e428738710f7e7087491e2d5267151b5d2f7203fd825aadda49fe2f9
pwa-192x192.png,1764000733136,baf66b39fcb1493fd5e21137b5888a9da30b105c080bf6444b33157b4f15076c
pwa-512x512.png,1764000733139,7a208a68f0b3aa41ee3fa588f7d390ac2101146634f1036c395d20a7a7732a86
assets/materialdesignicons-webfont-CSr8KVlo.eot,1764000733877,a1fe0badf3c77111061e351f71f20d41a882ab9bd30529cff5f51a0247523e5e
assets/materialdesignicons-webfont-B7mPwVP_.ttf,1764000733877,47e0181d794502d5cbf656d60110ad94d032bd2b6053ffe84dbd73e347e7632e
images/avatar.jpg,1764000733133,62619e77f9cab77ef37903f2455ac9d345bd217082a09996c1fd712dd2520b4b
robots.txt,1764165538372,4b9cf624931e727c0919569b25aef74749ba3d7d8013b4203fd674819b8913b8
sw.js,1764165541954,60cddf6d120ec0c30f4f372fa325867bcfd24f047938a705d160b988998a93fc
sitemap.xml,1764165538373,166ac3ce344adcbc0558dc9d9081483ea5932043b519b7fb53879f9a6f28622d
workbox-1ef09536.js,1764165541955,6e7ec8e1c2e45d9146923b40ccf2523be5303224abffb4a192100a9103df88c4
manifest.webmanifest,1764165539139,90853d14881989216e7f25601110614c72a3a294cfad3087ea0d0e5c26fa228c
registerSW.js,1764165539139,5b6445b5215737c53ef0d379c151d57de165a056de2d1c5812ed614f158ebcbd
index.html,1764165539139,69b2f3707fe957377820aac46020920cad4eb190e35fb0d562c86e688e62435d
ads.txt,1764165538360,ab73b82480afe402b81a0009dc9aa2dd054c5d3308413585762ee10fab799dde
favicon.ico,1764165538360,06e1fde5c923aac3b7daaf0ac876ce0b3babfa8d8106372dae49ed6c5836181b
assets/BlogListPage-BJ20JVTd.js,1764165539139,9a89104b48d2438ec9406f5dd3a0738f33a213279ddb6c78aac39decda00dba7
assets/BlogPostPage-DyAM8Unu.css,1764165539135,253eae97f24eebce82b6eaab8fe88a80ad60e95e5cb387a18954cd4e4bf4c47e
assets/BlogPostPage-2oQaPgqZ.js,1764165539139,0a9d6cde68dde6a31af3cd48506487ac7aad7c39aa89c9c88bcc7022cbe3cf4e
assets/AboutPage-BuICaZy1.js,1764165539141,f4e2e4d8e06e88ab35a7d9c9e796c68ceebee60bdb96710d4292fc2c7a89f41c
assets/vuetify-BYJP39XG.js,1764165539135,05544bd83e5042f5edcc1a126ce9c2cc39d2ed456698dfab50935bb66ca0ec11
assets/vue-B3w6wtCi.js,1764165539135,486589667cb51e2c2da5f4f8787e2bf973164699776bcbce6c9e8c49a3f8819b
assets/index-CyoiZZd4.js,1764165539140,c52268c08198008b2a7bc738a25501d38c13e66c164c25c866ea999ed6c8ef6b
assets/materialdesignicons-webfont-Dp5v-WZN.woff2,1764165539140,1ef6230119dac710f3d257778a5e33aaf49582ef395ccd486c5ca166fc28e1b7
assets/materialdesignicons-webfont-PXm3-2wK.woff,1764165539142,9311ef0007a6a4e58a880aae6d871e36e29dcfbb6b63f085a197ecf6ef5fbea4
pwa-192x192.png,1764165538369,baf66b39fcb1493fd5e21137b5888a9da30b105c080bf6444b33157b4f15076c
assets/index-DCD_mW0U.css,1764165539140,2eacdef7e428738710f7e7087491e2d5267151b5d2f7203fd825aadda49fe2f9
pwa-512x512.png,1764165538372,7a208a68f0b3aa41ee3fa588f7d390ac2101146634f1036c395d20a7a7732a86
assets/materialdesignicons-webfont-B7mPwVP_.ttf,1764165539140,47e0181d794502d5cbf656d60110ad94d032bd2b6053ffe84dbd73e347e7632e
assets/materialdesignicons-webfont-CSr8KVlo.eot,1764165539141,a1fe0badf3c77111061e351f71f20d41a882ab9bd30529cff5f51a0247523e5e
images/avatar.jpg,1764165538366,62619e77f9cab77ef37903f2455ac9d345bd217082a09996c1fd712dd2520b4b
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@vueuse/head": "^2.0.0",
"axios": "^1.8.3",
"firebase": "^11.4.0",
"fuse.js": "^7.1.0",
"pinia": "^3.0.1",
"vite-imagetools": "^9.0.0",
"vue": "^3.5.13",
Expand Down
4 changes: 0 additions & 4 deletions src/assets/styles/components/pwa/_install-pwa.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,14 @@
bottom: 20px;
left: 50%;
transform: translateX(-50%);

padding: 14px 18px;
width: calc(100% - 32px);
max-width: 420px;

border-radius: 18px;
background: rgba(18, 20, 26, 0.95);
backdrop-filter: blur(12px);

border: 1px solid rgba(66, 211, 146, 0.25);
box-shadow: 0 0 22px rgba(66, 211, 146, 0.35);

display: flex;
align-items: center;
justify-content: space-between;
Expand Down
4 changes: 2 additions & 2 deletions src/assets/styles/pages/home/_home-header-title.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
text-align: center;
overflow: hidden;
border-radius: var(--radius-xl);

background: radial-gradient(circle at 50% 0, rgba(77, 163, 255, 0.13), transparent 70%);
}

Expand Down Expand Up @@ -36,6 +35,7 @@
line-height: 1.6;
animation: fadeInUp 0.9s ease forwards;
}

.mm-hero__grid {
pointer-events: none;
position: absolute;
Expand Down Expand Up @@ -69,4 +69,4 @@
.mm-hero__subtitle {
font-size: 1.05rem;
}
}
}
10 changes: 4 additions & 6 deletions src/components/article/ArticleCardComponent.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup>
import { computed } from 'vue'
import { useOptimizedImage } from '@/composables/useOptimizedImage.js'

const props = defineProps({
article: { type: Object, required: true },
Expand All @@ -15,29 +16,26 @@
const src = props.article.excerpt || getPlainText(props.article.contentHtml)
if (!src) return 'Опис відсутній'

return src.split(' ').slice(0, 26).join(' ') + '…'
return src.split(' ').slice(0, 14).join(' ') + '…'
})

const tag = computed(() => props.article.tags?.[0] || null)
const img = useOptimizedImage(props.article?.image?.url, props.article?.title, 300)
</script>

<template>
<router-link :to="'/blog/' + article.handle" class="mm-article2">
<!-- BORDER GLOW -->
<div class="mm-article2__glow"></div>

<!-- TAG -->
<div v-if="tag" class="mm-article2__tag">
<v-icon icon="mdi-label" size="14" />
{{ tag }}
</div>

<!-- IMAGE -->
<div class="mm-article2__image-wrapper">
<v-img class="mm-article2__image" :src="article?.image?.url" height="220" cover />
<img class="mm-article2__image" v-bind="img.attributes" />
</div>

<!-- CONTENT -->
<div class="mm-article2__content">
<h2 class="mm-article2__title">{{ article.title }}</h2>

Expand Down
3 changes: 1 addition & 2 deletions src/components/donation/DonationSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
`
const donateHref = 'https://savelife.in.ua/donate/'
const donateBtnText = 'Підтримати «Повернись живим»'
const donateBtnIcon = 'mdi-heart'
const smallFooter = 'Дякуємо за внесок! Разом до перемоги.'

const isPopupVisible = ref(false)
Expand Down Expand Up @@ -77,4 +76,4 @@
<v-icon icon="mdi-close" size="18" />
</button>
</div>
</template>
</template>
28 changes: 14 additions & 14 deletions src/components/home/ArticleFiltersComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,37 @@
<div class="mm-filters">
<v-text-field
:model-value="searchQuery"
@update:model-value="emit('update:searchQuery', $event)"
prepend-inner-icon="mdi-magnify"
class="mm-search"
density="comfortable"
hide-details
placeholder="Пошук статей..."
variant="outlined"
prepend-inner-icon="mdi-magnify"
rounded="lg"
hide-details
density="comfortable"
class="mm-search"
variant="outlined"
@update:model-value="emit('update:searchQuery', $event)"
/>

<v-select
:model-value="sortBy"
@update:model-value="emit('update:sortBy', $event)"
:items="sortOptions"
:model-value="sortBy"
class="mm-sort"
density="comfortable"
hide-details
item-title="label"
item-value="value"
variant="outlined"
rounded="lg"
hide-details
density="comfortable"
class="mm-sort"
variant="outlined"
@update:model-value="emit('update:sortBy', $event)"
>
<template #item="{ props, item }">
<v-list-item v-bind="props">
<v-icon :icon="item.raw.icon" size="18" class="mr-2" />
<v-icon :icon="item.raw.icon" class="mr-2" size="18" />
{{ item.raw.label }}
</v-list-item>
</template>

<template #selection="{ item }">
<v-icon :icon="item.raw.icon" size="18" class="mr-1" />
<v-icon :icon="item.raw.icon" class="mr-1" size="18" />
{{ item.raw.label }}
</template>
</v-select>
Expand Down
18 changes: 12 additions & 6 deletions src/components/related-posts/RelatedPostsComponent.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { onMounted, onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { getBlogArticles } from '@/services/shopifyApi'
import { useOptimizedImage } from '@/composables/useOptimizedImage.js'

const props = defineProps({
currentArticle: Object,
allArticles: Array,
article: { type: Object, required: true },
})

const router = useRouter()
Expand Down Expand Up @@ -40,12 +42,13 @@
onUnmounted(() => clearInterval(interval))

const goTo = slug => router.push(`/blog/${slug}`)
const img = useOptimizedImage(props.article?.image?.url, props.article?.title, 700)
</script>

<template>
<section class="mm-related">
<h2 class="mm-related__title">
<v-icon icon="mdi-lightning-bolt" size="28" class="mr-2" />
<v-icon class="mr-2" icon="mdi-lightning-bolt" size="28" />
Рекомендовані статті
</h2>

Expand All @@ -54,16 +57,19 @@
v-for="post in related"
:key="post.handle"
class="mm-card"
rounded="xl"
elevation="6"
rounded="xl"
@click="goTo(post.handle)"
>
<div class="mm-card__glow"></div>

<v-img :src="post.image?.url" height="170" cover class="mm-card__img" />
<img
class="mm-article2__image"
v-bind="useOptimizedImage(post.image?.url, post.title, 600).attributes"
/>

<v-chip v-if="post.tags?.length" size="small" class="mm-card__tag" variant="flat">
<v-icon icon="mdi-tag-outline" size="14" class="mr-1" />
<v-chip v-if="post.tags?.length" class="mm-card__tag" size="small" variant="flat">
<v-icon class="mr-1" icon="mdi-tag-outline" size="14" />
{{ post.tags[0] }}
</v-chip>

Expand Down
57 changes: 57 additions & 0 deletions src/composables/useOptimizedImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { computed } from 'vue'

export function useOptimizedImage(rawUrl, alt = 'Image', maxWidth = 900) {
if (!rawUrl) {
return {
attributes: {
src: '',
alt,
title: alt,
loading: 'lazy',
decoding: 'async',
},
}
}

const baseUrl = rawUrl.split('?')[0]
const originalParams = rawUrl.includes('?') ? '?' + rawUrl.split('?')[1] : ''

const tryWebp = !baseUrl.endsWith('.gif')

const make = (w, fmt) => {
if (!tryWebp && fmt === 'webp') return `${baseUrl}${originalParams}`
return `${baseUrl}?width=${w}&format=${fmt}`
}

const src = computed(() => (tryWebp ? make(maxWidth, 'webp') : `${baseUrl}${originalParams}`))

const srcset = computed(() => {
if (!tryWebp) {
return `${baseUrl}${originalParams} ${maxWidth}w`
}
return `
${make(360, 'webp')} 360w,
${make(560, 'webp')} 560w,
${make(700, 'webp')} 700w,
${make(maxWidth, 'webp')} ${maxWidth}w
`.trim()
})

const sizes = `(max-width: 600px) 360px,
(max-width: 900px) 560px,
(max-width: 1200px) 700px,
${maxWidth}px`

return {
attributes: {
src: src.value,
srcset: srcset.value,
sizes,
alt,
title: alt,
loading: 'lazy',
decoding: 'async',
style: 'width:100%;height:auto;display:block;border-radius:inherit;',
},
}
}
2 changes: 1 addition & 1 deletion vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ export default defineConfig({
},
},
},
})
})
Loading