From 4c97a0104edf1b5ad337a6bc0dd644dc29c848e6 Mon Sep 17 00:00:00 2001 From: Anbraten Date: Tue, 31 Jan 2023 09:37:11 +0100 Subject: [PATCH] Hide not owned repos from sidebar and repo list (#1453) Co-authored-by: Lukas --- web/.eslintrc.js | 20 +++ web/components.d.ts | 42 ----- .../layout/header/ActivePipelines.vue | 22 +-- .../components/repo/settings/GeneralTab.vue | 4 +- web/src/compositions/useEvents.ts | 10 +- web/src/compositions/usePipelineFeed.ts | 6 +- web/src/lib/api/client.ts | 1 - web/src/store/pipelines.ts | 168 +++++++++--------- web/src/store/repos.ts | 74 ++++---- web/src/views/Repos.vue | 32 +--- web/src/views/ReposOwner.vue | 6 +- web/src/views/repo/RepoWrapper.vue | 14 +- .../views/repo/pipeline/PipelineWrapper.vue | 8 +- 13 files changed, 188 insertions(+), 219 deletions(-) diff --git a/web/.eslintrc.js b/web/.eslintrc.js index a5b4e0917..d5019b5bf 100644 --- a/web/.eslintrc.js +++ b/web/.eslintrc.js @@ -49,6 +49,26 @@ module.exports = { '@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-non-null-assertion': 'error', + // SOURCE: https://github.com/iamturns/eslint-config-airbnb-typescript/blob/4aec5702be5b4e74e0e2f40bc78b4bc961681de1/lib/shared.js#L41 + '@typescript-eslint/naming-convention': [ + 'error', + // Allow camelCase variables (23.2), PascalCase variables (23.8), and UPPER_CASE variables (23.10) + { + selector: 'variable', + format: ['camelCase', 'PascalCase', 'UPPER_CASE'], + leadingUnderscore: 'allow', + }, + // Allow camelCase functions (23.2), and PascalCase functions (23.8) + { + selector: 'function', + format: ['camelCase', 'PascalCase'], + }, + // Airbnb recommends PascalCase for classes (23.3), and although Airbnb does not make TypeScript recommendations, we are assuming this rule would similarly apply to anything "type like", including interfaces, type aliases, and enums + { + selector: 'typeLike', + format: ['PascalCase'], + }, + ], 'import/no-unresolved': 'off', // disable as this is handled by tsc itself 'import/first': 'error', diff --git a/web/components.d.ts b/web/components.d.ts index 0ae6cb8ec..32f425c69 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -22,51 +22,9 @@ declare module '@vue/runtime-core' { FluidContainer: typeof import('./src/components/layout/FluidContainer.vue')['default'] GeneralTab: typeof import('./src/components/repo/settings/GeneralTab.vue')['default'] Header: typeof import('./src/components/layout/scaffold/Header.vue')['default'] - IBiCheckCircleFill: typeof import('~icons/bi/check-circle-fill')['default'] - IBiCircle: typeof import('~icons/bi/circle')['default'] - IBiPlayCircleFill: typeof import('~icons/bi/play-circle-fill')['default'] - IBiSlashCircleFill: typeof import('~icons/bi/slash-circle-fill')['default'] - IBiStopCircleFill: typeof import('~icons/bi/stop-circle-fill')['default'] - IBiXCircleFill: typeof import('~icons/bi/x-circle-fill')['default'] - IBxBxPowerOff: typeof import('~icons/bx/bx-power-off')['default'] - ICarbonCloseOutline: typeof import('~icons/carbon/close-outline')['default'] - ICarbonInProgress: typeof import('~icons/carbon/in-progress')['default'] - IClarityDeployLine: typeof import('~icons/clarity/deploy-line')['default'] - IClaritySettingsSolid: typeof import('~icons/clarity/settings-solid')['default'] Icon: typeof import('./src/components/atomic/Icon.vue')['default'] IconButton: typeof import('./src/components/atomic/IconButton.vue')['default'] - IGgTrash: typeof import('~icons/gg/trash')['default'] - IIcBaselineDarkMode: typeof import('~icons/ic/baseline-dark-mode')['default'] - IIcBaselineDownloadForOffline: typeof import('~icons/ic/baseline-download-for-offline')['default'] - IIcBaselineEdit: typeof import('~icons/ic/baseline-edit')['default'] - IIcBaselineFileDownload: typeof import('~icons/ic/baseline-file-download')['default'] - IIcBaselineFileDownloadOff: typeof import('~icons/ic/baseline-file-download-off')['default'] - IIcBaselineHealing: typeof import('~icons/ic/baseline-healing')['default'] - IIcBaselinePlayArrow: typeof import('~icons/ic/baseline-play-arrow')['default'] - IIconoirArrowLeft: typeof import('~icons/iconoir/arrow-left')['default'] - IIconParkOutlineAlarmClock: typeof import('~icons/icon-park-outline/alarm-clock')['default'] - IIcRoundLightMode: typeof import('~icons/ic/round-light-mode')['default'] - IIcSharpTimelapse: typeof import('~icons/ic/sharp-timelapse')['default'] - IIcTwotoneAdd: typeof import('~icons/ic/twotone-add')['default'] - ILaTimes: typeof import('~icons/la/times')['default'] - IMdiBitbucket: typeof import('~icons/mdi/bitbucket')['default'] - IMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default'] - IMdiClockTimeEightOutline: typeof import('~icons/mdi/clock-time-eight-outline')['default'] - IMdiFormatListBulleted: typeof import('~icons/mdi/format-list-bulleted')['default'] - IMdiGestureTap: typeof import('~icons/mdi/gesture-tap')['default'] - IMdiGithub: typeof import('~icons/mdi/github')['default'] - IMdiLoading: typeof import('~icons/mdi/loading')['default'] - IMdiSync: typeof import('~icons/mdi/sync')['default'] - IMdiSourceBranch: typeof import('~icons/mdi/source-branch')['default'] - IMdisourceCommit: typeof import('~icons/mdi/source-commit')['default'] - IMdiSourcePull: typeof import('~icons/mdi/source-pull')['default'] - IMdiTagOutline: typeof import('~icons/mdi/tag-outline')['default'] InputField: typeof import('./src/components/form/InputField.vue')['default'] - IPhGitlabLogoSimpleFill: typeof import('~icons/ph/gitlab-logo-simple-fill')['default'] - ISimpleIconsGitea: typeof import('~icons/simple-icons/gitea')['default'] - ITeenyiconsGitSolid: typeof import('~icons/teenyicons/git-solid')['default'] - ITeenyiconsRefreshOutline: typeof import('~icons/teenyicons/refresh-outline')['default'] - IVaadinQuestionCircleO: typeof import('~icons/vaadin/question-circle-o')['default'] ListItem: typeof import('./src/components/atomic/ListItem.vue')['default'] ManualPipelinePopup: typeof import('./src/components/layout/popups/ManualPipelinePopup.vue')['default'] Navbar: typeof import('./src/components/layout/header/Navbar.vue')['default'] diff --git a/web/src/components/layout/header/ActivePipelines.vue b/web/src/components/layout/header/ActivePipelines.vue index 4da818434..dfb0c0fc2 100644 --- a/web/src/components/layout/header/ActivePipelines.vue +++ b/web/src/components/layout/header/ActivePipelines.vue @@ -14,26 +14,18 @@ - diff --git a/web/src/components/repo/settings/GeneralTab.vue b/web/src/components/repo/settings/GeneralTab.vue index a68cf3e8d..bae28a7ff 100644 --- a/web/src/components/repo/settings/GeneralTab.vue +++ b/web/src/components/repo/settings/GeneralTab.vue @@ -100,7 +100,7 @@ import { useAsyncAction } from '~/compositions/useAsyncAction'; import useAuthentication from '~/compositions/useAuthentication'; import useNotifications from '~/compositions/useNotifications'; import { Repo, RepoSettings, RepoVisibility, WebhookEvents } from '~/lib/api/types'; -import RepoStore from '~/store/repos'; +import { useRepoStore } from '~/store/repos'; export default defineComponent({ name: 'GeneralTab', @@ -111,7 +111,7 @@ export default defineComponent({ const apiClient = useApiClient(); const notifications = useNotifications(); const { user } = useAuthentication(); - const repoStore = RepoStore(); + const repoStore = useRepoStore(); const i18n = useI18n(); const repo = inject>('repo'); diff --git a/web/src/compositions/useEvents.ts b/web/src/compositions/useEvents.ts index 3c0c99d7b..f4fc292b5 100644 --- a/web/src/compositions/useEvents.ts +++ b/web/src/compositions/useEvents.ts @@ -1,6 +1,5 @@ -import PipelineStore from '~/store/pipelines'; -import RepoStore from '~/store/repos'; -import { repoSlug } from '~/utils/helpers'; +import { usePipelineStore } from '~/store/pipelines'; +import { useRepoStore } from '~/store/repos'; import useApiClient from './useApiClient'; @@ -11,8 +10,8 @@ export default () => { if (initialized) { return; } - const repoStore = RepoStore(); - const pipelineStore = PipelineStore(); + const repoStore = useRepoStore(); + const pipelineStore = usePipelineStore(); initialized = true; @@ -30,7 +29,6 @@ export default () => { } const { pipeline } = data; pipelineStore.setPipeline(repo.owner, repo.name, pipeline); - pipelineStore.setPipelineFeedItem({ ...pipeline, name: repo.name, owner: repo.owner, full_name: repoSlug(repo) }); // contains step update if (!data.step) { diff --git a/web/src/compositions/usePipelineFeed.ts b/web/src/compositions/usePipelineFeed.ts index 0916f7ece..17b445dd0 100644 --- a/web/src/compositions/usePipelineFeed.ts +++ b/web/src/compositions/usePipelineFeed.ts @@ -1,14 +1,14 @@ import { computed, toRef } from 'vue'; import useUserConfig from '~/compositions/useUserConfig'; -import PipelineStore from '~/store/pipelines'; +import { usePipelineStore } from '~/store/pipelines'; import useAuthentication from './useAuthentication'; const { userConfig, setUserConfig } = useUserConfig(); export default () => { - const pipelineStore = PipelineStore(); + const pipelineStore = usePipelineStore(); const { isAuthenticated } = useAuthentication(); const isOpen = computed(() => userConfig.value.isPipelineFeedOpen && !!isAuthenticated); @@ -17,7 +17,7 @@ export default () => { setUserConfig('isPipelineFeedOpen', !userConfig.value.isPipelineFeedOpen); } - const sortedPipelines = toRef(pipelineStore, 'sortedPipelineFeed'); + const sortedPipelines = toRef(pipelineStore, 'pipelineFeed'); const activePipelines = toRef(pipelineStore, 'activePipelines'); return { diff --git a/web/src/lib/api/client.ts b/web/src/lib/api/client.ts index e35c666ea..e384bddc1 100644 --- a/web/src/lib/api/client.ts +++ b/web/src/lib/api/client.ts @@ -108,7 +108,6 @@ export default class ApiClient { const query = encodeQueryString({ access_token: this.token || undefined, }); - // eslint-disable-next-line @typescript-eslint/naming-convention let _path = this.server ? this.server + path : path; _path = this.token ? `${path}?${query}` : path; diff --git a/web/src/store/pipelines.ts b/web/src/store/pipelines.ts index 8df59cd17..1387f49d4 100644 --- a/web/src/store/pipelines.ts +++ b/web/src/store/pipelines.ts @@ -1,99 +1,105 @@ import { defineStore } from 'pinia'; -import { computed, Ref, ref, toRef } from 'vue'; +import { computed, reactive, Ref, ref } from 'vue'; import useApiClient from '~/compositions/useApiClient'; import { Pipeline, PipelineFeed, PipelineStep } from '~/lib/api/types'; +import { useRepoStore } from '~/store/repos'; import { comparePipelines, isPipelineActive, repoSlug } from '~/utils/helpers'; -const apiClient = useApiClient(); +export const usePipelineStore = defineStore('pipelines', () => { + const apiClient = useApiClient(); + const repoStore = useRepoStore(); -export default defineStore({ - id: 'pipelines', + const pipelines: Map> = reactive(new Map()); - state: () => ({ - pipelines: {} as Record>, - pipelineFeed: [] as PipelineFeed[], - }), + function setPipeline(owner: string, repo: string, pipeline: Pipeline) { + const _repoSlug = repoSlug(owner, repo); + const repoPipelines = pipelines.get(_repoSlug) || new Map(); + repoPipelines.set(pipeline.number, pipeline); + pipelines.set(_repoSlug, repoPipelines); + } - getters: { - sortedPipelineFeed(state) { - return state.pipelineFeed.sort(comparePipelines); - }, - activePipelines(state) { - return state.pipelineFeed.filter(isPipelineActive); - }, - }, + function getRepoPipelines(owner: Ref, repo: Ref) { + return computed(() => { + const slug = repoSlug(owner.value, repo.value); + return Array.from(pipelines.get(slug)?.values() || []).sort(comparePipelines); + }); + } - actions: { - // setters - setPipeline(owner: string, repo: string, pipeline: Pipeline) { - // eslint-disable-next-line @typescript-eslint/naming-convention - const _repoSlug = repoSlug(owner, repo); - if (!this.pipelines[_repoSlug]) { - this.pipelines[_repoSlug] = {}; - } + function getPipeline(owner: Ref, repo: Ref, _pipelineNumber: Ref) { + return computed(() => { + const slug = repoSlug(owner.value, repo.value); + const pipelineNumber = parseInt(_pipelineNumber.value, 10); + return pipelines.get(slug)?.get(pipelineNumber); + }); + } - const repoPipelines = this.pipelines[_repoSlug]; + function setStep(owner: string, repo: string, pipelineNumber: number, step: PipelineStep) { + const pipeline = getPipeline(ref(owner), ref(repo), ref(pipelineNumber.toString())).value; + if (!pipeline) { + throw new Error("Can't find pipeline"); + } - // merge with available data - repoPipelines[pipeline.number] = { ...(repoPipelines[pipeline.number] || {}), ...pipeline }; + if (!pipeline.steps) { + pipeline.steps = []; + } - this.pipelines = { - ...this.pipelines, - [_repoSlug]: repoPipelines, - }; - }, - setStep(owner: string, repo: string, pipelineNumber: number, step: PipelineStep) { - const pipeline = this.getPipeline(ref(owner), ref(repo), ref(pipelineNumber.toString())).value; - if (!pipeline) { - throw new Error("Can't find pipeline"); - } + pipeline.steps = [...pipeline.steps.filter((p) => p.pid !== step.pid), step]; + setPipeline(owner, repo, pipeline); + } - if (!pipeline.steps) { - pipeline.steps = []; - } + async function loadRepoPipelines(owner: string, repo: string) { + const _pipelines = await apiClient.getPipelineList(owner, repo); + _pipelines.forEach((pipeline) => { + setPipeline(owner, repo, pipeline); + }); + } - pipeline.steps = [...pipeline.steps.filter((p) => p.pid !== step.pid), step]; - this.setPipeline(owner, repo, pipeline); - }, - setPipelineFeedItem(pipeline: PipelineFeed) { - const pipelineFeed = this.pipelineFeed.filter((b) => b.id !== pipeline.id); - this.pipelineFeed = [...pipelineFeed, pipeline]; - }, + async function loadPipeline(owner: string, repo: string, pipelinesNumber: number) { + const pipeline = await apiClient.getPipeline(owner, repo, pipelinesNumber); + setPipeline(owner, repo, pipeline); + } - // getters - getPipelines(owner: Ref, repo: Ref) { - return computed(() => { - const slug = repoSlug(owner.value, repo.value); - return toRef(this.pipelines, slug).value; - }); - }, - getSortedPipelines(owner: Ref, repo: Ref) { - return computed(() => Object.values(this.getPipelines(owner, repo).value || []).sort(comparePipelines)); - }, - getActivePipelines(owner: Ref, repo: Ref) { - const pipelines = this.getPipelines(owner, repo); - return computed(() => Object.values(pipelines.value).filter(isPipelineActive)); - }, - getPipeline(owner: Ref, repo: Ref, pipelineNumber: Ref) { - const pipelines = this.getPipelines(owner, repo); - return computed(() => (pipelines.value || {})[parseInt(pipelineNumber.value, 10)]); - }, + const pipelineFeed = computed(() => + Array.from(pipelines.entries()) + .reduce((acc, [_repoSlug, repoPipelines]) => { + const repoPipelinesArray = Array.from(repoPipelines.entries()).map( + ([_pipelineNumber, pipeline]) => + { + ...pipeline, + full_name: _repoSlug, + owner: _repoSlug.split('/')[0], + name: _repoSlug.split('/')[1], + number: _pipelineNumber, + }, + ); + return [...acc, ...repoPipelinesArray]; + }, []) + .sort(comparePipelines) + .filter((pipeline) => repoStore.ownedRepoSlugs.includes(pipeline.full_name)), + ); - // loading - async loadPipelines(owner: string, repo: string) { - const pipelines = await apiClient.getPipelineList(owner, repo); - pipelines.forEach((pipeline) => { - this.setPipeline(owner, repo, pipeline); - }); - }, - async loadPipeline(owner: string, repo: string, pipelinesNumber: number) { - const pipelines = await apiClient.getPipeline(owner, repo, pipelinesNumber); - this.setPipeline(owner, repo, pipelines); - }, - async loadPipelineFeed() { - const pipelines = await apiClient.getPipelineFeed(); - this.pipelineFeed = pipelines; - }, - }, + const activePipelines = computed(() => pipelineFeed.value.filter(isPipelineActive)); + + async function loadPipelineFeed() { + await repoStore.loadRepos(); + + const _pipelines = await apiClient.getPipelineFeed(); + _pipelines.forEach((pipeline) => { + setPipeline(pipeline.owner, pipeline.name, pipeline); + }); + } + + return { + pipelines, + setPipeline, + setStep, + getRepoPipelines, + getPipeline, + loadRepoPipelines, + loadPipeline, + activePipelines, + pipelineFeed, + loadPipelineFeed, + }; }); diff --git a/web/src/store/repos.ts b/web/src/store/repos.ts index b975c6b48..ff70de560 100644 --- a/web/src/store/repos.ts +++ b/web/src/store/repos.ts @@ -1,44 +1,54 @@ import { defineStore } from 'pinia'; -import { computed, Ref, toRef } from 'vue'; +import { computed, reactive, Ref, ref } from 'vue'; import useApiClient from '~/compositions/useApiClient'; import { Repo } from '~/lib/api/types'; import { repoSlug } from '~/utils/helpers'; -const apiClient = useApiClient(); +export const useRepoStore = defineStore('repos', () => { + const apiClient = useApiClient(); -export default defineStore({ - id: 'repos', + const repos: Map = reactive(new Map()); + const ownedRepoSlugs = ref([]); - state: () => ({ - repos: {} as Record, - }), + const ownedRepos = computed(() => + Array.from(repos.entries()) + .filter(([slug]) => ownedRepoSlugs.value.includes(slug)) + .map(([, repo]) => repo), + ); - actions: { - // getter - getRepo(owner: Ref, name: Ref) { - return computed(() => { - const slug = repoSlug(owner.value, name.value); - return toRef(this.repos, slug).value; - }); - }, + function getRepo(owner: Ref, name: Ref) { + return computed(() => { + const slug = repoSlug(owner.value, name.value); + return repos.get(slug); + }); + } - // setter - setRepo(repo: Repo) { - this.repos[repoSlug(repo)] = repo; - }, + function setRepo(repo: Repo) { + repos.set(repoSlug(repo), repo); + } - // loading - async loadRepo(owner: string, name: string) { - const repo = await apiClient.getRepo(owner, name); - this.repos[repoSlug(repo)] = repo; - return repo; - }, - async loadRepos() { - const repos = await apiClient.getRepoList(); - repos.forEach((repo) => { - this.repos[repoSlug(repo.owner, repo.name)] = repo; - }); - }, - }, + async function loadRepo(owner: string, name: string) { + const repo = await apiClient.getRepo(owner, name); + repos.set(repoSlug(repo), repo); + return repo; + } + + async function loadRepos() { + const _ownedRepos = await apiClient.getRepoList(); + _ownedRepos.forEach((repo) => { + repos.set(repoSlug(repo), repo); + }); + ownedRepoSlugs.value = _ownedRepos.map((repo) => repoSlug(repo)); + } + + return { + repos, + ownedRepos, + ownedRepoSlugs, + getRepo, + setRepo, + loadRepo, + loadRepos, + }; }); diff --git a/web/src/views/Repos.vue b/web/src/views/Repos.vue index 8231b45f8..2f5fb727e 100644 --- a/web/src/views/Repos.vue +++ b/web/src/views/Repos.vue @@ -20,36 +20,22 @@ - diff --git a/web/src/views/ReposOwner.vue b/web/src/views/ReposOwner.vue index b8f8cd040..98cd04940 100644 --- a/web/src/views/ReposOwner.vue +++ b/web/src/views/ReposOwner.vue @@ -37,7 +37,7 @@ import Scaffold from '~/components/layout/scaffold/Scaffold.vue'; import useApiClient from '~/compositions/useApiClient'; import { useRepoSearch } from '~/compositions/useRepoSearch'; import { OrgPermissions } from '~/lib/api/types'; -import RepoStore from '~/store/repos'; +import { useRepoStore } from '~/store/repos'; export default defineComponent({ name: 'ReposOwner', @@ -57,9 +57,9 @@ export default defineComponent({ setup(props) { const apiClient = useApiClient(); - const repoStore = RepoStore(); + const repoStore = useRepoStore(); // TODO: filter server side - const repos = computed(() => Object.values(repoStore.repos).filter((v) => v.owner === props.repoOwner)); + const repos = computed(() => Array.from(repoStore.repos.values()).filter((repo) => repo.owner === props.repoOwner)); const search = ref(''); const orgPermissions = ref({ member: false, admin: false }); diff --git a/web/src/views/repo/RepoWrapper.vue b/web/src/views/repo/RepoWrapper.vue index e27e1ec4a..22cbe8e61 100644 --- a/web/src/views/repo/RepoWrapper.vue +++ b/web/src/views/repo/RepoWrapper.vue @@ -64,8 +64,8 @@ import useAuthentication from '~/compositions/useAuthentication'; import useConfig from '~/compositions/useConfig'; import useNotifications from '~/compositions/useNotifications'; import { RepoPermissions } from '~/lib/api/types'; -import PipelineStore from '~/store/pipelines'; -import RepoStore from '~/store/repos'; +import { usePipelineStore } from '~/store/pipelines'; +import { useRepoStore } from '~/store/repos'; const props = defineProps({ repoOwner: { @@ -81,8 +81,8 @@ const props = defineProps({ const repoOwner = toRef(props, 'repoOwner'); const repoName = toRef(props, 'repoName'); -const repoStore = RepoStore(); -const pipelineStore = PipelineStore(); +const repoStore = useRepoStore(); +const pipelineStore = usePipelineStore(); const apiClient = useApiClient(); const notifications = useNotifications(); const { isAuthenticated } = useAuthentication(); @@ -93,7 +93,7 @@ const i18n = useI18n(); const { forge } = useConfig(); const repo = repoStore.getRepo(repoOwner, repoName); const repoPermissions = ref(); -const pipelines = pipelineStore.getSortedPipelines(repoOwner, repoName); +const pipelines = pipelineStore.getRepoPipelines(repoOwner, repoName); provide('repo', repo); provide('repo-permissions', repoPermissions); provide('pipelines', pipelines); @@ -121,7 +121,7 @@ async function loadRepo() { }); return; } - await pipelineStore.loadPipelines(repoOwner.value, repoName.value); + await pipelineStore.loadRepoPipelines(repoOwner.value, repoName.value); } onMounted(() => { @@ -132,7 +132,7 @@ watch([repoOwner, repoName], () => { loadRepo(); }); -const badgeUrl = computed(() => `/api/badges/${repo.value.owner}/${repo.value.name}/status.svg`); +const badgeUrl = computed(() => repo.value && `/api/badges/${repo.value.owner}/${repo.value.name}/status.svg`); const activeTab = computed({ get() { diff --git a/web/src/views/repo/pipeline/PipelineWrapper.vue b/web/src/views/repo/pipeline/PipelineWrapper.vue index 3b0836f75..150afe6a0 100644 --- a/web/src/views/repo/pipeline/PipelineWrapper.vue +++ b/web/src/views/repo/pipeline/PipelineWrapper.vue @@ -92,7 +92,7 @@ import useNotifications from '~/compositions/useNotifications'; import usePipeline from '~/compositions/usePipeline'; import { useRouteBackOrDefault } from '~/compositions/useRouteBackOrDefault'; import { Repo, RepoPermissions } from '~/lib/api/types'; -import PipelineStore from '~/store/pipelines'; +import { usePipelineStore } from '~/store/pipelines'; export default defineComponent({ name: 'PipelineWrapper', @@ -130,7 +130,7 @@ export default defineComponent({ const favicon = useFavicon(); const i18n = useI18n(); - const pipelineStore = PipelineStore(); + const pipelineStore = usePipelineStore(); const pipelineId = toRef(props, 'pipelineId'); const repoOwner = toRef(props, 'repoOwner'); const repoName = toRef(props, 'repoName'); @@ -155,7 +155,7 @@ export default defineComponent({ await pipelineStore.loadPipeline(repo.value.owner, repo.value.name, parseInt(pipelineId.value, 10)); - favicon.updateStatus(pipeline.value.status); + favicon.updateStatus(pipeline.value?.status); } const { doSubmit: cancelPipeline, isLoading: isCancelingPipeline } = useAsyncAction(async () => { @@ -163,7 +163,7 @@ export default defineComponent({ throw new Error('Unexpected: Repo is undefined'); } - if (!pipeline.value.steps) { + if (!pipeline.value?.steps) { throw new Error('Unexpected: Pipeline steps not loaded'); }