Skip to content

Commit 69b329a

Browse files
committed
feat: added download page, centralized data and misc fixes
1 parent 24bfcc1 commit 69b329a

19 files changed

+544
-222
lines changed

app/app.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSe
3434
const { data: app } = await useAsyncData('links', () => queryCollection('app').first())
3535
3636
provide('navigation', navigation)
37+
38+
const dataStore = useDataStore()
39+
await dataStore.fetchProjects()
40+
await dataStore.fetchContributors()
3741
</script>
3842

3943
<template>

app/components/app/Footer.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const { data: app } = await useAsyncData('app-footer', () => queryCollection('ap
1717
<template>
1818
<UFooter v-if="app?.footer">
1919
<template #left>
20-
<p class="text-muted text-sm">
20+
<p class="text-muted text-sm text-center max-w-[256px] sm:max-w-none">
2121
{{ app?.footer.copyright }}
2222
</p>
2323
</template>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script setup lang="ts">
2+
import DownloadsGridCard, { type DownloadsGridCardProps } from './DownloadsGridCard.vue'
3+
4+
export interface DownloadsGridProps {
5+
items?: DownloadsGridCardProps[]
6+
}
7+
8+
const props = defineProps<DownloadsGridProps>()
9+
</script>
10+
11+
<template>
12+
<UPageGrid
13+
class="relative grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 gap-8 flex-1"
14+
>
15+
<DownloadsGridCard
16+
v-for="(item, index) in props.items"
17+
:key="index"
18+
v-bind="item"
19+
/>
20+
</UPageGrid>
21+
</template>
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<script setup lang="ts">
2+
export interface DownloadsGridCardProps {
3+
icon?: string
4+
title?: string
5+
description?: string
6+
items?: {
7+
icon?: string
8+
label?: string
9+
repoName?: string
10+
releaseUrl?: string
11+
version?: string
12+
releaseDate?: string
13+
downloadUrl?: string
14+
}[]
15+
}
16+
17+
const props = defineProps<DownloadsGridCardProps>()
18+
19+
const dataStore = useDataStore()
20+
21+
const remapItems = computed(() => {
22+
return props.items?.map((item) => {
23+
if (!item.repoName) return item
24+
25+
const project = dataStore.getProjectReleaseByName(item.repoName)
26+
const release = project?.release
27+
const assets = release?.assets ?? []
28+
29+
const title = props.title?.toLowerCase() || ''
30+
31+
let downloadAsset = undefined
32+
33+
if (title.includes('windows')) {
34+
downloadAsset = assets.find(asset =>
35+
asset.url?.toLowerCase().includes('.exe')
36+
|| asset.url?.toLowerCase().includes('.zip')
37+
)
38+
} else if (title.includes('macos')) {
39+
downloadAsset = assets.find(asset =>
40+
asset.url?.toLowerCase().includes('.dmg')
41+
|| asset.url?.toLowerCase().includes('.pkg')
42+
)
43+
44+
if (!downloadAsset) {
45+
downloadAsset = assets.find(asset =>
46+
asset.url?.toLowerCase().includes('.tar.gz')
47+
|| asset.url?.toLowerCase().includes('.zip')
48+
)
49+
}
50+
} else if (title.includes('linux')) {
51+
downloadAsset = assets.find(asset =>
52+
asset.url?.toLowerCase().includes('.tar.gz')
53+
|| asset.url?.toLowerCase().includes('.deb')
54+
)
55+
}
56+
57+
return {
58+
...item,
59+
label: item.label,
60+
releaseUrl: release?.url || item.releaseUrl,
61+
downloadUrl: downloadAsset?.url,
62+
version: release?.tag || item.version,
63+
releaseDate: release?.publishedAt || item.releaseDate
64+
}
65+
}) || []
66+
})
67+
</script>
68+
69+
<template>
70+
<UPageCard
71+
variant="ghost"
72+
target="_blank"
73+
v-bind="props"
74+
>
75+
<UPageList>
76+
<div
77+
v-for="(item, index) in remapItems"
78+
:key="index"
79+
class="flex gap-4 mb-4"
80+
>
81+
<UButton
82+
:icon="item.icon"
83+
:label="item.label"
84+
:to="item.downloadUrl"
85+
:disabled="!item.downloadUrl"
86+
variant="outline"
87+
size="xl"
88+
:ui="{
89+
base: 'w-full'
90+
}"
91+
>
92+
<template #trailing>
93+
<span class="ml-auto text-xs text-muted">
94+
{{ item.version }} ({{ item.releaseDate ? new Date(item.releaseDate).toLocaleDateString() : 'N/A' }})
95+
</span>
96+
</template>
97+
</UButton>
98+
99+
<UButton
100+
:disabled="!item.releaseUrl"
101+
:to="item.releaseUrl"
102+
icon="i-simple-icons-github"
103+
variant="ghost"
104+
size="xl"
105+
/>
106+
</div>
107+
</UPageList>
108+
</UPageCard>
109+
</template>

app/components/projects/GitHubGridCard.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<script setup lang="ts">
22
import type { HTMLAttributes } from 'vue'
3-
import type { ProjectExportItem } from '#shared/utils/useProjects'
3+
import type { Project } from '#shared/types/projects'
44
5-
export interface GitHubGridCardProps extends ProjectExportItem {
5+
export interface GitHubGridCardProps extends Project {
66
spotlight?: boolean
77
spotlightClass?: HTMLAttributes['class']
88
}
@@ -24,8 +24,8 @@ const props = defineProps<GitHubGridCardProps>()
2424
<template #body>
2525
<UUser
2626
:name="useStyleName(props.name)"
27-
:description="props.description"
28-
:avatar="{ src: props.owner.src, alt: props.owner.alt }"
27+
:description="props.description || undefined"
28+
:avatar="props.owner"
2929
class="relative"
3030
orientation="horizontal"
3131
:ui="{

app/pages/download.vue

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,21 @@ useSeoMeta({
1919
v-if="page.download"
2020
v-bind="page.download"
2121
:ui="{
22-
container: 'pb-8 sm:pb-12 lg:pb-20'
22+
container: 'pb-8 sm:pb-12 lg:pb-20 flex-col',
23+
features: 'flex'
2324
}"
2425
>
25-
<!-- -->
26+
<template #features>
27+
<UPageCard
28+
variant="subtle"
29+
class="rounded-2xl shadow-2xl flex-1"
30+
>
31+
<DownloadDownloadsGrid
32+
v-if="page.download.items"
33+
:items="page.download.items"
34+
/>
35+
</UPageCard>
36+
</template>
2637
</UPageSection>
2738
</div>
2839
</template>

app/pages/index.vue

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { VideoPlayer } from '@videojs-player/vue'
33
import 'video.js/dist/video-js.css'
44
55
const { data: page } = await useAsyncData('page-index', () => queryCollection('index').first())
6+
const dataStore = useDataStore()
67
78
const title = page.value?.seo?.title || page.value?.title
89
const description = page.value?.seo?.description || page.value?.description
@@ -19,8 +20,8 @@ const { isMobile } = useDevice()
1920
2021
const videoReady = ref(false)
2122
22-
const projects = ref<ProjectExportItem[]>([])
23-
const contributors = ref<ContributorItem[]>([])
23+
const projects = ref<Project[]>([])
24+
const contributors = ref<Contributor[]>([])
2425
2526
const loadingProjects = ref(true)
2627
const loadingContributors = ref(true)
@@ -49,16 +50,12 @@ onMounted(async () => {
4950
isClient.value = true
5051
5152
try {
52-
const projectData = await useProjects({
53+
projects.value = dataStore.getProjects({
5354
itemsToShow: page.value?.projects?.itemsToShow || 6,
5455
featured: page.value?.projects?.featured || [],
5556
sortBy: page.value?.projects?.sortBy || 'stars'
5657
})
5758
58-
projects.value = projectData
59-
60-
console.log('Projects loaded:', projects.value)
61-
6259
await new Promise(resolve => setTimeout(resolve, 300))
6360
} catch (error) {
6461
console.error('Error loading projects:', error)
@@ -71,9 +68,7 @@ onMounted(async () => {
7168
}
7269
7370
try {
74-
const contributorData = await useContributors()
75-
76-
contributors.value = contributorData
71+
contributors.value = dataStore.getContributors()
7772
7873
await new Promise(resolve => setTimeout(resolve, 300))
7974
} catch (error) {
@@ -156,16 +151,15 @@ onMounted(async () => {
156151
<NuxtLink
157152
v-for="item in batchOne"
158153
:key="item.id"
159-
:to="item.html_url"
154+
:to="item.to"
160155
target="_blank"
161156
class="flex items-center"
162157
>
163158
<UAvatar
164-
:src="item.avatar_url"
165-
:alt="item.login"
159+
v-bind="item.avatar"
166160
class="mr-2"
167161
/>
168-
<span>{{ item.login }}</span>
162+
<span>{{ item.avatar?.alt }}</span>
169163
</NuxtLink>
170164
</template>
171165

@@ -193,16 +187,15 @@ onMounted(async () => {
193187
<NuxtLink
194188
v-for="item in batchTwo"
195189
:key="item.id"
196-
:to="item.html_url"
190+
:to="item.to"
197191
target="_blank"
198192
class="flex items-center"
199193
>
200194
<UAvatar
201-
:src="item.avatar_url"
202-
:alt="item.login"
195+
v-bind="item.avatar"
203196
class="mr-2"
204197
/>
205-
<span>{{ item.login }}</span>
198+
<span>{{ item.avatar?.alt }}</span>
206199
</NuxtLink>
207200
</template>
208201

app/pages/projects.vue

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script setup lang="ts">
22
const { data: page } = await useAsyncData('page-projects', () => queryCollection('projects').first())
3+
const dataStore = useDataStore()
34
45
const title = page.value?.seo?.title || page.value?.title
56
const description = page.value?.seo?.description || page.value?.description
@@ -12,19 +13,17 @@ useSeoMeta({
1213
ogDescription: description
1314
})
1415
15-
const projects = ref<ProjectExportItem[]>([])
16+
const projects = ref<Project[]>([])
1617
const loadingProjects = ref(true)
1718
1819
onMounted(async () => {
1920
try {
20-
const projectData = await useProjects({
21+
projects.value = dataStore.getProjects({
2122
itemsToShow: page.value?.projects?.itemsToShow || 0,
2223
featured: page.value?.projects?.featured || [],
2324
sortBy: page.value?.projects?.sortBy || 'stars'
2425
})
2526
26-
projects.value = projectData
27-
2827
await new Promise(resolve => setTimeout(resolve, 300))
2928
} catch (error) {
3029
console.error('Error loading projects:', error)

0 commit comments

Comments
 (0)