diff --git a/src/app/components/ui/markdown/markdown.component.html b/src/app/components/ui/markdown/markdown.component.html new file mode 100644 index 0000000..ab4bf55 --- /dev/null +++ b/src/app/components/ui/markdown/markdown.component.html @@ -0,0 +1 @@ + diff --git a/src/app/components/ui/markdown/markdown.component.scss b/src/app/components/ui/markdown/markdown.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/ui/markdown/markdown.component.spec.ts b/src/app/components/ui/markdown/markdown.component.spec.ts new file mode 100644 index 0000000..42df238 --- /dev/null +++ b/src/app/components/ui/markdown/markdown.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MarkdownComponent } from './markdown.component'; + +describe('MarkdownComponent', () => { + let component: MarkdownComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MarkdownComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(MarkdownComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/ui/markdown/markdown.component.ts b/src/app/components/ui/markdown/markdown.component.ts new file mode 100644 index 0000000..234474e --- /dev/null +++ b/src/app/components/ui/markdown/markdown.component.ts @@ -0,0 +1,33 @@ +import { Component, Input, inject } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Router } from '@angular/router'; +import { MarkedPipe } from '../../../pipes/marked.pipe'; + +@Component({ + selector: 'app-markdown', + standalone: true, + templateUrl: './markdown.component.html', + styleUrl: './markdown.component.scss', + imports: [CommonModule, MarkedPipe], +}) +export class MarkdownComponent { + private _router = inject(Router); + @Input({ required: true }) content!: string; + + // https://stackoverflow.com/questions/51764517/use-angular-router-inside-markdown-links + public onClick(e: MouseEvent): void { + const srcElem = e.target; + if (srcElem instanceof HTMLAnchorElement) { + const href = srcElem.href; + const isLocalLink = href.startsWith(srcElem.baseURI); + if (isLocalLink) { + e.preventDefault(); + e.stopPropagation(); + + const url = new URL(href); + const fragment = url.hash ? url.hash.substring(1) : undefined; + this._router.navigate([url.pathname], { fragment }); + } + } + } +} diff --git a/src/app/guards/terms-of-use.guard.ts b/src/app/guards/terms-of-use.guard.ts index 84e1ba9..f4957f3 100644 --- a/src/app/guards/terms-of-use.guard.ts +++ b/src/app/guards/terms-of-use.guard.ts @@ -2,12 +2,12 @@ import { inject } from '@angular/core'; import { CanActivateFn, Router } from '@angular/router'; import { UserDataService } from '../services/user-data.service'; -const pageId = '0-termsofuse'; +export const termsOfUseId = '0-termsofuse'; export const termsOfUseGuard: CanActivateFn = (route, state) => { - const data = inject(UserDataService).getEntry(pageId); + const data = inject(UserDataService).getEntry(termsOfUseId); if (!data['terms-accepted']) { - inject(Router).navigate(['/p', pageId], { + inject(Router).navigate(['/p', termsOfUseId], { queryParams: { from: state.url }, }); return false; diff --git a/src/app/pages/blog-post/blog-post.component.html b/src/app/pages/blog-post/blog-post.component.html index 8420169..4ebe982 100644 --- a/src/app/pages/blog-post/blog-post.component.html +++ b/src/app/pages/blog-post/blog-post.component.html @@ -4,7 +4,7 @@

{{doc.title}}

@if (item.type === 'iframe' && item.value) { } @else { -
+ } } diff --git a/src/app/pages/blog-post/blog-post.component.ts b/src/app/pages/blog-post/blog-post.component.ts index 4e47a3d..eaa4e7e 100644 --- a/src/app/pages/blog-post/blog-post.component.ts +++ b/src/app/pages/blog-post/blog-post.component.ts @@ -2,14 +2,14 @@ import { Component, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ActivatedRoute } from '@angular/router'; import { IFrameComponent } from '../../components/ui/iframe/iframe.component'; +import { MarkdownComponent } from '../../components/ui/markdown/markdown.component'; import { BlogService } from '../../services/blog.service'; import { BlogPost } from '../../models/blog.model'; -import { MarkedPipe } from '../../pipes/marked.pipe'; @Component({ selector: 'app-blog-post', standalone: true, - imports: [CommonModule, IFrameComponent, MarkedPipe], + imports: [CommonModule, IFrameComponent, MarkdownComponent], templateUrl: './blog-post.component.html', styles: ``, }) diff --git a/src/app/pages/page/page.component.html b/src/app/pages/page/page.component.html index 9d84fa6..48f2c8d 100644 --- a/src/app/pages/page/page.component.html +++ b/src/app/pages/page/page.component.html @@ -10,12 +10,12 @@ } @else if (item.type === 'text') { -
+ } @else if (item.type === 'expand') {
- +
} @else if (item.type === 'stepper') { @@ -56,7 +56,7 @@ } } @else { - + } \ No newline at end of file diff --git a/src/app/pages/page/page.component.ts b/src/app/pages/page/page.component.ts index fb35caf..dd3fa1b 100644 --- a/src/app/pages/page/page.component.ts +++ b/src/app/pages/page/page.component.ts @@ -12,12 +12,12 @@ import { HeroSectionComponent } from '../../components/hero-section/hero-section import { ImageSliderComponent } from '../../components/image-slider/image-slider.component'; import { InputSectionComponent } from '../../components/input-section/input-section.component'; import { ContinueEventArgs, InputStepperComponent } from '../../components/input-stepper/input-stepper.component'; +import { MarkdownComponent } from '../../components/ui/markdown/markdown.component'; import { currentGuideId } from '../../services/common.service'; import { UnitService } from '../../services/unit.service'; import { PageService } from '../../services/page.service'; import { UserDataService, pageReadTime } from '../../services/user-data.service'; -import { MarkedPipe } from '../../pipes/marked.pipe'; -import { Page } from '../../models/page.model'; +import { Page, PageContent } from '../../models/page.model'; import { InputValue } from '../../models/content.model'; import { expandTrigger } from '../../animations.helper'; @@ -37,7 +37,7 @@ import { expandTrigger } from '../../animations.helper'; ImageSliderComponent, InputSectionComponent, InputStepperComponent, - MarkedPipe + MarkdownComponent ], templateUrl: './page.component.html', styleUrl: './page.component.scss', @@ -84,7 +84,7 @@ export class PageComponent { private async getPages(unitIndex: string, pageIndex: number): Promise<[Page, boolean?, string?]> { if (isNaN(+unitIndex)) { const page = await this._pageService.getDocument(unitIndex); - return [page ?? {} as Page]; + return [page ?? { content: [] as PageContent[] } as Page]; } const pages = await this._unitService.getPages(+unitIndex); return [pages[pageIndex], pageIndex + 1 < pages.length, currentGuideId()]; diff --git a/src/app/pages/start/start.component.ts b/src/app/pages/start/start.component.ts index 8320eb9..bf6958b 100644 --- a/src/app/pages/start/start.component.ts +++ b/src/app/pages/start/start.component.ts @@ -6,8 +6,10 @@ import { MatDividerModule } from '@angular/material/divider'; import { MatIconModule } from '@angular/material/icon'; import { LoadingComponent } from '../../components/ui/loading/loading.component'; import { ProgressSpinnerComponent } from '../../components/ui/progress-spinner/progress-spinner.component'; +import { termsOfUseId } from '../../guards/terms-of-use.guard'; import { CommonService, currentGuideId } from '../../services/common.service'; import { UnitService } from '../../services/unit.service'; +import { UserDataService } from '../../services/user-data.service'; import { UserResultService } from '../../services/user-result.service'; import { Unit } from '../../models/unit.model'; import { Result } from '../../models/result.model'; @@ -31,6 +33,7 @@ export class StartComponent { private readonly _commonService = inject(CommonService); private readonly _unitService = inject(UnitService); private readonly _resultService = inject(UserResultService); + private readonly _dataService = inject(UserDataService); private _resources: Record = {}; private _units!: Unit[]; @@ -44,15 +47,19 @@ export class StartComponent { this._results = await this._resultService.resultTree(currentGuideId()); this._resources = await this._commonService.getResources('start'); - this.setUserName(this._resources['user-names'] as string[]); + this._userName = this.getUserName(this._resources['user-names'] as string[]); this.setGreeting(this._resources['greetings'] as Record); this.loading = false; } - private setUserName(userNames: string[]) { - const randomIndex = Math.floor(Math.random() * userNames.length); - this._userName = userNames[randomIndex]; + private getUserName(defaultNames: string[]): string { + const entry = this._dataService.getEntry(termsOfUseId); + if ('display-name' in entry && entry['display-name']) { + return entry['display-name']; + } + const randomIndex = Math.floor(Math.random() * defaultNames.length); + return defaultNames[randomIndex]; } private setGreeting(greetings: Record) {