diff --git a/src-angular/app/components/browse/chart-sidebar/chart-sidebar.component.html b/src-angular/app/components/browse/chart-sidebar/chart-sidebar.component.html index 010397d..144e770 100644 --- a/src-angular/app/components/browse/chart-sidebar/chart-sidebar.component.html +++ b/src-angular/app/components/browse/chart-sidebar/chart-sidebar.component.html @@ -42,6 +42,40 @@
diff --git a/src-angular/app/components/browse/chart-sidebar/chart-sidebar.component.ts b/src-angular/app/components/browse/chart-sidebar/chart-sidebar.component.ts
index 87b1671..a038fb4 100644
--- a/src-angular/app/components/browse/chart-sidebar/chart-sidebar.component.ts
+++ b/src-angular/app/components/browse/chart-sidebar/chart-sidebar.component.ts
@@ -1,13 +1,13 @@
import { Component, ElementRef, HostBinding, OnInit, Renderer2, ViewChild } from '@angular/core'
import { FormControl } from '@angular/forms'
-import { chain, compact, flatMap, intersection, round, sortBy } from 'lodash'
-import { Difficulty, Instrument } from 'scan-chart'
+import { capitalize, chain, compact, flatMap, intersection, round, sortBy } from 'lodash'
+import { ChartIssueType, Difficulty, FolderIssueType, Instrument, MetadataIssueType, NoteIssueType, TrackIssueType } from 'scan-chart'
import { SearchService } from 'src-angular/app/core/services/search.service'
import { SettingsService } from 'src-angular/app/core/services/settings.service'
import { ChartData } from 'src-shared/interfaces/search.interface'
import { setlistNames } from 'src-shared/setlist-names'
-import { difficulties, difficultyDisplay, driveLink, instruments, msToRoughTime, removeStyleTags, shortInstrumentDisplay } from 'src-shared/UtilFunctions'
+import { difficulties, difficultyDisplay, driveLink, hasIssues, instruments, msToRoughTime, removeStyleTags, shortInstrumentDisplay } from 'src-shared/UtilFunctions'
@Component({
selector: 'app-chart-sidebar',
@@ -91,6 +91,103 @@ export class ChartSidebarComponent implements OnInit {
return round((this.selectedChart!.notesData.length - this.selectedChart!.notesData.effectiveLength) / 1000, 1)
}
+ public get hasIssues() {
+ return hasIssues(this.selectedChart!)
+ }
+
+ public get metadataIssues() {
+ return this.selectedChart!.metadataIssues
+ }
+ public getMetadataIssueText(issue: MetadataIssueType) {
+ switch (issue) {
+ case 'noName': return 'Chart has no name'
+ case 'noArtist': return 'Chart has no artist'
+ case 'noAlbum': return 'Chart has no album'
+ case 'noGenre': return 'Chart has no genre'
+ case 'noYear': return 'Chart has no year'
+ case 'noCharter': return 'Chart has no charter'
+ case 'missingInstrumentDiff': return 'Metadata is missing an instrument intensity rating'
+ case 'extraInstrumentDiff': return 'Metadata contains an instrument intensity rating for an uncharted instrument'
+ case 'nonzeroDelay': return 'Chart uses "delay" for the audio offset'
+ case 'drumsSetTo4And5Lane': return 'It is unclear if the drums chart is intended to be 4-lane or 5-lane'
+ case 'nonzeroOffset': return 'Chart uses "delay" for the audio offset'
+ }
+ }
+ public get folderIssues() {
+ return chain(this.selectedChart!.folderIssues)
+ .filter(i => !['albumArtSize', 'invalidIni', 'multipleVideo', 'badIniLine'].includes(i.folderIssue))
+ .map(i => i.folderIssue)
+ .uniq()
+ .value()
+ }
+ public getFolderIssueText(folderIssue: FolderIssueType) {
+ switch (folderIssue) {
+ case 'noMetadata': return `Metadata file is missing`
+ case 'invalidMetadata': return `Metadata file is invalid`
+ case 'multipleIniFiles': return `Multiple metadata files`
+ case 'noAlbumArt': return `Album art is missing`
+ case 'badAlbumArt': return `Album art is invalid`
+ case 'multipleAlbumArt': return `Multiple album art files`
+ case 'noAudio': return `Audio file is missing`
+ case 'invalidAudio': return `Audio file is invalid`
+ case 'badAudio': return `Audio file is invalid`
+ case 'multipleAudio': return `Audio file is invalid`
+ case 'noChart': return `Notes file is missing`
+ case 'invalidChart': return `Notes file is invalid`
+ case 'badChart': return `Notes file is invalid`
+ case 'multipleChart': return `Multiple notes files`
+ case 'badVideo': return `Video background won't work on Linux`
+ }
+ }
+
+ public get chartIssues() {
+ return this.selectedChart!.notesData?.chartIssues.filter(i => i !== 'isDefaultBPM')
+ }
+ public getChartIssueText(issue: ChartIssueType) {
+ switch (issue) {
+ case 'noResolution': return 'No resolution in chart file'
+ case 'noSyncTrackSection': return 'No tempo map in chart file'
+ case 'noNotes': return 'No notes in chart file'
+ case 'noExpert': return 'Expert is not charted'
+ case 'misalignedTimeSignatures': return 'Broken time signatures'
+ case 'noSections': return 'No sections'
+ }
+ }
+
+ public get trackIssuesGroups() {
+ return chain([
+ ...this.selectedChart!.notesData.trackIssues.map(i => ({ ...i, issues: i.trackIssues })),
+ ...this.selectedChart!.notesData.noteIssues.map(i => ({ ...i, issues: i.noteIssues.map(ni => ni.issueType) })),
+ ])
+ .sortBy(g => instruments.indexOf(g.instrument), g => difficulties.indexOf(g.difficulty))
+ .groupBy(g => `${capitalize(g.instrument)} - ${capitalize(g.difficulty)} Issues Found:`)
+ .toPairs()
+ .map(([groupName, group]) => ({
+ groupName,
+ issues: chain(group)
+ .flatMap(g => g.issues)
+ .filter(i => i !== 'babySustain' && i !== 'noNotesOnNonemptyTrack')
+ .uniq()
+ .value(),
+ }))
+ .value()
+ }
+
+ public getTrackIssueText(issue: NoteIssueType | TrackIssueType) {
+ switch (issue) {
+ case 'babySustain': return 'Has baby sustains'
+ case 'badSustainGap': return 'Has sustain gaps that are too small'
+ case 'brokenNote': return 'Has broken notes'
+ case 'difficultyForbiddenNote': return 'Has notes not allowed on this difficulty'
+ case 'fiveNoteChord': return 'Has five-note chords'
+ case 'noDrumActivationLanes': return 'Has no activation lanes'
+ case 'has4And5LaneFeatures': return 'Has a mix of 4 and 5 lane features on the drum chart'
+ case 'noStarPower': return 'Has no star power'
+ case 'smallLeadingSilence': return 'Leading silence is too small'
+ case 'threeNoteDrumChord': return 'Has three-note drum chords'
+ }
+ }
+
public get boolProperties(): ({ value: boolean; text: string })[] {
const notesData = this.selectedChart!.notesData
const showGuitarlikeProperties = intersection(this.instruments, this.guitarlikeInstruments).length > 0
diff --git a/src-angular/styles.scss b/src-angular/styles.scss
index b00c78a..7cdd07b 100644
--- a/src-angular/styles.scss
+++ b/src-angular/styles.scss
@@ -13,3 +13,7 @@ input[type='number'] {
user-select: none;
-webkit-user-drag: none;
}
+
+.dropdown:not(:hover):not(:focus):not(:focus-within):not(.dropdown-open) .dropdown-content {
+ height: 0px;
+}
diff --git a/src-shared/UtilFunctions.ts b/src-shared/UtilFunctions.ts
index e03c9d2..bac6d06 100644
--- a/src-shared/UtilFunctions.ts
+++ b/src-shared/UtilFunctions.ts
@@ -2,6 +2,8 @@ import _ from 'lodash'
import sanitize from 'sanitize-filename'
import { Difficulty, Instrument } from 'scan-chart'
+import { ChartData } from './interfaces/search.interface'
+
// WARNING: do not import anything related to Electron; the code will not compile correctly.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -166,3 +168,25 @@ export function removeStyleTags(text: string) {
} while (newText !== oldText)
return newText
}
+
+export function hasIssues(chart: Pick