modifiche face offline e 2 secondi di attesa

This commit is contained in:
David Frassi
2026-07-13 17:55:07 +02:00
parent 333858bf48
commit 9f9b164937
20 changed files with 936 additions and 128 deletions
@@ -1,4 +1,4 @@
import { Component, OnInit, ViewChild, ElementRef, inject } from '@angular/core';
import { Component, OnInit, OnDestroy, ViewChild, ElementRef, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule, ToastController, IonTextarea, PopoverController, NavController, AlertController } from '@ionic/angular';
@@ -9,6 +9,7 @@ import { PlaylistService } from '../../services/playlist.service';
import { ThemeService } from '../../services/theme.service';
import { ActivatedRoute, RouterModule, Router } from '@angular/router';
import { LyricsParserService, ParsedSection } from '../../services/lyrics-parser.service';
import { YoutubePlayerService } from '../../services/youtube-player.service';
@Component({
selector: 'app-propose-canto',
@@ -17,8 +18,9 @@ import { LyricsParserService, ParsedSection } from '../../services/lyrics-parser
standalone: true,
imports: [CommonModule, FormsModule, IonicModule, RouterModule]
})
export class ProposeCantoPage implements OnInit {
@ViewChild('contentTextarea', { static: false }) contentTextarea!: IonTextarea;
export class ProposeCantoPage implements OnInit, OnDestroy {
@ViewChild('nativeTextarea', { static: false }) nativeTextarea!: ElementRef<HTMLTextAreaElement>;
@ViewChild('cameraInput', { static: false }) cameraInput!: ElementRef;
@ViewChild('fileInput', { static: false }) fileInput!: ElementRef;
@@ -31,12 +33,154 @@ export class ProposeCantoPage implements OnInit {
private router = inject(Router);
public lyricsParser = inject(LyricsParserService);
private alertCtrl = inject(AlertController);
public youtubePlayerService = inject(YoutubePlayerService);
showChordsPreview: boolean = true;
activeTab: string = 'editor';
transposeAmount: number = 0;
get highlightedHtml(): string {
if (!this.content) return '';
let escaped = this.content
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
escaped = escaped.replace(/\[([^\]]+)\]/g, (match, chord) => {
const transposed = this.transposeAmount !== 0
? this.lyricsParser.transposeChord(chord, this.transposeAmount)
: chord;
return `<span class="editor-chord">[${transposed}]</span>`;
});
const lines = escaped.split('\n');
let htmlLines: string[] = [];
let inChorus = false;
let inVerse = false;
let inVerseNum = false;
for (const line of lines) {
const trimmed = line.trim();
let currentInChorus = inChorus;
let currentInVerse = inVerse;
let currentInVerseNum = inVerseNum;
let isTagLine = false;
let processedContent = line;
if (trimmed === '{start_chorus}' || trimmed === '{soc}') {
inChorus = true;
currentInChorus = true;
isTagLine = true;
processedContent = `<span class="editor-tag">${line}</span>`;
} else if (trimmed === '{end_chorus}' || trimmed === '{eoc}') {
inChorus = false;
currentInChorus = true;
isTagLine = true;
processedContent = `<span class="editor-tag">${line}</span>`;
} else if (trimmed === '{start_verse}' || trimmed === '{sov}') {
inVerse = true;
currentInVerse = true;
isTagLine = true;
processedContent = `<span class="editor-tag">${line}</span>`;
} else if (trimmed === '{end_verse}' || trimmed === '{eov}') {
inVerse = false;
currentInVerse = true;
isTagLine = true;
processedContent = `<span class="editor-tag">${line}</span>`;
} else if (trimmed === '{start_verse_num}') {
inVerseNum = true;
currentInVerseNum = true;
isTagLine = true;
processedContent = `<span class="editor-tag">${line}</span>`;
} else if (trimmed === '{end_verse_num}') {
inVerseNum = false;
currentInVerseNum = true;
isTagLine = true;
processedContent = `<span class="editor-tag">${line}</span>`;
} else if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
isTagLine = true;
processedContent = `<span class="editor-tag">${line}</span>`;
}
let classes = ['editor-line'];
if (currentInChorus) classes.push('chorus');
if (currentInVerse) classes.push('verse');
if (currentInVerseNum) classes.push('verse-num');
if (isTagLine) classes.push('tag-line');
const finalContent = processedContent || '&#8203;';
htmlLines.push(`<div class="${classes.join(' ')}">${finalContent}</div>`);
}
return htmlLines.join('\n');
}
get parsedSections(): ParsedSection[] {
return this.lyricsParser.parseAccordi(this.content);
const sections = this.lyricsParser.parseAccordi(this.content);
if (this.showChordsPreview && this.transposeAmount !== 0) {
return this.lyricsParser.transposeSections(sections, this.transposeAmount);
}
return sections;
}
transposeUp() {
this.transposeAmount = (this.transposeAmount + 1) > 12 ? -11 : this.transposeAmount + 1;
}
transposeDown() {
this.transposeAmount = (this.transposeAmount - 1) < -12 ? 11 : this.transposeAmount - 1;
}
setDefaultTonalita() {
if (this.transposeAmount === 0 || !this.content) return;
const chordRegex = /\[([^\]]+)\]/g;
this.content = this.content.replace(chordRegex, (match, chord) => {
const transposed = this.lyricsParser.transposeChord(chord, this.transposeAmount);
return `[${transposed}]`;
});
this.transposeAmount = 0;
}
isAudioLoaded(): boolean {
return !!this.youtubeLink && this.cantiService.getYoutubeId(this.youtubeLink) !== null;
}
loadedYoutubeLink: string = '';
loadEditorAudio() {
if (!this.youtubeLink) return;
const videoId = this.cantiService.getYoutubeId(this.youtubeLink);
if (videoId) {
this.youtubePlayerService.initPlayer(this.editId || 'editing_song', 0, undefined, undefined, this.youtubeLink);
this.loadedYoutubeLink = this.youtubeLink;
}
}
togglePlayPause() {
const currentId = this.editId || 'editing_song';
const isLoaded = this.youtubePlayerService.currentCantoId() === currentId && this.loadedYoutubeLink === this.youtubeLink;
if (!isLoaded && this.youtubeLink) {
this.loadEditorAudio();
} else {
this.youtubePlayerService.togglePlayPause();
}
}
onSeek(event: any) {
const value = event.detail.value;
this.youtubePlayerService.seekTo(value);
}
formatSeconds(seconds: number): string {
if (isNaN(seconds)) return '0:00';
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
}
toggleChordsPreview() {
@@ -164,8 +308,12 @@ export class ProposeCantoPage implements OnInit {
});
}
ngOnDestroy() {
this.youtubePlayerService.stop();
}
async insertText(tag: string) {
const input = await this.contentTextarea.getInputElement();
const input = this.nativeTextarea.nativeElement;
const start = input.selectionStart || 0;
const end = input.selectionEnd || 0;