Files
api/web/store/updateOs.ts
Eli Bosley 345e83bfb0 feat: upgrade nuxt-custom-elements (#1461)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added new modal dialogs and UI components, including activation steps,
OS update feedback, and expanded notification management.
* Introduced a plugin to configure internationalization, state
management, and Apollo client support in web components.
* Added a new Log Viewer page with a streamlined interface for viewing
logs.

* **Improvements**
* Centralized Pinia state management by consolidating all stores to use
a shared global Pinia instance.
* Simplified component templates by removing redundant
internationalization host wrappers.
* Enhanced ESLint configuration with stricter rules and global variable
declarations.
* Refined custom element build process to prevent jQuery conflicts and
optimize minification.
* Updated component imports and templates for consistent structure and
maintainability.
* Streamlined log viewer dropdowns using simplified select components
with improved formatting.
* Improved notification sidebar with filtering by importance and modular
components.
* Replaced legacy notification popups with new UI components and added
automatic root session creation for localhost requests.
* Updated OS version display and user profile UI with refined styling
and component usage.

* **Bug Fixes**
* Fixed component tag capitalization and improved type annotations
across components.

* **Chores**
* Updated development dependencies including ESLint plugins and build
tools.
* Removed deprecated log viewer patch class and cleaned up related test
fixtures.
  * Removed unused imports and simplified Apollo client setup.
* Cleaned up test mocks and removed obsolete i18n host component tests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1210730229632804

---------

Co-authored-by: Pujit Mehrotra <pujit@lime-technology.com>
Co-authored-by: Zack Spear <zackspear@users.noreply.github.com>
2025-07-08 10:05:39 -04:00

156 lines
5.2 KiB
TypeScript

import { computed, ref } from 'vue';
import { defineStore } from 'pinia';
import dayjs, { extend } from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import relativeTime from 'dayjs/plugin/relativeTime';
import type { ServerUpdateOsResponse } from '~/types/server';
import { WebguiCheckForUpdate, WebguiUpdateCancel } from '~/composables/services/webgui';
import { useServerStore } from '~/store/server';
import prerelease from 'semver/functions/prerelease';
import { useCallbackActionsStore } from '~/store/callbackActions';
/**
* Uses the shared global Pinia instance from ~/store/globalPinia.ts
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
* @see https://github.com/vuejs/pinia/discussions/1085
*/
import '~/store/globalPinia';
// dayjs plugins
extend(customParseFormat);
extend(relativeTime);
export const useUpdateOsStore = defineStore('updateOs', () => {
// state
const checkForUpdatesLoading = ref<boolean>(false);
const updateOsModalVisible = ref<boolean>(false);
const releaseForUpdate = ref<ServerUpdateOsResponse | null>(null);
const changelogModalVisible = computed(() => !!releaseForUpdate.value);
// getters from other stores
const serverStore = useServerStore();
const regExp = computed(() => serverStore.regExp);
const regUpdatesExpired = computed(() => serverStore.regUpdatesExpired);
const updateOsResponse = computed(() => serverStore.updateOsResponse);
const updateOsIgnoredReleases = computed(() => serverStore.updateOsIgnoredReleases);
// local getters
const available = computed(() => {
if (!updateOsResponse.value) {
return undefined;
}
// ignore any releases that are in the updateOsIgnoredReleases array
if (updateOsIgnoredReleases.value.includes(updateOsResponse.value.version)) {
return undefined;
}
return updateOsResponse.value.isNewer ? updateOsResponse.value.version : undefined;
});
const availableWithRenewal = computed((): string | undefined => {
if (!available.value || !updateOsResponse.value || !regExp.value || !regUpdatesExpired.value) {
return undefined;
}
return !updateOsResponse.value?.isEligible ? updateOsResponse.value.version : undefined;
});
const availableReleaseDate = computed(() =>
updateOsResponse.value?.date ? dayjs(updateOsResponse.value.date, 'YYYY-MM-DD') : undefined
);
/**
* If the updateOsResponse does not have a sha256, then the user is required to authenticate to download the update
*/
const availableRequiresAuth = computed((): boolean => !updateOsResponse.value?.sha256);
// Changelog logic
const changelogUrl = computed((): string => releaseForUpdate.value?.changelog || '');
const changelogPretty = computed(() => releaseForUpdate.value?.changelogPretty ?? null);
const setReleaseForUpdate = (release: ServerUpdateOsResponse | null) => {
releaseForUpdate.value = release;
};
// isReleaseForUpdateStable logic (true if no prerelease in version)
const isReleaseForUpdateStable = computed(() => {
if (!releaseForUpdate.value?.version) return false;
return !prerelease(releaseForUpdate.value.version);
});
// fetchAndConfirmInstall logic
const callbackStore = useCallbackActionsStore();
const fetchAndConfirmInstall = (sha256: string) => {
callbackStore.send(
window.location.href,
[
{
sha256,
type: 'updateOs',
},
],
undefined,
'forUpc'
);
};
// actions
const localCheckForUpdate = async (): Promise<void> => {
checkForUpdatesLoading.value = true;
setModalOpen(true);
try {
const response = await WebguiCheckForUpdate();
console.debug('[localCheckForUpdate] response', response);
serverStore.setUpdateOsResponse(response as ServerUpdateOsResponse);
checkForUpdatesLoading.value = false;
} catch (error) {
throw new Error('[localCheckForUpdate] Error checking for updates\n' + JSON.stringify(error));
}
};
const cancelUpdate = async (): Promise<void> => {
try {
const response = await WebguiUpdateCancel();
if (!response.success) {
throw new Error('Unable to cancel update');
}
// if current path is /Tools/Update, then we should redirect to /Tools
// otherwise it will redirect to the account update os page.
if (window.location.pathname === '/Tools/Update') {
window.location.href = '/Tools';
return;
}
// otherwise refresh the page
window.location.reload();
} catch (error) {
throw new Error(
`[cancelUpdate] Error cancelling update with error: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
};
const setModalOpen = (val: boolean) => {
updateOsModalVisible.value = val;
};
return {
// state
available,
availableWithRenewal,
checkForUpdatesLoading,
updateOsModalVisible,
changelogModalVisible,
releaseForUpdate,
updateOsIgnoredReleases,
// getters
availableReleaseDate,
availableRequiresAuth,
changelogUrl,
changelogPretty,
isReleaseForUpdateStable,
// actions
localCheckForUpdate,
cancelUpdate,
setModalOpen,
setReleaseForUpdate,
fetchAndConfirmInstall,
};
});