feat: update os notifications enabled usage + link to enable & more options to account app

This commit is contained in:
Zack Spear
2024-01-31 12:39:14 -08:00
parent b35a440792
commit bf99eb25c8
5 changed files with 131 additions and 51 deletions

View File

@@ -49,6 +49,7 @@ class ServerState
private $caseModel = '';
private $keyfileBase64UrlSafe = '';
private $updateOsCheck;
private $updateOsNotificationsEnabled = false;
private $updateOsResponse;
private $updateOsIgnoredReleases = [];
@@ -168,8 +169,9 @@ class ServerState
}
$this->updateOsCheck = new UnraidOsCheck();
$this->updateOsResponse = $this->updateOsCheck->getUnraidOSCheckResult();
$this->updateOsIgnoredReleases = $this->updateOsCheck->getIgnoredReleases();
$this->updateOsNotificationsEnabled = !empty(@$this->getWebguiGlobal('notify')['unraidos']);
$this->updateOsResponse = $this->updateOsCheck->getUnraidOSCheckResult();
}
/**
@@ -251,14 +253,18 @@ class ServerState
$serverState['combinedKnownOrigins'] = $this->combinedKnownOrigins;
}
if ($this->updateOsResponse) {
$serverState['updateOsResponse'] = $this->updateOsResponse;
}
if ($this->updateOsIgnoredReleases) {
$serverState['updateOsIgnoredReleases'] = $this->updateOsIgnoredReleases;
}
if ($this->updateOsNotificationsEnabled) {
$serverState['updateOsNotificationsEnabled'] = $this->updateOsNotificationsEnabled;
}
if ($this->updateOsResponse) {
$serverState['updateOsResponse'] = $this->updateOsResponse;
}
return $serverState;
}

View File

@@ -1,5 +1,12 @@
<script lang="ts" setup>
import { ArrowTopRightOnSquareIcon, EyeIcon, IdentificationIcon, KeyIcon, XMarkIcon } from '@heroicons/vue/24/solid';
import {
ArrowTopRightOnSquareIcon,
CogIcon,
EyeIcon,
IdentificationIcon,
KeyIcon,
XMarkIcon,
} from '@heroicons/vue/24/solid';
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue';
import { storeToRefs } from 'pinia';
@@ -27,11 +34,12 @@ const updateOsStore = useUpdateOsStore();
const updateOsChangelogStore = useUpdateOsChangelogStore();
const {
dateTimeFormat,
updateOsResponse,
updateOsIgnoredReleases,
regExp,
regUpdatesExpired,
dateTimeFormat,
updateOsIgnoredReleases,
updateOsNotificationsEnabled,
updateOsResponse,
} = storeToRefs(serverStore);
const {
available,
@@ -49,6 +57,12 @@ const {
// @todo - when true change primary action button to be close and hide secondary button
const ignoreThisRelease = ref(false);
const notificationsSettings = computed(() => {
return !updateOsNotificationsEnabled.value
? props.t('Go to Settings > Notifications to enable automatic OS update notifications for future releases.')
: undefined;
});
interface ModalCopy {
title: string;
description?: string;
@@ -84,15 +98,35 @@ const modalCopy = computed((): ModalCopy | null => {
description: description ? `<p>${formattedReleaseDate}</p><p>${description}</p>` : formattedReleaseDate,
};
} else if (!available.value && !availableWithRenewal.value) {
/** @todo - conditionally show this description for when the setting isn't set */
return {
title: props.t('Unraid OS is up-to-date'),
description: props.t('Go to Settings > Notifications to enable OS update notifications for future releases.'),
description: notificationsSettings.value ?? undefined,
};
}
return null;
});
const showNotificationsSettingsLink = computed(() => {
return !updateOsNotificationsEnabled.value && !available.value && !availableWithRenewal.value;
});
const extraLinks = computed((): ButtonProps[] | null => {
if (!showNotificationsSettingsLink.value) { return null; }
const buttons: ButtonProps[] = [];
if (showNotificationsSettingsLink.value) {
buttons.push({
btnStyle: 'outline',
href: '/Settings/Notifications',
icon: CogIcon,
text: props.t('Enable update notifications'),
});
}
return buttons;
});
const actionButtons = computed((): ButtonProps[] | null => {
// update not available or no action buttons default closing
if (!available.value || ignoreThisRelease.value) { return null; }
@@ -173,6 +207,13 @@ onBeforeMount(() => {
setUserFormattedReleaseDate();
}
});
const modalWidth = computed(() => {
if (availableWithRenewal.value) { // wider since we'll have four buttons
return 'max-w-800px';
}
return 'max-w-640px';
});
</script>
<template>
@@ -182,41 +223,58 @@ onBeforeMount(() => {
:title="modalCopy?.title"
:description="modalCopy?.description"
:show-close-x="!checkForUpdatesLoading"
max-width="max-w-640px"
:max-width="modalWidth"
@close="close"
>
<template v-if="renderMainSlot" #main>
<BrandLoading v-if="checkForUpdatesLoading" class="w-[150px] mx-auto" />
<div v-else-if="available || availableWithRenewal" class="mx-auto">
<SwitchGroup>
<div class="flex justify-center items-center gap-8px p-8px rounded">
<Switch
v-model="ignoreThisRelease"
:class="ignoreThisRelease ? 'bg-gradient-to-r from-unraid-red to-orange' : 'bg-transparent'"
class="relative inline-flex h-24px w-[48px] items-center rounded-full overflow-hidden"
>
<span v-show="!ignoreThisRelease" class="absolute z-0 inset-0 opacity-10 bg-beta" />
<span
:class="ignoreThisRelease ? 'translate-x-[26px]' : 'translate-x-[2px]'"
class="inline-block h-20px w-20px transform rounded-full bg-white transition"
/>
</Switch>
<SwitchLabel class="text-16px">
{{ t('Ignore this release until next reboot') }}
</SwitchLabel>
</div>
</SwitchGroup>
</div>
<div v-else-if="updateOsIgnoredReleases.length > 0" class="w-full flex flex-col gap-8px">
<h3 class="text-left text-16px font-semibold italic">
{{ t('Ignored Releases') }}
</h3>
<UpdateOsIgnoredRelease
v-for="ignoredRelease in updateOsIgnoredReleases"
:key="ignoredRelease"
:label="ignoredRelease"
:t="t"
/>
<div v-else class="flex flex-col gap-y-16px">
<div v-if="extraLinks" class="flex flex-col xs:flex-row justify-center gap-8px">
<BrandButton
v-for="item in extraLinks"
:key="item.text"
:btn-style="item.btnStyle ?? undefined"
:href="item.href ?? undefined"
:icon="item.icon"
:icon-right="item.iconRight"
:icon-right-hover-display="item.iconRightHoverDisplay"
:text="t(item.text)"
:title="item.title ? t(item.title) : undefined"
@click="item.click ? item.click() : undefined"
/>
</div>
<div v-if="available || availableWithRenewal" class="mx-auto">
<SwitchGroup>
<div class="flex justify-center items-center gap-8px p-8px rounded">
<Switch
v-model="ignoreThisRelease"
:class="ignoreThisRelease ? 'bg-gradient-to-r from-unraid-red to-orange' : 'bg-transparent'"
class="relative inline-flex h-24px w-[48px] items-center rounded-full overflow-hidden"
>
<span v-show="!ignoreThisRelease" class="absolute z-0 inset-0 opacity-10 bg-beta" />
<span
:class="ignoreThisRelease ? 'translate-x-[26px]' : 'translate-x-[2px]'"
class="inline-block h-20px w-20px transform rounded-full bg-white transition"
/>
</Switch>
<SwitchLabel class="text-16px">
{{ t('Ignore this release until next reboot') }}
</SwitchLabel>
</div>
</SwitchGroup>
</div>
<div v-else-if="updateOsIgnoredReleases.length > 0" class="w-full max-w-640px mx-auto flex flex-col gap-8px">
<h3 class="text-left text-16px font-semibold italic">
{{ t('Ignored Releases') }}
</h3>
<UpdateOsIgnoredRelease
v-for="ignoredRelease in updateOsIgnoredReleases"
:key="ignoredRelease"
:label="ignoredRelease"
:t="t"
/>
</div>
</div>
</template>
@@ -228,12 +286,20 @@ onBeforeMount(() => {
'justify-center': !actionButtons,
}"
>
<BrandButton
btn-style="underline-hover-red"
:icon="XMarkIcon"
:text="t('Close')"
@click="close"
/>
<div class="flex flex-col xs:flex-row justify-start gap-8px">
<BrandButton
btn-style="underline"
:icon="ArrowTopRightOnSquareIcon"
:text="t('More options')"
@click="accountStore.updateOs()"
/>
<BrandButton
btn-style="underline-hover-red"
:icon="XMarkIcon"
:text="t('Close')"
@click="close"
/>
</div>
<div v-if="actionButtons" class="flex flex-col xs:flex-row justify-end gap-8px">
<BrandButton
v-for="item in actionButtons"

View File

@@ -353,5 +353,9 @@
"Continue": "Continue",
"Verify to Update": "Verify to Update",
"Release date {0}": "Release date {0}",
"Update Released": "Update Released"
"Update Released": "Update Released",
"Go to Tools > Update OS for more options.": "Go to Tools > Update OS for more options.",
"Go to Settings > Notifications to enable automatic OS update notifications for future releases.": "Go to Settings > Notifications to enable automatic OS update notifications for future releases.",
"More options": "More options",
"Extend Key": "Extend Key"
}

View File

@@ -118,6 +118,7 @@ export const useServerStore = defineStore('server', () => {
});
const updateOsResponse = ref<ServerUpdateOsResponse>();
const updateOsIgnoredReleases = ref<string[]>([]);
const updateOsNotificationsEnabled = ref<boolean>(false);
const uptime = ref<number>(0);
const username = ref<string>(''); // @todo potentially move to a user store
const wanFQDN = ref<string>('');
@@ -820,8 +821,9 @@ export const useServerStore = defineStore('server', () => {
if (typeof data?.site !== 'undefined') { site.value = data.site; }
if (typeof data?.state !== 'undefined') { state.value = data.state; }
if (typeof data?.theme !== 'undefined') { theme.value = data.theme; }
if (typeof data?.updateOsResponse !== 'undefined') { updateOsResponse.value = data.updateOsResponse; }
if (typeof data?.updateOsIgnoredReleases !== 'undefined') { updateOsIgnoredReleases.value = data.updateOsIgnoredReleases; }
if (typeof data?.updateOsNotificationsEnabled !== 'undefined') { updateOsNotificationsEnabled.value = data.updateOsNotificationsEnabled; }
if (typeof data?.updateOsResponse !== 'undefined') { updateOsResponse.value = data.updateOsResponse; }
if (typeof data?.uptime !== 'undefined') { uptime.value = data.uptime; }
if (typeof data?.username !== 'undefined') { username.value = data.username; }
if (typeof data?.wanFQDN !== 'undefined') { wanFQDN.value = data.wanFQDN; }
@@ -1031,6 +1033,7 @@ export const useServerStore = defineStore('server', () => {
state,
theme,
updateOsIgnoredReleases,
updateOsNotificationsEnabled,
updateOsResponse,
uptime,
username,

View File

@@ -102,8 +102,9 @@ export interface Server {
site?: string;
state?: ServerState;
theme?: Theme | undefined;
updateOsResponse?: ServerUpdateOsResponse;
updateOsIgnoredReleases?: string[];
updateOsNotificationsEnabled?: boolean;
updateOsResponse?: ServerUpdateOsResponse;
uptime?: number;
username?: string;
wanFQDN?: string;