Imrpove chat with gifs, videos, music player, redesigns and improved filesharing errors

This commit is contained in:
2026-03-06 04:47:07 +01:00
parent 2d84fbd91a
commit fe2347b54e
65 changed files with 3593 additions and 1030 deletions

View File

@@ -1,3 +1,4 @@
<!-- eslint-disable @angular-eslint/template/cyclomatic-complexity -->
@if (isOpen()) {
<!-- Backdrop -->
<div
@@ -15,7 +16,7 @@
<!-- Modal -->
<div class="fixed inset-0 z-[91] flex items-center justify-center p-4 pointer-events-none">
<div
class="pointer-events-auto bg-card border border-border rounded-xl shadow-2xl w-full max-w-4xl h-[min(680px,85vh)] flex overflow-hidden transition-all duration-200"
class="pointer-events-auto relative bg-card border border-border rounded-xl shadow-2xl w-full max-w-4xl h-[min(680px,85vh)] flex overflow-hidden transition-all duration-200"
[class.scale-100]="animating()"
[class.opacity-100]="animating()"
[class.scale-95]="!animating()"
@@ -97,6 +98,16 @@
</div>
}
</div>
<div class="mt-auto border-t border-border px-4 py-3">
<button
type="button"
(click)="openThirdPartyLicenses()"
class="text-left text-xs text-muted-foreground transition-colors hover:text-foreground hover:underline underline-offset-4"
>
Third-party licenses
</button>
</div>
</nav>
<!-- Content -->
@@ -174,6 +185,68 @@
}
</div>
</div>
@if (showThirdPartyLicenses()) {
<div
class="absolute inset-0 z-10 bg-background/70 backdrop-blur-sm"
(click)="closeThirdPartyLicenses()"
(keydown.enter)="closeThirdPartyLicenses()"
(keydown.space)="closeThirdPartyLicenses()"
role="button"
tabindex="0"
aria-label="Close third-party licenses"
></div>
<div class="pointer-events-none absolute inset-0 z-[11] flex items-center justify-center p-4 sm:p-6">
<div class="pointer-events-auto w-full max-w-2xl max-h-full overflow-hidden rounded-xl border border-border bg-card shadow-2xl">
<div class="flex items-start justify-between gap-4 border-b border-border px-5 py-4">
<div>
<h4 class="text-base font-semibold text-foreground">Third-party licenses</h4>
<p class="mt-1 text-sm text-muted-foreground">License notices for bundled third-party libraries used by the app.</p>
</div>
<button
type="button"
(click)="closeThirdPartyLicenses()"
class="p-2 hover:bg-secondary rounded-lg transition-colors text-muted-foreground hover:text-foreground"
aria-label="Close third-party licenses"
>
<ng-icon
name="lucideX"
class="w-5 h-5"
/>
</button>
</div>
<div class="max-h-[min(70vh,42rem)] overflow-y-auto px-5 py-4 space-y-4">
@for (license of thirdPartyLicenses; track license.id) {
<section class="rounded-lg border border-border bg-secondary/20 p-4">
<div class="flex items-start justify-between gap-4">
<div>
<h5 class="text-sm font-semibold text-foreground">{{ license.name }}</h5>
<p class="text-xs text-muted-foreground">{{ license.licenseName }}</p>
</div>
<a
[href]="license.sourceUrl"
target="_blank"
rel="noopener noreferrer"
class="text-xs font-medium text-primary hover:underline underline-offset-4"
>
Source
</a>
</div>
<pre
class="mt-4 whitespace-pre-wrap break-words rounded-md bg-background/80 px-3 py-2 text-[11px] leading-5 text-muted-foreground"
>{{ license.text }}</pre
>
</section>
}
</div>
</div>
</div>
}
</div>
</div>
}

View File

@@ -33,6 +33,7 @@ import { ServerSettingsComponent } from './server-settings/server-settings.compo
import { MembersSettingsComponent } from './members-settings/members-settings.component';
import { BansSettingsComponent } from './bans-settings/bans-settings.component';
import { PermissionsSettingsComponent } from './permissions-settings/permissions-settings.component';
import { THIRD_PARTY_LICENSES, type ThirdPartyLicense } from './third-party-licenses';
@Component({
selector: 'app-settings-modal',
@@ -64,6 +65,7 @@ import { PermissionsSettingsComponent } from './permissions-settings/permissions
export class SettingsModalComponent {
readonly modal = inject(SettingsModalService);
private store = inject(Store);
readonly thirdPartyLicenses: readonly ThirdPartyLicense[] = THIRD_PARTY_LICENSES;
private permissionsComponent = viewChild<PermissionsSettingsComponent>('permissionsComp');
@@ -80,7 +82,8 @@ export class SettingsModalComponent {
readonly globalPages: { id: SettingsPage; label: string; icon: string }[] = [
{ id: 'network',
label: 'Network',
icon: 'lucideGlobe' }, { id: 'voice',
icon: 'lucideGlobe' },
{ id: 'voice',
label: 'Voice & Audio',
icon: 'lucideAudioLines' }
];
@@ -128,6 +131,7 @@ export class SettingsModalComponent {
// Animation
animating = signal(false);
showThirdPartyLicenses = signal(false);
constructor() {
// Sync selected server when modal opens with a target
@@ -164,6 +168,11 @@ export class SettingsModalComponent {
}
@HostListener('document:keydown.escape')
onEscapeKey(): void {
if (this.showThirdPartyLicenses()) {
this.closeThirdPartyLicenses();
return;
}
if (this.isOpen()) {
this.close();
}
@@ -171,10 +180,19 @@ export class SettingsModalComponent {
// ===== MODAL CONTROLS =====
close(): void {
this.showThirdPartyLicenses.set(false);
this.animating.set(false);
setTimeout(() => this.modal.close(), 200);
}
openThirdPartyLicenses(): void {
this.showThirdPartyLicenses.set(true);
}
closeThirdPartyLicenses(): void {
this.showThirdPartyLicenses.set(false);
}
navigate(page: SettingsPage): void {
this.modal.navigate(page);
}

View File

@@ -0,0 +1,44 @@
export interface ThirdPartyLicense {
id: string;
name: string;
licenseName: string;
sourceUrl: string;
text: string;
}
export const THIRD_PARTY_LICENSES: ThirdPartyLicense[] = [
{
id: 'wavesurfer-js',
name: 'wavesurfer.js',
licenseName: 'BSD 3-Clause License',
sourceUrl: 'https://github.com/katspaugh/wavesurfer.js/blob/main/LICENSE',
text: [
'BSD 3-Clause License',
'',
'Copyright (c) 2012-2023, katspaugh and contributors',
'All rights reserved.',
'',
'Redistribution and use in source and binary forms, with or without modification, are permitted provided',
'that the following conditions are met:',
'',
'* Redistributions of source code must retain the above copyright notice, this list of conditions and',
' the following disclaimer.',
'',
'* Redistributions in binary form must reproduce the above copyright notice, this list of conditions',
' and the following disclaimer in the documentation and/or other materials provided with the',
' distribution.',
'',
'* Neither the name of the copyright holder nor the names of its contributors may be used to endorse',
' or promote products derived from this software without specific prior written permission.',
'',
'THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR',
'IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND',
'FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR',
'CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL',
'DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,',
'DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER',
'IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT',
'OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.'
].join('\n')
}
];