feat: WIP first pass at UpdateOs page replacement component

This commit is contained in:
Zack Spear
2023-09-20 17:55:03 -07:00
committed by Zack Spear
parent 5c58a86d86
commit 57efcef072
20 changed files with 602 additions and 163 deletions

View File

@@ -30,11 +30,11 @@ defineEmits(['click']);
const classes = computed(() => {
switch (props.btnStyle) {
case 'fill':
return 'text-white bg-gradient-to-r from-unraid-red to-orange hover:from-unraid-red/60 hover:to-orange/60 focus:from-unraid-red/60 focus:to-orange/60';
return 'text-white bg-gradient-to-r from-unraid-red to-orange shadow-none hover:from-unraid-red/60 hover:to-orange/60 focus:from-unraid-red/60 focus:to-orange/60 hover:shadow-md focus:shadow-md';
case 'outline':
return 'text-orange bg-gradient-to-r from-transparent to-transparent border-2 border-solid border-orange hover:text-white focus:text-white hover:from-unraid-red hover:to-orange focus:from-unraid-red focus:to-orange hover:border-transparent focus:border-transparent';
return 'text-orange bg-gradient-to-r from-transparent to-transparent border-2 border-solid border-orange shadow-none hover:text-white focus:text-white hover:from-unraid-red hover:to-orange focus:from-unraid-red focus:to-orange hover:shadow-md focus:shadow-md';
case 'underline':
return 'opacity-75 hover:opacity-100 focus:opacity-100 underline transition hover:text-alpha hover:bg-beta focus:text-alpha focus:bg-beta';
return 'opacity-75 hover:opacity-100 focus:opacity-100 underline transition shadow-none hover:text-alpha hover:bg-beta focus:text-alpha focus:bg-beta hover:shadow-md focus:shadow-md';
}
});
</script>

View File

@@ -45,3 +45,9 @@ provide(I18nInjectionKey, i18n);
<template>
<slot />
</template>
<style>
/* unraid-i18n-host {
font-size: 16px;
} */
</style>

109
web/components/Ui/Badge.vue Normal file
View File

@@ -0,0 +1,109 @@
<script setup lang="ts">
import { XCircleIcon } from '@heroicons/vue/24/solid';
import BrandLoading from '~/components/Brand/Loading.vue';
import BrandLoadingWhite from '~/components/Brand/LoadingWhite.vue';
const props = withDefaults(defineProps<{
color?: 'gray' | 'red' | 'yellow' | 'green' | 'blue' | 'indigo' | 'purple' | 'pink' | 'orange' | 'black' | 'white' | 'transparent' | 'current';
icon?: typeof XCircleIcon | typeof BrandLoading | typeof BrandLoadingWhite;
iconRight?: typeof XCircleIcon | typeof BrandLoading | typeof BrandLoadingWhite;
size?: '12px' | '14px' | '16px' | '18px' | '20px' | '24px';
}>(), {
color: 'gray',
icon: undefined,
iconRight: undefined,
size: '16px',
});
const computedStyleClasses = computed(() => {
let colorClasses = '';
let textSize = '';
let iconSize = '';
switch (props.color) {
case 'red':
colorClasses = 'bg-unraid-red text-white group-hover:bg-orange-dark group-focus:bg-orange-dark';
break;
case 'yellow':
colorClasses = 'bg-yellow-100 text-yellow-800 group-hover:bg-yellow-200 group-focus:bg-yellow-200';
break;
case 'green':
colorClasses = 'bg-green-100 text-green-800 group-hover:bg-green-200 group-focus:bg-green-200';
break;
case 'blue':
colorClasses = 'bg-blue-100 text-blue-800 group-hover:bg-blue-200 group-focus:bg-blue-200';
break;
case 'indigo':
colorClasses = 'bg-indigo-100 text-indigo-800 group-hover:bg-indigo-200 group-focus:bg-indigo-200';
break;
case 'purple':
colorClasses = 'bg-purple-100 text-purple-800 group-hover:bg-purple-200 group-focus:bg-purple-200';
break;
case 'pink':
colorClasses = 'bg-pink-100 text-pink-800 group-hover:bg-pink-200 group-focus:bg-pink-200';
break;
case 'orange':
colorClasses = 'bg-orange text-white group-hover:bg-orange-dark group-focus:bg-orange-dark';
break;
case 'black':
colorClasses = 'bg-black text-white group-hover:bg-gray-800 group-focus:bg-gray-800';
break;
case 'white':
colorClasses = 'bg-white text-black group-hover:bg-gray-100 group-focus:bg-gray-100';
break;
case 'transparent':
colorClasses = 'bg-transparent text-black group-hover:bg-gray-100 group-focus:bg-gray-100';
break;
case 'current':
colorClasses = 'bg-current text-black group-hover:bg-gray-100 group-focus:bg-gray-100';
break;
case 'gray':
colorClasses = 'bg-gray-200 text-gray-800 group-hover:bg-gray-300 group-focus:bg-gray-300';
break;
}
switch (props.size) {
case '12px':
textSize = 'text-12px px-8px py-4px';
iconSize = 'w-12px';
break;
case '14px':
textSize = 'text-14px px-8px py-4px';
iconSize = 'w-14px';
break;
case '16px':
textSize = 'text-16px px-12px py-8px';
iconSize = 'w-16px';
break;
case '18px':
textSize = 'text-18px px-12px py-8px';
iconSize = 'w-18px';
break;
case '20px':
textSize = 'text-20px px-16px py-12px';
iconSize = 'w-20px';
break;
case '24px':
textSize = 'text-24px px-16px py-12px';
iconSize = 'w-24px';
break;
}
return {
badge: `${textSize} ${colorClasses}`,
icon: iconSize,
};
});
</script>
<template>
<span
class="inline-flex items-center rounded-full font-semibold leading-none transition-all duration-200 ease-in-out"
:class="[
computedStyleClasses.badge,
icon || iconRight ? 'gap-8px' : '',
]"
>
<component :is="icon" v-if="icon" class="flex-shrink-0" :class="computedStyleClasses.icon" />
<slot></slot>
<component :is="iconRight" v-if="iconRight" class="flex-shrink-0" :class="computedStyleClasses.icon" />
</span>
</template>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
withDefaults(defineProps<{ increasedPadding?: boolean }>(), {
increasedPadding: false,
});
</script>
<template>
<div
class="group/card p-4 text-left relative flex flex-col flex-1 text-beta bg-alpha border-2 border-solid border-gamma/50 rounded-md shadow-md hover:shadow-orange/50 transition-all"
:class="increasedPadding && 'md:p-6'"
>
<slot></slot>
</div>
</template>

View File

@@ -3,118 +3,44 @@
* @todo require keyfile to be set before allowing user to check for updates
* @todo require keyfile to update
* @todo require valid guid / server state to update
* @todo detect downgrade possibility
*/
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue'
import { ArrowTopRightOnSquareIcon, BellAlertIcon } from '@heroicons/vue/24/solid';
import dayjs from 'dayjs';
import dayjs, { extend } from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { storeToRefs } from 'pinia';
import { ref, watchEffect } from 'vue';
import { useI18n } from 'vue-i18n';
import { useUpdateOsStore } from '~/store/updateOsActions';
import 'tailwindcss/tailwind.css';
import '~/assets/main.css';
import { useServerStore } from '~/store/server';
import { useUpdateOsStore, useUpdateOsActionsStore } from '~/store/updateOsActions';
import type { UserProfileLink } from '~/types/userProfile';
const { t } = useI18n();
const serverStore = useServerStore();
const updateOsStore = useUpdateOsStore();
const updateOsActionsStore = useUpdateOsActionsStore();
const { guid, keyfile, osVersion } = storeToRefs(serverStore);
const { available, cachedReleasesTimestamp } = storeToRefs(updateOsStore);
const includeNext = ref(false);
const updateButton = ref<UserProfileLink | undefined>();
const parsedCachedReleasesTimestamp = computed(() => {
if (!cachedReleasesTimestamp.value) { return ''; }
return dayjs(cachedReleasesTimestamp.value).format('YYYY-MM-DD HH:mm:ss');
export interface Props {
restoreVersion?: string;
}
withDefaults(defineProps<Props>(), {
restoreVersion: '',
});
const check = () => {
updateOsStore.checkForUpdate({
cache: true,
guid: guid.value,
includeNext: includeNext.value,
keyfile: keyfile.value,
osVersion: osVersion.value,
skipCache: true,
});
};
const updateOsStore = useUpdateOsStore();
const { cachedReleasesTimestamp } = storeToRefs(updateOsStore);
watchEffect(() => {
if (available.value) {
updateButton.value = updateOsActionsStore.initUpdateOsCallback();
} else {
updateButton.value = undefined;
}
extend(relativeTime);
const parsedReleaseTimestamp = computed(() => {
if (!cachedReleasesTimestamp.value) { return ''; }
return {
formatted: dayjs(cachedReleasesTimestamp.value).format('YYYY-MM-DD HH:mm:ss'),
relative: dayjs().to(dayjs(cachedReleasesTimestamp.value)),
};
});
</script>
<template>
<div class="grid gap-y-24px">
<div class="grid gap-y-16px">
<h1 class="text-24px">{{ t('Update Unraid OS') }}</h1>
<p>Current Version: {{ osVersion }}</p>
<p>Status: {{ available ? 'Update Available' : 'Up-to-date' }}</p>
</div>
<div class="text-16px text-alpha bg-beta text-left relative flex flex-col justify-around border-2 border-solid shadow-xl transform overflow-hidden rounded-lg transition-all sm:w-full">
<div class="px-16px py-20px sm:p-24px">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-20px">
<div class="grid gap-y-16px">
<h3 class="text-20px font-semibold leading-6 text-gray-900 flex flex-row items-center gap-8px">
<BellAlertIcon class="w-20px shrink-0" />
<span>
{{ available ? t(updateButton?.text ?? '', updateButton?.textParams) : t('Check for Updates')}}
</span>
</h3>
<div class="max-w-xl text-sm text-gray-500 whitespace-normal">
<p class="text-18px">{{ t('Receive the latest and greatest for Unraid OS. Whether it new features, security patches, or bug fixes keeping your server up-to-date ensures the best experience that Unraid has to offer.') }}</p>
</div>
</div>
<div class="flex flex-col sm:flex-shrink-0 items-center gap-16px">
<SwitchGroup v-if="!available" as="div">
<div class="flex flex-shrink-0 items-center gap-16px">
<Switch v-model="includeNext" :class="[includeNext ? 'bg-green-500' : 'bg-gray-200', 'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2']">
<span :class="[includeNext ? 'translate-x-5' : 'translate-x-0', 'pointer-events-none relative inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out']">
<span :class="[includeNext ? 'opacity-0 duration-100 ease-out' : 'opacity-100 duration-200 ease-in', 'absolute inset-0 flex h-full w-full items-center justify-center transition-opacity']" aria-hidden="true">
<svg class="h-12px w-12px text-gray-400" fill="none" viewBox="0 0 12 12">
<path d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</span>
<span :class="[includeNext ? 'opacity-100 duration-200 ease-in' : 'opacity-0 duration-100 ease-out', 'absolute inset-0 flex h-full w-full items-center justify-center transition-opacity']" aria-hidden="true">
<svg class="h-12px w-12px text-green-500" fill="currentColor" viewBox="0 0 12 12">
<path d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z" />
</svg>
</span>
</span>
</Switch>
<SwitchLabel class="text-14px">{{ t('Include Prereleases') }}</SwitchLabel>
</div>
</SwitchGroup>
<span class="flex flex-col gap-y-8px">
<BrandButton
v-if="available && updateButton"
@click="updateButton?.click"
:external="updateButton?.external"
:icon-right="ArrowTopRightOnSquareIcon"
:name="updateButton?.name"
:text="t('View changelog & update')" />
<BrandButton v-else @click="check" btn-style="outline" :text="t('Check Now')" />
<span class="text-14px text-gamma text-center">{{ t('Last checked: {0}', [parsedCachedReleasesTimestamp]) }}</span>
</span>
</div>
</div>
</div>
</div>
<div class="grid gap-y-24px max-w-1024px mx-auto">
<UpdateOsStatus :release-check-time="parsedReleaseTimestamp" :t="t" />
<UpdateOsUpdate :release-check-time="parsedReleaseTimestamp" :t="t" />
<UpdateOsDowngrade v-if="restoreVersion" :version="restoreVersion" :t="t" />
</div>
</template>

View File

@@ -0,0 +1,88 @@
<script setup lang="ts">
/**
* @todo require keyfile to be set before allowing user to check for updates
* @todo require keyfile to update
* @todo require valid guid / server state to update
* @todo detect downgrade possibility
*/
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue';
import { storeToRefs } from 'pinia';
import { ref, watchEffect } from 'vue';
import 'tailwindcss/tailwind.css';
import '~/assets/main.css';
import { useServerStore } from '~/store/server';
import { useUpdateOsStore, useUpdateOsActionsStore } from '~/store/updateOsActions';
import type { UserProfileLink } from '~/types/userProfile';
import { stat } from 'fs';
const props = defineProps<{
releaseCheckTime: {
formatted: string;
relative: string;
};
t: any;
}>();
const serverStore = useServerStore();
const updateOsStore = useUpdateOsStore();
const updateOsActionsStore = useUpdateOsActionsStore();
const { guid, keyfile, osVersion } = storeToRefs(serverStore);
const { available } = storeToRefs(updateOsStore);
const includeNext = ref(false);
const status = ref<'ready' | 'checking'>('ready');
const buttonText = computed(() => {
if (status.value === 'checking') {
return props.t('Checking...');
}
return props.t('Check For Updates');
});
const check = async () => {
status.value = 'checking';
await updateOsStore.checkForUpdate({
cache: true,
guid: guid.value,
includeNext: includeNext.value,
keyfile: keyfile.value,
osVersion: osVersion.value,
skipCache: true,
}).finally(() => {
status.value = 'ready';
})
};
</script>
<template>
<div class="flex flex-col sm:flex-shrink-0 items-center gap-16px">
<SwitchGroup as="div">
<div class="flex flex-shrink-0 items-center gap-16px">
<Switch v-model="includeNext" :class="[includeNext ? 'bg-green-500' : 'bg-gray-200', 'relative inline-flex h-24px w-[44px] flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2']">
<span :class="[includeNext ? 'translate-x-20px' : 'translate-x-0', 'pointer-events-none relative inline-block h-20px w-20px transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out']">
<span :class="[includeNext ? 'opacity-0 duration-100 ease-out' : 'opacity-100 duration-200 ease-in', 'absolute inset-0 flex h-full w-full items-center justify-center transition-opacity']" aria-hidden="true">
<svg class="h-12px w-12px text-gray-400" fill="none" viewBox="0 0 12 12">
<path d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</span>
<span :class="[includeNext ? 'opacity-100 duration-200 ease-in' : 'opacity-0 duration-100 ease-out', 'absolute inset-0 flex h-full w-full items-center justify-center transition-opacity']" aria-hidden="true">
<svg class="h-12px w-12px text-green-500" fill="currentColor" viewBox="0 0 12 12">
<path d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z" />
</svg>
</span>
</span>
</Switch>
<SwitchLabel class="text-14px">{{ t('Include Prereleases') }}</SwitchLabel>
</div>
</SwitchGroup>
<span class="flex flex-col gap-y-8px">
<BrandButton @click="check" :disabled="status === 'checking'" btn-style="outline" :text="buttonText" class="flex-0" />
<span class="text-14px opacity-75 text-center" :title="releaseCheckTime.formatted">{{ t('Last checked: {0}', [releaseCheckTime.relative]) }}</span>
</span>
</div>
</template>

View File

@@ -0,0 +1,64 @@
<script lang="ts" setup>
import { ArrowUturnDownIcon, InformationCircleIcon } from '@heroicons/vue/24/solid';
import type { SemVer } from 'semver';
import { ref } from 'vue';
import 'tailwindcss/tailwind.css';
import '~/assets/main.css';
import { useUpdateOsActionsStore } from '~/store/updateOsActions';
import type { UserProfileLink } from '~/types/userProfile';
const props = defineProps<{
t: any;
version: string;
}>();
const updateOsActionsStore = useUpdateOsActionsStore();
const downgradeButton = ref<UserProfileLink | undefined>({
click: () => {
// @ts-ignore global function provided by the webgui on the update page
downgrade();
},
name: 'downgrade',
text: props.t('Begin restore to Unraid {0}', [props.version]),
});
</script>
<template>
<UiCardWrapper :increased-padding="true">
<div class="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-20px sm:gap-24px">
<div class="grid gap-y-16px">
<h3 class="text-20px font-semibold leading-6 flex flex-row items-center gap-8px">
<ArrowUturnDownIcon class="w-20px shrink-0" />
{{ t('Downgrade Unraid OS to {0}', [version]) }}
</h3>
<div class="text-16px leading-relaxed opacity-75 whitespace-normal">
<p>{{ t('Downgrades are only recommended if you\'re unable to solve a critical issue. In the rare event you need to downgrade we ask that you please provide us with Diagnostics so we can investigate your issue. You will be prompted with the option download the Diagnostics zip once the downgrade process is started. From there please open a bug report on our forums.') }}</p>
</div>
</div>
<div v-if="downgradeButton" class="flex flex-col sm:flex-shrink-0 items-center gap-16px">
<BrandButton
@click="downgradeButton?.click"
btn-style="underline"
:icon="InformationCircleIcon"
:text="t('View Changelog for {0}', [version])" />
<BrandButton
@click="downgradeButton?.click"
btn-style="outline"
:external="downgradeButton?.external"
:icon="ArrowUturnDownIcon"
:name="downgradeButton?.name"
:text="downgradeButton?.text" />
</div>
</div>
</UiCardWrapper>
</template>
<style lang="postcss">
@tailwind base;
@tailwind components;
@tailwind utilities;
</style>

View File

@@ -0,0 +1,66 @@
<script lang="ts" setup>
import {
BellAlertIcon,
CheckCircleIcon,
InformationCircleIcon,
} from '@heroicons/vue/24/solid';
import { storeToRefs } from 'pinia';
import { useServerStore } from '~/store/server';
import { useUpdateOsStore } from '~/store/updateOsActions';
const props = defineProps<{
releaseCheckTime: {
formatted: string;
relative: string;
};
t: any;
}>();
const serverStore = useServerStore();
const updateOsStore = useUpdateOsStore();
const { guid, keyfile, osVersion } = storeToRefs(serverStore);
const { available } = storeToRefs(updateOsStore);
const viewReleaseNotes = () => {
// @ts-ignore  this is a global function provided by the webgui
if (typeof openChanges === 'function') {
// @ts-ignore
openChanges(
'showchanges /var/tmp/unRAIDServer.txt',
props.t('{0} Release Notes', [osVersion.value]),
);
} else {
alert('Unable to open release notes');
}
};
</script>
<template>
<div class="grid gap-y-16px">
<h1 class="text-24px">{{ t('Update Unraid OS') }}</h1>
<div class="flex flex-col md:flex-row gap-16px justify-start md:items-start md:justify-between">
<div class="inline-flex gap-8px">
<button
@click="viewReleaseNotes"
class="group"
:title="t('View changelog for current version {0}', [osVersion])"
>
<UiBadge :icon="InformationCircleIcon">
{{ t('Current Version {0}', [osVersion]) }}
</UiBadge>
</button>
<UiBadge
:color="available ? 'orange' : 'green'"
:icon="available ? BellAlertIcon : CheckCircleIcon"
:title="t('Last checked: {0}', [releaseCheckTime.relative])"
>
{{ available ? 'Update Available' : 'Up-to-date' }}
</UiBadge>
</div>
<UpdateOsCheckButton :releaseCheckTime="releaseCheckTime" :t="t" />
</div>
</div>
</template>

View File

@@ -0,0 +1,102 @@
<script lang="ts" setup>
/**
* @todo require keyfile to be set before allowing user to check for updates
* @todo require keyfile to update
* @todo require valid guid / server state to update
* @todo detect downgrade possibility
*/
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue'
import {
ArrowPathIcon,
ArrowTopRightOnSquareIcon,
BellAlertIcon,
} from '@heroicons/vue/24/solid';
import dayjs from 'dayjs';
import { storeToRefs } from 'pinia';
import { ref, watchEffect } from 'vue';
import 'tailwindcss/tailwind.css';
import '~/assets/main.css';
import { useServerStore } from '~/store/server';
import { useUpdateOsStore, useUpdateOsActionsStore } from '~/store/updateOsActions';
import type { UserProfileLink } from '~/types/userProfile';
const props = defineProps<{
releaseCheckTime: {
formatted: string;
relative: string;
};
t: any;
}>();
const serverStore = useServerStore();
const updateOsStore = useUpdateOsStore();
const updateOsActionsStore = useUpdateOsActionsStore();
const { guid, keyfile, osVersion } = storeToRefs(serverStore);
const { available } = storeToRefs(updateOsStore);
const includeNext = ref(false);
const updateButton = ref<UserProfileLink | undefined>();
const availableText = computed(() => {
if (available.value && updateButton?.value?.text && updateButton?.value?.textParams) {
return props.t(updateButton?.value.text, updateButton?.value.textParams);
}
});
const check = () => {
updateOsStore.checkForUpdate({
cache: true,
guid: guid.value,
includeNext: includeNext.value,
keyfile: keyfile.value,
osVersion: osVersion.value,
skipCache: true,
});
};
watchEffect(() => {
if (available.value) {
updateButton.value = updateOsActionsStore.initUpdateOsCallback();
} else {
updateButton.value = undefined;
}
});
</script>
<template>
<UiCardWrapper :increased-padding="true">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-20px sm:gap-24px">
<div class="grid gap-y-16px">
<h3 class="text-20px font-semibold leading-6 flex flex-row items-center gap-8px">
<BellAlertIcon v-if="available" class="w-20px shrink-0" />
<ArrowPathIcon v-else class="w-20px shrink-0" />
<span>
{{ availableText ? availableText : t('Check for Updates')}}
</span>
</h3>
<div class="text-16px leading-relaxed whitespace-normal opacity-75">
<p>{{ t('Receive the latest and greatest for Unraid OS. Whether it new features, security patches, or bug fixes keeping your server up-to-date ensures the best experience that Unraid has to offer.') }}</p>
</div>
</div>
<BrandButton
v-if="available && updateButton"
@click="updateButton?.click"
:external="updateButton?.external"
:icon-right="ArrowTopRightOnSquareIcon"
:name="updateButton?.name"
:text="t('View changelog & update')" />
</div>
</UiCardWrapper>
</template>
<style lang="postcss">
@tailwind base;
@tailwind components;
@tailwind utilities;
</style>

View File

@@ -36,6 +36,7 @@ const {
connectPluginInstalled,
} = storeToRefs(serverStore);
const { bannerGradient, theme } = storeToRefs(useThemeStore());
const { isOsVersionStable } = storeToRefs(updateOsStore);
const hideDropdown = computed(() => state.value === 'PRO' && !connectPluginInstalled.value);
@@ -84,9 +85,11 @@ onBeforeMount(() => {
}
callbackStore.watcher();
updateOsStore.checkForUpdate({
cache: true,
guid: guid.value,
includeNext: isOsVersionStable.value, // @todo ensure this is correct
keyfile: keyfile.value,
osVersion: osVersion.value,
});

View File

@@ -3,6 +3,7 @@ import { storeToRefs } from 'pinia';
import {
Bars3Icon,
Bars3BottomRightIcon,
BellAlertIcon,
ExclamationTriangleIcon,
InformationCircleIcon,
ShieldExclamationIcon,
@@ -11,6 +12,7 @@ import {
import { useDropdownStore } from '~/store/dropdown';
import { useErrorsStore } from '~/store/errors';
import { useServerStore } from '~/store/server';
import { useUpdateOsStore } from '~/store/updateOsActions';
const props = defineProps<{ t: any; }>();
@@ -18,6 +20,7 @@ const dropdownStore = useDropdownStore();
const { dropdownVisible } = storeToRefs(dropdownStore);
const { errors } = storeToRefs(useErrorsStore());
const { state, stateData } = storeToRefs(useServerStore());
const { available: osUpdateAvailable } = storeToRefs(useUpdateOsStore());
const showErrorIcon = computed(() => errors.value.length || stateData.value.error);
@@ -49,9 +52,16 @@ const title = computed((): string => {
<span class="absolute bottom-[-3px] inset-x-0 h-2px w-full bg-gradient-to-r from-unraid-red to-orange rounded opacity-0 group-hover:opacity-100 group-focus:opacity-100 transition-opacity" />
</span>
<BellAlertIcon v-if="osUpdateAvailable" class="hover:animate-pulse text-white fill-current relative w-16px h-16px" />
<Bars3Icon v-if="!dropdownVisible" class="w-20px" />
<Bars3BottomRightIcon v-else class="w-20px" />
<BrandAvatar />
<span class="relative">
<BrandAvatar />
<!-- <span v-if="osUpdateAvailable" class="absolute z-10 -bottom-1 -right-3 w-24px h-24px flex items-center justify-center shadow border border-white bg-gradient-to-r from-unraid-red to-orange rounded-full">
<BellAlertIcon class="hover:animate-pulse text-white fill-current relative w-12px h-12px" />
</span> -->
</span>
</button>
</template>