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,280 +1,178 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useHead } from '@unhead/vue';
|
import { useHead } from '@unhead/vue';
|
||||||
import { computed, onMounted, ref } from 'vue';
|
|
||||||
import { t } from '../i18n';
|
import { t } from '../i18n';
|
||||||
|
|
||||||
defineOptions({ name: 'AboutView' });
|
defineOptions({ name: 'AboutView' });
|
||||||
|
|
||||||
useHead(() => ({ title: t('aboutTitle') }));
|
useHead(() => ({ title: t('aboutTitle') }));
|
||||||
|
|
||||||
type ReleaseResponse = {
|
|
||||||
html_url: string;
|
|
||||||
name: string | null;
|
|
||||||
tag_name: string;
|
|
||||||
published_at: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const appName = 'Scoreko-dev';
|
const appName = 'Scoreko-dev';
|
||||||
const currentVersion = import.meta.env.PACKAGE_VERSION;
|
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 = [
|
const collaborators = [
|
||||||
{
|
{
|
||||||
name: 'Pandipipas',
|
name: 'Pandipipas',
|
||||||
role: 'Development and maintenance of Scoreko-dev',
|
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',
|
name: 'Dan Shields',
|
||||||
role: 'nodecg-vue-composable helper',
|
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',
|
name: 'NodeCG',
|
||||||
role: 'Broadcast graphics framework',
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QPage class="q-pa-lg">
|
<QPage class="q-pa-lg">
|
||||||
<div class="text-h4 q-mb-md">
|
<div class="q-mb-lg">
|
||||||
{{ t('aboutTitle') }}
|
<div class="text-h5 text-weight-medium">
|
||||||
|
{{ t('aboutTitle') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row q-col-gutter-lg">
|
<QCard
|
||||||
<div class="col-12 col-md-6">
|
flat
|
||||||
<QCard
|
bordered
|
||||||
flat
|
class="about-card"
|
||||||
bordered
|
>
|
||||||
>
|
<!-- App identity -->
|
||||||
<QCardSection class="row items-center q-col-gutter-md">
|
<QCardSection class="q-pa-lg">
|
||||||
<div class="col-auto">
|
<div class="row items-center q-gutter-md">
|
||||||
<QImg
|
<QImg
|
||||||
src="../image.png"
|
src="../image.png"
|
||||||
alt="Scoreko logo"
|
alt="Scoreko logo"
|
||||||
width="72px"
|
class="app-logo"
|
||||||
height="72px"
|
fit="contain"
|
||||||
fit="contain"
|
/>
|
||||||
/>
|
<div>
|
||||||
|
<div class="text-h6 text-weight-bold">
|
||||||
|
{{ appName }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<QBadge
|
||||||
<div class="text-h6">
|
outline
|
||||||
{{ appName }}
|
|
||||||
</div>
|
|
||||||
<div class="text-caption text-grey-7">
|
|
||||||
{{ t('aboutVersion') }} {{ currentVersion }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</QCardSection>
|
|
||||||
|
|
||||||
<QSeparator />
|
|
||||||
|
|
||||||
<QCardSection>
|
|
||||||
<p class="q-mb-sm">
|
|
||||||
{{ t('aboutDescription') }}
|
|
||||||
</p>
|
|
||||||
<div class="column q-gutter-sm">
|
|
||||||
<QBtn
|
|
||||||
href="https://github.com/nodecg/nodecg"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
icon="open_in_new"
|
|
||||||
:label="t('aboutFrameworkNodeCG')"
|
|
||||||
color="primary"
|
|
||||||
flat
|
|
||||||
no-caps
|
|
||||||
align="left"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</QCardSection>
|
|
||||||
|
|
||||||
<QSeparator />
|
|
||||||
|
|
||||||
<QCardSection>
|
|
||||||
<div class="text-subtitle2 q-mb-sm">
|
|
||||||
{{ t('aboutCollaboratorsTitle') }}
|
|
||||||
</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">
|
|
||||||
{{ 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"
|
color="primary"
|
||||||
icon="sync"
|
class="q-mt-xs version-badge"
|
||||||
:loading="checkingUpdates"
|
|
||||||
no-caps
|
|
||||||
@click="checkForUpdates"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<QBanner
|
|
||||||
v-if="latestRelease"
|
|
||||||
rounded
|
|
||||||
class="bg-grey-2"
|
|
||||||
>
|
>
|
||||||
<template #avatar>
|
v{{ currentVersion }}
|
||||||
<QIcon
|
</QBadge>
|
||||||
:name="hasUpdate ? 'system_update_alt' : 'check_circle'"
|
</div>
|
||||||
:color="hasUpdate ? 'warning' : 'positive'"
|
</div>
|
||||||
/>
|
</QCardSection>
|
||||||
</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
|
<QSeparator />
|
||||||
v-if="updateError"
|
|
||||||
rounded
|
|
||||||
class="bg-red-1 text-red-10"
|
|
||||||
>
|
|
||||||
{{ updateError }}
|
|
||||||
</QBanner>
|
|
||||||
|
|
||||||
<QBanner
|
<!-- Description + framework -->
|
||||||
rounded
|
<QCardSection class="q-pa-lg">
|
||||||
class="bg-blue-1 text-blue-10"
|
<p class="text-body2 text-grey-7 q-mb-md">
|
||||||
>
|
{{ t('aboutDescription') }}
|
||||||
{{ t('aboutElectronNote') }}
|
</p>
|
||||||
</QBanner>
|
<QBtn
|
||||||
</QCardSection>
|
href="https://github.com/nodecg/nodecg"
|
||||||
</QCard>
|
target="_blank"
|
||||||
</div>
|
rel="noopener noreferrer"
|
||||||
</div>
|
icon="open_in_new"
|
||||||
|
:label="t('aboutFrameworkNodeCG')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
no-caps
|
||||||
|
/>
|
||||||
|
</QCardSection>
|
||||||
|
|
||||||
|
<QSeparator />
|
||||||
|
|
||||||
|
<!-- Collaborators -->
|
||||||
|
<QCardSection class="q-pa-lg">
|
||||||
|
<div class="text-overline text-grey-6 q-mb-sm">
|
||||||
|
{{ t('aboutCollaboratorsTitle') }}
|
||||||
|
</div>
|
||||||
|
<QList
|
||||||
|
dense
|
||||||
|
class="collaborators-list"
|
||||||
|
>
|
||||||
|
<QItem
|
||||||
|
v-for="person in collaborators"
|
||||||
|
:key="person.name"
|
||||||
|
tag="a"
|
||||||
|
: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 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>
|
||||||
</QPage>
|
</QPage>
|
||||||
</template>
|
</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