mirror of
https://github.com/Pandipipas/scoreko-dev.git
synced 2026-06-06 03:32:06 +00:00
Refactor About.vue to simplify collaborator display and remove update checking logic
This commit is contained in:
@@ -1,163 +1,80 @@
|
||||
<script setup lang="ts">
|
||||
import { useHead } from '@unhead/vue';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { t } from '../i18n';
|
||||
|
||||
defineOptions({ name: 'AboutView' });
|
||||
|
||||
useHead(() => ({ title: t('aboutTitle') }));
|
||||
|
||||
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 updateRepoOwner = 'Pandipipas';
|
||||
const updateRepoName = 'scoreko';
|
||||
|
||||
const checkingUpdates = ref(false);
|
||||
const updateError = ref('');
|
||||
const latestRelease = ref<ReleaseResponse | null>(null);
|
||||
|
||||
const collaborators = [
|
||||
{
|
||||
name: 'Pandipipas',
|
||||
role: 'Development and maintenance of Scoreko-dev',
|
||||
url: 'https://github.com/Pandipipas/scoreko-dev'
|
||||
url: 'http://10.0.0.10:3002/Pandipipas/scoreko-dev',
|
||||
icon: 'code',
|
||||
},
|
||||
{
|
||||
name: 'Dan Shields',
|
||||
role: 'nodecg-vue-composable helper',
|
||||
url: 'https://github.com/Dan-Shields/nodecg-vue-composable'
|
||||
url: 'https://github.com/Dan-Shields/nodecg-vue-composable',
|
||||
icon: 'extension',
|
||||
},
|
||||
{
|
||||
name: 'NodeCG',
|
||||
role: 'Broadcast graphics framework',
|
||||
url: 'https://github.com/nodecg/nodecg'
|
||||
}
|
||||
url: 'https://github.com/nodecg/nodecg',
|
||||
icon: 'layers',
|
||||
},
|
||||
];
|
||||
|
||||
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/${updateRepoOwner}/${updateRepoName}`);
|
||||
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 {
|
||||
const response = await fetch(
|
||||
`https://api.github.com/repos/${encodeURIComponent(updateRepoOwner)}/${encodeURIComponent(updateRepoName)}/releases/latest`,
|
||||
{
|
||||
headers: {
|
||||
Accept: 'application/vnd.github+json'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`${t('aboutGitHubStatusError')} ${response.status}.`);
|
||||
}
|
||||
|
||||
latestRelease.value = await response.json() as ReleaseResponse;
|
||||
} catch (error) {
|
||||
latestRelease.value = null;
|
||||
updateError.value = error instanceof Error ? error.message : t('aboutUnknownReleaseError');
|
||||
} finally {
|
||||
checkingUpdates.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void checkForUpdates();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage class="q-pa-lg">
|
||||
<div class="text-h4 q-mb-md">
|
||||
<div class="q-mb-lg">
|
||||
<div class="text-h5 text-weight-medium">
|
||||
{{ t('aboutTitle') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row q-col-gutter-lg">
|
||||
<div class="col-12 col-md-6">
|
||||
<QCard
|
||||
flat
|
||||
bordered
|
||||
class="about-card"
|
||||
>
|
||||
<QCardSection class="row items-center q-col-gutter-md">
|
||||
<div class="col-auto">
|
||||
<!-- App identity -->
|
||||
<QCardSection class="q-pa-lg">
|
||||
<div class="row items-center q-gutter-md">
|
||||
<QImg
|
||||
src="../image.png"
|
||||
alt="Scoreko logo"
|
||||
width="72px"
|
||||
height="72px"
|
||||
class="app-logo"
|
||||
fit="contain"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="text-h6">
|
||||
<div>
|
||||
<div class="text-h6 text-weight-bold">
|
||||
{{ appName }}
|
||||
</div>
|
||||
<div class="text-caption text-grey-7">
|
||||
{{ t('aboutVersion') }} {{ currentVersion }}
|
||||
<QBadge
|
||||
outline
|
||||
color="primary"
|
||||
class="q-mt-xs version-badge"
|
||||
>
|
||||
v{{ currentVersion }}
|
||||
</QBadge>
|
||||
</div>
|
||||
</div>
|
||||
</QCardSection>
|
||||
|
||||
<QSeparator />
|
||||
|
||||
<QCardSection>
|
||||
<p class="q-mb-sm">
|
||||
<!-- Description + framework -->
|
||||
<QCardSection class="q-pa-lg">
|
||||
<p class="text-body2 text-grey-7 q-mb-md">
|
||||
{{ t('aboutDescription') }}
|
||||
</p>
|
||||
<div class="column q-gutter-sm">
|
||||
<QBtn
|
||||
href="https://github.com/nodecg/nodecg"
|
||||
target="_blank"
|
||||
@@ -166,19 +83,22 @@ onMounted(() => {
|
||||
:label="t('aboutFrameworkNodeCG')"
|
||||
color="primary"
|
||||
flat
|
||||
dense
|
||||
no-caps
|
||||
align="left"
|
||||
/>
|
||||
</div>
|
||||
</QCardSection>
|
||||
|
||||
<QSeparator />
|
||||
|
||||
<QCardSection>
|
||||
<div class="text-subtitle2 q-mb-sm">
|
||||
<!-- Collaborators -->
|
||||
<QCardSection class="q-pa-lg">
|
||||
<div class="text-overline text-grey-6 q-mb-sm">
|
||||
{{ t('aboutCollaboratorsTitle') }}
|
||||
</div>
|
||||
<QList dense>
|
||||
<QList
|
||||
dense
|
||||
class="collaborators-list"
|
||||
>
|
||||
<QItem
|
||||
v-for="person in collaborators"
|
||||
:key="person.name"
|
||||
@@ -186,95 +106,73 @@ onMounted(() => {
|
||||
:href="person.url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="collaborator-item rounded-borders"
|
||||
>
|
||||
<QItemSection avatar>
|
||||
<QIcon
|
||||
:name="person.icon"
|
||||
size="18px"
|
||||
color="primary"
|
||||
class="collaborator-icon"
|
||||
/>
|
||||
</QItemSection>
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ person.name }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
<QItemLabel class="text-weight-medium">
|
||||
{{ person.name }}
|
||||
</QItemLabel>
|
||||
<QItemLabel
|
||||
caption
|
||||
class="text-grey-6"
|
||||
>
|
||||
{{ person.role }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
<QItemSection side>
|
||||
<QIcon
|
||||
name="arrow_forward_ios"
|
||||
size="12px"
|
||||
color="grey-5"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<QCard
|
||||
flat
|
||||
bordered
|
||||
>
|
||||
<QCardSection>
|
||||
<div class="text-h6">
|
||||
{{ t('aboutUpdateSystemTitle') }}
|
||||
</div>
|
||||
<div class="text-body2 text-grey-7 q-mt-xs">
|
||||
{{ t('aboutUpdateSystemDescription') }}
|
||||
</div>
|
||||
</QCardSection>
|
||||
|
||||
<QSeparator />
|
||||
|
||||
<QCardSection class="q-gutter-md">
|
||||
<QBtn
|
||||
:label="t('aboutCheckUpdates')"
|
||||
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">
|
||||
{{ t('aboutLatestRelease') }}: {{ releaseLabel }}
|
||||
</div>
|
||||
<div class="text-caption text-grey-7">
|
||||
{{ t('aboutPublished') }}: {{ new Date(latestRelease.published_at).toLocaleString() }}
|
||||
</div>
|
||||
<div class="q-mt-sm">
|
||||
{{ hasUpdate ? t('aboutUpdateAvailable') : t('aboutUpToDate') }}
|
||||
</div>
|
||||
<template #action>
|
||||
<QBtn
|
||||
flat
|
||||
color="primary"
|
||||
:label="t('aboutViewRelease')"
|
||||
: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"
|
||||
>
|
||||
{{ t('aboutElectronNote') }}
|
||||
</QBanner>
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
</div>
|
||||
</div>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.about-card {
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
.app-logo {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.version-badge {
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.collaborators-list {
|
||||
margin: 0 -8px;
|
||||
}
|
||||
|
||||
.collaborator-item {
|
||||
border-radius: 8px;
|
||||
transition: background 0.15s ease;
|
||||
padding: 6px 8px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.collaborator-item:hover {
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.collaborator-icon {
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user