Skip to content

Commit

Permalink
add prerendered landing page
Browse files Browse the repository at this point in the history
  • Loading branch information
dominikks committed Feb 14, 2025
1 parent 51e0810 commit e8c8aed
Show file tree
Hide file tree
Showing 20 changed files with 414 additions and 279 deletions.
8 changes: 7 additions & 1 deletion frontend/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@
},
"scripts": [],
"baseHref": "/",
"browser": "src/main.ts"
"browser": "src/main.ts",
"server": "src/main.server.ts",
"prerender": {
"discoverRoutes": false,
"routesFile": "src/app/app.routes-prerender.txt"
},
"ssr": false
},
"configurations": {
"production": {
Expand Down
448 changes: 251 additions & 197 deletions frontend/package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
"@angular/material": "^19.1.3",
"@angular/platform-browser": "^19.1.5",
"@angular/platform-browser-dynamic": "^19.1.5",
"@angular/platform-server": "^19.1.5",
"@angular/router": "^19.1.5",
"@angular/ssr": "^19.1.7",
"@ng-web-apis/audio": "^4.11.1",
"fuse.js": "^7.1.0",
"lodash-es": "^4.17.21",
Expand Down
11 changes: 1 addition & 10 deletions frontend/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,10 +1 @@
<ng-container *appDataLoad="data$; callback: loadedData$">
@switch (apiService.user() != null) {
@case (true) {
<router-outlet></router-outlet>
}
@case (false) {
<app-login></app-login>
}
}
</ng-container>
<router-outlet></router-outlet>
7 changes: 0 additions & 7 deletions frontend/src/app/app.component.scss
Original file line number Diff line number Diff line change
@@ -1,7 +0,0 @@
:host {
min-height: 100vh;
display: flex;
flex-direction: column;
max-width: 100vw;
overflow-x: hidden;
}
28 changes: 3 additions & 25 deletions frontend/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,11 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { forkJoin, of, Subject } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { ApiService, AppInfo, User } from './services/api.service';
import { DataLoadDirective } from './common/data-load/data-load.directive';

import { LoginComponent } from './pages/login/login.component';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [DataLoadDirective, RouterOutlet, LoginComponent],
imports: [RouterOutlet],
})
export class AppComponent {
protected apiService = inject(ApiService);

readonly data$ = forkJoin([
this.apiService.loadAppInfo(),
this.apiService.loadUser().pipe(catchError(() => of(null))),
]);
readonly loadedData$ = new Subject<[AppInfo, User]>();

constructor() {
this.loadedData$.pipe(takeUntilDestroyed()).subscribe(data => {
this.apiService.appInfo.set(data[0]);
this.apiService.user.set(data[1]);
});
}
}
export class AppComponent {}
11 changes: 11 additions & 0 deletions frontend/src/app/app.config.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { appConfig } from './app.config';

const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(),
]
};

export const config = mergeApplicationConfig(appConfig, serverConfig);
4 changes: 3 additions & 1 deletion frontend/src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { PreloadAllModules, provideRouter, withComponentInputBinding, withPreloading } from '@angular/router';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
import { APP_ROUTES } from './app.routes';
import { authInterceptor } from './services/auth-interceptor';

export const APP_CONFIG: ApplicationConfig = {
export const appConfig: ApplicationConfig = {
providers: [
{
provide: MAT_SNACK_BAR_DEFAULT_OPTIONS,
Expand All @@ -23,5 +24,6 @@ export const APP_CONFIG: ApplicationConfig = {
provideRouter(APP_ROUTES, withComponentInputBinding(), withPreloading(PreloadAllModules)),
provideHttpClient(withInterceptors([authInterceptor])),
provideAnimationsAsync(),
provideClientHydration(withEventReplay()),
],
};
1 change: 1 addition & 0 deletions frontend/src/app/app.routes-prerender.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/
68 changes: 41 additions & 27 deletions frontend/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,69 @@ import { Routes } from '@angular/router';
import { guildPermissionGuard } from './guards/guild-permission.guard';
import { canDeactivateSoundManagerGuard } from './pages/settings/sound-manager/can-deactivate-sound-manager.guard';
import { canDeactivateGuildSettingsGuard } from './pages/settings/guild-settings/can-deactivate-guild-settings.guard';
import { LandingPageComponent } from './pages/landing-page/landing-page.component';
import { MainLayoutComponent } from './pages/main-layout/main-layout.component';

export const APP_ROUTES: Routes = [
{
path: '',
loadComponent: () => import('./pages/soundboard/soundboard.component').then(m => m.SoundboardComponent),
component: LandingPageComponent,
},
{
path: 'keybind-generator',
loadComponent: () =>
import('./pages/keybind-generator/keybind-generator.component').then(m => m.KeybindGeneratorComponent),
},
{
path: 'recorder',
loadComponent: () => import('./pages/recorder/recorder.component').then(m => m.RecorderComponent),
},
{
path: 'settings',
loadComponent: () => import('./pages/settings/settings.component').then(m => m.SettingsComponent),
path: '',
component: MainLayoutComponent,
children: [
{
path: '',
pathMatch: 'full',
redirectTo: 'user',
path: 'soundboard',
loadComponent: () => import('./pages/soundboard/soundboard.component').then(m => m.SoundboardComponent),
},
{
path: 'user',
path: 'keybind-generator',
loadComponent: () =>
import('./pages/settings/user-settings/user-settings.component').then(m => m.UserSettingsComponent),
import('./pages/keybind-generator/keybind-generator.component').then(m => m.KeybindGeneratorComponent),
},
{
path: 'recorder',
loadComponent: () => import('./pages/recorder/recorder.component').then(m => m.RecorderComponent),
},
{
path: 'guilds/:guildId',
canActivate: [guildPermissionGuard],
path: 'settings',
loadComponent: () => import('./pages/settings/settings.component').then(m => m.SettingsComponent),
children: [
{
path: '',
pathMatch: 'full',
redirectTo: 'settings',
redirectTo: 'user',
},
{
path: 'settings',
path: 'user',
loadComponent: () =>
import('./pages/settings/guild-settings/guild-settings.component').then(m => m.GuildSettingsComponent),
canDeactivate: [canDeactivateGuildSettingsGuard],
import('./pages/settings/user-settings/user-settings.component').then(m => m.UserSettingsComponent),
},
{
path: 'sounds',
loadComponent: () =>
import('./pages/settings/sound-manager/sound-manager.component').then(m => m.SoundManagerComponent),
canDeactivate: [canDeactivateSoundManagerGuard],
path: 'guilds/:guildId',
canActivate: [guildPermissionGuard],
children: [
{
path: '',
pathMatch: 'full',
redirectTo: 'settings',
},
{
path: 'settings',
loadComponent: () =>
import('./pages/settings/guild-settings/guild-settings.component').then(
m => m.GuildSettingsComponent,
),
canDeactivate: [canDeactivateGuildSettingsGuard],
},
{
path: 'sounds',
loadComponent: () =>
import('./pages/settings/sound-manager/sound-manager.component').then(m => m.SoundManagerComponent),
canDeactivate: [canDeactivateSoundManagerGuard],
},
],
},
],
},
Expand Down
32 changes: 24 additions & 8 deletions frontend/src/app/common/header/header.component.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<mat-toolbar color="primary">
<mat-toolbar-row>
@if (showSidenavToggle) {
<button mat-icon-button (click)="toggleSidenav.emit()"><mat-icon>menu</mat-icon></button>
<button mat-icon-button (click)="toggleSidenav.emit()">
<mat-icon>menu</mat-icon>
</button>
}
<a class="app-title" routerLink="/">📢 {{ pageTitle }}</a>
<a class="app-title" routerLink="/soundboard">📢 {{ pageTitle }}</a>
<div class="spacer"></div>

<div class="first-row-navigation">
Expand All @@ -22,8 +24,14 @@
</div>

<mat-menu #userMenu="matMenu">
<a mat-menu-item routerLink="/settings"><mat-icon>settings</mat-icon> Settings</a>
<a mat-menu-item routerLink="/keybind-generator"><mat-icon>keyboard</mat-icon> Keybinds</a>
<a mat-menu-item routerLink="/settings">
<mat-icon>settings</mat-icon>
Settings</a
>
<a mat-menu-item routerLink="/keybind-generator">
<mat-icon>keyboard</mat-icon>
Keybinds</a
>
<mat-divider></mat-divider>
<a
mat-menu-item
Expand All @@ -33,10 +41,15 @@
'&scope=bot&permissions=3147776'
"
target="_blank"
><mat-icon>add</mat-icon> Add bot to server</a
>
<mat-icon>add</mat-icon>
Add bot to server</a
>
<mat-divider></mat-divider>
<button mat-menu-item (click)="logout()"><mat-icon>exit_to_app</mat-icon> Logout</button>
<button mat-menu-item (click)="logout()">
<mat-icon>exit_to_app</mat-icon>
Logout
</button>
</mat-menu>
</mat-toolbar-row>
<mat-toolbar-row class="second-row">
Expand All @@ -45,9 +58,12 @@
</mat-toolbar>

<ng-template #toolbarButtons>
<a mat-button routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
<a mat-button routerLink="/soundboard" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
<mat-icon>speaker</mat-icon>
Soundboard
</a>
<a mat-button routerLink="/recorder" routerLinkActive="active"><mat-icon>voicemail</mat-icon> Recorder</a>
<a mat-button routerLink="/recorder" routerLinkActive="active">
<mat-icon>voicemail</mat-icon>
Recorder</a
>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<a mat-button routerLink="/soundboard">Soundboard</a>
Empty file.
11 changes: 11 additions & 0 deletions frontend/src/app/pages/landing-page/landing-page.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { MatAnchor } from '@angular/material/button';
import { RouterLink } from '@angular/router';

@Component({
imports: [MatAnchor, RouterLink],
templateUrl: './landing-page.component.html',
styleUrl: './landing-page.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LandingPageComponent {}
10 changes: 10 additions & 0 deletions frontend/src/app/pages/main-layout/main-layout.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<ng-container *appDataLoad="data$; callback: loadedData$">
@switch (apiService.user() != null) {
@case (true) {
<router-outlet></router-outlet>
}
@case (false) {
<app-login></app-login>
}
}
</ng-container>
7 changes: 7 additions & 0 deletions frontend/src/app/pages/main-layout/main-layout.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
:host {
min-height: 100vh;
display: flex;
flex-direction: column;
max-width: 100vw;
overflow-x: hidden;
}
31 changes: 31 additions & 0 deletions frontend/src/app/pages/main-layout/main-layout.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { forkJoin, of, Subject } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { RouterOutlet } from '@angular/router';
import { ApiService, AppInfo, User } from '../../services/api.service';
import { LoginComponent } from '../login/login.component';
import { DataLoadDirective } from '../../common/data-load/data-load.directive';

@Component({
imports: [RouterOutlet, LoginComponent, DataLoadDirective],
templateUrl: './main-layout.component.html',
styleUrl: './main-layout.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MainLayoutComponent {
protected apiService = inject(ApiService);

readonly data$ = forkJoin([
this.apiService.loadAppInfo(),
this.apiService.loadUser().pipe(catchError(() => of(null))),
]);
readonly loadedData$ = new Subject<[AppInfo, User]>();

constructor() {
this.loadedData$.pipe(takeUntilDestroyed()).subscribe(data => {
this.apiService.appInfo.set(data[0]);
this.apiService.user.set(data[1]);
});
}
}
7 changes: 7 additions & 0 deletions frontend/src/main.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { config } from './app/app.config.server';

const bootstrap = () => bootstrapApplication(AppComponent, config);

export default bootstrap;
4 changes: 2 additions & 2 deletions frontend/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { APP_CONFIG } from './app/app.config';
import { appConfig } from './app/app.config';

bootstrapApplication(AppComponent, APP_CONFIG).catch(err => console.error(err));
bootstrapApplication(AppComponent, appConfig).catch(err => console.error(err));
2 changes: 1 addition & 1 deletion frontend/tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
"outDir": "./out-tsc/app",
"types": []
},
"files": ["src/main.ts", "src/polyfills.ts"],
"files": ["src/main.ts", "src/polyfills.ts", "src/main.server.ts"],
"include": ["src/**/*.d.ts"]
}

0 comments on commit e8c8aed

Please sign in to comment.