diff --git a/src/app/app.css b/src/app/app.css index e126d2c..e69de29 100644 --- a/src/app/app.css +++ b/src/app/app.css @@ -1,12 +0,0 @@ -.private { - padding: 1rem; - container-type: inline-size; -} - -.public { - display: flex; - align-items: center; - justify-content: center; - min-height: 100vh; -} - diff --git a/src/app/app.html b/src/app/app.html index 884fd2b..f0f7a9a 100644 --- a/src/app/app.html +++ b/src/app/app.html @@ -1,11 +1 @@ -@if (authStatus()?.authenticated) { -
- - - -
-} @else { -
- -
-} + diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 70e3c70..40c2bca 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -8,18 +8,18 @@ export const routes: Routes = [ { path: '', redirectTo: 'auth', pathMatch: 'full' }, { path: 'auth', component: AuthPage, data: { title: 'Authentication' } }, { - path: 'home', + path: 'articles', loadComponent: async () => { - const c = await import('./pages/home/home-page.component') - return c.HomePage + const c = await import('./pages/articles-page/articles-page') + return c.ArticlesPage }, canMatch: [authGuard], }, { path: 'bookmarks', loadComponent: async () => { - const c = await import('./pages/bookmarks/bookmarks') - return c.Bookmarks + const c = await import('./pages/bookmarks-page/bookmarks-page') + return c.BookmarksPage }, canMatch: [authGuard], }, @@ -35,7 +35,7 @@ export const routes: Routes = [ { path: 'subscription/:subscriptionId/article/:articleId', loadComponent: async () => { - const c = await import('./pages/article-page/article-page.component') + const c = await import('./pages/article-page/article-page') return c.ArticlePage }, data: { title: 'Article' }, @@ -44,7 +44,7 @@ export const routes: Routes = [ { path: 'tags', loadComponent: async () => { - const c = await import('./pages/tags/tags-page.component') + const c = await import('./pages/tags-page/tags-page') return c.TagsPage }, data: { title: 'Tags' }, @@ -63,6 +63,7 @@ export const routes: Routes = [ path: 'status', component: StatusPage, data: { title: 'Status' }, + canMatch: [authGuard], }, { path: '**', diff --git a/src/app/app.ts b/src/app/app.ts index b209834..11e2d60 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -1,16 +1,21 @@ import { Component, inject } from '@angular/core' -import { RouterOutlet } from '@angular/router' -import { NavComponent } from './components/nav/nav.component' import { AuthService } from './services/auth-service' import { toSignal } from '@angular/core/rxjs-interop' +import { PrivateOutlet } from './outlet/private-outlet/private-outlet' +import { PublicOutlet } from './outlet/public-outlet/public-outlet' +import { NgComponentOutlet } from '@angular/common' @Component({ selector: 'app-root', - imports: [RouterOutlet, NavComponent], + imports: [NgComponentOutlet], templateUrl: './app.html', styleUrl: './app.css', }) export class App { private readonly authService = inject(AuthService) - protected readonly authStatus = toSignal(this.authService.$authStatus) + private readonly authStatus = toSignal(this.authService.$authStatus) + + getOutlet() { + return this.authStatus()?.authenticated ? PrivateOutlet : PublicOutlet + } } diff --git a/src/app/components/login-form/login-form.css b/src/app/components/login-form/login-form.css index ac5b999..46ba7b7 100644 --- a/src/app/components/login-form/login-form.css +++ b/src/app/components/login-form/login-form.css @@ -4,3 +4,7 @@ gap: 1rem; margin: 1rem 0; } + +.description { + max-width: 30ch; +} diff --git a/src/app/components/login-form/login-form.html b/src/app/components/login-form/login-form.html index 658e2ec..a58c371 100644 --- a/src/app/components/login-form/login-form.html +++ b/src/app/components/login-form/login-form.html @@ -1,5 +1,8 @@
+ + Login with your existing password and login. + Login { if (result) { - this.router.navigate(['/home']) + this.router.navigate(['/articles']) } }) } diff --git a/src/app/components/nav/nav.component.ts b/src/app/components/nav/nav.component.ts index 6098a80..58f2505 100644 --- a/src/app/components/nav/nav.component.ts +++ b/src/app/components/nav/nav.component.ts @@ -1,11 +1,4 @@ -import { - Component, - DestroyRef, - inject, - OnInit, - signal, - viewChild -} from '@angular/core' +import { Component, DestroyRef, inject, OnInit, signal, viewChild } from '@angular/core' import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout' import { AsyncPipe } from '@angular/common' import { MatToolbarModule } from '@angular/material/toolbar' @@ -55,7 +48,7 @@ export class NavComponent implements OnInit { private router = inject(Router) menuItems: { title: string; icon?: string; url: string }[] = [ - { title: 'Articles', url: '/home', icon: 'library_books' }, + { title: 'Articles', url: '/articles', icon: 'library_books' }, { title: 'Bookmarks', url: '/bookmarks', icon: 'bookmark' }, { title: 'Subscriptions', url: '/subscriptions', icon: 'rss_feed' }, { title: 'Tags', url: '/tags', icon: 'tag' }, diff --git a/src/app/components/page-display-toggle/page-display-toggle.html b/src/app/components/page-display-toggle/page-display-toggle.html index 55b2d97..9626151 100644 --- a/src/app/components/page-display-toggle/page-display-toggle.html +++ b/src/app/components/page-display-toggle/page-display-toggle.html @@ -1,12 +1,16 @@ - view_day - - + view_day + + diff --git a/src/app/components/page-display-toggle/page-display-toggle.ts b/src/app/components/page-display-toggle/page-display-toggle.ts index 329f223..4b8f4da 100644 --- a/src/app/components/page-display-toggle/page-display-toggle.ts +++ b/src/app/components/page-display-toggle/page-display-toggle.ts @@ -4,10 +4,11 @@ import { MatButtonToggle } from '@angular/material/button-toggle' import { MatIcon } from '@angular/material/icon' import { PageDisplay } from '../../entities/page/page.enums' import { AsyncPipe } from '@angular/common' +import { MatIconButton } from '@angular/material/button' @Component({ selector: 'app-page-display-toggle', - imports: [MatButtonToggle, MatIcon, MatButtonToggle, MatIcon, AsyncPipe], + imports: [MatButtonToggle, MatIcon, MatButtonToggle, MatIcon, AsyncPipe, MatIconButton], templateUrl: './page-display-toggle.html', styleUrl: './page-display-toggle.css', }) diff --git a/src/app/components/page-title/page-title.css b/src/app/components/page-title/page-title.css deleted file mode 100644 index 69a6e05..0000000 --- a/src/app/components/page-title/page-title.css +++ /dev/null @@ -1,10 +0,0 @@ -:host { - --background: var(--mat-sys-primary-container); - --border: 0; -} - -mat-toolbar { - background: var(--background); - border-radius: var(--border); - transition: border-radius 0.5s ease-in-out; -} diff --git a/src/app/components/page-title/page-title.html b/src/app/components/page-title/page-title.html deleted file mode 100644 index f657a5f..0000000 --- a/src/app/components/page-title/page-title.html +++ /dev/null @@ -1,3 +0,0 @@ - -

{{ title() }}

-
diff --git a/src/app/components/page-title/page-title.ts b/src/app/components/page-title/page-title.ts deleted file mode 100644 index 37a949d..0000000 --- a/src/app/components/page-title/page-title.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Component, computed, HostBinding, HostListener, input } from '@angular/core' -import { MatToolbar } from '@angular/material/toolbar' - -@Component({ - selector: 'app-page-title', - imports: [MatToolbar], - templateUrl: './page-title.html', - styleUrl: './page-title.css', -}) -export class PageTitle { - title = input() - randomColor = computed(() => { - return `#${Math.floor(Math.random() * 16777215) - .toString(16) - .padStart(6, '0')}` - }) - - @HostBinding('style.--background') - value: string = this.randomColor() - - @HostListener('mouseenter', ['$event']) - onMouseEnter(event: MouseEvent) { - const element = event.target as HTMLElement - if (element) { - element.style.setProperty('--border', '50cqh') - } - } - - @HostListener('mouseleave', ['$event']) - onMouseLeave(event: MouseEvent) { - const element = event.target as HTMLElement - if (element) { - element.style.setProperty('--border', '0') - } - } -} diff --git a/src/app/outlet/private-outlet/private-outlet.css b/src/app/outlet/private-outlet/private-outlet.css new file mode 100644 index 0000000..84a9cbc --- /dev/null +++ b/src/app/outlet/private-outlet/private-outlet.css @@ -0,0 +1,4 @@ +.private { + padding: 1rem; + container-type: inline-size; +} diff --git a/src/app/outlet/private-outlet/private-outlet.html b/src/app/outlet/private-outlet/private-outlet.html new file mode 100644 index 0000000..f649745 --- /dev/null +++ b/src/app/outlet/private-outlet/private-outlet.html @@ -0,0 +1,5 @@ +
+ + + +
diff --git a/src/app/outlet/private-outlet/private-outlet.ts b/src/app/outlet/private-outlet/private-outlet.ts new file mode 100644 index 0000000..c0b28f4 --- /dev/null +++ b/src/app/outlet/private-outlet/private-outlet.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core' +import { NavComponent } from '../../components/nav/nav.component' +import { RouterOutlet } from '@angular/router' + +@Component({ + selector: 'app-private-outlet', + imports: [NavComponent, RouterOutlet], + templateUrl: './private-outlet.html', + styleUrl: './private-outlet.css', +}) +export class PrivateOutlet {} diff --git a/src/app/outlet/public-outlet/public-outlet.css b/src/app/outlet/public-outlet/public-outlet.css new file mode 100644 index 0000000..971f48a --- /dev/null +++ b/src/app/outlet/public-outlet/public-outlet.css @@ -0,0 +1,6 @@ +.public { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; +} diff --git a/src/app/outlet/public-outlet/public-outlet.html b/src/app/outlet/public-outlet/public-outlet.html new file mode 100644 index 0000000..807e82b --- /dev/null +++ b/src/app/outlet/public-outlet/public-outlet.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/app/outlet/public-outlet/public-outlet.ts b/src/app/outlet/public-outlet/public-outlet.ts new file mode 100644 index 0000000..30dd0b4 --- /dev/null +++ b/src/app/outlet/public-outlet/public-outlet.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core' +import { RouterOutlet } from '@angular/router' + +@Component({ + selector: 'app-public', + imports: [RouterOutlet], + templateUrl: './public-outlet.html', + styleUrl: './public-outlet.css', +}) +export class PublicOutlet {} diff --git a/src/app/pages/article-page/article-page.component.css b/src/app/pages/article-page/article-page.css similarity index 100% rename from src/app/pages/article-page/article-page.component.css rename to src/app/pages/article-page/article-page.css diff --git a/src/app/pages/article-page/article-page.component.html b/src/app/pages/article-page/article-page.html similarity index 100% rename from src/app/pages/article-page/article-page.component.html rename to src/app/pages/article-page/article-page.html diff --git a/src/app/pages/article-page/article-page.component.ts b/src/app/pages/article-page/article-page.ts similarity index 97% rename from src/app/pages/article-page/article-page.component.ts rename to src/app/pages/article-page/article-page.ts index a973b50..6ac1d09 100644 --- a/src/app/pages/article-page/article-page.component.ts +++ b/src/app/pages/article-page/article-page.ts @@ -17,7 +17,7 @@ import { TitleService } from '../../services/title-service' import { DomSanitizer, SafeHtml } from '@angular/platform-browser' @Component({ - selector: 'app-article', + selector: 'app-article-page', imports: [ MatToolbarModule, MatProgressBarModule, @@ -28,8 +28,8 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser' MatIconButton, MatDivider, ], - templateUrl: './article-page.component.html', - styleUrl: './article-page.component.css', + templateUrl: './article-page.html', + styleUrl: './article-page.css', }) export class ArticlePage implements OnInit { feedService = inject(FeedService) diff --git a/src/app/pages/bookmarks/bookmarks.css b/src/app/pages/articles-page/articles-page.css similarity index 100% rename from src/app/pages/bookmarks/bookmarks.css rename to src/app/pages/articles-page/articles-page.css diff --git a/src/app/pages/home/home-page.component.html b/src/app/pages/articles-page/articles-page.html similarity index 79% rename from src/app/pages/home/home-page.component.html rename to src/app/pages/articles-page/articles-page.html index 14df542..c681b17 100644 --- a/src/app/pages/home/home-page.component.html +++ b/src/app/pages/articles-page/articles-page.html @@ -10,24 +10,34 @@ - schedule - - + + schedule + + + diff --git a/src/app/pages/home/home-page.component.ts b/src/app/pages/articles-page/articles-page.ts similarity index 83% rename from src/app/pages/home/home-page.component.ts rename to src/app/pages/articles-page/articles-page.ts index d3afcb8..084d0c9 100644 --- a/src/app/pages/home/home-page.component.ts +++ b/src/app/pages/articles-page/articles-page.ts @@ -3,7 +3,7 @@ import { MatCardModule } from '@angular/material/card' import { FeedService } from '../../services/feed-service' import { takeUntilDestroyed } from '@angular/core/rxjs-interop' import { HttpErrorResponse } from '@angular/common/http' -import { BehaviorSubject, catchError, combineLatest, of, switchMap } from 'rxjs' +import { BehaviorSubject, catchError, combineLatest, forkJoin, of, switchMap } from 'rxjs' import { MatButton, MatIconButton } from '@angular/material/button' import { MatToolbarModule } from '@angular/material/toolbar' import { MatButtonToggleModule } from '@angular/material/button-toggle' @@ -23,7 +23,7 @@ import { AsyncPipe } from '@angular/common' import { SortOrder } from '../../entities/base/base.enums' @Component({ - selector: 'app-home', + selector: 'app-articles-page', imports: [ MatCardModule, MatToolbarModule, @@ -38,10 +38,10 @@ import { SortOrder } from '../../entities/base/base.enums' PageDisplayToggle, AsyncPipe, ], - templateUrl: './home-page.component.html', - styleUrl: './home-page.component.css', + templateUrl: './articles-page.html', + styleUrl: './articles-page.css', }) -export class HomePage implements OnInit { +export class ArticlesPage implements OnInit { feedService = inject(FeedService) router = inject(Router) route = inject(ActivatedRoute) @@ -56,6 +56,7 @@ export class HomePage implements OnInit { $readFilter = new BehaviorSubject(true) $favFilter = new BehaviorSubject(false) $subscriptionFilter = new BehaviorSubject(null) + $tagFilter = new BehaviorSubject(null) $dateOrder = new BehaviorSubject(SortOrder.Desc) favTagId = signal('') @@ -96,9 +97,10 @@ export class HomePage implements OnInit { this.$favFilter, this.$readFilter, this.$subscriptionFilter, + this.$tagFilter, this.$dateOrder, ]).pipe( - switchMap(([perPage, pageNumber, fav, read, subscription, dateSort]) => { + switchMap(([perPage, pageNumber, fav, read, subscription, tag, dateSort]) => { const filters: Record = {} if (read) { @@ -113,6 +115,10 @@ export class HomePage implements OnInit { filters['subscription'] = subscription } + if (tag) { + filters['tags'] = tag + } + return this.feedService.getAllArticles({ pagination: { perPage, @@ -142,20 +148,38 @@ export class HomePage implements OnInit { takeUntilDestroyed(this.destroyRef), switchMap((params) => { const subscriptionId: string = params['subscription'] + const tagName: string = params['tag'] + const tag = this.userTags().find((t) => t.name === tagName) if (!subscriptionId) { - return of(null) + return forkJoin([of(null), this.tagService.getOne({ name: tagName })]) + } else { + return forkJoin([this.feedService.getOneSubscription({ subscriptionId }), of(null)]) } - return this.feedService.getOneSubscription({ subscriptionId }) }), catchError((e) => { console.error(e) return of(null) }), ) - .subscribe((feed) => { + .subscribe((results) => { + if (!results) { + return + } + + const [feed, tag] = results + if (feed) { this.titleService.setSubtitle(feed.title) this.$subscriptionFilter.next(feed._id) + this.$tagFilter.next(null) + } else if (tag) { + this.titleService.setSubtitle(tag.name) + this.$subscriptionFilter.next(null) + this.$tagFilter.next(tag._id) + } else { + this.titleService.setSubtitle(null) + this.$subscriptionFilter.next(null) + this.$tagFilter.next(null) } }) } diff --git a/src/app/pages/auth-page/auth-page.component.css b/src/app/pages/auth-page/auth-page.component.css index e718862..a37ef5f 100644 --- a/src/app/pages/auth-page/auth-page.component.css +++ b/src/app/pages/auth-page/auth-page.component.css @@ -4,4 +4,5 @@ justify-content: center; align-items: center; padding: 2rem; + gap: 1rem; } diff --git a/src/app/pages/auth-page/auth-page.component.html b/src/app/pages/auth-page/auth-page.component.html index a6330d6..bc4a51d 100644 --- a/src/app/pages/auth-page/auth-page.component.html +++ b/src/app/pages/auth-page/auth-page.component.html @@ -8,3 +8,11 @@ + + diff --git a/src/app/pages/auth-page/auth-page.component.ts b/src/app/pages/auth-page/auth-page.component.ts index 00403b1..d0394e8 100644 --- a/src/app/pages/auth-page/auth-page.component.ts +++ b/src/app/pages/auth-page/auth-page.component.ts @@ -1,15 +1,35 @@ -import { Component } from '@angular/core' +import { Component, inject } from '@angular/core' import { MatFormFieldModule } from '@angular/material/form-field' import { FormsModule } from '@angular/forms' import { LoginForm } from '../../components/login-form/login-form' import { SignupForm } from '../../components/signup-form/signup-form' import { MatTab, MatTabGroup } from '@angular/material/tabs' import { MatCard } from '@angular/material/card' +import { MatIconButton } from '@angular/material/button' +import { HealthStatus } from '../../components/health-status/health-status' +import { MatBottomSheet } from '@angular/material/bottom-sheet' +import { MatIconModule } from '@angular/material/icon' @Component({ selector: 'app-auth', - imports: [MatFormFieldModule, FormsModule, LoginForm, SignupForm, MatTabGroup, MatTab, MatCard], + imports: [ + MatFormFieldModule, + FormsModule, + LoginForm, + SignupForm, + MatTabGroup, + MatTab, + MatCard, + MatIconModule, + MatIconButton, + ], templateUrl: './auth-page.component.html', styleUrl: './auth-page.component.css', }) -export class AuthPage {} +export class AuthPage { + private bottomSheet = inject(MatBottomSheet) + + showAppHealth() { + this.bottomSheet.open(HealthStatus) + } +} diff --git a/src/app/pages/home/home-page.component.css b/src/app/pages/bookmarks-page/bookmarks-page.css similarity index 100% rename from src/app/pages/home/home-page.component.css rename to src/app/pages/bookmarks-page/bookmarks-page.css diff --git a/src/app/pages/bookmarks/bookmarks.html b/src/app/pages/bookmarks-page/bookmarks-page.html similarity index 100% rename from src/app/pages/bookmarks/bookmarks.html rename to src/app/pages/bookmarks-page/bookmarks-page.html diff --git a/src/app/pages/bookmarks/bookmarks.ts b/src/app/pages/bookmarks-page/bookmarks-page.ts similarity index 94% rename from src/app/pages/bookmarks/bookmarks.ts rename to src/app/pages/bookmarks-page/bookmarks-page.ts index 4db04b4..37a098a 100644 --- a/src/app/pages/bookmarks/bookmarks.ts +++ b/src/app/pages/bookmarks-page/bookmarks-page.ts @@ -14,12 +14,12 @@ import { PageService } from '../../services/page-service' import { PageDisplayToggle } from '../../components/page-display-toggle/page-display-toggle' @Component({ - selector: 'app-bookmarks', + selector: 'app-bookmarks-page', imports: [ArticleList, MatToolbarRow, Paginator, PageDisplayToggle], - templateUrl: './bookmarks.html', - styleUrl: './bookmarks.css', + templateUrl: './bookmarks-page.html', + styleUrl: './bookmarks-page.css', }) -export class Bookmarks implements OnInit { +export class BookmarksPage implements OnInit { feedService = inject(FeedService) destroyRef = inject(DestroyRef) tagService = inject(TagService) diff --git a/src/app/pages/subscriptions-page/subscriptions-page.html b/src/app/pages/subscriptions-page/subscriptions-page.html index da676ce..0707a86 100644 --- a/src/app/pages/subscriptions-page/subscriptions-page.html +++ b/src/app/pages/subscriptions-page/subscriptions-page.html @@ -25,7 +25,7 @@ @for (f of feeds(); track f._id) { diff --git a/src/app/pages/tags/tags-page.component.css b/src/app/pages/tags-page/tags-page.css similarity index 100% rename from src/app/pages/tags/tags-page.component.css rename to src/app/pages/tags-page/tags-page.css diff --git a/src/app/pages/tags/tags-page.component.html b/src/app/pages/tags-page/tags-page.html similarity index 78% rename from src/app/pages/tags/tags-page.component.html rename to src/app/pages/tags-page/tags-page.html index b5fd309..7c49acf 100644 --- a/src/app/pages/tags/tags-page.component.html +++ b/src/app/pages/tags-page/tags-page.html @@ -13,7 +13,11 @@ @defer { @for (t of tags(); track t._id) { - + {{ t.name }} @if (t.userId !== 'all') {