mirror of
https://github.com/Pandipipas/scoreko-dev.git
synced 2026-06-06 03:32:06 +00:00
Merge pull request #72 from Pandipipas/add-about-section-and-update-system
feat(about): redesign About and add GitHub release update checker
This commit is contained in:
@@ -1,9 +1,124 @@
|
||||
<script setup lang="ts">
|
||||
import { useHead } from '@unhead/vue';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
defineOptions({ name: 'AboutView' });
|
||||
|
||||
useHead({ title: 'About' });
|
||||
|
||||
type ReleaseResponse = {
|
||||
html_url: string;
|
||||
name: string | null;
|
||||
tag_name: string;
|
||||
published_at: string;
|
||||
};
|
||||
|
||||
const appName = 'Scoreko Dev';
|
||||
const currentVersion = import.meta.env.PACKAGE_VERSION;
|
||||
const repoOwner = ref(localStorage.getItem('scoreko:repoOwner') ?? 'Pandipipas');
|
||||
const repoName = ref(localStorage.getItem('scoreko:repoName') ?? 'scoreko-dev');
|
||||
|
||||
const checkingUpdates = ref(false);
|
||||
const updateError = ref('');
|
||||
const latestRelease = ref<ReleaseResponse | null>(null);
|
||||
|
||||
const collaborators = [
|
||||
{
|
||||
name: 'zoton2',
|
||||
role: 'Template base (NodeCG + Vue + TS)',
|
||||
url: 'https://github.com/zoton2/nodecg-vue-ts-template'
|
||||
},
|
||||
{
|
||||
name: 'Dan Shields',
|
||||
role: 'nodecg-vue-composable helper',
|
||||
url: 'https://github.com/Dan-Shields/nodecg-vue-composable'
|
||||
},
|
||||
{
|
||||
name: 'NodeCG',
|
||||
role: 'Broadcast graphics framework',
|
||||
url: 'https://github.com/nodecg/nodecg'
|
||||
}
|
||||
];
|
||||
|
||||
const releaseLabel = computed(() => {
|
||||
if (!latestRelease.value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return latestRelease.value.name?.trim().length
|
||||
? latestRelease.value.name
|
||||
: latestRelease.value.tag_name;
|
||||
});
|
||||
|
||||
const hasUpdate = computed(() => {
|
||||
if (!latestRelease.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return compareVersions(normalizeVersion(latestRelease.value.tag_name), normalizeVersion(currentVersion)) > 0;
|
||||
});
|
||||
|
||||
const repoUrl = computed(() => `https://github.com/${repoOwner.value}/${repoName.value}`);
|
||||
const releaseUrl = computed(() => latestRelease.value?.html_url ?? `${repoUrl.value}/releases`);
|
||||
|
||||
function normalizeVersion(version: string) {
|
||||
return version.replace(/^v/i, '');
|
||||
}
|
||||
|
||||
function compareVersions(a: string, b: string) {
|
||||
const aParts = a.split('.').map((value) => Number(value));
|
||||
const bParts = b.split('.').map((value) => Number(value));
|
||||
const max = Math.max(aParts.length, bParts.length);
|
||||
|
||||
for (let index = 0; index < max; index += 1) {
|
||||
const aPart = Number.isFinite(aParts[index]) ? aParts[index]! : 0;
|
||||
const bPart = Number.isFinite(bParts[index]) ? bParts[index]! : 0;
|
||||
|
||||
if (aPart > bPart) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (aPart < bPart) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
async function checkForUpdates() {
|
||||
checkingUpdates.value = true;
|
||||
updateError.value = '';
|
||||
|
||||
try {
|
||||
localStorage.setItem('scoreko:repoOwner', repoOwner.value.trim());
|
||||
localStorage.setItem('scoreko:repoName', repoName.value.trim());
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.github.com/repos/${encodeURIComponent(repoOwner.value.trim())}/${encodeURIComponent(repoName.value.trim())}/releases/latest`,
|
||||
{
|
||||
headers: {
|
||||
Accept: 'application/vnd.github+json'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`GitHub respondió con estado ${response.status}.`);
|
||||
}
|
||||
|
||||
latestRelease.value = await response.json() as ReleaseResponse;
|
||||
} catch (error) {
|
||||
latestRelease.value = null;
|
||||
updateError.value = error instanceof Error ? error.message : 'Error desconocido revisando releases.';
|
||||
} finally {
|
||||
checkingUpdates.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void checkForUpdates();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -11,8 +126,183 @@ useHead({ title: 'About' });
|
||||
<div class="text-h4 q-mb-md">
|
||||
About
|
||||
</div>
|
||||
<div class="text-body1">
|
||||
Bundle information and relevant links.
|
||||
|
||||
<div class="row q-col-gutter-lg">
|
||||
<div class="col-12 col-md-6">
|
||||
<QCard
|
||||
flat
|
||||
bordered
|
||||
>
|
||||
<QCardSection class="row items-center q-col-gutter-md">
|
||||
<div class="col-auto">
|
||||
<QImg
|
||||
src="../image.png"
|
||||
alt="Scoreko logo"
|
||||
width="72px"
|
||||
height="72px"
|
||||
fit="contain"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="text-h6">
|
||||
{{ appName }}
|
||||
</div>
|
||||
<div class="text-caption text-grey-7">
|
||||
Versión {{ currentVersion }}
|
||||
</div>
|
||||
</div>
|
||||
</QCardSection>
|
||||
|
||||
<QSeparator />
|
||||
|
||||
<QCardSection>
|
||||
<p class="q-mb-sm">
|
||||
Dashboard para producción de overlays de fighting games usando NodeCG, Vue y Quasar.
|
||||
</p>
|
||||
<div class="column q-gutter-sm">
|
||||
<QBtn
|
||||
:href="repoUrl"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
icon="open_in_new"
|
||||
label="Repositorio en GitHub"
|
||||
color="primary"
|
||||
flat
|
||||
no-caps
|
||||
align="left"
|
||||
/>
|
||||
<QBtn
|
||||
href="https://github.com/nodecg/nodecg"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
icon="open_in_new"
|
||||
label="Framework NodeCG"
|
||||
color="primary"
|
||||
flat
|
||||
no-caps
|
||||
align="left"
|
||||
/>
|
||||
</div>
|
||||
</QCardSection>
|
||||
|
||||
<QSeparator />
|
||||
|
||||
<QCardSection>
|
||||
<div class="text-subtitle2 q-mb-sm">
|
||||
Colaboradores y agradecimientos
|
||||
</div>
|
||||
<QList dense>
|
||||
<QItem
|
||||
v-for="person in collaborators"
|
||||
:key="person.name"
|
||||
tag="a"
|
||||
:href="person.url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ person.name }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ person.role }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<QCard
|
||||
flat
|
||||
bordered
|
||||
>
|
||||
<QCardSection>
|
||||
<div class="text-h6">
|
||||
Sistema de updates (GitHub Releases)
|
||||
</div>
|
||||
<div class="text-body2 text-grey-7 q-mt-xs">
|
||||
Este chequeo consulta el último release del repo y lo compara con la versión actual.
|
||||
</div>
|
||||
</QCardSection>
|
||||
|
||||
<QSeparator />
|
||||
|
||||
<QCardSection class="q-gutter-md">
|
||||
<QInput
|
||||
v-model="repoOwner"
|
||||
label="GitHub owner"
|
||||
outlined
|
||||
dense
|
||||
/>
|
||||
<QInput
|
||||
v-model="repoName"
|
||||
label="GitHub repo"
|
||||
outlined
|
||||
dense
|
||||
/>
|
||||
|
||||
<QBtn
|
||||
label="Buscar updates"
|
||||
color="primary"
|
||||
icon="sync"
|
||||
:loading="checkingUpdates"
|
||||
no-caps
|
||||
@click="checkForUpdates"
|
||||
/>
|
||||
|
||||
<QBanner
|
||||
v-if="latestRelease"
|
||||
rounded
|
||||
class="bg-grey-2"
|
||||
>
|
||||
<template #avatar>
|
||||
<QIcon
|
||||
:name="hasUpdate ? 'system_update_alt' : 'check_circle'"
|
||||
:color="hasUpdate ? 'warning' : 'positive'"
|
||||
/>
|
||||
</template>
|
||||
<div class="text-subtitle2">
|
||||
Último release: {{ releaseLabel }}
|
||||
</div>
|
||||
<div class="text-caption text-grey-7">
|
||||
Publicado: {{ new Date(latestRelease.published_at).toLocaleString() }}
|
||||
</div>
|
||||
<div class="q-mt-sm">
|
||||
{{ hasUpdate ? 'Hay una versión más nueva disponible.' : 'Tu versión está actualizada frente al último release.' }}
|
||||
</div>
|
||||
<template #action>
|
||||
<QBtn
|
||||
flat
|
||||
color="primary"
|
||||
label="Ver release"
|
||||
:href="releaseUrl"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
no-caps
|
||||
/>
|
||||
</template>
|
||||
</QBanner>
|
||||
|
||||
<QBanner
|
||||
v-if="updateError"
|
||||
rounded
|
||||
class="bg-red-1 text-red-10"
|
||||
>
|
||||
{{ updateError }}
|
||||
</QBanner>
|
||||
|
||||
<QBanner
|
||||
rounded
|
||||
class="bg-blue-1 text-blue-10"
|
||||
>
|
||||
Nota para Electron: este panel implementa solo <strong>detección y aviso</strong>. Para updates
|
||||
automáticos reales en desktop, hay que integrar `autoUpdater` en el proceso principal de Electron
|
||||
y publicar artefactos firmados por plataforma.
|
||||
</QBanner>
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
</div>
|
||||
</div>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
Vendored
+8
@@ -1 +1,9 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly PACKAGE_VERSION: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import { quasar, transformAssetUrls } from '@quasar/vite-plugin';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { defineConfig } from 'vite';
|
||||
import checker from 'vite-plugin-checker';
|
||||
import NodeCGPlugin from 'vite-plugin-nodecg';
|
||||
|
||||
const packageJson = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf-8')) as { version: string };
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
define: {
|
||||
'import.meta.env.PACKAGE_VERSION': JSON.stringify(packageJson.version),
|
||||
},
|
||||
plugins: [
|
||||
vue({ template: { transformAssetUrls } }),
|
||||
quasar({
|
||||
|
||||
Reference in New Issue
Block a user