rss feed and download stats
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<title>Teletype Games</title>
|
||||
<link rel="alternate" type="application/rss+xml" title="Teletype Games Blog" href="/api/rss/blog" />
|
||||
<link rel="alternate" type="application/rss+xml" title="Teletype Games Releases" href="/api/rss/releases" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -9,6 +9,7 @@ export default {
|
||||
contact: 'Contact us',
|
||||
retro: 'Retro Mode',
|
||||
exitRetro: 'Exit Retro Mode',
|
||||
rss: 'RSS',
|
||||
},
|
||||
common: {
|
||||
status: {
|
||||
@@ -120,6 +121,7 @@ export default {
|
||||
platform: 'Platform:',
|
||||
play: '▶ Play',
|
||||
moreInfo: 'ℹ More Info',
|
||||
downloads: 'downloads',
|
||||
},
|
||||
catalogShow: {
|
||||
back: '← Back to Catalog',
|
||||
@@ -142,6 +144,7 @@ export default {
|
||||
failedToLoad: 'Failed to load:',
|
||||
loading: 'Loading...',
|
||||
play: '▶ Play',
|
||||
downloads: 'downloads',
|
||||
},
|
||||
team: {
|
||||
title: 'Our Team',
|
||||
|
||||
@@ -9,6 +9,7 @@ export default {
|
||||
contact: 'Kapcsolat',
|
||||
retro: 'Retró mód',
|
||||
exitRetro: 'Retró mód kikapcsolása',
|
||||
rss: 'RSS',
|
||||
},
|
||||
common: {
|
||||
status: {
|
||||
@@ -120,6 +121,7 @@ export default {
|
||||
platform: 'Platform:',
|
||||
play: '▶ Játék',
|
||||
moreInfo: 'ℹ Részletek',
|
||||
downloads: 'letöltés',
|
||||
},
|
||||
catalogShow: {
|
||||
back: '← Vissza a katalógushoz',
|
||||
@@ -142,6 +144,7 @@ export default {
|
||||
failedToLoad: 'Nem sikerült betölteni:',
|
||||
loading: 'Betöltés...',
|
||||
play: '▶ Játék',
|
||||
downloads: 'letöltés',
|
||||
},
|
||||
team: {
|
||||
title: 'Csapatunk',
|
||||
|
||||
@@ -49,9 +49,20 @@
|
||||
</main>
|
||||
|
||||
<div class="retro-toggle-bar">
|
||||
<button class="retro-toggle-btn" @click="toggleRetroMode">
|
||||
🖥 {{ isRetroMode ? t('nav.exitRetro') : t('nav.retro') }}
|
||||
</button>
|
||||
<div class="flex items-center justify-center gap-4">
|
||||
<button class="retro-toggle-btn" @click="toggleRetroMode">
|
||||
🖥 {{ isRetroMode ? t('nav.exitRetro') : t('nav.retro') }}
|
||||
</button>
|
||||
<div class="flex items-center gap-2 text-xs text-gray-400">
|
||||
<a href="/api/rss/blog" target="_blank" class="hover:text-orange-400 transition-colors" title="Blog RSS">
|
||||
📡 Blog
|
||||
</a>
|
||||
<span class="text-gray-600">|</span>
|
||||
<a href="/api/rss/releases" target="_blank" class="hover:text-orange-400 transition-colors" title="Releases RSS">
|
||||
📡 Releases
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -4,6 +4,7 @@ export interface Release {
|
||||
cartridgePath?: string
|
||||
sourcePath?: string
|
||||
docsFolderPath?: string
|
||||
downloadCount?: number
|
||||
UpdatedAt: string
|
||||
}
|
||||
|
||||
@@ -30,4 +31,5 @@ export interface Software {
|
||||
export interface SoftwareEntry {
|
||||
software: Software
|
||||
releases: Release[]
|
||||
totalDownloads?: number
|
||||
}
|
||||
|
||||
@@ -51,6 +51,9 @@
|
||||
<RouterLink :to="`/catalog/${software.name}`" class="btn-info-sm">
|
||||
{{ t('catalog.moreInfo') }}
|
||||
</RouterLink>
|
||||
<span v-if="getTotalDownloads(software.name)" class="text-xs text-gray-400 font-medium ml-1">
|
||||
↓ {{ getTotalDownloads(software.name) }} {{ t('catalog.downloads') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -66,7 +69,7 @@ import { storeToRefs } from 'pinia'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useSoftwareStore } from '../../stores/software.store'
|
||||
import type { Software } from '../../lib/interfaces/software.interface'
|
||||
import type { Software, SoftwareEntry } from '../../lib/interfaces/software.interface'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -77,6 +80,11 @@ const store = useSoftwareStore()
|
||||
const { items: softwares } = storeToRefs(store)
|
||||
const { getLatestStable } = store
|
||||
|
||||
function getTotalDownloads(name: string): number | null {
|
||||
const entry = softwares.value.find((e: SoftwareEntry) => e.software.name === name)
|
||||
return entry?.totalDownloads || null
|
||||
}
|
||||
|
||||
function getDefaultImageUrl(sw: Software): string | null {
|
||||
if (sw.images?.length) {
|
||||
const def = sw.images.find(i => i.isDefault)
|
||||
|
||||
@@ -95,8 +95,8 @@
|
||||
|
||||
<div class="grid grid-cols-2 sm:grid-cols-4 md:grid-cols-2 xl:grid-cols-4 gap-2 w-full md:w-auto">
|
||||
<a v-if="latestStable.htmlFolderPath" :href="latestStable.htmlFolderPath" target="_blank" class="flex items-center justify-center py-3 px-4 bg-white text-indigo-900 font-bold rounded-xl transition-all hover:bg-indigo-50 shadow-lg shadow-black/10 text-sm whitespace-nowrap">{{ t('catalogShow.playNow') }}</a>
|
||||
<a v-if="latestStable.cartridgePath" :href="latestStable.cartridgePath" target="_blank" class="flex items-center justify-center py-3 px-4 bg-indigo-500/30 border border-indigo-400/30 text-white font-bold rounded-xl transition-all hover:bg-indigo-500/40 text-sm whitespace-nowrap">{{ t('catalogShow.download') }}</a>
|
||||
<a v-if="latestStable.sourcePath" :href="latestStable.sourcePath" target="_blank" class="flex items-center justify-center py-3 px-4 bg-indigo-500/30 border border-indigo-400/30 text-white font-bold rounded-xl transition-all hover:bg-indigo-500/40 text-sm whitespace-nowrap">{{ t('catalogShow.source') }}</a>
|
||||
<a v-if="latestStable.cartridgePath" :href="downloadUrl(latestStable.cartridgePath)" class="flex items-center justify-center py-3 px-4 bg-indigo-500/30 border border-indigo-400/30 text-white font-bold rounded-xl transition-all hover:bg-indigo-500/40 text-sm whitespace-nowrap">{{ t('catalogShow.download') }}</a>
|
||||
<a v-if="latestStable.sourcePath" :href="downloadUrl(latestStable.sourcePath)" class="flex items-center justify-center py-3 px-4 bg-indigo-500/30 border border-indigo-400/30 text-white font-bold rounded-xl transition-all hover:bg-indigo-500/40 text-sm whitespace-nowrap">{{ t('catalogShow.source') }}</a>
|
||||
<a v-if="latestStable.docsFolderPath" :href="latestStable.docsFolderPath" target="_blank" class="flex items-center justify-center py-3 px-4 bg-indigo-500/30 border border-indigo-400/30 text-white font-bold rounded-xl transition-all hover:bg-indigo-500/40 text-sm whitespace-nowrap">{{ t('catalogShow.docs') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -119,11 +119,14 @@
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
<tr v-for="release in stableReleases" :key="release.version" class="hover:bg-gray-50/50 transition-colors">
|
||||
<td class="px-8 py-4">
|
||||
<div class="font-bold text-gray-900 text-lg mb-2">{{ release.version }}</div>
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span class="font-bold text-gray-900 text-lg">{{ release.version }}</span>
|
||||
<span v-if="release.downloadCount" class="text-xs text-gray-400 font-medium">↓ {{ release.downloadCount }}</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<a v-if="release.htmlFolderPath" :href="release.htmlFolderPath" target="_blank" class="text-purple-600 hover:text-purple-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.play') }}</a>
|
||||
<a v-if="release.cartridgePath" :href="release.cartridgePath" target="_blank" class="text-blue-600 hover:text-blue-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.download') }}</a>
|
||||
<a v-if="release.sourcePath" :href="release.sourcePath" target="_blank" class="text-green-600 hover:text-green-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.source') }}</a>
|
||||
<a v-if="release.cartridgePath" :href="downloadUrl(release.cartridgePath)" class="text-blue-600 hover:text-blue-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.download') }}</a>
|
||||
<a v-if="release.sourcePath" :href="downloadUrl(release.sourcePath)" class="text-green-600 hover:text-green-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.source') }}</a>
|
||||
<a v-if="release.docsFolderPath" :href="release.docsFolderPath" target="_blank" class="text-yellow-600 hover:text-yellow-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.docs') }}</a>
|
||||
</div>
|
||||
</td>
|
||||
@@ -139,7 +142,7 @@
|
||||
<div class="font-bold text-yellow-800 text-lg mb-2">{{ release.version }}</div>
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<a v-if="release.htmlFolderPath" :href="release.htmlFolderPath" target="_blank" class="text-purple-600 hover:text-purple-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.play') }}</a>
|
||||
<a v-if="release.cartridgePath" :href="release.cartridgePath" target="_blank" class="text-blue-600 hover:text-blue-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.download') }}</a>
|
||||
<a v-if="release.cartridgePath" :href="downloadUrl(release.cartridgePath)" class="text-blue-600 hover:text-blue-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.download') }}</a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-8 py-4 text-gray-500 align-top pt-5">{{ formatDateTime(release.UpdatedAt) }}</td>
|
||||
@@ -164,8 +167,8 @@
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<a v-if="release.htmlFolderPath" :href="release.htmlFolderPath" target="_blank" class="flex items-center justify-center py-2 bg-purple-100 text-purple-700 font-bold rounded-lg text-sm">{{ t('catalogShow.play') }}</a>
|
||||
<a v-if="release.cartridgePath" :href="release.cartridgePath" target="_blank" class="flex items-center justify-center py-2 bg-blue-100 text-blue-700 font-bold rounded-lg text-sm">{{ t('catalogShow.download') }}</a>
|
||||
<a v-if="release.sourcePath" :href="release.sourcePath" target="_blank" class="flex items-center justify-center py-2 bg-green-100 text-green-700 font-bold rounded-lg text-sm">{{ t('catalogShow.source') }}</a>
|
||||
<a v-if="release.cartridgePath" :href="downloadUrl(release.cartridgePath)" class="flex items-center justify-center py-2 bg-blue-100 text-blue-700 font-bold rounded-lg text-sm">{{ t('catalogShow.download') }}</a>
|
||||
<a v-if="release.sourcePath" :href="downloadUrl(release.sourcePath)" class="flex items-center justify-center py-2 bg-green-100 text-green-700 font-bold rounded-lg text-sm">{{ t('catalogShow.source') }}</a>
|
||||
<a v-if="release.docsFolderPath" :href="release.docsFolderPath" target="_blank" class="flex items-center justify-center py-2 bg-yellow-100 text-yellow-700 font-bold rounded-lg text-sm">{{ t('catalogShow.docs') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -250,6 +253,9 @@ const devReleases = computed(() =>
|
||||
const allReleases = computed(() => [...stableReleases.value, ...devReleases.value])
|
||||
const latestStable = computed(() => stableReleases.value[0] ?? null)
|
||||
|
||||
const downloadUrl = (path: string) =>
|
||||
`/download?path=${encodeURIComponent(path.replace('/file/', ''))}`
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await store.fetchAll()
|
||||
|
||||
Reference in New Issue
Block a user