From 1518f4ed123ae5f4f094e7a335b1d1400949265d Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 8 Dec 2024 03:58:18 +0100 Subject: [PATCH] Fix typescript errors in Vue files, fix regression in "Recent Commits" chart (#32649) - Fix all typescript errors in `.vue` files - Fix regression from https://github.com/go-gitea/gitea/pull/32329 where "Recent Commits" chart would not render. --------- Co-authored-by: wxiaoguang --- web_src/js/components/DashboardRepoList.vue | 17 ++++++++--- web_src/js/components/DiffCommitSelector.vue | 14 ++++----- web_src/js/components/RepoCodeFrequency.vue | 14 +++++---- web_src/js/components/RepoContributors.vue | 31 ++++++++++++-------- web_src/js/components/RepoRecentCommits.vue | 14 +++++---- web_src/js/utils/time.ts | 6 +++- 6 files changed, 59 insertions(+), 37 deletions(-) diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue index 3d3ac2fc69..41793d60ed 100644 --- a/web_src/js/components/DashboardRepoList.vue +++ b/web_src/js/components/DashboardRepoList.vue @@ -6,8 +6,17 @@ import {fomanticQuery} from '../modules/fomantic/base.ts'; const {appSubUrl, assetUrlPrefix, pageData} = window.config; +type CommitStatus = 'pending' | 'success' | 'error' | 'failure' | 'warning'; + +type CommitStatusMap = { + [status in CommitStatus]: { + name: string, + color: string, + }; +}; + // make sure this matches templates/repo/commit_status.tmpl -const commitStatus = { +const commitStatus: CommitStatusMap = { pending: {name: 'octicon-dot-fill', color: 'yellow'}, success: {name: 'octicon-check', color: 'green'}, error: {name: 'gitea-exclamation', color: 'red'}, @@ -281,18 +290,18 @@ const sfc = { return 'octicon-repo'; }, - statusIcon(status) { + statusIcon(status: CommitStatus) { return commitStatus[status].name; }, - statusColor(status) { + statusColor(status: CommitStatus) { return commitStatus[status].color; }, reposFilterKeyControl(e) { switch (e.key) { case 'Enter': - document.querySelector('.repo-owner-name-list li.active a')?.click(); + document.querySelector('.repo-owner-name-list li.active a')?.click(); break; case 'ArrowUp': if (this.activeIndex > 0) { diff --git a/web_src/js/components/DiffCommitSelector.vue b/web_src/js/components/DiffCommitSelector.vue index c78531cf9f..3a394955ca 100644 --- a/web_src/js/components/DiffCommitSelector.vue +++ b/web_src/js/components/DiffCommitSelector.vue @@ -14,7 +14,7 @@ export default { issueLink: el.getAttribute('data-issuelink'), locale: { filter_changes_by_commit: el.getAttribute('data-filter_changes_by_commit'), - }, + } as Record, commits: [], hoverActivated: false, lastReviewCommitSha: null, @@ -41,16 +41,16 @@ export default { this.$el.removeEventListener('keyup', this.onKeyUp); }, methods: { - onBodyClick(event) { + onBodyClick(event: MouseEvent) { // close this menu on click outside of this element when the dropdown is currently visible opened if (this.$el.contains(event.target)) return; if (this.menuVisible) { this.toggleMenu(); } }, - onKeyDown(event) { + onKeyDown(event: KeyboardEvent) { if (!this.menuVisible) return; - const item = document.activeElement; + const item = document.activeElement as HTMLElement; if (!this.$el.contains(item)) return; switch (event.key) { case 'ArrowDown': // select next element @@ -73,7 +73,7 @@ export default { if (commitIdx) this.highlight(this.commits[commitIdx]); } }, - onKeyUp(event) { + onKeyUp(event: KeyboardEvent) { if (!this.menuVisible) return; const item = document.activeElement; if (!this.$el.contains(item)) return; @@ -95,7 +95,7 @@ export default { } }, /** Focus given element */ - focusElem(elem, prevElem) { + focusElem(elem: HTMLElement, prevElem: HTMLElement) { if (elem) { elem.tabIndex = 0; if (prevElem) prevElem.tabIndex = -1; @@ -149,7 +149,7 @@ export default { window.location.assign(`${this.issueLink}/files/${this.lastReviewCommitSha}..${this.commits.at(-1).id}${this.queryParams}`); }, /** Clicking on a single commit opens this specific commit */ - commitClicked(commitId, newWindow = false) { + commitClicked(commitId: string, newWindow = false) { const url = `${this.issueLink}/commits/${commitId}${this.queryParams}`; if (newWindow) { window.open(url); diff --git a/web_src/js/components/RepoCodeFrequency.vue b/web_src/js/components/RepoCodeFrequency.vue index eaff8ae0af..7696996cf6 100644 --- a/web_src/js/components/RepoCodeFrequency.vue +++ b/web_src/js/components/RepoCodeFrequency.vue @@ -8,6 +8,8 @@ import { PointElement, LineElement, Filler, + type ChartOptions, + type ChartData, } from 'chart.js'; import {GET} from '../modules/fetch.ts'; import {Line as ChartLine} from 'vue-chartjs'; @@ -16,6 +18,7 @@ import { firstStartDateAfterDate, fillEmptyStartDaysWithZeroes, type DayData, + type DayDataObject, } from '../utils/time.ts'; import {chartJsColors} from '../utils/color.ts'; import {sleep} from '../utils.ts'; @@ -64,12 +67,12 @@ async function fetchGraphData() { } } while (response.status === 202); if (response.ok) { - data.value = await response.json(); - const weekValues = Object.values(data.value); + const dayDataObject: DayDataObject = await response.json(); + const weekValues = Object.values(dayDataObject); const start = weekValues[0].week; const end = firstStartDateAfterDate(new Date()); const startDays = startDaysBetween(start, end); - data.value = fillEmptyStartDaysWithZeroes(startDays, data.value); + data.value = fillEmptyStartDaysWithZeroes(startDays, dayDataObject); errorText.value = ''; } else { errorText.value = response.statusText; @@ -81,7 +84,7 @@ async function fetchGraphData() { } } -function toGraphData(data) { +function toGraphData(data: Array>): ChartData<'line'> { return { datasets: [ { @@ -108,10 +111,9 @@ function toGraphData(data) { }; } -const options = { +const options: ChartOptions<'line'> = { responsive: true, maintainAspectRatio: false, - animation: true, plugins: { legend: { display: true, diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue index f42278ef6b..97678b9a13 100644 --- a/web_src/js/components/RepoContributors.vue +++ b/web_src/js/components/RepoContributors.vue @@ -9,6 +9,9 @@ import { PointElement, LineElement, Filler, + type ChartOptions, + type ChartData, + type Plugin, } from 'chart.js'; import {GET} from '../modules/fetch.ts'; import zoomPlugin from 'chartjs-plugin-zoom'; @@ -22,8 +25,9 @@ import {chartJsColors} from '../utils/color.ts'; import {sleep} from '../utils.ts'; import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm'; import {fomanticQuery} from '../modules/fomantic/base.ts'; +import type {Entries} from 'type-fest'; -const customEventListener = { +const customEventListener: Plugin = { id: 'customEventListener', afterEvent: (chart, args, opts) => { // event will be replayed from chart.update when reset zoom, @@ -65,10 +69,10 @@ export default { data: () => ({ isLoading: false, errorText: '', - totalStats: {}, - sortedContributors: {}, + totalStats: {} as Record, + sortedContributors: {} as Record, type: 'commits', - contributorsStats: [], + contributorsStats: {} as Record, xAxisStart: null, xAxisEnd: null, xAxisMin: null, @@ -99,7 +103,7 @@ export default { async fetchGraphData() { this.isLoading = true; try { - let response; + let response: Response; do { response = await GET(`${this.repoLink}/activity/contributors/data`); if (response.status === 202) { @@ -112,7 +116,7 @@ export default { // below line might be deleted if we are sure go produces map always sorted by keys total.weeks = Object.fromEntries(Object.entries(total.weeks).sort()); - const weekValues = Object.values(total.weeks); + const weekValues = Object.values(total.weeks) as any; this.xAxisStart = weekValues[0].week; this.xAxisEnd = firstStartDateAfterDate(new Date()); const startDays = startDaysBetween(this.xAxisStart, this.xAxisEnd); @@ -120,7 +124,7 @@ export default { this.xAxisMin = this.xAxisStart; this.xAxisMax = this.xAxisEnd; this.contributorsStats = {}; - for (const [email, user] of Object.entries(rest)) { + for (const [email, user] of Object.entries(rest) as Entries>>) { user.weeks = fillEmptyStartDaysWithZeroes(startDays, user.weeks); this.contributorsStats[email] = user; } @@ -146,7 +150,7 @@ export default { user.total_additions = 0; user.total_deletions = 0; user.max_contribution_type = 0; - const filteredWeeks = user.weeks.filter((week) => { + const filteredWeeks = user.weeks.filter((week: Record) => { const oneWeek = 7 * 24 * 60 * 60 * 1000; if (week.week >= this.xAxisMin - oneWeek && week.week <= this.xAxisMax + oneWeek) { user.total_commits += week.commits; @@ -195,7 +199,7 @@ export default { return (1 - (coefficient % 1)) * 10 ** exp + maxValue; }, - toGraphData(data) { + toGraphData(data: Array>): ChartData<'line'> { return { datasets: [ { @@ -211,9 +215,9 @@ export default { }; }, - updateOtherCharts(event, reset) { - const minVal = event.chart.options.scales.x.min; - const maxVal = event.chart.options.scales.x.max; + updateOtherCharts({chart}: {chart: Chart}, reset?: boolean = false) { + const minVal = chart.options.scales.x.min; + const maxVal = chart.options.scales.x.max; if (reset) { this.xAxisMin = this.xAxisStart; this.xAxisMax = this.xAxisEnd; @@ -225,7 +229,7 @@ export default { } }, - getOptions(type) { + getOptions(type: string): ChartOptions<'line'> { return { responsive: true, maintainAspectRatio: false, @@ -238,6 +242,7 @@ export default { position: 'top', align: 'center', }, + // @ts-expect-error: bug in chart.js types customEventListener: { chartType: type, instance: this, diff --git a/web_src/js/components/RepoRecentCommits.vue b/web_src/js/components/RepoRecentCommits.vue index 8d2a14cd2c..10e1fdd70c 100644 --- a/web_src/js/components/RepoRecentCommits.vue +++ b/web_src/js/components/RepoRecentCommits.vue @@ -7,6 +7,7 @@ import { LinearScale, TimeScale, type ChartOptions, + type ChartData, } from 'chart.js'; import {GET} from '../modules/fetch.ts'; import {Bar} from 'vue-chartjs'; @@ -15,6 +16,7 @@ import { firstStartDateAfterDate, fillEmptyStartDaysWithZeroes, type DayData, + type DayDataObject, } from '../utils/time.ts'; import {chartJsColors} from '../utils/color.ts'; import {sleep} from '../utils.ts'; @@ -61,11 +63,11 @@ async function fetchGraphData() { } } while (response.status === 202); if (response.ok) { - const data = await response.json(); - const start = Object.values(data)[0].week; + const dayDataObj: DayDataObject = await response.json(); + const start = Object.values(dayDataObj)[0].week; const end = firstStartDateAfterDate(new Date()); const startDays = startDaysBetween(start, end); - data.value = fillEmptyStartDaysWithZeroes(startDays, data).slice(-52); + data.value = fillEmptyStartDaysWithZeroes(startDays, dayDataObj).slice(-52); errorText.value = ''; } else { errorText.value = response.statusText; @@ -77,10 +79,11 @@ async function fetchGraphData() { } } -function toGraphData(data) { +function toGraphData(data: DayData[]): ChartData<'bar'> { return { datasets: [ { + // @ts-expect-error -- bar chart expects one-dimensional data, but apparently x/y still works data: data.map((i) => ({x: i.week, y: i.commits})), label: 'Commits', backgroundColor: chartJsColors['commits'], @@ -91,10 +94,9 @@ function toGraphData(data) { }; } -const options = { +const options: ChartOptions<'bar'> = { responsive: true, maintainAspectRatio: false, - animation: true, scales: { x: { type: 'time', diff --git a/web_src/js/utils/time.ts b/web_src/js/utils/time.ts index c661155442..6951ebfedb 100644 --- a/web_src/js/utils/time.ts +++ b/web_src/js/utils/time.ts @@ -49,7 +49,11 @@ export type DayData = { commits: number, } -export function fillEmptyStartDaysWithZeroes(startDays: number[], data: DayData[]): DayData[] { +export type DayDataObject = { + [timestamp: string]: DayData, +} + +export function fillEmptyStartDaysWithZeroes(startDays: number[], data: DayDataObject): DayData[] { const result = {}; for (const startDay of startDays) {